Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge trunk into markdown-tagrefs branch and resolve conflict. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | markdown-tagrefs |
| Files: | files | file ages | folders |
| SHA3-256: |
fdd3fe21c25a45cc5c02ea3116d91216 |
| User & Date: | stephan 2023-06-03 08:59:40.646 |
Context
|
2024-03-05
| ||
| 12:53 | Merge trunk into markdown-tagrefs branch. check-in: 0517bd2dd8 user: stephan tags: markdown-tagrefs | |
|
2023-06-03
| ||
| 08:59 | Merge trunk into markdown-tagrefs branch and resolve conflict. check-in: fdd3fe21c2 user: stephan tags: markdown-tagrefs | |
|
2023-06-01
| ||
| 18:02 | Admin users have a link in /forumthread to show the hash of all artifacts associated with that thread. check-in: 83928d8a02 user: drh tags: trunk | |
|
2023-01-21
| ||
| 12:14 | Merge trunk into markdown-tagrefs branch. check-in: baf038b1aa user: stephan tags: markdown-tagrefs | |
Changes
Changes to Dockerfile.
|
| | | > > > > > | | | | < < < < < < < < < < < < | < < < < | < < < | > | | | < | > | | | > | < | | < < < < < < < < > | | < | | | | | | | < > | | | | | < < < < < | > | | | | < | 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 |
# syntax=docker/dockerfile:1.3
# See www/containers.md for documentation on how to use this file.
## ---------------------------------------------------------------------
## STAGE 1: Build static Fossil binary
## ---------------------------------------------------------------------
### We aren't pinning to a more stable version of Alpine because we want
### to build with the latest tools and libraries available in case they
### fixed something that matters to us since the last build. Everything
### below depends on this layer, and so, alas, we toss this container's
### cache on Alpine's release schedule, roughly once a month.
FROM alpine:latest AS bld
WORKDIR /fsl
### Bake the basic Alpine Linux into a base layer so it only changes
### when the upstream image is updated or we change the package set.
RUN set -x \
&& apk update \
&& apk upgrade --no-cache \
&& apk add --no-cache \
gcc make \
linux-headers musl-dev \
openssl-dev openssl-libs-static \
zlib-dev zlib-static
### Build Fossil as a separate layer so we don't have to rebuild the
### Alpine environment for each iteration of Fossil's dev cycle.
###
### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
### automatically when you give it a local file name but not if you give
### it a /tarball URL! It matters because we default to a URL in case
### you're building outside a Fossil checkout, but when building via the
### container-image target, we avoid a costly hit on fossil-scm.org
### by leveraging its DVCS nature via the "tarball" command and passing
### the resulting file's name in.
ARG FSLCFG=""
ARG FSLVER="trunk"
ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
ENV FSLSTB=/fsl/src.tar.gz
ADD $FSLURL $FSLSTB
RUN set -x \
&& if [ -d $FSLSTB ] ; \
then mv $FSLSTB/src . ; \
else tar -xf src.tar.gz ; fi \
&& src/configure --static CFLAGS='-Os -s' $FSLCFG && make -j16
## ---------------------------------------------------------------------
## STAGE 2: Pare that back to the bare essentials.
## ---------------------------------------------------------------------
FROM busybox AS os
ARG UID=499
### Set up that base OS for our specific use without tying it to
### anything likely to change often. So long as the user leaves
### UID alone, this layer will be durable.
RUN set -x \
&& mkdir e log museum \
&& echo "root:x:0:0:Admin:/:/false" > /e/passwd \
&& echo "root:x:0:root" > /e/group \
&& echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /e/passwd \
&& echo "fossil:x:${UID}:fossil" >> /e/group
## ---------------------------------------------------------------------
## STAGE 3: Drop BusyBox, too, now that we're done with its /bin/sh &c
## ---------------------------------------------------------------------
FROM scratch AS run
COPY --from=bld --chmod=755 /fsl/fossil /bin/
COPY --from=os --chmod=600 /e/* /etc/
COPY --from=os --chmod=1777 /tmp /tmp/
COPY --from=os --chown=fossil:fossil /log /log/
COPY --from=os --chown=fossil:fossil /museum /museum/
## ---------------------------------------------------------------------
## RUN!
## ---------------------------------------------------------------------
ENV PATH "/bin"
EXPOSE 8080/tcp
USER fossil
ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ]
CMD [ \
"--create", \
"--jsmode", "bundled", \
"--user", "admin" ]
|
Changes to Makefile.in.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 126 | @AUTOREMAKE@ touch @builddir@/Makefile # Container stuff SRCTB := src-@FOSSIL_CI_PFX@.tar.gz IMGVER := fossil:@FOSSIL_CI_PFX@ CNTVER := fossil-@FOSSIL_CI_PFX@ container: | > | | | | | | | | | | | | 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 | @AUTOREMAKE@ touch @builddir@/Makefile # Container stuff SRCTB := src-@FOSSIL_CI_PFX@.tar.gz IMGVER := fossil:@FOSSIL_CI_PFX@ CNTVER := fossil-@FOSSIL_CI_PFX@ CENGINE := docker container: $(CENGINE) image inspect $(IMGVER) > /dev/null 2>&1 || \ $(MAKE) container-image $(CENGINE) container inspect $(CNTVER) > /dev/null 2>&1 || \ $(CENGINE) create \ --name $(CNTVER) \ --cap-drop AUDIT_WRITE \ --cap-drop CHOWN \ --cap-drop FSETID \ --cap-drop KILL \ --cap-drop MKNOD \ --cap-drop NET_BIND_SERVICE \ --cap-drop NET_RAW \ --cap-drop SETFCAP \ --cap-drop SETPCAP \ --publish 8080:8080 \ $(DCFLAGS) $(IMGVER) $(DCCMD) container-clean: -$(CENGINE) container kill $(CNTVER) -$(CENGINE) container rm $(CNTVER) -$(CENGINE) image rm $(IMGVER) container-image: $(APPNAME) tarball --name src @FOSSIL_CI_PFX@ $(SRCTB) $(CENGINE) buildx build \ --load \ --tag $(IMGVER) \ --build-arg FSLURL=$(SRCTB) \ $(DBFLAGS) @srcdir@ rm -f $(SRCTB) container-run container-start: container $(CENGINE) start $(DSFLAGS) $(CNTVER) @sleep 1 # decrease likelihood of logging race condition $(CENGINE) container logs $(CNTVER) container-stop: $(CENGINE) stop $(CNTVER) container-version: @echo $(CNTVER) |
Changes to VERSION.
|
| | | 1 | 2.23 |
Changes to auto.def.
| ︙ | ︙ | |||
771 772 773 774 775 776 777 | # of Fossil each one contains. This not only allows multiple images # to coexist and multiple containers to be created unamgiguosly from # them, it also changes the URL we fetch the source tarball from, so # repeated builds of a given version generate and fetch the source # tarball once only, keeping it in the local Docker/Podman cache. set ci [readfile "$::autosetup(srcdir)/manifest.uuid"] define FOSSIL_CI_PFX [string range $ci 0 11] | < | 771 772 773 774 775 776 777 778 779 780 |
# of Fossil each one contains. This not only allows multiple images
# to coexist and multiple containers to be created unamgiguosly from
# them, it also changes the URL we fetch the source tarball from, so
# repeated builds of a given version generate and fetch the source
# tarball once only, keeping it in the local Docker/Podman cache.
set ci [readfile "$::autosetup(srcdir)/manifest.uuid"]
define FOSSIL_CI_PFX [string range $ci 0 11]
make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
|
Deleted containers/Dockerfile-nojail.patch.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted containers/busybox-config.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted containers/os-release.in.
|
| < < < < < < |
Changes to extsrc/cson_amalgamation.c.
| ︙ | ︙ | |||
5387 5388 5389 5390 5391 5392 5393 |
cson_value * rootV = NULL;
cson_object * root = NULL;
cson_string * colName = NULL;
int i = 0;
int rc = 0;
cson_value * currentValue = NULL;
int const colCount = sqlite3_column_count(st);
| | | 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 |
cson_value * rootV = NULL;
cson_object * root = NULL;
cson_string * colName = NULL;
int i = 0;
int rc = 0;
cson_value * currentValue = NULL;
int const colCount = sqlite3_column_count(st);
if( !colCount || (colCount>(int)cson_array_length_get(colNames)) ) {
return NULL;
}
rootV = cson_value_new_object();
if(!rootV) return NULL;
root = cson_value_get_object(rootV);
for( i = 0; i < colCount; ++i )
{
|
| ︙ | ︙ |
Changes to extsrc/pikchr.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | ** ** lemon pikchr.y ** cc pikchr.c -o pikchr.o ** ** Add -DPIKCHR_SHELL to add a main() routine that reads input files ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for ** -fsanitizer=fuzzer testing. | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** ** lemon pikchr.y ** cc pikchr.c -o pikchr.o ** ** Add -DPIKCHR_SHELL to add a main() routine that reads input files ** and sends them through Pikchr, for testing. Add -DPIKCHR_FUZZ for ** -fsanitizer=fuzzer testing. ** **************************************************************************** ** IMPLEMENTATION NOTES (for people who want to understand the internal ** operation of this software, perhaps to extend the code or to fix bugs): ** ** Each call to pikchr() uses a single instance of the Pik structure to ** track its internal state. The Pik structure lives for the duration ** of the pikchr() call. |
| ︙ | ︙ | |||
201 202 203 204 205 206 207 | #define TP_VMASK 0x007c /* Mask for text positioning flags */ #define TP_BIG 0x0100 /* Larger font */ #define TP_SMALL 0x0200 /* Smaller font */ #define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */ #define TP_SZMASK 0x0700 /* Font size mask */ #define TP_ITALIC 0x1000 /* Italic font */ #define TP_BOLD 0x2000 /* Bold font */ | > | | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
#define TP_VMASK 0x007c /* Mask for text positioning flags */
#define TP_BIG 0x0100 /* Larger font */
#define TP_SMALL 0x0200 /* Smaller font */
#define TP_XTRA 0x0400 /* Amplify TP_BIG or TP_SMALL */
#define TP_SZMASK 0x0700 /* Font size mask */
#define TP_ITALIC 0x1000 /* Italic font */
#define TP_BOLD 0x2000 /* Bold font */
#define TP_MONO 0x4000 /* Monospace font family */
#define TP_FMASK 0x7000 /* Mask for font style */
#define TP_ALIGN 0x8000 /* Rotate to align with the line */
/* An object to hold a position in 2-D space */
struct PPoint {
PNum x, y; /* X and Y coordinates */
};
static const PPoint cZeroPoint = {0.0,0.0};
|
| ︙ | ︙ | |||
468 469 470 471 472 473 474 | static int pik_bbox_isempty(PBox*); static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); | | | | 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 | static int pik_bbox_isempty(PBox*); static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); static void pik_size_to_fit(Pik*,PToken*,int); static int pik_text_position(int,PToken*); static PNum pik_property_of(PObj*,PToken*); static PNum pik_func(Pik*,PToken*,PNum,PNum); static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt); static void pik_same(Pik *p, PObj*, PToken*); static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj); static PToken pik_next_semantic_token(PToken *pThis); static void pik_compute_layout_settings(Pik*); static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); #line 521 "pikchr.c" /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols. ***************** Begin token definitions *************************************/ #ifndef T_ID #define T_ID 1 #define T_EDGEPT 2 #define T_OF 3 |
| ︙ | ︙ | |||
562 563 564 565 566 567 568 | #define T_CENTER 64 #define T_LJUST 65 #define T_RJUST 66 #define T_ABOVE 67 #define T_BELOW 68 #define T_ITALIC 69 #define T_BOLD 70 | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | #define T_CENTER 64 #define T_LJUST 65 #define T_RJUST 66 #define T_ABOVE 67 #define T_BELOW 68 #define T_ITALIC 69 #define T_BOLD 70 #define T_MONO 71 #define T_ALIGNED 72 #define T_BIG 73 #define T_SMALL 74 #define T_AND 75 #define T_LT 76 #define T_GT 77 #define T_ON 78 #define T_WAY 79 #define T_BETWEEN 80 #define T_THE 81 #define T_NTH 82 #define T_VERTEX 83 #define T_TOP 84 #define T_BOTTOM 85 #define T_START 86 #define T_END 87 #define T_IN 88 #define T_THIS 89 #define T_DOT_U 90 #define T_LAST 91 #define T_NUMBER 92 #define T_FUNC1 93 #define T_FUNC2 94 #define T_DIST 95 #define T_DOT_XY 96 #define T_X 97 #define T_Y 98 #define T_DOT_L 99 #endif /**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. ** YYCODETYPE is the data type used to store the integer codes ** that represent terminal and non-terminal symbols. |
| ︙ | ︙ | |||
649 650 651 652 653 654 655 | ** YY_MAX_REDUCE Maximum value for reduce actions */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char | | > > | | < < | | | | 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 |
** YY_MAX_REDUCE Maximum value for reduce actions
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
#define YYNOCODE 136
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
int yyinit;
pik_parserTOKENTYPE yy0;
PNum yy21;
PPoint yy63;
PRel yy72;
PObj* yy162;
short int yy188;
PList* yy235;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM
#define pik_parserARG_FETCH
#define pik_parserARG_STORE
#define pik_parserCTX_SDECL Pik *p;
#define pik_parserCTX_PDECL ,Pik *p
#define pik_parserCTX_PARAM ,p
#define pik_parserCTX_FETCH Pik *p=yypParser->p;
#define pik_parserCTX_STORE yypParser->p=p;
#define YYFALLBACK 1
#define YYNSTATE 164
#define YYNRULE 156
#define YYNRULE_WITH_ACTION 116
#define YYNTOKEN 100
#define YY_MAX_SHIFT 163
#define YY_MIN_SHIFTREDUCE 287
#define YY_MAX_SHIFTREDUCE 442
#define YY_ERROR_ACTION 443
#define YY_ACCEPT_ACTION 444
#define YY_NO_ACTION 445
#define YY_MIN_REDUCE 446
|
| ︙ | ︙ | |||
754 755 756 757 758 759 760 | ** 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 **********************************************/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < > | | | | | | | | | < | | | | > > > | | | | | | < < < | | | | > | | | < | | > | | | | | | | | | | | | | | | | > > | | | | > > > | | | | | | | > | | | | | | | | | | | | < < | | | | < | < | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 |
** 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 (1313)
static const YYACTIONTYPE yy_action[] = {
/* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148,
/* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530,
/* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64,
/* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32,
/* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339,
/* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344,
/* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77,
/* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113,
/* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531,
/* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9,
/* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66,
/* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340,
/* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435,
/* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376,
/* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413,
/* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440,
/* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308,
/* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350,
/* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62,
/* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427,
/* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106,
/* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393,
/* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339,
/* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63,
/* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60,
/* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42,
/* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15,
/* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423,
/* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435,
/* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376,
/* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439,
/* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440,
/* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376,
/* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153,
/* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147,
/* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155,
/* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106,
/* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393,
/* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140,
/* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108,
/* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445,
/* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124,
/* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64,
/* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64,
/* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445,
/* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445,
/* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445,
/* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132,
/* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445,
/* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60,
/* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42,
/* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119,
/* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445,
/* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152,
/* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64,
/* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155,
/* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445,
/* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113,
/* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153,
/* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445,
/* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445,
/* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445,
/* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60,
/* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40,
/* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110,
/* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445,
/* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376,
/* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445,
/* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445,
/* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120,
/* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434,
/* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153,
/* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436,
/* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106,
/* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393,
/* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74,
/* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161,
/* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445,
/* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86,
/* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161,
/* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134,
/* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445,
/* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62,
/* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74,
/* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161,
/* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113,
/* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445,
/* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74,
/* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161,
/* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113,
/* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148,
/* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74,
/* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161,
/* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445,
/* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445,
/* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445,
/* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74,
/* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161,
/* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445,
/* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90,
/* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161,
/* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136,
/* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445,
/* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61,
/* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88,
/* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161,
/* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445,
/* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434,
/* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454,
/* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436,
/* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119,
/* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62,
/* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152,
/* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91,
/* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161,
/* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445,
/* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104,
/* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161,
/* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445,
/* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95,
/* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161,
/* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445,
/* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445,
/* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445,
/* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445,
/* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
/* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115,
/* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161,
/* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445,
/* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445,
/* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445,
/* 1310 */ 445, 445, 152,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106,
/* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106,
/* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4,
/* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39,
/* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49,
/* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
/* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131,
/* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112,
/* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113,
/* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35,
/* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45,
/* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55,
/* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2,
/* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12,
/* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31,
/* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32,
/* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26,
/* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65,
/* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6,
/* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20,
/* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82,
/* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92,
/* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50,
/* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5,
/* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5,
/* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15,
/* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35,
/* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41,
/* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2,
/* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12,
/* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87,
/* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32,
/* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12,
/* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95,
/* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31,
/* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93,
/* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82,
/* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92,
/* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3,
/* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82,
/* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136,
/* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111,
/* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4,
/* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4,
/* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136,
/* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136,
/* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136,
/* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46,
/* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136,
/* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5,
/* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15,
/* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115,
/* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136,
/* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135,
/* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4,
/* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93,
/* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136,
/* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112,
/* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95,
/* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136,
/* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136,
/* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136,
/* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5,
/* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15,
/* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121,
/* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136,
/* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12,
/* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136,
/* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136,
/* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113,
/* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64,
/* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95,
/* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84,
/* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82,
/* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92,
/* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104,
/* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114,
/* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136,
/* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104,
/* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114,
/* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111,
/* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136,
/* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6,
/* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104,
/* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114,
/* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112,
/* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136,
/* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104,
/* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114,
/* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112,
/* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106,
/* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104,
/* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114,
/* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136,
/* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136,
/* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136,
/* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104,
/* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114,
/* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136,
/* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104,
/* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114,
/* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111,
/* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136,
/* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7,
/* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104,
/* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114,
/* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136,
/* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64,
/* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102,
/* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84,
/* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115,
/* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6,
/* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135,
/* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104,
/* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114,
/* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136,
/* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104,
/* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114,
/* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136,
/* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104,
/* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114,
/* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136,
/* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136,
/* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136,
/* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136,
/* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
/* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104,
/* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114,
/* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136,
/* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136,
/* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136,
/* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100,
/* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
/* 1410 */ 100, 100, 100,
};
#define YY_SHIFT_COUNT (163)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (1113)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287,
/* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287,
/* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266,
/* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589,
/* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266,
/* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266,
/* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317,
/* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313,
/* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234,
/* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25,
/* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128,
/* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174,
/* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217,
/* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224,
/* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249,
/* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342,
/* 160 */ 363, 293, 374, 373,
};
#define YY_REDUCE_COUNT (82)
#define YY_REDUCE_MIN (-130)
#define YY_REDUCE_MAX (1177)
static const short yy_reduce_ofst[] = {
/* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670,
/* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815,
/* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957,
/* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994,
/* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035,
/* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130,
/* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38,
/* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41,
/* 80 */ 46, 52, -36,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443,
/* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
/* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576,
/* 30 */ 443, 449, 580, 485, 581, 581, 449, 443, 443, 443,
/* 40 */ 443, 443, 443, 443, 443, 443, 443, 443, 477, 443,
|
| ︙ | ︙ | |||
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 |
0, /* CENTER => nothing */
0, /* LJUST => nothing */
0, /* RJUST => nothing */
0, /* ABOVE => nothing */
0, /* BELOW => nothing */
0, /* ITALIC => nothing */
0, /* BOLD => nothing */
0, /* ALIGNED => nothing */
0, /* BIG => nothing */
0, /* SMALL => nothing */
0, /* AND => nothing */
0, /* LT => nothing */
0, /* GT => nothing */
0, /* ON => nothing */
| > | 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 |
0, /* CENTER => nothing */
0, /* LJUST => nothing */
0, /* RJUST => nothing */
0, /* ABOVE => nothing */
0, /* BELOW => nothing */
0, /* ITALIC => nothing */
0, /* BOLD => nothing */
0, /* MONO => nothing */
0, /* ALIGNED => nothing */
0, /* BIG => nothing */
0, /* SMALL => nothing */
0, /* AND => nothing */
0, /* LT => nothing */
0, /* GT => nothing */
0, /* ON => nothing */
|
| ︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 | /* 64 */ "CENTER", /* 65 */ "LJUST", /* 66 */ "RJUST", /* 67 */ "ABOVE", /* 68 */ "BELOW", /* 69 */ "ITALIC", /* 70 */ "BOLD", | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 |
/* 64 */ "CENTER",
/* 65 */ "LJUST",
/* 66 */ "RJUST",
/* 67 */ "ABOVE",
/* 68 */ "BELOW",
/* 69 */ "ITALIC",
/* 70 */ "BOLD",
/* 71 */ "MONO",
/* 72 */ "ALIGNED",
/* 73 */ "BIG",
/* 74 */ "SMALL",
/* 75 */ "AND",
/* 76 */ "LT",
/* 77 */ "GT",
/* 78 */ "ON",
/* 79 */ "WAY",
/* 80 */ "BETWEEN",
/* 81 */ "THE",
/* 82 */ "NTH",
/* 83 */ "VERTEX",
/* 84 */ "TOP",
/* 85 */ "BOTTOM",
/* 86 */ "START",
/* 87 */ "END",
/* 88 */ "IN",
/* 89 */ "THIS",
/* 90 */ "DOT_U",
/* 91 */ "LAST",
/* 92 */ "NUMBER",
/* 93 */ "FUNC1",
/* 94 */ "FUNC2",
/* 95 */ "DIST",
/* 96 */ "DOT_XY",
/* 97 */ "X",
/* 98 */ "Y",
/* 99 */ "DOT_L",
/* 100 */ "statement_list",
/* 101 */ "statement",
/* 102 */ "unnamed_statement",
/* 103 */ "basetype",
/* 104 */ "expr",
/* 105 */ "numproperty",
/* 106 */ "edge",
/* 107 */ "direction",
/* 108 */ "dashproperty",
/* 109 */ "colorproperty",
/* 110 */ "locproperty",
/* 111 */ "position",
/* 112 */ "place",
/* 113 */ "object",
/* 114 */ "objectname",
/* 115 */ "nth",
/* 116 */ "textposition",
/* 117 */ "rvalue",
/* 118 */ "lvalue",
/* 119 */ "even",
/* 120 */ "relexpr",
/* 121 */ "optrelexpr",
/* 122 */ "document",
/* 123 */ "print",
/* 124 */ "prlist",
/* 125 */ "pritem",
/* 126 */ "prsep",
/* 127 */ "attribute_list",
/* 128 */ "savelist",
/* 129 */ "alist",
/* 130 */ "attribute",
/* 131 */ "go",
/* 132 */ "boolproperty",
/* 133 */ "withclause",
/* 134 */ "between",
/* 135 */ "place2",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
|
| ︙ | ︙ | |||
1496 1497 1498 1499 1500 1501 1502 | /* 56 */ "boolproperty ::= RARROW", /* 57 */ "boolproperty ::= LRARROW", /* 58 */ "boolproperty ::= INVIS", /* 59 */ "boolproperty ::= THICK", /* 60 */ "boolproperty ::= THIN", /* 61 */ "boolproperty ::= SOLID", /* 62 */ "textposition ::=", | | | 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 | /* 56 */ "boolproperty ::= RARROW", /* 57 */ "boolproperty ::= LRARROW", /* 58 */ "boolproperty ::= INVIS", /* 59 */ "boolproperty ::= THICK", /* 60 */ "boolproperty ::= THIN", /* 61 */ "boolproperty ::= SOLID", /* 62 */ "textposition ::=", /* 63 */ "textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL", /* 64 */ "position ::= expr COMMA expr", /* 65 */ "position ::= place PLUS expr COMMA expr", /* 66 */ "position ::= place MINUS expr COMMA expr", /* 67 */ "position ::= place PLUS LP expr COMMA expr RP", /* 68 */ "position ::= place MINUS LP expr COMMA expr RP", /* 69 */ "position ::= LP position COMMA position RP", /* 70 */ "position ::= LP position RP", |
| ︙ | ︙ | |||
1714 1715 1716 1717 1718 1719 1720 |
** being destroyed before it is finished parsing.
**
** 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 ***************************************/
| | | | | | | | | | | | 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 |
** being destroyed before it is finished parsing.
**
** 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 100: /* statement_list */
{
#line 510 "pikchr.y"
pik_elist_free(p,(yypminor->yy235));
#line 1756 "pikchr.c"
}
break;
case 101: /* statement */
case 102: /* unnamed_statement */
case 103: /* basetype */
{
#line 512 "pikchr.y"
pik_elem_free(p,(yypminor->yy162));
#line 1765 "pikchr.c"
}
break;
/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
|
| ︙ | ︙ | |||
1945 1946 1947 1948 1949 1950 1951 |
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
| | | | 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 |
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 544 "pikchr.y"
pik_error(p, 0, "parser stack overflow");
#line 1986 "pikchr.c"
/******** End %stack_overflow code ********************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
pik_parserCTX_STORE
}
/*
** Print tracing information for a SHIFT action
|
| ︙ | ︙ | |||
2020 2021 2022 2023 2024 2025 2026 |
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 |
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
122, /* (0) document ::= statement_list */
100, /* (1) statement_list ::= statement */
100, /* (2) statement_list ::= statement_list EOL statement */
101, /* (3) statement ::= */
101, /* (4) statement ::= direction */
101, /* (5) statement ::= lvalue ASSIGN rvalue */
101, /* (6) statement ::= PLACENAME COLON unnamed_statement */
101, /* (7) statement ::= PLACENAME COLON position */
101, /* (8) statement ::= unnamed_statement */
101, /* (9) statement ::= print prlist */
101, /* (10) statement ::= ASSERT LP expr EQ expr RP */
101, /* (11) statement ::= ASSERT LP position EQ position RP */
101, /* (12) statement ::= DEFINE ID CODEBLOCK */
117, /* (13) rvalue ::= PLACENAME */
125, /* (14) pritem ::= FILL */
125, /* (15) pritem ::= COLOR */
125, /* (16) pritem ::= THICKNESS */
125, /* (17) pritem ::= rvalue */
125, /* (18) pritem ::= STRING */
126, /* (19) prsep ::= COMMA */
102, /* (20) unnamed_statement ::= basetype attribute_list */
103, /* (21) basetype ::= CLASSNAME */
103, /* (22) basetype ::= STRING textposition */
103, /* (23) basetype ::= LB savelist statement_list RB */
128, /* (24) savelist ::= */
120, /* (25) relexpr ::= expr */
120, /* (26) relexpr ::= expr PERCENT */
121, /* (27) optrelexpr ::= */
127, /* (28) attribute_list ::= relexpr alist */
130, /* (29) attribute ::= numproperty relexpr */
130, /* (30) attribute ::= dashproperty expr */
130, /* (31) attribute ::= dashproperty */
130, /* (32) attribute ::= colorproperty rvalue */
130, /* (33) attribute ::= go direction optrelexpr */
130, /* (34) attribute ::= go direction even position */
130, /* (35) attribute ::= CLOSE */
130, /* (36) attribute ::= CHOP */
130, /* (37) attribute ::= FROM position */
130, /* (38) attribute ::= TO position */
130, /* (39) attribute ::= THEN */
130, /* (40) attribute ::= THEN optrelexpr HEADING expr */
130, /* (41) attribute ::= THEN optrelexpr EDGEPT */
130, /* (42) attribute ::= GO optrelexpr HEADING expr */
130, /* (43) attribute ::= GO optrelexpr EDGEPT */
130, /* (44) attribute ::= AT position */
130, /* (45) attribute ::= SAME */
130, /* (46) attribute ::= SAME AS object */
130, /* (47) attribute ::= STRING textposition */
130, /* (48) attribute ::= FIT */
130, /* (49) attribute ::= BEHIND object */
133, /* (50) withclause ::= DOT_E edge AT position */
133, /* (51) withclause ::= edge AT position */
105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
132, /* (53) boolproperty ::= CW */
132, /* (54) boolproperty ::= CCW */
132, /* (55) boolproperty ::= LARROW */
132, /* (56) boolproperty ::= RARROW */
132, /* (57) boolproperty ::= LRARROW */
132, /* (58) boolproperty ::= INVIS */
132, /* (59) boolproperty ::= THICK */
132, /* (60) boolproperty ::= THIN */
132, /* (61) boolproperty ::= SOLID */
116, /* (62) textposition ::= */
116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
111, /* (64) position ::= expr COMMA expr */
111, /* (65) position ::= place PLUS expr COMMA expr */
111, /* (66) position ::= place MINUS expr COMMA expr */
111, /* (67) position ::= place PLUS LP expr COMMA expr RP */
111, /* (68) position ::= place MINUS LP expr COMMA expr RP */
111, /* (69) position ::= LP position COMMA position RP */
111, /* (70) position ::= LP position RP */
111, /* (71) position ::= expr between position AND position */
111, /* (72) position ::= expr LT position COMMA position GT */
111, /* (73) position ::= expr ABOVE position */
111, /* (74) position ::= expr BELOW position */
111, /* (75) position ::= expr LEFT OF position */
111, /* (76) position ::= expr RIGHT OF position */
111, /* (77) position ::= expr ON HEADING EDGEPT OF position */
111, /* (78) position ::= expr HEADING EDGEPT OF position */
111, /* (79) position ::= expr EDGEPT OF position */
111, /* (80) position ::= expr ON HEADING expr FROM position */
111, /* (81) position ::= expr HEADING expr FROM position */
112, /* (82) place ::= edge OF object */
135, /* (83) place2 ::= object */
135, /* (84) place2 ::= object DOT_E edge */
135, /* (85) place2 ::= NTH VERTEX OF object */
113, /* (86) object ::= nth */
113, /* (87) object ::= nth OF|IN object */
114, /* (88) objectname ::= THIS */
114, /* (89) objectname ::= PLACENAME */
114, /* (90) objectname ::= objectname DOT_U PLACENAME */
115, /* (91) nth ::= NTH CLASSNAME */
115, /* (92) nth ::= NTH LAST CLASSNAME */
115, /* (93) nth ::= LAST CLASSNAME */
115, /* (94) nth ::= LAST */
115, /* (95) nth ::= NTH LB RB */
115, /* (96) nth ::= NTH LAST LB RB */
115, /* (97) nth ::= LAST LB RB */
104, /* (98) expr ::= expr PLUS expr */
104, /* (99) expr ::= expr MINUS expr */
104, /* (100) expr ::= expr STAR expr */
104, /* (101) expr ::= expr SLASH expr */
104, /* (102) expr ::= MINUS expr */
104, /* (103) expr ::= PLUS expr */
104, /* (104) expr ::= LP expr RP */
104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
104, /* (106) expr ::= NUMBER */
104, /* (107) expr ::= ID */
104, /* (108) expr ::= FUNC1 LP expr RP */
104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
104, /* (110) expr ::= DIST LP position COMMA position RP */
104, /* (111) expr ::= place2 DOT_XY X */
104, /* (112) expr ::= place2 DOT_XY Y */
104, /* (113) expr ::= object DOT_L numproperty */
104, /* (114) expr ::= object DOT_L dashproperty */
104, /* (115) expr ::= object DOT_L colorproperty */
118, /* (116) lvalue ::= ID */
118, /* (117) lvalue ::= FILL */
118, /* (118) lvalue ::= COLOR */
118, /* (119) lvalue ::= THICKNESS */
117, /* (120) rvalue ::= expr */
123, /* (121) print ::= PRINT */
124, /* (122) prlist ::= pritem */
124, /* (123) prlist ::= prlist prsep pritem */
107, /* (124) direction ::= UP */
107, /* (125) direction ::= DOWN */
107, /* (126) direction ::= LEFT */
107, /* (127) direction ::= RIGHT */
121, /* (128) optrelexpr ::= relexpr */
127, /* (129) attribute_list ::= alist */
129, /* (130) alist ::= */
129, /* (131) alist ::= alist attribute */
130, /* (132) attribute ::= boolproperty */
130, /* (133) attribute ::= WITH withclause */
131, /* (134) go ::= GO */
131, /* (135) go ::= */
119, /* (136) even ::= UNTIL EVEN WITH */
119, /* (137) even ::= EVEN WITH */
108, /* (138) dashproperty ::= DOTTED */
108, /* (139) dashproperty ::= DASHED */
109, /* (140) colorproperty ::= FILL */
109, /* (141) colorproperty ::= COLOR */
111, /* (142) position ::= place */
134, /* (143) between ::= WAY BETWEEN */
134, /* (144) between ::= BETWEEN */
134, /* (145) between ::= OF THE WAY BETWEEN */
112, /* (146) place ::= place2 */
106, /* (147) edge ::= CENTER */
106, /* (148) edge ::= EDGEPT */
106, /* (149) edge ::= TOP */
106, /* (150) edge ::= BOTTOM */
106, /* (151) edge ::= START */
106, /* (152) edge ::= END */
106, /* (153) edge ::= RIGHT */
106, /* (154) edge ::= LEFT */
113, /* (155) object ::= objectname */
};
/* 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) document ::= statement_list */
-1, /* (1) statement_list ::= statement */
|
| ︙ | ︙ | |||
2244 2245 2246 2247 2248 2249 2250 |
-1, /* (56) boolproperty ::= RARROW */
-1, /* (57) boolproperty ::= LRARROW */
-1, /* (58) boolproperty ::= INVIS */
-1, /* (59) boolproperty ::= THICK */
-1, /* (60) boolproperty ::= THIN */
-1, /* (61) boolproperty ::= SOLID */
0, /* (62) textposition ::= */
| | | 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 |
-1, /* (56) boolproperty ::= RARROW */
-1, /* (57) boolproperty ::= LRARROW */
-1, /* (58) boolproperty ::= INVIS */
-1, /* (59) boolproperty ::= THICK */
-1, /* (60) boolproperty ::= THIN */
-1, /* (61) boolproperty ::= SOLID */
0, /* (62) textposition ::= */
-2, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
-3, /* (64) position ::= expr COMMA expr */
-5, /* (65) position ::= place PLUS expr COMMA expr */
-5, /* (66) position ::= place MINUS expr COMMA expr */
-7, /* (67) position ::= place PLUS LP expr COMMA expr RP */
-7, /* (68) position ::= place MINUS LP expr COMMA expr RP */
-5, /* (69) position ::= LP position COMMA position RP */
-3, /* (70) position ::= LP position RP */
|
| ︙ | ︙ | |||
2379 2380 2381 2382 2383 2384 2385 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 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 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
#line 548 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy235);}
#line 2419 "pikchr.c"
break;
case 1: /* statement_list ::= statement */
#line 551 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); }
#line 2424 "pikchr.c"
yymsp[0].minor.yy235 = yylhsminor.yy235;
break;
case 2: /* statement_list ::= statement_list EOL statement */
#line 553 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); }
#line 2430 "pikchr.c"
yymsp[-2].minor.yy235 = yylhsminor.yy235;
break;
case 3: /* statement ::= */
#line 556 "pikchr.y"
{ yymsp[1].minor.yy162 = 0; }
#line 2436 "pikchr.c"
break;
case 4: /* statement ::= direction */
#line 557 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; }
#line 2441 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 5: /* statement ::= lvalue ASSIGN rvalue */
#line 558 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;}
#line 2447 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 6: /* statement ::= PLACENAME COLON unnamed_statement */
#line 560 "pikchr.y"
{ yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); }
#line 2453 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 7: /* statement ::= PLACENAME COLON position */
#line 562 "pikchr.y"
{ yylhsminor.yy162 = pik_elem_new(p,0,0,0);
if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }}
#line 2460 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 8: /* statement ::= unnamed_statement */
#line 564 "pikchr.y"
{yylhsminor.yy162 = yymsp[0].minor.yy162;}
#line 2466 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 9: /* statement ::= print prlist */
#line 565 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;}
#line 2472 "pikchr.c"
break;
case 10: /* statement ::= ASSERT LP expr EQ expr RP */
#line 570 "pikchr.y"
{yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);}
#line 2477 "pikchr.c"
break;
case 11: /* statement ::= ASSERT LP position EQ position RP */
#line 572 "pikchr.y"
{yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);}
#line 2482 "pikchr.c"
break;
case 12: /* statement ::= DEFINE ID CODEBLOCK */
#line 573 "pikchr.y"
{yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
#line 2487 "pikchr.c"
break;
case 13: /* rvalue ::= PLACENAME */
#line 584 "pikchr.y"
{yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2492 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 14: /* pritem ::= FILL */
case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
#line 589 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2500 "pikchr.c"
break;
case 17: /* pritem ::= rvalue */
#line 592 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy21);}
#line 2505 "pikchr.c"
break;
case 18: /* pritem ::= STRING */
#line 593 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2510 "pikchr.c"
break;
case 19: /* prsep ::= COMMA */
#line 594 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2515 "pikchr.c"
break;
case 20: /* unnamed_statement ::= basetype attribute_list */
#line 597 "pikchr.y"
{yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);}
#line 2520 "pikchr.c"
yymsp[-1].minor.yy162 = yylhsminor.yy162;
break;
case 21: /* basetype ::= CLASSNAME */
#line 599 "pikchr.y"
{yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2526 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 22: /* basetype ::= STRING textposition */
#line 601 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2532 "pikchr.c"
yymsp[-1].minor.yy162 = yylhsminor.yy162;
break;
case 23: /* basetype ::= LB savelist statement_list RB */
#line 603 "pikchr.y"
{ p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; }
#line 2538 "pikchr.c"
break;
case 24: /* savelist ::= */
#line 608 "pikchr.y"
{yymsp[1].minor.yy235 = p->list; p->list = 0;}
#line 2543 "pikchr.c"
break;
case 25: /* relexpr ::= expr */
#line 615 "pikchr.y"
{yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;}
#line 2548 "pikchr.c"
yymsp[0].minor.yy72 = yylhsminor.yy72;
break;
case 26: /* relexpr ::= expr PERCENT */
#line 616 "pikchr.y"
{yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;}
#line 2554 "pikchr.c"
yymsp[-1].minor.yy72 = yylhsminor.yy72;
break;
case 27: /* optrelexpr ::= */
#line 618 "pikchr.y"
{yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;}
#line 2560 "pikchr.c"
break;
case 28: /* attribute_list ::= relexpr alist */
#line 620 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy72);}
#line 2565 "pikchr.c"
break;
case 29: /* attribute ::= numproperty relexpr */
#line 624 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); }
#line 2570 "pikchr.c"
break;
case 30: /* attribute ::= dashproperty expr */
#line 625 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); }
#line 2575 "pikchr.c"
break;
case 31: /* attribute ::= dashproperty */
#line 626 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
#line 2580 "pikchr.c"
break;
case 32: /* attribute ::= colorproperty rvalue */
#line 627 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); }
#line 2585 "pikchr.c"
break;
case 33: /* attribute ::= go direction optrelexpr */
#line 628 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);}
#line 2590 "pikchr.c"
break;
case 34: /* attribute ::= go direction even position */
#line 629 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);}
#line 2595 "pikchr.c"
break;
case 35: /* attribute ::= CLOSE */
#line 630 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2600 "pikchr.c"
break;
case 36: /* attribute ::= CHOP */
#line 631 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2605 "pikchr.c"
break;
case 37: /* attribute ::= FROM position */
#line 632 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2610 "pikchr.c"
break;
case 38: /* attribute ::= TO position */
#line 633 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2615 "pikchr.c"
break;
case 39: /* attribute ::= THEN */
#line 634 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2620 "pikchr.c"
break;
case 40: /* attribute ::= THEN optrelexpr HEADING expr */
case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
#line 636 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);}
#line 2626 "pikchr.c"
break;
case 41: /* attribute ::= THEN optrelexpr EDGEPT */
case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
#line 637 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2632 "pikchr.c"
break;
case 44: /* attribute ::= AT position */
#line 642 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2637 "pikchr.c"
break;
case 45: /* attribute ::= SAME */
#line 644 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2642 "pikchr.c"
break;
case 46: /* attribute ::= SAME AS object */
#line 645 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2647 "pikchr.c"
break;
case 47: /* attribute ::= STRING textposition */
#line 646 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);}
#line 2652 "pikchr.c"
break;
case 48: /* attribute ::= FIT */
#line 647 "pikchr.y"
{pik_size_to_fit(p,&yymsp[0].minor.yy0,3); }
#line 2657 "pikchr.c"
break;
case 49: /* attribute ::= BEHIND object */
#line 648 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy162);}
#line 2662 "pikchr.c"
break;
case 50: /* withclause ::= DOT_E edge AT position */
case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
#line 656 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2668 "pikchr.c"
break;
case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 660 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2673 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 53: /* boolproperty ::= CW */
#line 671 "pikchr.y"
{p->cur->cw = 1;}
#line 2679 "pikchr.c"
break;
case 54: /* boolproperty ::= CCW */
#line 672 "pikchr.y"
{p->cur->cw = 0;}
#line 2684 "pikchr.c"
break;
case 55: /* boolproperty ::= LARROW */
#line 673 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2689 "pikchr.c"
break;
case 56: /* boolproperty ::= RARROW */
#line 674 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2694 "pikchr.c"
break;
case 57: /* boolproperty ::= LRARROW */
#line 675 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2699 "pikchr.c"
break;
case 58: /* boolproperty ::= INVIS */
#line 676 "pikchr.y"
{p->cur->sw = 0.0;}
#line 2704 "pikchr.c"
break;
case 59: /* boolproperty ::= THICK */
#line 677 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2709 "pikchr.c"
break;
case 60: /* boolproperty ::= THIN */
#line 678 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2714 "pikchr.c"
break;
case 61: /* boolproperty ::= SOLID */
#line 679 "pikchr.y"
{p->cur->sw = pik_value(p,"thickness",9,0);
p->cur->dotted = p->cur->dashed = 0.0;}
#line 2720 "pikchr.c"
break;
case 62: /* textposition ::= */
#line 682 "pikchr.y"
{yymsp[1].minor.yy188 = 0;}
#line 2725 "pikchr.c"
break;
case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
#line 685 "pikchr.y"
{yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);}
#line 2730 "pikchr.c"
yymsp[-1].minor.yy188 = yylhsminor.yy188;
break;
case 64: /* position ::= expr COMMA expr */
#line 688 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;}
#line 2736 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 65: /* position ::= place PLUS expr COMMA expr */
#line 690 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;}
#line 2742 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 66: /* position ::= place MINUS expr COMMA expr */
#line 691 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;}
#line 2748 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 67: /* position ::= place PLUS LP expr COMMA expr RP */
#line 693 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;}
#line 2754 "pikchr.c"
yymsp[-6].minor.yy63 = yylhsminor.yy63;
break;
case 68: /* position ::= place MINUS LP expr COMMA expr RP */
#line 695 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;}
#line 2760 "pikchr.c"
yymsp[-6].minor.yy63 = yylhsminor.yy63;
break;
case 69: /* position ::= LP position COMMA position RP */
#line 696 "pikchr.y"
{yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;}
#line 2766 "pikchr.c"
break;
case 70: /* position ::= LP position RP */
#line 697 "pikchr.y"
{yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;}
#line 2771 "pikchr.c"
break;
case 71: /* position ::= expr between position AND position */
#line 699 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);}
#line 2776 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 72: /* position ::= expr LT position COMMA position GT */
#line 701 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);}
#line 2782 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 73: /* position ::= expr ABOVE position */
#line 702 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;}
#line 2788 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 74: /* position ::= expr BELOW position */
#line 703 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;}
#line 2794 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 75: /* position ::= expr LEFT OF position */
#line 704 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;}
#line 2800 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 76: /* position ::= expr RIGHT OF position */
#line 705 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;}
#line 2806 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 77: /* position ::= expr ON HEADING EDGEPT OF position */
#line 707 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2812 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 78: /* position ::= expr HEADING EDGEPT OF position */
#line 709 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2818 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 79: /* position ::= expr EDGEPT OF position */
#line 711 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2824 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 80: /* position ::= expr ON HEADING expr FROM position */
#line 713 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2830 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 81: /* position ::= expr HEADING expr FROM position */
#line 715 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2836 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 82: /* place ::= edge OF object */
#line 727 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2842 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 83: /* place2 ::= object */
#line 728 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);}
#line 2848 "pikchr.c"
yymsp[0].minor.yy63 = yylhsminor.yy63;
break;
case 84: /* place2 ::= object DOT_E edge */
#line 729 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2854 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 85: /* place2 ::= NTH VERTEX OF object */
#line 730 "pikchr.y"
{yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);}
#line 2860 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 86: /* object ::= nth */
#line 742 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2866 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 87: /* object ::= nth OF|IN object */
#line 743 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2872 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 88: /* objectname ::= THIS */
#line 745 "pikchr.y"
{yymsp[0].minor.yy162 = p->cur;}
#line 2878 "pikchr.c"
break;
case 89: /* objectname ::= PLACENAME */
#line 746 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2883 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 90: /* objectname ::= objectname DOT_U PLACENAME */
#line 748 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2889 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 91: /* nth ::= NTH CLASSNAME */
#line 750 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2895 "pikchr.c"
yymsp[-1].minor.yy0 = yylhsminor.yy0;
break;
case 92: /* nth ::= NTH LAST CLASSNAME */
#line 751 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2901 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 93: /* nth ::= LAST CLASSNAME */
#line 752 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2907 "pikchr.c"
break;
case 94: /* nth ::= LAST */
#line 753 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2912 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 95: /* nth ::= NTH LB RB */
#line 754 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2918 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 96: /* nth ::= NTH LAST LB RB */
#line 755 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2924 "pikchr.c"
yymsp[-3].minor.yy0 = yylhsminor.yy0;
break;
case 97: /* nth ::= LAST LB RB */
#line 756 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2930 "pikchr.c"
break;
case 98: /* expr ::= expr PLUS expr */
#line 758 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;}
#line 2935 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 99: /* expr ::= expr MINUS expr */
#line 759 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;}
#line 2941 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 100: /* expr ::= expr STAR expr */
#line 760 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;}
#line 2947 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 101: /* expr ::= expr SLASH expr */
#line 761 "pikchr.y"
{
if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; }
else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; }
}
#line 2956 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 102: /* expr ::= MINUS expr */
#line 765 "pikchr.y"
{yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;}
#line 2962 "pikchr.c"
break;
case 103: /* expr ::= PLUS expr */
#line 766 "pikchr.y"
{yymsp[-1].minor.yy21=yymsp[0].minor.yy21;}
#line 2967 "pikchr.c"
break;
case 104: /* expr ::= LP expr RP */
#line 767 "pikchr.y"
{yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;}
#line 2972 "pikchr.c"
break;
case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
#line 768 "pikchr.y"
{yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);}
#line 2977 "pikchr.c"
break;
case 106: /* expr ::= NUMBER */
#line 769 "pikchr.y"
{yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);}
#line 2982 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 107: /* expr ::= ID */
#line 770 "pikchr.y"
{yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 2988 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 108: /* expr ::= FUNC1 LP expr RP */
#line 771 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);}
#line 2994 "pikchr.c"
yymsp[-3].minor.yy21 = yylhsminor.yy21;
break;
case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 772 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);}
#line 3000 "pikchr.c"
yymsp[-5].minor.yy21 = yylhsminor.yy21;
break;
case 110: /* expr ::= DIST LP position COMMA position RP */
#line 773 "pikchr.y"
{yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);}
#line 3006 "pikchr.c"
break;
case 111: /* expr ::= place2 DOT_XY X */
#line 774 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.x;}
#line 3011 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 112: /* expr ::= place2 DOT_XY Y */
#line 775 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.y;}
#line 3017 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 113: /* expr ::= object DOT_L numproperty */
case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
#line 776 "pikchr.y"
{yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 3025 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
default:
/* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
/* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
/* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
/* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
/* (120) rvalue ::= expr */ yytestcase(yyruleno==120);
|
| ︙ | ︙ | |||
3090 3091 3092 3093 3094 3095 3096 |
int yymajor, /* The major type of the error token */
pik_parserTOKENTYPE yyminor /* The minor type of the error token */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
| | | | 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 |
int yymajor, /* The major type of the error token */
pik_parserTOKENTYPE yyminor /* The minor type of the error token */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 536 "pikchr.y"
if( TOKEN.z && TOKEN.z[0] ){
pik_error(p, &TOKEN, "syntax error");
}else{
pik_error(p, 0, "syntax error");
}
UNUSED_PARAMETER(yymajor);
#line 3136 "pikchr.c"
/************ End %syntax_error code ******************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
pik_parserCTX_STORE
}
/*
** The following is executed when the parser accepts
|
| ︙ | ︙ | |||
3374 3375 3376 3377 3378 3379 3380 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } | | | 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 781 "pikchr.y" /* Chart of the 148 official CSS color names with their ** corresponding RGB values thru Color Module Level 4: ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value ** |
| ︙ | ︙ | |||
4976 4977 4978 4979 4980 4981 4982 |
if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
if( t->eCode & TP_LJUST ) nx -= jw;
if( t->eCode & TP_RJUST ) nx += jw;
if( pBox!=0 ){
/* If pBox is not NULL, do not draw any <text>. Instead, just expand
** pBox to include the text */
| | > | > | 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 |
if( t->eCode & TP_BELOW2 ) y -= 0.5*hc + hb1 + 0.5*hb2;
if( t->eCode & TP_LJUST ) nx -= jw;
if( t->eCode & TP_RJUST ) nx += jw;
if( pBox!=0 ){
/* If pBox is not NULL, do not draw any <text>. Instead, just expand
** pBox to include the text */
PNum cw = pik_text_length(t, t->eCode & TP_MONO)*p->charWidth*xtraFontScale*0.01;
PNum ch = p->charHeight*0.5*xtraFontScale;
PNum x0, y0, x1, y1; /* Boundary of text relative to pObj->ptAt */
if( (t->eCode & (TP_BOLD|TP_MONO))==TP_BOLD ){
cw *= 1.1;
}
if( t->eCode & TP_RJUST ){
x0 = nx;
y0 = y-ch;
x1 = nx-cw;
y1 = y+ch;
}else if( t->eCode & TP_LJUST ){
x0 = nx;
|
| ︙ | ︙ | |||
5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 |
}
if( t->eCode & TP_ITALIC ){
pik_append(p, " font-style=\"italic\"", -1);
}
if( t->eCode & TP_BOLD ){
pik_append(p, " font-weight=\"bold\"", -1);
}
if( pObj->color>=0.0 ){
pik_append_clr(p, " fill=\"", pObj->color, "\"",0);
}
xtraFontScale *= p->fontScale;
if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
pik_append_num(p, " font-size=\"", xtraFontScale*100.0);
pik_append(p, "%\"", 2);
| > > > | 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 |
}
if( t->eCode & TP_ITALIC ){
pik_append(p, " font-style=\"italic\"", -1);
}
if( t->eCode & TP_BOLD ){
pik_append(p, " font-weight=\"bold\"", -1);
}
if( t->eCode & TP_MONO ){
pik_append(p, " font-family=\"monospace\"", -1);
}
if( pObj->color>=0.0 ){
pik_append_clr(p, " fill=\"", pObj->color, "\"",0);
}
xtraFontScale *= p->fontScale;
if( xtraFontScale<=0.99 || xtraFontScale>=1.01 ){
pik_append_num(p, " font-size=\"", xtraFontScale*100.0);
pik_append(p, "%\"", 2);
|
| ︙ | ︙ | |||
6042 6043 6044 6045 6046 6047 6048 |
int iRes = iPrev;
switch( pFlag->eType ){
case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break;
case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break;
case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break;
case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break;
| | | > | 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 |
int iRes = iPrev;
switch( pFlag->eType ){
case T_LJUST: iRes = (iRes&~TP_JMASK) | TP_LJUST; break;
case T_RJUST: iRes = (iRes&~TP_JMASK) | TP_RJUST; break;
case T_ABOVE: iRes = (iRes&~TP_VMASK) | TP_ABOVE; break;
case T_CENTER: iRes = (iRes&~TP_VMASK) | TP_CENTER; break;
case T_BELOW: iRes = (iRes&~TP_VMASK) | TP_BELOW; break;
case T_ITALIC: iRes |= TP_ITALIC; break;
case T_BOLD: iRes |= TP_BOLD; break;
case T_MONO: iRes |= TP_MONO; break;
case T_ALIGNED: iRes |= TP_ALIGN; break;
case T_BIG: if( iRes & TP_BIG ) iRes |= TP_XTRA;
else iRes = (iRes &~TP_SZMASK)|TP_BIG; break;
case T_SMALL: if( iRes & TP_SMALL ) iRes |= TP_XTRA;
else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
}
return iRes;
|
| ︙ | ︙ | |||
6178 6179 6180 6181 6182 6183 6184 | ** in a character string. The returned value is 100 times the ** average character width. ** ** Omit "\" used to escape characters. And count entities like ** "<" as a single character. Multi-byte UTF8 characters count ** as a single character. ** | > | | | | > | | > > | | | 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 |
** in a character string. The returned value is 100 times the
** average character width.
**
** Omit "\" used to escape characters. And count entities like
** "<" as a single character. Multi-byte UTF8 characters count
** as a single character.
**
** Unless using a monospaced font, attempt to scale the answer by
** the actual characters seen. Wide characters count more than
** narrow characters. But the widths are only guesses.
**
*/
static int pik_text_length(const PToken *pToken, const int isMonospace){
const int stdAvg=100, monoAvg=82;
int n = pToken->n;
const char *z = pToken->z;
int cnt, j;
for(j=1, cnt=0; j<n-1; j++){
char c = z[j];
if( c=='\\' && z[j+1]!='&' ){
c = z[++j];
}else if( c=='&' ){
int k;
for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
if( z[k]==';' ) j = k;
cnt += (isMonospace ? monoAvg : stdAvg) * 3 / 2;
continue;
}
if( (c & 0xc0)==0xc0 ){
while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
cnt += isMonospace ? monoAvg : stdAvg;
continue;
}
if( isMonospace ){
cnt += monoAvg;
}else if( c >= 0x20 && c <= 0x7e ){
cnt += awChar[c-0x20];
}else{
cnt += stdAvg;
}
}
return cnt;
}
/* Adjust the width, height, and/or radius of the object so that
** it fits around the text that has been added so far.
|
| ︙ | ︙ | |||
7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 |
{ "invisible", 9, T_INVIS, 0, 0 },
{ "italic", 6, T_ITALIC, 0, 0 },
{ "last", 4, T_LAST, 0, 0 },
{ "left", 4, T_LEFT, DIR_LEFT, CP_W },
{ "ljust", 5, T_LJUST, 0, 0 },
{ "max", 3, T_FUNC2, FN_MAX, 0 },
{ "min", 3, T_FUNC2, FN_MIN, 0 },
{ "n", 1, T_EDGEPT, 0, CP_N },
{ "ne", 2, T_EDGEPT, 0, CP_NE },
{ "north", 5, T_EDGEPT, 0, CP_N },
{ "nw", 2, T_EDGEPT, 0, CP_NW },
{ "of", 2, T_OF, 0, 0 },
{ "previous", 8, T_LAST, 0, 0, },
{ "print", 5, T_PRINT, 0, 0 },
| > > | 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 |
{ "invisible", 9, T_INVIS, 0, 0 },
{ "italic", 6, T_ITALIC, 0, 0 },
{ "last", 4, T_LAST, 0, 0 },
{ "left", 4, T_LEFT, DIR_LEFT, CP_W },
{ "ljust", 5, T_LJUST, 0, 0 },
{ "max", 3, T_FUNC2, FN_MAX, 0 },
{ "min", 3, T_FUNC2, FN_MIN, 0 },
{ "mono", 4, T_MONO, 0, 0 },
{ "monospace", 9, T_MONO, 0, 0 },
{ "n", 1, T_EDGEPT, 0, CP_N },
{ "ne", 2, T_EDGEPT, 0, CP_NE },
{ "north", 5, T_EDGEPT, 0, CP_N },
{ "nw", 2, T_EDGEPT, 0, CP_NW },
{ "of", 2, T_OF, 0, 0 },
{ "previous", 8, T_LAST, 0, 0, },
{ "print", 5, T_PRINT, 0, 0 },
|
| ︙ | ︙ | |||
8120 8121 8122 8123 8124 8125 8126 | return TCL_OK; } #endif /* PIKCHR_TCL */ | | | 8138 8139 8140 8141 8142 8143 8144 8145 | return TCL_OK; } #endif /* PIKCHR_TCL */ #line 8170 "pikchr.c" |
Changes to extsrc/pikchr.js.
1 2 3 4 5 |
var initPikchrModule = (() => {
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
return (
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var initPikchrModule = (() => {
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
return (
function(config) {
var initPikchrModule = config || {};
var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {};
var readyPromiseResolve, readyPromiseReject;
Module["ready"] = new Promise(function(resolve, reject) {
readyPromiseResolve = resolve;
|
| ︙ | ︙ | |||
193 194 195 196 197 198 199 |
return outIdx - startIdx;
}
function stringToUTF8(str, outPtr, maxBytesToWrite) {
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
}
| | | | | | | | | | | | | 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 |
return outIdx - startIdx;
}
function stringToUTF8(str, outPtr, maxBytesToWrite) {
return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
}
var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64;
function updateMemoryViews() {
var b = wasmMemory.buffer;
Module["HEAP8"] = HEAP8 = new Int8Array(b);
Module["HEAP16"] = HEAP16 = new Int16Array(b);
Module["HEAP32"] = HEAP32 = new Int32Array(b);
Module["HEAPU8"] = HEAPU8 = new Uint8Array(b);
Module["HEAPU16"] = HEAPU16 = new Uint16Array(b);
Module["HEAPU32"] = HEAPU32 = new Uint32Array(b);
Module["HEAPF32"] = HEAPF32 = new Float32Array(b);
Module["HEAPF64"] = HEAPF64 = new Float64Array(b);
}
var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216;
var wasmTable;
var __ATPRERUN__ = [];
|
| ︙ | ︙ | |||
361 362 363 364 365 366 367 |
var info = {
"a": asmLibraryArg
};
function receiveInstance(instance, module) {
var exports = instance.exports;
Module["asm"] = exports;
wasmMemory = Module["asm"]["d"];
| | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
var info = {
"a": asmLibraryArg
};
function receiveInstance(instance, module) {
var exports = instance.exports;
Module["asm"] = exports;
wasmMemory = Module["asm"]["d"];
updateMemoryViews();
wasmTable = Module["asm"]["g"];
addOnInit(Module["asm"]["e"]);
removeRunDependency("wasm-instantiate");
}
addRunDependency("wasm-instantiate");
function receiveInstantiationResult(result) {
receiveInstance(result["instance"]);
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
var stackRestore = Module["stackRestore"] = function() {
return (stackRestore = Module["stackRestore"] = Module["asm"]["i"]).apply(null, arguments);
};
var stackAlloc = Module["stackAlloc"] = function() {
return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments);
};
Module["stackSave"] = stackSave;
Module["stackRestore"] = stackRestore;
Module["cwrap"] = cwrap;
| > > | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 |
var stackRestore = Module["stackRestore"] = function() {
return (stackRestore = Module["stackRestore"] = Module["asm"]["i"]).apply(null, arguments);
};
var stackAlloc = Module["stackAlloc"] = function() {
return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments);
};
Module["stackAlloc"] = stackAlloc;
Module["stackSave"] = stackSave;
Module["stackRestore"] = stackRestore;
Module["cwrap"] = cwrap;
|
| ︙ | ︙ |
Changes to extsrc/pikchr.wasm.
cannot compute difference between binary files
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | # define _POSIX_SOURCE #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #if SQLITE_USER_AUTHENTICATION # include "sqlite3userauth.h" #endif #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> | > | | 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 | # define _POSIX_SOURCE #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #if SQLITE_USER_AUTHENTICATION # include "sqlite3userauth.h" #endif #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include <pwd.h> # endif #endif #if (!defined(_WIN32) && !defined(WIN32)) || defined(__MINGW32__) # include <unistd.h> # include <dirent.h> # define GETPID getpid |
| ︙ | ︙ | |||
182 183 184 185 186 187 188 | # define shell_stifle_history(X) # define SHELL_USE_LOCAL_GETLINE 1 #endif #ifndef deliberate_fall_through /* Quiet some compilers about some of our intentional code. */ | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | # define shell_stifle_history(X) # define SHELL_USE_LOCAL_GETLINE 1 #endif #ifndef deliberate_fall_through /* Quiet some compilers about some of our intentional code. */ # if defined(GCC_VERSION) && GCC_VERSION>=7000000 # define deliberate_fall_through __attribute__((fallthrough)); # else # define deliberate_fall_through # endif #endif #if defined(_WIN32) || defined(WIN32) |
| ︙ | ︙ | |||
214 215 216 217 218 219 220 | # undef pclose # define pclose _pclose # endif #else /* Make sure isatty() has a prototype. */ extern int isatty(int); | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | # undef pclose # define pclose _pclose # endif #else /* Make sure isatty() has a prototype. */ extern int isatty(int); # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) /* popen and pclose are not C89 functions and so are ** sometimes omitted from the <stdio.h> header */ extern FILE *popen(const char*,const char*); extern int pclose(FILE*); # else # define SQLITE_OMIT_POPEN 1 # endif |
| ︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 | #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT #include <intrin.h> #endif #include <windows.h> /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); | > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT #include <intrin.h> #endif #define WIN32_LEAN_AND_MEAN #include <windows.h> /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); |
| ︙ | ︙ | |||
455 456 457 458 459 460 461 | /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. */ static int bail_on_error = 0; /* | | > > > > > > > > > > > > > > | 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 | /* ** If the following flag is set, then command execution stops ** at an error if we are not interactive. */ static int bail_on_error = 0; /* ** Treat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; #if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \ && !defined(SHELL_OMIT_WIN_UTF8) # define SHELL_WIN_UTF8_OPT 1 #else # define SHELL_WIN_UTF8_OPT 0 #endif #if SHELL_WIN_UTF8_OPT /* ** Setup console for UTF-8 input/output when following variable true. */ static int console_utf8 = 0; #endif /* ** On Windows systems we have to know if standard output is a console ** in order to translate UTF-8 into MBCS. The following variable is ** true if translation is required. */ static int stdout_is_console = 1; |
| ︙ | ︙ | |||
592 593 594 595 596 597 598 599 600 |
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4);
}
}
return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
/*
** Render output like fprintf(). Except, if the output is going to the
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | > > > > | 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 |
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4);
}
}
return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
#if SHELL_WIN_UTF8_OPT
/* Following struct is used for -utf8 operation. */
static struct ConsoleState {
int stdinEof; /* EOF has been seen on console input */
int infsMode; /* Input file stream mode upon shell start */
UINT inCodePage; /* Input code page upon shell start */
UINT outCodePage; /* Output code page upon shell start */
HANDLE hConsoleIn; /* Console input handle */
DWORD consoleMode; /* Console mode upon shell start */
} conState = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 };
#ifndef _O_U16TEXT /* For build environments lacking this constant: */
# define _O_U16TEXT 0x20000
#endif
/*
** Prepare console, (if known to be a WIN32 console), for UTF-8
** input (from either typing or suitable paste operations) and for
** UTF-8 rendering. This may "fail" with a message to stderr, where
** the preparation is not done and common "code page" issues occur.
*/
static void console_prepare(void){
HANDLE hCI = GetStdHandle(STD_INPUT_HANDLE);
DWORD consoleMode = 0;
if( isatty(0) && GetFileType(hCI)==FILE_TYPE_CHAR
&& GetConsoleMode( hCI, &consoleMode) ){
if( !IsValidCodePage(CP_UTF8) ){
fprintf(stderr, "Cannot use UTF-8 code page.\n");
console_utf8 = 0;
return;
}
conState.hConsoleIn = hCI;
conState.consoleMode = consoleMode;
conState.inCodePage = GetConsoleCP();
conState.outCodePage = GetConsoleOutputCP();
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
consoleMode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
SetConsoleMode(conState.hConsoleIn, consoleMode);
conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT);
console_utf8 = 1;
}else{
console_utf8 = 0;
}
}
/*
** Undo the effects of console_prepare(), if any.
*/
static void SQLITE_CDECL console_restore(void){
if( console_utf8 && conState.inCodePage!=0
&& conState.hConsoleIn!=INVALID_HANDLE_VALUE ){
_setmode(_fileno(stdin), conState.infsMode);
SetConsoleCP(conState.inCodePage);
SetConsoleOutputCP(conState.outCodePage);
SetConsoleMode(conState.hConsoleIn, conState.consoleMode);
/* Avoid multiple calls. */
conState.hConsoleIn = INVALID_HANDLE_VALUE;
conState.consoleMode = 0;
console_utf8 = 0;
}
}
/*
** Collect input like fgets(...) with special provisions for input
** from the Windows console to get around its strange coding issues.
** Defers to plain fgets() when input is not interactive or when the
** startup option, -utf8, has not been provided or taken effect.
*/
static char* utf8_fgets(char *buf, int ncmax, FILE *fin){
if( fin==0 ) fin = stdin;
if( fin==stdin && stdin_is_interactive && console_utf8 ){
# define SQLITE_IALIM 150
wchar_t wbuf[SQLITE_IALIM];
int lend = 0;
int noc = 0;
if( ncmax==0 || conState.stdinEof ) return 0;
buf[0] = 0;
while( noc<ncmax-7-1 && !lend ){
/* There is room for at least 2 more characters and a 0-terminator. */
int na = (ncmax > SQLITE_IALIM*4+1 + noc)
? SQLITE_IALIM : (ncmax-1 - noc)/4;
# undef SQLITE_IALIM
DWORD nbr = 0;
BOOL bRC = ReadConsoleW(conState.hConsoleIn, wbuf, na, &nbr, 0);
if( !bRC || (noc==0 && nbr==0) ) return 0;
if( nbr > 0 ){
int nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR,
wbuf,nbr,0,0,0,0);
if( nmb !=0 && noc+nmb <= ncmax ){
int iseg = noc;
nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR,
wbuf,nbr,buf+noc,nmb,0,0);
noc += nmb;
/* Fixup line-ends as coded by Windows for CR (or "Enter".)*/
if( noc > 0 ){
if( buf[noc-1]=='\n' ){
lend = 1;
if( noc > 1 && buf[noc-2]=='\r' ){
buf[noc-2] = '\n';
--noc;
}
}
}
/* Check for ^Z (anywhere in line) too. */
while( iseg < noc ){
if( buf[iseg]==0x1a ){
conState.stdinEof = 1;
noc = iseg; /* Chop ^Z and anything following. */
break;
}
++iseg;
}
}else break; /* Drop apparent garbage in. (Could assert.) */
}else break;
}
/* If got nothing, (after ^Z chop), must be at end-of-file. */
if( noc == 0 ) return 0;
buf[noc] = 0;
return buf;
}else{
return fgets(buf, ncmax, fin);
}
}
# define fgets(b,n,f) utf8_fgets(b,n,f)
#endif /* SHELL_WIN_UTF8_OPT */
/*
** Render output like fprintf(). Except, if the output is going to the
** console and if this is running on a Windows machine, and if the -utf8
** option is unavailable or (available and inactive), translate the
** output from UTF-8 into MBCS for output through 8-bit stdout stream.
** (With -utf8 active, no translation is needed and must not be done.)
*/
#if defined(_WIN32) || defined(WIN32)
void utf8_printf(FILE *out, const char *zFormat, ...){
va_list ap;
va_start(ap, zFormat);
if( stdout_is_console && (out==stdout || out==stderr)
# if SHELL_WIN_UTF8_OPT
&& !console_utf8
# endif
){
char *z1 = sqlite3_vmprintf(zFormat, ap);
char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0);
sqlite3_free(z1);
fputs(z2, out);
sqlite3_free(z2);
}else{
vfprintf(out, zFormat, ap);
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 | raw_printf(stderr,"Error: out of memory\n"); exit(1); } /* Check a pointer to see if it is NULL. If it is NULL, exit with an ** out-of-memory error. */ | | | 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
raw_printf(stderr,"Error: out of memory\n");
exit(1);
}
/* Check a pointer to see if it is NULL. If it is NULL, exit with an
** out-of-memory error.
*/
static void shell_check_oom(const void *p){
if( p==0 ) shell_out_of_memory();
}
/*
** Write I/O traces to the following stream.
*/
#ifdef SQLITE_ENABLE_IOTRACE
|
| ︙ | ︙ | |||
812 813 814 815 816 817 818 |
n--;
if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0;
break;
}
}
#if defined(_WIN32) || defined(WIN32)
| | | > | > > > > | 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
n--;
if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0;
break;
}
}
#if defined(_WIN32) || defined(WIN32)
/* For interactive input on Windows systems, without -utf8,
** translate the multi-byte characterset characters into UTF-8.
** This is the translation that predates the -utf8 option. */
if( stdin_is_interactive && in==stdin
# if SHELL_WIN_UTF8_OPT
&& !console_utf8
# endif /* SHELL_WIN_UTF8_OPT */
){
char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
if( zTrans ){
i64 nTrans = strlen(zTrans)+1;
if( nTrans>nLine ){
zLine = realloc(zLine, nTrans);
shell_check_oom(zLine);
}
|
| ︙ | ︙ | |||
855 856 857 858 859 860 861 |
if( in!=0 ){
zResult = local_getline(zPrior, in);
}else{
zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
#if SHELL_USE_LOCAL_GETLINE
printf("%s", zPrompt);
fflush(stdout);
| > | > > > > > > > > > > | 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 |
if( in!=0 ){
zResult = local_getline(zPrior, in);
}else{
zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
#if SHELL_USE_LOCAL_GETLINE
printf("%s", zPrompt);
fflush(stdout);
do{
zResult = local_getline(zPrior, stdin);
zPrior = 0;
/* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
if( zResult==0 ) sqlite3_sleep(50);
}while( zResult==0 && seenInterrupt>0 );
#else
free(zPrior);
zResult = shell_readline(zPrompt);
while( zResult==0 ){
/* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
sqlite3_sleep(50);
if( seenInterrupt==0 ) break;
zResult = shell_readline("");
}
if( zResult && *zResult ) shell_add_history(zResult);
#endif
}
return zResult;
}
#endif /* !SQLITE_SHELL_FIDDLE */
|
| ︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
** SQLite keyword. Be conservative in this estimate: When in doubt assume
** that quoting is required.
**
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
int i;
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
for(i=0; zName[i]; i++){
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
}
return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
| > | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 |
** SQLite keyword. Be conservative in this estimate: When in doubt assume
** that quoting is required.
**
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
int i;
if( zName==0 ) return '"';
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
for(i=0; zName[i]; i++){
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
}
return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
|
| ︙ | ︙ | |||
1641 1642 1643 1644 1645 1646 1647 | ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** | | > | 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 | ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. ** Two SQL functions are implemented: ** ** sha3(X,SIZE) ** sha3_query(Y,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if ** X is NULL. |
| ︙ | ︙ | |||
3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 |
#define PAD_CHAR '='
#ifndef U8_TYPEDEF
/* typedef unsigned char u8; */
#define U8_TYPEDEF
#endif
static const u8 b64DigitValues[128] = {
/* HT LF VT FF CR */
ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
/* US */
ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
/*sp + / */
WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63,
| > | 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 |
#define PAD_CHAR '='
#ifndef U8_TYPEDEF
/* typedef unsigned char u8; */
#define U8_TYPEDEF
#endif
/* Decoding table, ASCII (7-bit) value to base 64 digit value or other */
static const u8 b64DigitValues[128] = {
/* HT LF VT FF CR */
ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND,
/* US */
ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND,
/*sp + / */
WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63,
|
| ︙ | ︙ | |||
3240 3241 3242 3243 3244 3245 3246 |
*pOut++ = '\n';
}
*pOut = 0;
return pOut;
}
/* Skip over text which is not base64 numeral(s). */
| | | | | 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 |
*pOut++ = '\n';
}
*pOut = 0;
return pOut;
}
/* Skip over text which is not base64 numeral(s). */
static char * skipNonB64( char *s, int nc ){
char c;
while( nc-- > 0 && (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s;
return s;
}
/* Decode base64 text into a byte buffer. */
static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
while( ncIn>0 && *pIn!=PAD_CHAR ){
static signed char nboi[] = { 0, 0, 1, 2, 3 };
char *pUse = skipNonB64(pIn, ncIn);
unsigned long qv = 0L;
int nti, nbo, nac;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>4)? 4 : ncIn;
ncIn -= nti;
nbo = nboi[nti];
|
| ︙ | ︙ | |||
3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 |
case SQLITE_BLOB:
nb = nv;
nc = 4*(nv+2/3); /* quads needed */
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
return;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
| > > > > > > > > < > > > > > > > > < | 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 |
case SQLITE_BLOB:
nb = nv;
nc = 4*(nv+2/3); /* quads needed */
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
return;
}
bBuf = (u8*)sqlite3_value_blob(av[0]);
if( !bBuf ){
if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break;
case SQLITE_TEXT:
nc = nv;
nb = 3*((nv+3)/4); /* may overestimate due to LF and padding */
if( nvMax < nb ){
sqlite3_result_error(context, "blob from base64 may be too big", -1);
return;
}else if( nb<1 ){
nb = 1;
}
cBuf = (char *)sqlite3_value_text(av[0]);
if( !cBuf ){
if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break;
default:
sqlite3_result_error(context, "base64 accepts only blob or text", -1);
return;
}
|
| ︙ | ︙ | |||
3516 3517 3518 3519 3520 3521 3522 | } #endif /* Width of base64 lines. Should be an integer multiple of 5. */ #define B85_DARK_MAX 80 | | | | 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 |
}
#endif
/* Width of base64 lines. Should be an integer multiple of 5. */
#define B85_DARK_MAX 80
static char * skipNonB85( char *s, int nc ){
char c;
while( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s;
return s;
}
/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral.
* Do not use the macro form with argument expression having a side-effect.*/
#if 0
static char base85Numeral( u8 b ){
|
| ︙ | ︙ | |||
3547 3548 3549 3550 3551 3552 3553 |
** to be appended to encoded groups to limit their length to B85_DARK_MAX
** or to terminate the last group (to aid concatenation.)
*/
static char* toBase85( u8 *pIn, int nbIn, char *pOut, char *pSep ){
int nCol = 0;
while( nbIn >= 4 ){
int nco = 5;
| | > | 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 |
** to be appended to encoded groups to limit their length to B85_DARK_MAX
** or to terminate the last group (to aid concatenation.)
*/
static char* toBase85( u8 *pIn, int nbIn, char *pOut, char *pSep ){
int nCol = 0;
while( nbIn >= 4 ){
int nco = 5;
unsigned long qbv = (((unsigned long)pIn[0])<<24) |
(pIn[1]<<16) | (pIn[2]<<8) | pIn[3];
while( nco > 0 ){
unsigned nqv = (unsigned)(qbv/85UL);
unsigned char dv = qbv - 85UL*nqv;
qbv = nqv;
pOut[--nco] = base85Numeral(dv);
}
nbIn -= 4;
|
| ︙ | ︙ | |||
3587 3588 3589 3590 3591 3592 3593 |
}
/* Decode base85 text into a byte buffer. */
static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
while( ncIn>0 ){
static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
| | | 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 |
}
/* Decode base85 text into a byte buffer. */
static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){
if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn;
while( ncIn>0 ){
static signed char nboi[] = { 0, 0, 1, 2, 3, 4 };
char *pUse = skipNonB85(pIn, ncIn);
unsigned long qv = 0L;
int nti, nbo;
ncIn -= (pUse - pIn);
pIn = pUse;
nti = (ncIn>5)? 5 : ncIn;
nbo = nboi[nti];
if( nbo==0 ) break;
|
| ︙ | ︙ | |||
3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 |
case SQLITE_BLOB:
nb = nv;
/* ulongs tail newlines tailenc+nul*/
nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base85 too big", -1);
return;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
| > > > > > > > > < > > > > > > > > < | 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 |
case SQLITE_BLOB:
nb = nv;
/* ulongs tail newlines tailenc+nul*/
nc = 5*(nv/4) + nv%4 + nv/64+1 + 2;
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base85 too big", -1);
return;
}
bBuf = (u8*)sqlite3_value_blob(av[0]);
if( !bBuf ){
if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
cBuf = sqlite3_malloc(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
break;
case SQLITE_TEXT:
nc = nv;
nb = 4*(nv/5) + nv%5; /* may overestimate */
if( nvMax < nb ){
sqlite3_result_error(context, "blob from base85 may be too big", -1);
return;
}else if( nb<1 ){
nb = 1;
}
cBuf = (char *)sqlite3_value_text(av[0]);
if( !cBuf ){
if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){
goto memFail;
}
sqlite3_result_zeroblob(context, 0);
break;
}
bBuf = sqlite3_malloc(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
break;
default:
sqlite3_result_error(context, "base85 accepts only blob or text.", -1);
return;
}
|
| ︙ | ︙ | |||
4109 4110 4111 4112 4113 4114 4115 | } return rc; } /************************* End ../ext/misc/ieee754.c ********************/ /************************* Begin ../ext/misc/series.c ******************/ /* | | | > > > > > > > > > > > > > > > > > > > > > > > | 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 | } return rc; } /************************* End ../ext/misc/ieee754.c ********************/ /************************* Begin ../ext/misc/series.c ******************/ /* ** 2015-08-18, 2023-04-28 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file demonstrates how to create a table-valued-function using ** a virtual table. This demo implements the generate_series() function ** which gives the same results as the eponymous function in PostgreSQL, ** within the limitation that its arguments are signed 64-bit integers. ** ** Considering its equivalents to generate_series(start,stop,step): A ** value V[n] sequence is produced for integer n ascending from 0 where ** ( V[n] == start + n * step && sgn(V[n] - stop) * sgn(step) >= 0 ) ** for each produced value (independent of production time ordering.) ** ** All parameters must be either integer or convertable to integer. ** The start parameter is required. ** The stop parameter defaults to (1<<32)-1 (aka 4294967295 or 0xffffffff) ** The step parameter defaults to 1 and 0 is treated as 1. ** ** Examples: ** ** SELECT * FROM generate_series(0,100,5); ** ** The query above returns integers from 0 through 100 counting by steps ** of 5. ** ** SELECT * FROM generate_series(0,100); ** ** Integers from 0 through 100 with a step size of 1. ** ** SELECT * FROM generate_series(20) LIMIT 10; ** ** Integers 20 through 29. ** ** SELECT * FROM generate_series(0,-100,-5); ** ** Integers 0 -5 -10 ... -100. ** ** SELECT * FROM generate_series(0,-1); ** ** Empty sequence. ** ** HOW IT WORKS ** ** The generate_series "function" is really a virtual table with the ** following schema: ** ** CREATE TABLE generate_series( ** value, ** start HIDDEN, ** stop HIDDEN, ** step HIDDEN ** ); ** ** The virtual table also has a rowid, logically equivalent to n+1 where ** "n" is the ascending integer in the aforesaid production definition. ** ** Function arguments in queries against this virtual table are translated ** into equality constraints against successive hidden columns. In other ** words, the following pairs of queries are equivalent to each other: ** ** SELECT * FROM generate_series(0,100,5); ** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5; |
| ︙ | ︙ | |||
4182 4183 4184 4185 4186 4187 4188 4189 4190 | ** encourages the query planner to order joins such that the bounds of the ** series are well-defined. */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < | 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 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 |
** encourages the query planner to order joins such that the bounds of the
** series are well-defined.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <limits.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Return that member of a generate_series(...) sequence whose 0-based
** index is ix. The 0th member is given by smBase. The sequence members
** progress per ix increment by smStep.
*/
static sqlite3_int64 genSeqMember(sqlite3_int64 smBase,
sqlite3_int64 smStep,
sqlite3_uint64 ix){
if( ix>=(sqlite3_uint64)LLONG_MAX ){
/* Get ix into signed i64 range. */
ix -= (sqlite3_uint64)LLONG_MAX;
/* With 2's complement ALU, this next can be 1 step, but is split into
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
smBase += (LLONG_MAX/2) * smStep;
smBase += (LLONG_MAX - LLONG_MAX/2) * smStep;
}
/* Under UBSAN (or on 1's complement machines), must do this last term
* in steps to avoid the dreaded (and harmless) signed multiply overlow. */
if( ix>=2 ){
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
smBase += ix2*smStep;
ix -= ix2;
}
return smBase + ((sqlite3_int64)ix)*smStep;
}
/* typedef unsigned char u8; */
typedef struct SequenceSpec {
sqlite3_int64 iBase; /* Starting value ("start") */
sqlite3_int64 iTerm; /* Given terminal value ("stop") */
sqlite3_int64 iStep; /* Increment ("step") */
sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
sqlite3_int64 iValueNow; /* Current value during generation */
u8 isNotEOF; /* Sequence generation not exhausted */
u8 isReversing; /* Sequence is being reverse generated */
} SequenceSpec;
/*
** Prepare a SequenceSpec for use in generating an integer series
** given initialized iBase, iTerm and iStep values. Sequence is
** initialized per given isReversing. Other members are computed.
*/
static void setupSequence( SequenceSpec *pss ){
int bSameSigns;
pss->uSeqIndexMax = 0;
pss->isNotEOF = 0;
bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
if( pss->iTerm < pss->iBase ){
sqlite3_uint64 nuspan = 0;
if( bSameSigns ){
nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
}else{
/* Under UBSAN (or on 1's complement machines), must do this in steps.
* In this clause, iBase>=0 and iTerm<0 . */
nuspan = 1;
nuspan += pss->iBase;
nuspan += -(pss->iTerm+1);
}
if( pss->iStep<0 ){
pss->isNotEOF = 1;
if( nuspan==ULONG_MAX ){
pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
}else if( pss->iStep>LLONG_MIN ){
pss->uSeqIndexMax = nuspan/-pss->iStep;
}
}
}else if( pss->iTerm > pss->iBase ){
sqlite3_uint64 puspan = 0;
if( bSameSigns ){
puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
}else{
/* Under UBSAN (or on 1's complement machines), must do this in steps.
* In this clause, iTerm>=0 and iBase<0 . */
puspan = 1;
puspan += pss->iTerm;
puspan += -(pss->iBase+1);
}
if( pss->iStep>0 ){
pss->isNotEOF = 1;
pss->uSeqIndexMax = puspan/pss->iStep;
}
}else if( pss->iTerm == pss->iBase ){
pss->isNotEOF = 1;
pss->uSeqIndexMax = 0;
}
pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
pss->iValueNow = (pss->isReversing)
? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
: pss->iBase;
}
/*
** Progress sequence generator to yield next value, if any.
** Leave its state to either yield next value or be at EOF.
** Return whether there is a next value, or 0 at EOF.
*/
static int progressSequence( SequenceSpec *pss ){
if( !pss->isNotEOF ) return 0;
if( pss->isReversing ){
if( pss->uSeqIndexNow > 0 ){
pss->uSeqIndexNow--;
pss->iValueNow -= pss->iStep;
}else{
pss->isNotEOF = 0;
}
}else{
if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
pss->uSeqIndexNow++;
pss->iValueNow += pss->iStep;
}else{
pss->isNotEOF = 0;
}
}
return pss->isNotEOF;
}
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
SequenceSpec ss; /* (this) Derived class data */
};
/*
** The seriesConnect() method is invoked to create a new
** series_vtab that describes the generate_series virtual table.
**
** Think of this routine as the constructor for series_vtab objects.
|
| ︙ | ︙ | |||
4280 4281 4282 4283 4284 4285 4286 |
/*
** Advance a series_cursor to its next row of output.
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
| < < < < < | | | | | | | < | > | < < < | | < | | | | | | | | | | | < | | | > < | < < < < | | | 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 |
/*
** Advance a series_cursor to its next row of output.
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
progressSequence( & pCur->ss );
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the series_cursor
** is currently pointing.
*/
static int seriesColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case SERIES_COLUMN_START: x = pCur->ss.iBase; break;
case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break;
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
default: x = pCur->ss.iValueNow; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
/*
** Return the rowid for the current row, logically equivalent to n+1 where
** "n" is the ascending integer in the aforesaid production definition.
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_uint64 n = pCur->ss.uSeqIndexNow;
*pRowid = (sqlite3_int64)((n<0xffffffffffffffff)? n+1 : 0);
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
return !pCur->ss.isNotEOF;
}
/* True to cause run-time checking of the start=, stop=, and/or step=
** parameters. The only reason to do this is for testing the
** constraint checking logic for virtual tables in the SQLite core.
*/
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
#endif
/*
** This method is called to "rewind" the series_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to seriesColumn() or seriesRowid() or
** seriesEof().
**
** The query plan selected by seriesBestIndex is passed in the idxNum
** parameter. (idxStr is not used in this implementation.) idxNum
** is a bitmask showing which constraints are available:
**
** 1: start=VALUE
** 2: stop=VALUE
** 4: step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order. If bit 16 is
** set, then output must appear in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that seriesEof() will return true) if the table is empty.
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStrUnused,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int i = 0;
(void)idxStrUnused;
if( idxNum & 1 ){
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
}else{
pCur->ss.iBase = 0;
}
if( idxNum & 2 ){
pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
}else{
pCur->ss.iTerm = 0xffffffff;
}
if( idxNum & 4 ){
pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
if( pCur->ss.iStep==0 ){
pCur->ss.iStep = 1;
}else if( pCur->ss.iStep<0 ){
if( (idxNum & 16)==0 ) idxNum |= 8;
}
}else{
pCur->ss.iStep = 1;
}
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
/* If any of the constraints have a NULL value, then return no rows.
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
pCur->ss.iBase = 1;
pCur->ss.iTerm = 0;
pCur->ss.iStep = 1;
break;
}
}
if( idxNum & 8 ){
pCur->ss.isReversing = pCur->ss.iStep > 0;
}else{
pCur->ss.isReversing = pCur->ss.iStep < 0;
}
setupSequence( &pCur->ss );
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
|
| ︙ | ︙ | |||
5167 5168 5169 5170 5171 5172 5173 |
}
if( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -sz);
}
break;
}
case '[': {
| | | 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 |
}
if( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -sz);
}
break;
}
case '[': {
unsigned int iFirst = p->nState;
if( rePeek(p)=='^' ){
re_append(p, RE_OP_CC_EXC, 0);
p->sIn.i++;
}else{
re_append(p, RE_OP_CC_INC, 0);
}
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
| ︙ | ︙ | |||
5191 5192 5193 5194 5195 5196 5197 |
re_append(p, RE_OP_CC_RANGE, c);
}else{
re_append(p, RE_OP_CC_VALUE, c);
}
if( rePeek(p)==']' ){ p->sIn.i++; break; }
}
if( c==0 ) return "unclosed '['";
| | | 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 |
re_append(p, RE_OP_CC_RANGE, c);
}else{
re_append(p, RE_OP_CC_VALUE, c);
}
if( rePeek(p)==']' ){ p->sIn.i++; break; }
}
if( c==0 ) return "unclosed '['";
if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
break;
}
case '\\': {
int specialOp = 0;
switch( rePeek(p) ){
case 'b': specialOp = RE_OP_BOUNDARY; break;
case 'd': specialOp = RE_OP_DIGIT; break;
|
| ︙ | ︙ | |||
8768 8769 8770 8771 8772 8773 8774 |
}
sqlite3_free(aFree);
}else{
/* Figure out if this is a directory or a zero-sized file. Consider
** it to be a directory either if the mode suggests so, or if
** the final character in the name is '/'. */
u32 mode = pCDS->iExternalAttr >> 16;
| | > > > | 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 |
}
sqlite3_free(aFree);
}else{
/* Figure out if this is a directory or a zero-sized file. Consider
** it to be a directory either if the mode suggests so, or if
** the final character in the name is '/'. */
u32 mode = pCDS->iExternalAttr >> 16;
if( !(mode & S_IFDIR)
&& pCDS->nFile>=1
&& pCDS->zFile[pCDS->nFile-1]!='/'
){
sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC);
}
}
}
break;
}
case 6: /* method */
|
| ︙ | ︙ | |||
9205 9206 9207 9208 9209 9210 9211 |
/*
** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
** linked list. Remove it from the list and free the object.
*/
static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
if( pOld ){
| > > > > | | | > > > > > > | 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 |
/*
** Unless it is NULL, entry pOld is currently part of the pTab->pFirstEntry
** linked list. Remove it from the list and free the object.
*/
static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){
if( pOld ){
if( pTab->pFirstEntry==pOld ){
pTab->pFirstEntry = pOld->pNext;
if( pTab->pLastEntry==pOld ) pTab->pLastEntry = 0;
}else{
ZipfileEntry *p;
for(p=pTab->pFirstEntry; p; p=p->pNext){
if( p->pNext==pOld ){
p->pNext = pOld->pNext;
if( pTab->pLastEntry==pOld ) pTab->pLastEntry = p;
break;
}
}
}
zipfileEntryFree(pOld);
}
}
/*
** xUpdate method.
*/
|
| ︙ | ︙ | |||
12673 12674 12675 12676 12677 12678 12679 12680 12681 12682 12683 12684 12685 12686 |
){
DbdataTable *pTab = 0;
int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA);
(void)argc;
(void)argv;
(void)pzErr;
if( rc==SQLITE_OK ){
pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable));
if( pTab==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pTab, 0, sizeof(DbdataTable));
pTab->db = db;
| > | 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 |
){
DbdataTable *pTab = 0;
int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA);
(void)argc;
(void)argv;
(void)pzErr;
sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS);
if( rc==SQLITE_OK ){
pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable));
if( pTab==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pTab, 0, sizeof(DbdataTable));
pTab->db = db;
|
| ︙ | ︙ | |||
13018 13019 13020 13021 13022 13023 13024 |
int bNextPage = 0;
if( pCsr->aPage==0 ){
while( 1 ){
if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK;
rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage);
if( rc!=SQLITE_OK ) return rc;
| | > > > > | 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 |
int bNextPage = 0;
if( pCsr->aPage==0 ){
while( 1 ){
if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK;
rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage);
if( rc!=SQLITE_OK ) return rc;
if( pCsr->aPage && pCsr->nPage>=256 ) break;
sqlite3_free(pCsr->aPage);
pCsr->aPage = 0;
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}
assert( iOff+3+2<=pCsr->nPage );
pCsr->iCell = pTab->bPtr ? -2 : 0;
pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]);
}
if( pTab->bPtr ){
if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){
pCsr->iCell = pCsr->nCell;
|
| ︙ | ︙ | |||
13256 13257 13258 13259 13260 13261 13262 |
** and return SQLITE_OK. Otherwise, return an SQLite error code.
*/
static int dbdataGetEncoding(DbdataCursor *pCsr){
int rc = SQLITE_OK;
int nPg1 = 0;
u8 *aPg1 = 0;
rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1);
| < | | 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606 13607 |
** and return SQLITE_OK. Otherwise, return an SQLite error code.
*/
static int dbdataGetEncoding(DbdataCursor *pCsr){
int rc = SQLITE_OK;
int nPg1 = 0;
u8 *aPg1 = 0;
rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1);
if( rc==SQLITE_OK && nPg1>=(56+4) ){
pCsr->enc = get_uint32(&aPg1[56]);
}
sqlite3_free(aPg1);
return rc;
}
|
| ︙ | ︙ | |||
13315 13316 13317 13318 13319 13320 13321 |
"SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1,
&pCsr->pStmt, 0
);
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT);
| < < > > > > | 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 |
"SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1,
&pCsr->pStmt, 0
);
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT);
}
/* Try to determine the encoding of the db by inspecting the header
** field on page 1. */
if( rc==SQLITE_OK ){
rc = dbdataGetEncoding(pCsr);
}
if( rc!=SQLITE_OK ){
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
if( rc==SQLITE_OK ){
rc = dbdataNext(pCursor);
}
return rc;
}
|
| ︙ | ︙ | |||
14563 14564 14565 14566 14567 14568 14569 |
recoverFinalize(p, pStmt);
pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName);
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
int iField = sqlite3_column_int(pStmt, 0);
int iCol = sqlite3_column_int(pStmt, 1);
| | | 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 14915 |
recoverFinalize(p, pStmt);
pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName);
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
int iField = sqlite3_column_int(pStmt, 0);
int iCol = sqlite3_column_int(pStmt, 1);
assert( iCol<pNew->nCol );
pNew->aCol[iCol].iField = iField;
pNew->bIntkey = 0;
iPk = -2;
}
recoverFinalize(p, pStmt);
|
| ︙ | ︙ | |||
16327 16328 16329 16330 16331 16332 16333 | } return rc; } #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /************************* End ../ext/recover/sqlite3recover.c ********************/ | | | 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 | } return rc; } #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /************************* End ../ext/recover/sqlite3recover.c ********************/ # endif /* SQLITE_HAVE_SQLITE3R */ #endif #ifdef SQLITE_SHELL_EXTSRC # include SHELL_STRINGIFY(SQLITE_SHELL_EXTSRC) #endif #if defined(SQLITE_ENABLE_SESSION) /* |
| ︙ | ︙ | |||
16509 16510 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 | #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 on/off, 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) #define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) #define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) | > | 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 | #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 on/off, 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 */ #define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */ /* ** Macros for testing and setting shellFlgs */ #define ShellHasFlag(P,X) (((P)->shellFlgs & (X))!=0) #define ShellSetFlag(P,X) ((P)->shellFlgs|=(X)) #define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X))) |
| ︙ | ︙ | |||
16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 |
}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];
}
sz = j;
p[sz] = 0;
}
| > | 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 |
}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 */
p[sz] = 0;
for(i=j=0; i<sz; i++){
if( p[i]=='\r' && p[i+1]=='\n' ) i++;
p[j++] = p[i];
}
sz = j;
p[sz] = 0;
}
|
| ︙ | ︙ | |||
16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 |
**
** See also: output_quoted_escaped_string()
*/
static void output_quoted_string(FILE *out, const char *z){
int i;
char c;
setBinaryMode(out, 1);
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
utf8_printf(out,"'%s'",z);
}else{
raw_printf(out, "'");
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
| > | 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 |
**
** See also: output_quoted_escaped_string()
*/
static void output_quoted_string(FILE *out, const char *z){
int i;
char c;
setBinaryMode(out, 1);
if( z==0 ) return;
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
utf8_printf(out,"'%s'",z);
}else{
raw_printf(out, "'");
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
|
| ︙ | ︙ | |||
16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 |
}
/*
** Output the given string as a quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, i64 n){
unsigned int c;
if( n<0 ) n = strlen(z);
fputc('"', out);
while( n-- ){
c = *(z++);
if( c=='\\' || c=='"' ){
fputc('\\', out);
fputc(c, out);
| > | 17312 17313 17314 17315 17316 17317 17318 17319 17320 17321 17322 17323 17324 17325 17326 |
}
/*
** Output the given string as a quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, i64 n){
unsigned int c;
if( z==0 ) z = "";
if( n<0 ) n = strlen(z);
fputc('"', out);
while( n-- ){
c = *(z++);
if( c=='\\' || c=='"' ){
fputc('\\', out);
fputc(c, out);
|
| ︙ | ︙ | |||
17095 17096 17097 17098 17099 17100 17101 |
}
/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
| | < | 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 17447 17448 17449 17450 17451 |
}
/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
if( ++seenInterrupt>1 ) exit(1);
if( globalDb ) sqlite3_interrupt(globalDb);
}
#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
/*
** This routine runs for console events (e.g. Ctrl-C) on Win32
*/
|
| ︙ | ︙ | |||
17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 17241 17242 17243 |
if( zTail==0 ) return;
if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){
const char *zOrig = z;
static const char *azTerm[] = { "", "*/", "\n" };
int i;
for(i=0; i<ArraySize(azTerm); i++){
char *zNew = sqlite3_mprintf("%s%s;", zOrig, azTerm[i]);
if( sqlite3_complete(zNew) ){
size_t n = strlen(zNew);
zNew[n-1] = 0;
zToFree = zNew;
z = zNew;
break;
}
| > | 17571 17572 17573 17574 17575 17576 17577 17578 17579 17580 17581 17582 17583 17584 17585 |
if( zTail==0 ) return;
if( zTail[0]==';' && (strstr(z, "/*")!=0 || strstr(z,"--")!=0) ){
const char *zOrig = z;
static const char *azTerm[] = { "", "*/", "\n" };
int i;
for(i=0; i<ArraySize(azTerm); i++){
char *zNew = sqlite3_mprintf("%s%s;", zOrig, azTerm[i]);
shell_check_oom(zNew);
if( sqlite3_complete(zNew) ){
size_t n = strlen(zNew);
zNew[n-1] = 0;
zToFree = zNew;
z = zNew;
break;
}
|
| ︙ | ︙ | |||
17664 17665 17666 17667 17668 17669 17670 |
utf8_printf(p->out,"%s", azArg[i]);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
| | | | 18006 18007 18008 18009 18010 18011 18012 18013 18014 18015 18016 18017 18018 18019 18020 18021 18022 |
utf8_printf(p->out,"%s", azArg[i]);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
raw_printf(p->out, "9.0e+999");
}else if( ur==0xfff0000000000000LL ){
raw_printf(p->out, "-9.0e+999");
}else{
sqlite3_int64 ir = (sqlite3_int64)r;
if( r==(double)ir ){
sqlite3_snprintf(50,z,"%lld.0", ir);
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
}
|
| ︙ | ︙ | |||
17710 17711 17712 17713 17714 17715 17716 |
fputs("null",p->out);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
| | | | 18052 18053 18054 18055 18056 18057 18058 18059 18060 18061 18062 18063 18064 18065 18066 18067 18068 |
fputs("null",p->out);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
raw_printf(p->out, "9.0e+999");
}else if( ur==0xfff0000000000000LL ){
raw_printf(p->out, "-9.0e+999");
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
raw_printf(p->out, "%s", z);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
|
| ︙ | ︙ | |||
17916 17917 17918 17919 17920 17921 17922 17923 17924 17925 17926 17927 17928 17929 17930 17931 17932 17933 |
size_t len;
char *zCode;
char *zMsg;
int i;
if( db==0
|| zSql==0
|| (iOffset = sqlite3_error_offset(db))<0
){
return sqlite3_mprintf("");
}
while( iOffset>50 ){
iOffset--;
zSql++;
while( (zSql[0]&0xc0)==0x80 ){ zSql++; iOffset--; }
}
len = strlen(zSql);
if( len>78 ){
len = 78;
| > | | 18258 18259 18260 18261 18262 18263 18264 18265 18266 18267 18268 18269 18270 18271 18272 18273 18274 18275 18276 18277 18278 18279 18280 18281 18282 18283 18284 |
size_t len;
char *zCode;
char *zMsg;
int i;
if( db==0
|| zSql==0
|| (iOffset = sqlite3_error_offset(db))<0
|| iOffset>=(int)strlen(zSql)
){
return sqlite3_mprintf("");
}
while( iOffset>50 ){
iOffset--;
zSql++;
while( (zSql[0]&0xc0)==0x80 ){ zSql++; iOffset--; }
}
len = strlen(zSql);
if( len>78 ){
len = 78;
while( len>0 && (zSql[len]&0xc0)==0x80 ) len--;
}
zCode = sqlite3_mprintf("%.*s", len, zSql);
shell_check_oom(zCode);
for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; }
if( iOffset<25 ){
zMsg = sqlite3_mprintf("\n %z\n %*s^--- error here", zCode,iOffset,"");
}else{
|
| ︙ | ︙ | |||
18528 18529 18530 18531 18532 18533 18534 |
int rc;
sqlite3_stmt *pQ = 0;
nVar = sqlite3_bind_parameter_count(pStmt);
if( nVar==0 ) return; /* Nothing to do */
if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters",
"key", 0, 0, 0, 0, 0)!=SQLITE_OK ){
| | > | | | | < > | > > > > > > > > | 18871 18872 18873 18874 18875 18876 18877 18878 18879 18880 18881 18882 18883 18884 18885 18886 18887 18888 18889 18890 18891 18892 18893 18894 18895 18896 18897 18898 18899 18900 18901 18902 18903 18904 18905 18906 18907 18908 18909 |
int rc;
sqlite3_stmt *pQ = 0;
nVar = sqlite3_bind_parameter_count(pStmt);
if( nVar==0 ) return; /* Nothing to do */
if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters",
"key", 0, 0, 0, 0, 0)!=SQLITE_OK ){
rc = SQLITE_NOTFOUND;
pQ = 0;
}else{
rc = sqlite3_prepare_v2(pArg->db,
"SELECT value FROM temp.sqlite_parameters"
" WHERE key=?1", -1, &pQ, 0);
}
for(i=1; i<=nVar; i++){
char zNum[30];
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
if( zVar==0 ){
sqlite3_snprintf(sizeof(zNum),zNum,"?%d",i);
zVar = zNum;
}
sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC);
if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){
sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0));
#ifdef NAN
}else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, NAN);
#endif
#ifdef INFINITY
}else if( sqlite3_strlike("_INF", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, INFINITY);
#endif
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
|
| ︙ | ︙ | |||
18787 18788 18789 18790 18791 18792 18793 | if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); nAlloc = nColumn*4; if( nAlloc<=0 ) nAlloc = 1; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); | | | 19139 19140 19141 19142 19143 19144 19145 19146 19147 19148 19149 19150 19151 19152 19153 |
if( rc!=SQLITE_ROW ) return;
nColumn = sqlite3_column_count(pStmt);
nAlloc = nColumn*4;
if( nAlloc<=0 ) nAlloc = 1;
azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
shell_check_oom(azData);
azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
shell_check_oom(azNextLine);
memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
if( p->cmOpts.bQuote ){
azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
shell_check_oom(azQuoted);
memset(azQuoted, 0, nColumn*sizeof(char*) );
}
abRowDiv = sqlite3_malloc64( nAlloc/nColumn );
|
| ︙ | ︙ | |||
18817 18818 18819 18820 18821 18822 18823 18824 18825 18826 18827 18828 18829 18830 |
const unsigned char *zNotUsed;
int wx = p->colWidth[i];
if( wx==0 ){
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
}
do{
int useNextLine = bNextLine;
bNextLine = 0;
if( (nRow+2)*nColumn >= nAlloc ){
nAlloc *= 2;
| > | 19169 19170 19171 19172 19173 19174 19175 19176 19177 19178 19179 19180 19181 19182 19183 |
const unsigned char *zNotUsed;
int wx = p->colWidth[i];
if( wx==0 ){
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
if( uz==0 ) uz = (u8*)"";
azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
}
do{
int useNextLine = bNextLine;
bNextLine = 0;
if( (nRow+2)*nColumn >= nAlloc ){
nAlloc *= 2;
|
| ︙ | ︙ | |||
19750 19751 19752 19753 19754 19755 19756 | " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL | | | | | > > | 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 | " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE) ".load FILE ?ENTRY? Load an extension library", #endif #if !defined(SQLITE_SHELL_FIDDLE) ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", #else ".log on|off Turn logging on or off.", #endif ".mode MODE ?OPTIONS? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", " box Tables using unicode box-drawing characters", " csv Comma-separated values", " column Output in columns. (See .width)", |
| ︙ | ︙ | |||
19863 19864 19865 19866 19867 19868 19869 | ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)", #endif ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", " Options:", " --indent Try to pretty-print the schema", " --nosys Omit objects whose names start with \"sqlite_\"", | | | 20218 20219 20220 20221 20222 20223 20224 20225 20226 20227 20228 20229 20230 20231 20232 | ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)", #endif ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", " Options:", " --indent Try to pretty-print the schema", " --nosys Omit objects whose names start with \"sqlite_\"", ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", " Options:", " --init Create a new SELFTEST table", " -v Verbose output", ".separator COL ?ROW? Change the column and row separators", #if defined(SQLITE_ENABLE_SESSION) ".session ?NAME? CMD ... Create or control sessions", " Subcommands:", |
| ︙ | ︙ | |||
19905 19906 19907 19908 19909 19910 19911 | " stmt Show statement stats", " vmstep Show the virtual machine step count only", #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) ".system CMD ARGS... Run CMD ARGS... in a system shell", #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", #ifndef SQLITE_SHELL_FIDDLE | | | | 20260 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 | " stmt Show statement stats", " vmstep Show the virtual machine step count only", #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) ".system CMD ARGS... Run CMD ARGS... in a system shell", #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", #ifndef SQLITE_SHELL_FIDDLE ",testcase NAME Begin redirecting output to 'testcase-out.txt'", #endif ",testctrl CMD ... Run various sqlite3_test_control() operations", " Run \".testctrl\" with no arguments for details", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", #ifndef SQLITE_OMIT_TRACE ".trace ?OPTIONS? Output each SQL statement as it is run", " FILE Send output to FILE", " stdout Send output to stdout", |
| ︙ | ︙ | |||
19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 | " --row Trace each row (SQLITE_TRACE_ROW)", " --close Trace connection close (SQLITE_TRACE_CLOSE)", #endif /* SQLITE_OMIT_TRACE */ #ifdef SQLITE_DEBUG ".unmodule NAME ... Unregister virtual table modules", " --allexcept Unregister everything except those named", #endif ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", }; | > | 20286 20287 20288 20289 20290 20291 20292 20293 20294 20295 20296 20297 20298 20299 20300 | " --row Trace each row (SQLITE_TRACE_ROW)", " --close Trace connection close (SQLITE_TRACE_CLOSE)", #endif /* SQLITE_OMIT_TRACE */ #ifdef SQLITE_DEBUG ".unmodule NAME ... Unregister virtual table modules", " --allexcept Unregister everything except those named", #endif ".version Show source, library and compiler versions", ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", }; |
| ︙ | ︙ | |||
19958 19959 19960 19961 19962 19963 19964 |
char *zPat;
if( zPattern==0
|| zPattern[0]=='0'
|| cli_strcmp(zPattern,"-a")==0
|| cli_strcmp(zPattern,"-all")==0
|| cli_strcmp(zPattern,"--all")==0
){
| > > | > > > > > > | > > | > > > > > > > > > > > > > > > | < > | | | | | | > > > > | | 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 20354 20355 20356 20357 20358 20359 20360 20361 20362 20363 20364 20365 20366 20367 20368 20369 20370 20371 20372 20373 20374 20375 20376 20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 20395 20396 |
char *zPat;
if( zPattern==0
|| zPattern[0]=='0'
|| cli_strcmp(zPattern,"-a")==0
|| cli_strcmp(zPattern,"-all")==0
|| cli_strcmp(zPattern,"--all")==0
){
enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 };
enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 };
/* Show all or most commands
** *zPattern==0 => summary of documented commands only
** *zPattern=='0' => whole help for undocumented commands
** Otherwise => whole help for documented commands
*/
enum HelpWanted hw = HW_SummaryOnly;
enum HelpHave hh = HH_More;
if( zPattern!=0 ){
hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull;
}
for(i=0; i<ArraySize(azHelp); i++){
switch( azHelp[i][0] ){
case ',':
hh = HH_Summary|HH_Undoc;
break;
case '.':
hh = HH_Summary;
break;
default:
hh &= ~HH_Summary;
break;
}
if( ((hw^hh)&HH_Undoc)==0 ){
if( (hh&HH_Summary)!=0 ){
utf8_printf(out, ".%s\n", azHelp[i]+1);
++n;
}else if( (hw&HW_SummaryOnly)==0 ){
utf8_printf(out, "%s\n", azHelp[i]);
}
}
}
}else{
/* Seek documented commands for which zPattern is an exact prefix */
zPat = sqlite3_mprintf(".%s*", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
utf8_printf(out, "%s\n", azHelp[i]);
j = i+1;
n++;
}
}
sqlite3_free(zPat);
if( n ){
if( n==1 ){
/* when zPattern is a prefix of exactly one command, then include
** the details of that command, which should begin at offset j */
while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
utf8_printf(out, "%s\n", azHelp[j]);
j++;
}
}
return n;
}
/* Look for documented commands that contain zPattern anywhere.
** Show complete text of all documented commands that match. */
zPat = sqlite3_mprintf("%%%s%%", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( azHelp[i][0]==',' ){
while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
continue;
}
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
utf8_printf(out, "%s\n", azHelp[j]);
while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
j++;
utf8_printf(out, "%s\n", azHelp[j]);
}
i = j;
n++;
}
}
|
| ︙ | ︙ | |||
20033 20034 20035 20036 20037 20038 20039 20040 |
** is undefined in this case.
*/
static char *readFile(const char *zName, int *pnByte){
FILE *in = fopen(zName, "rb");
long nIn;
size_t nRead;
char *pBuf;
if( in==0 ) return 0;
| > | > > > > > | > > > > > | 20418 20419 20420 20421 20422 20423 20424 20425 20426 20427 20428 20429 20430 20431 20432 20433 20434 20435 20436 20437 20438 20439 20440 20441 20442 20443 20444 20445 20446 20447 20448 20449 20450 20451 20452 |
** is undefined in this case.
*/
static char *readFile(const char *zName, int *pnByte){
FILE *in = fopen(zName, "rb");
long nIn;
size_t nRead;
char *pBuf;
int rc;
if( in==0 ) return 0;
rc = fseek(in, 0, SEEK_END);
if( rc!=0 ){
raw_printf(stderr, "Error: '%s' not seekable\n", zName);
fclose(in);
return 0;
}
nIn = ftell(in);
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
if( pBuf==0 ){
raw_printf(stderr, "Error: out of memory\n");
fclose(in);
return 0;
}
nRead = fread(pBuf, nIn, 1, in);
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
raw_printf(stderr, "Error: cannot read '%s'\n", zName);
return 0;
}
pBuf[nIn] = 0;
if( pnByte ) *pnByte = nIn;
return pBuf;
}
|
| ︙ | ︙ | |||
20231 20232 20233 20234 20235 20236 20237 | } sqlite3_free(a); utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine); return 0; } #endif /* SQLITE_OMIT_DESERIALIZE */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 20627 20628 20629 20630 20631 20632 20633 20634 20635 20636 20637 20638 20639 20640 20641 20642 20643 20644 20645 20646 20647 20648 20649 20650 20651 20652 20653 20654 |
}
sqlite3_free(a);
utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
/*
** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X.
*/
static void shellUSleepFunc(
sqlite3_context *context,
int argcUnused,
sqlite3_value **argv
){
int sleep = sqlite3_value_int(argv[0]);
(void)argcUnused;
sqlite3_sleep(sleep/1000);
sqlite3_result_int(context, sleep);
}
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
** but still returns without calling exit.
**
** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
|
| ︙ | ︙ | |||
20442 20443 20444 20445 20446 20447 20448 |
break;
}
}
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
| | > > > | > > > > > | > > > > > | > > > > > < < < | 20700 20701 20702 20703 20704 20705 20706 20707 20708 20709 20710 20711 20712 20713 20714 20715 20716 20717 20718 20719 20720 20721 20722 20723 20724 20725 20726 20727 20728 20729 20730 20731 20732 20733 20734 20735 20736 20737 20738 20739 20740 20741 20742 20743 20744 20745 20746 20747 20748 20749 20750 20751 20752 20753 |
break;
}
}
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,
"Also: unable to open substitute in-memory database.\n"
);
exit(1);
}else{
utf8_printf(stderr,
"Notice: using substitute in-memory database instead of \"%s\"\n",
zDbFilename);
}
}
sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);
/* Reflect the use or absence of --unsafe-testing invocation. */
{
int testmode_on = ShellHasFlag(p,SHFLG_TestingMode);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_fileio_init(p->db, 0, 0);
sqlite3_completion_init(p->db, 0, 0);
#endif
#ifdef SQLITE_HAVE_ZLIB
if( !p->bSafeModePersist ){
sqlite3_zipfile_init(p->db, 0, 0);
sqlite3_sqlar_init(p->db, 0, 0);
}
#endif
#ifdef SQLITE_SHELL_EXTFUNCS
|
| ︙ | ︙ | |||
20507 20508 20509 20510 20511 20512 20513 |
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
| < < < < < < | 20780 20781 20782 20783 20784 20785 20786 20787 20788 20789 20790 20791 20792 20793 |
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
#ifndef SQLITE_NOHAVE_SYSTEM
sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
editFunc, 0, 0);
sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0,
editFunc, 0, 0);
|
| ︙ | ︙ | |||
20539 20540 20541 20542 20543 20544 20545 |
int rc;
int nData = 0;
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
aData = (unsigned char*)readFile(zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
| > | | < > | | > > > > | 20806 20807 20808 20809 20810 20811 20812 20813 20814 20815 20816 20817 20818 20819 20820 20821 20822 20823 20824 20825 20826 20827 20828 20829 20830 20831 20832 20833 20834 20835 20836 20837 20838 20839 20840 20841 20842 |
int rc;
int nData = 0;
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
aData = (unsigned char*)readFile(zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
}
if( aData==0 ){
return;
}
rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
if( p->db!=0 ){
if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
sqlite3_db_config(
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
);
}
}
/*
** Attempt to close the databaes connection. Report errors.
*/
void close_db(sqlite3 *db){
|
| ︙ | ︙ | |||
20615 20616 20617 20618 20619 20620 20621 |
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
i64 nLine = strlen(zLine);
i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
| | | | 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 20897 20898 20899 20900 20901 20902 20903 20904 20905 20906 20907 20908 20909 20910 20911 20912 20913 20914 20915 20916 20917 |
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
i64 nLine = strlen(zLine);
i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
if( nLine>(i64)sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' || zLine[0]=='#') return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
" FROM completion(%Q,%Q) ORDER BY 1",
&zLine[iStart], zLine);
shell_check_oom(zSql);
sqlite3_prepare_v2(globalDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
sqlite3_exec(globalDb, "PRAGMA page_count", 0, 0, 0); /* Load the schema */
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0);
int nCompletion = sqlite3_column_bytes(pStmt, 0);
if( iStart+nCompletion < (i64)sizeof(zBuf)-1 && zCompletion ){
memcpy(zBuf+iStart, zCompletion, nCompletion+1);
linenoiseAddCompletion(lc, zBuf);
}
}
sqlite3_finalize(pStmt);
}
#endif
|
| ︙ | ︙ | |||
20655 20656 20657 20658 20659 20660 20661 20662 20663 20664 20665 20666 20667 20668 |
** \f -> form feed
** \r -> carriage return
** \s -> space
** \" -> "
** \' -> '
** \\ -> backslash
** \NNN -> ascii character NNN in octal
*/
static void resolve_backslashes(char *z){
int i, j;
char c;
while( *z && *z!='\\' ) z++;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' && z[i+1]!=0 ){
| > | 20927 20928 20929 20930 20931 20932 20933 20934 20935 20936 20937 20938 20939 20940 20941 |
** \f -> form feed
** \r -> carriage return
** \s -> space
** \" -> "
** \' -> '
** \\ -> backslash
** \NNN -> ascii character NNN in octal
** \xHH -> ascii character HH in hexadecimal
*/
static void resolve_backslashes(char *z){
int i, j;
char c;
while( *z && *z!='\\' ) z++;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' && z[i+1]!=0 ){
|
| ︙ | ︙ | |||
20683 20684 20685 20686 20687 20688 20689 20690 20691 20692 20693 20694 20695 20696 |
c = '\r';
}else if( c=='"' ){
c = '"';
}else if( c=='\'' ){
c = '\'';
}else if( c=='\\' ){
c = '\\';
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
c = (c<<3) + z[i] - '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
| > > > > > > > > > | 20956 20957 20958 20959 20960 20961 20962 20963 20964 20965 20966 20967 20968 20969 20970 20971 20972 20973 20974 20975 20976 20977 20978 |
c = '\r';
}else if( c=='"' ){
c = '"';
}else if( c=='\'' ){
c = '\'';
}else if( c=='\\' ){
c = '\\';
}else if( c=='x' ){
int nhd = 0, hdv;
u8 hv = 0;
while( nhd<2 && (c=z[i+1+nhd])!=0 && (hdv=hexDigitValue(c))>=0 ){
hv = (u8)((hv<<4)|hdv);
++nhd;
}
i += nhd;
c = (u8)hv;
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
c = (c<<3) + z[i] - '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
i++;
|
| ︙ | ︙ | |||
20782 20783 20784 20785 20786 20787 20788 |
const char *zSql;
i64 nSql;
if( p->traceOut==0 ) return 0;
if( mType==SQLITE_TRACE_CLOSE ){
utf8_printf(p->traceOut, "-- closing database connection\n");
return 0;
}
| | | 21064 21065 21066 21067 21068 21069 21070 21071 21072 21073 21074 21075 21076 21077 21078 |
const char *zSql;
i64 nSql;
if( p->traceOut==0 ) return 0;
if( mType==SQLITE_TRACE_CLOSE ){
utf8_printf(p->traceOut, "-- closing database connection\n");
return 0;
}
if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){
zSql = (const char*)pX;
}else{
pStmt = (sqlite3_stmt*)pP;
switch( p->eTraceType ){
case SHELL_TRACE_EXPANDED: {
zSql = sqlite3_expanded_sql(pStmt);
break;
|
| ︙ | ︙ | |||
20814 20815 20816 20817 20818 20819 20820 |
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
| | > > > | | | 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 21119 21120 21121 21122 21123 21124 21125 21126 21127 21128 |
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
break;
}
}
return 0;
}
#endif
/*
** A no-op routine that runs with the ".breakpoint" doc-command. This is
** a useful spot to set a debugger breakpoint.
**
** This routine does not do anything practical. The code are there simply
** to prevent the compiler from optimizing this routine out.
*/
static void test_breakpoint(void){
static unsigned int nCall = 0;
if( (nCall++)==0xffffffff ) printf("Many .breakpoints have run\n");
}
/*
** An object used to read a CSV and other files for import.
*/
typedef struct ImportCtx ImportCtx;
struct ImportCtx {
|
| ︙ | ︙ | |||
20887 20888 20889 20890 20891 20892 20893 |
** + Keep track of the line number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int c;
| | | | 21172 21173 21174 21175 21176 21177 21178 21179 21180 21181 21182 21183 21184 21185 21186 21187 |
** + Keep track of the line number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int c;
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
c = fgetc(p->in);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
}
if( c=='"' ){
|
| ︙ | ︙ | |||
20977 20978 20979 20980 20981 20982 20983 |
** + Keep track of the row number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
int c;
| | | | 21262 21263 21264 21265 21266 21267 21268 21269 21270 21271 21272 21273 21274 21275 21276 21277 |
** + Keep track of the row number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
** + Report syntax errors on stderr
*/
static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
int c;
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
c = fgetc(p->in);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
}
while( c!=EOF && c!=cSep && c!=rSep ){
|
| ︙ | ︙ | |||
21128 21129 21130 21131 21132 21133 21134 |
char *zQuery = 0;
int rc;
const unsigned char *zName;
const unsigned char *zSql;
char *zErrMsg = 0;
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
| | > | | | | | | > | 21413 21414 21415 21416 21417 21418 21419 21420 21421 21422 21423 21424 21425 21426 21427 21428 21429 21430 21431 21432 21433 21434 21435 21436 21437 21438 21439 21440 21441 21442 21443 21444 21445 21446 21447 |
char *zQuery = 0;
int rc;
const unsigned char *zName;
const unsigned char *zSql;
char *zErrMsg = 0;
zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema"
" WHERE %s ORDER BY rowid ASC", zWhere);
shell_check_oom(zQuery);
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( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){
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);
zErrMsg = 0;
}
}
if( xForEach ){
xForEach(p, newDb, (const char*)zName);
}
printf("done\n");
}
if( rc!=SQLITE_DONE ){
|
| ︙ | ︙ | |||
21170 21171 21172 21173 21174 21175 21176 21177 21178 21179 21180 21181 21182 21183 |
zQuery);
goto end_schema_xfer;
}
while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
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);
zErrMsg = 0;
}
| > | 21457 21458 21459 21460 21461 21462 21463 21464 21465 21466 21467 21468 21469 21470 21471 |
zQuery);
goto end_schema_xfer;
}
while( sqlite3_step(pQuery)==SQLITE_ROW ){
zName = sqlite3_column_text(pQuery, 0);
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue;
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);
zErrMsg = 0;
}
|
| ︙ | ︙ | |||
21273 21274 21275 21276 21277 21278 21279 |
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
res = sqlite3_column_int(pStmt,0);
}
sqlite3_finalize(pStmt);
return res;
}
| | | 21561 21562 21563 21564 21565 21566 21567 21568 21569 21570 21571 21572 21573 21574 21575 |
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
res = sqlite3_column_int(pStmt,0);
}
sqlite3_finalize(pStmt);
return res;
}
#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
static unsigned int get2byteInt(unsigned char *a){
return (a[0]<<8) + a[1];
}
static unsigned int get4byteInt(unsigned char *a){
|
| ︙ | ︙ | |||
21336 21337 21338 21339 21340 21341 21342 |
sqlite3_finalize(pStmt);
return 1;
}
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
if( sqlite3_step(pStmt)==SQLITE_ROW
&& sqlite3_column_bytes(pStmt,0)>100
){
| | > > | 21624 21625 21626 21627 21628 21629 21630 21631 21632 21633 21634 21635 21636 21637 21638 21639 21640 |
sqlite3_finalize(pStmt);
return 1;
}
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
if( sqlite3_step(pStmt)==SQLITE_ROW
&& sqlite3_column_bytes(pStmt,0)>100
){
const u8 *pb = sqlite3_column_blob(pStmt,0);
shell_check_oom(pb);
memcpy(aHdr, pb, 100);
sqlite3_finalize(pStmt);
}else{
raw_printf(stderr, "unable to read database header\n");
sqlite3_finalize(pStmt);
return 1;
}
i = get2byteInt(aHdr+16);
|
| ︙ | ︙ | |||
22153 22154 22155 22156 22157 22158 22159 |
zArg = azArg[++iArg];
}
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
}
}
}
}
| > > > | | 22443 22444 22445 22446 22447 22448 22449 22450 22451 22452 22453 22454 22455 22456 22457 22458 22459 22460 |
zArg = azArg[++iArg];
}
if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
}
}
}
}
if( pAr->eCmd==0 ){
utf8_printf(stderr, "Required argument missing. Usage:\n");
return arUsage(stderr);
}
return SQLITE_OK;
}
/*
** This function assumes that all arguments within the ArCommand.azArg[]
** array refer to archive members, as for the --extract, --list or --remove
** commands. It checks that each of them are "present". If any specified
|
| ︙ | ︙ | |||
22919 22920 22921 22922 22923 22924 22925 |
rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
rc_err_oom_die(rc);
rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
rc_err_oom_die(rc);
sqlite3_bind_int(pStmt, 1, nDigits);
rc = sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
| | | 23212 23213 23214 23215 23216 23217 23218 23219 23220 23221 23222 23223 23224 23225 23226 |
rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
rc_err_oom_die(rc);
rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
rc_err_oom_die(rc);
sqlite3_bind_int(pStmt, 1, nDigits);
rc = sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
}
assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
rc_err_oom_die(rc);
rc = sqlite3_step(pStmt);
if( rc==SQLITE_ROW ){
zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
|
| ︙ | ︙ | |||
23169 23170 23171 23172 23173 23174 23175 |
if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){
char *zRes = 0;
output_reset(p);
if( nArg!=2 ){
raw_printf(stderr, "Usage: .check GLOB-PATTERN\n");
rc = 2;
}else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
| < | 23462 23463 23464 23465 23466 23467 23468 23469 23470 23471 23472 23473 23474 23475 |
if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){
char *zRes = 0;
output_reset(p);
if( nArg!=2 ){
raw_printf(stderr, "Usage: .check GLOB-PATTERN\n");
rc = 2;
}else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
rc = 2;
}else if( testcase_glob(azArg[1],zRes)==0 ){
utf8_printf(stderr,
"testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
p->zTestcase, azArg[1], zRes);
rc = 1;
}else{
|
| ︙ | ︙ | |||
23299 23300 23301 23302 23303 23304 23305 23306 23307 23308 23309 23310 23311 23312 |
{ "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW },
{ "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
{ "legacy_alter_table", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE },
{ "legacy_file_format", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
{ "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
{ "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
{ "reset_database", SQLITE_DBCONFIG_RESET_DATABASE },
{ "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP },
{ "trusted_schema", SQLITE_DBCONFIG_TRUSTED_SCHEMA },
{ "writable_schema", SQLITE_DBCONFIG_WRITABLE_SCHEMA },
};
int ii, v;
open_db(p, 0);
for(ii=0; ii<ArraySize(aDbConfig); ii++){
| > > | 23591 23592 23593 23594 23595 23596 23597 23598 23599 23600 23601 23602 23603 23604 23605 23606 |
{ "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW },
{ "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
{ "legacy_alter_table", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE },
{ "legacy_file_format", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
{ "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
{ "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
{ "reset_database", SQLITE_DBCONFIG_RESET_DATABASE },
{ "reverse_scanorder", SQLITE_DBCONFIG_REVERSE_SCANORDER },
{ "stmt_scanstatus", SQLITE_DBCONFIG_STMT_SCANSTATUS },
{ "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP },
{ "trusted_schema", SQLITE_DBCONFIG_TRUSTED_SCHEMA },
{ "writable_schema", SQLITE_DBCONFIG_WRITABLE_SCHEMA },
};
int ii, v;
open_db(p, 0);
for(ii=0; ii<ArraySize(aDbConfig); ii++){
|
| ︙ | ︙ | |||
23850 23851 23852 23853 23854 23855 23856 |
nSep = strlen30(p->rowSeparator);
}
if( nSep>1 ){
raw_printf(stderr, "Error: multi-character row separators not allowed"
" for import\n");
goto meta_command_exit;
}
| | | | 24144 24145 24146 24147 24148 24149 24150 24151 24152 24153 24154 24155 24156 24157 24158 24159 |
nSep = strlen30(p->rowSeparator);
}
if( nSep>1 ){
raw_printf(stderr, "Error: multi-character row separators not allowed"
" for import\n");
goto meta_command_exit;
}
sCtx.cColSep = (u8)p->colSeparator[0];
sCtx.cRowSep = (u8)p->rowSeparator[0];
}
sCtx.zFile = zFile;
sCtx.nLine = 1;
if( sCtx.zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
raw_printf(stderr, "Error: pipes are not supported in this OS\n");
goto meta_command_exit;
|
| ︙ | ︙ | |||
24047 24048 24049 24050 24051 24052 24053 24054 24055 24056 24057 24058 24059 24060 |
char *zSql;
char *zCollist = 0;
sqlite3_stmt *pStmt;
int tnum = 0;
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
" .imposter off\n");
/* Also allowed, but not documented:
**
** .imposter TABLE IMPOSTER
**
| > > > > > > | 24341 24342 24343 24344 24345 24346 24347 24348 24349 24350 24351 24352 24353 24354 24355 24356 24357 24358 24359 24360 |
char *zSql;
char *zCollist = 0;
sqlite3_stmt *pStmt;
int tnum = 0;
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
if( !ShellHasFlag(p,SHFLG_TestingMode) ){
utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n",
"imposter");
rc = 1;
goto meta_command_exit;
}
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
" .imposter off\n");
/* Also allowed, but not documented:
**
** .imposter TABLE IMPOSTER
**
|
| ︙ | ︙ | |||
24231 24232 24233 24234 24235 24236 24237 |
}else
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE)
if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
failIfSafeMode(p, "cannot run .load in safe mode");
| | > < < > > > > > > > > > < | 24531 24532 24533 24534 24535 24536 24537 24538 24539 24540 24541 24542 24543 24544 24545 24546 24547 24548 24549 24550 24551 24552 24553 24554 24555 24556 24557 24558 24559 24560 24561 24562 24563 24564 24565 24566 24567 24568 24569 24570 24571 24572 24573 24574 24575 24576 24577 24578 24579 24580 24581 |
}else
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE)
if( c=='l' && cli_strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 || azArg[1][0]==0 ){
/* Must have a non-empty FILE. (Will not load self.) */
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' && cli_strncmp(azArg[0], "log", n)==0 ){
if( nArg!=2 ){
raw_printf(stderr, "Usage: .log FILENAME\n");
rc = 1;
}else{
const char *zFile = azArg[1];
if( p->bSafeMode
&& cli_strcmp(zFile,"on")!=0
&& cli_strcmp(zFile,"off")!=0
){
raw_printf(stdout, "cannot set .log to anything other "
"than \"on\" or \"off\"\n");
zFile = "off";
}
output_file_close(p->pLog);
if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout";
p->pLog = output_file_open(zFile, 0);
}
}else
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
const char *zMode = 0;
const char *zTabname = 0;
int i, n2;
ColModeOpts cmOpts = ColModeOpts_default;
for(i=1; i<nArg; i++){
|
| ︙ | ︙ | |||
24896 24897 24898 24899 24900 24901 24902 24903 24904 24905 24906 24907 24908 24909 |
if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
if( nArg==2 ){
if( cli_strcmp(azArg[1], "est")==0 ){
p->scanstatsOn = 2;
}else{
p->scanstatsOn = (u8)booleanValue(azArg[1]);
}
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
#endif
}else{
raw_printf(stderr, "Usage: .scanstats on|off|est\n");
rc = 1;
}
| > > > > | 25203 25204 25205 25206 25207 25208 25209 25210 25211 25212 25213 25214 25215 25216 25217 25218 25219 25220 |
if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
if( nArg==2 ){
if( cli_strcmp(azArg[1], "est")==0 ){
p->scanstatsOn = 2;
}else{
p->scanstatsOn = (u8)booleanValue(azArg[1]);
}
open_db(p, 0);
sqlite3_db_config(
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
);
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
#endif
}else{
raw_printf(stderr, "Usage: .scanstats on|off|est\n");
rc = 1;
}
|
| ︙ | ︙ | |||
25545 25546 25547 25548 25549 25550 25551 |
"||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
"|| ' AND typeof('||cname||')=''text'' ',\n"
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
| | > > > > | | | | | | | | > > | | | | | | | | | | | | | > > | 25856 25857 25858 25859 25860 25861 25862 25863 25864 25865 25866 25867 25868 25869 25870 25871 25872 25873 25874 25875 25876 25877 25878 25879 25880 25881 25882 25883 25884 25885 25886 25887 25888 25889 25890 25891 25892 25893 25894 25895 25896 25897 25898 25899 |
"||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n"
"|| ' AND typeof('||cname||')=''text'' ',\n"
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
if( lrc!=SQLITE_OK ){
/* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the
** user does cruel and unnatural things like ".limit expr_depth 0". */
rc = 1;
}else{
if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC);
lrc = SQLITE_ROW==sqlite3_step(pStmt);
if( lrc ){
const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
sqlite3_stmt *pCheckStmt;
lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery);
if( lrc!=SQLITE_OK ){
rc = 1;
}else{
if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){
double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
if( countIrreversible>0 ){
int sz = (int)(countIrreversible + 0.5);
utf8_printf(stderr,
"Digest includes %d invalidly encoded text field%s.\n",
sz, (sz>1)? "s": "");
}
}
sqlite3_finalize(pCheckStmt);
}
sqlite3_finalize(pStmt);
}
}
if( rc ) utf8_printf(stderr, ".sha3sum failed.\n");
sqlite3_free(zRevText);
}
#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
sqlite3_free(zSql);
}else
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE)
|
| ︙ | ︙ | |||
25829 25830 25831 25832 25833 25834 25835 25836 25837 25838 25839 25840 25841 25842 |
int testctrl = -1;
int iCtrl = -1;
int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
int isOk = 0;
int i, n2;
const char *zCmd = 0;
open_db(p, 0);
zCmd = nArg>=2 ? azArg[1] : "help";
/* The argument can optionally begin with "-" or "--" */
if( zCmd[0]=='-' && zCmd[1] ){
zCmd++;
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
| > > > > > > | 26148 26149 26150 26151 26152 26153 26154 26155 26156 26157 26158 26159 26160 26161 26162 26163 26164 26165 26166 26167 |
int testctrl = -1;
int iCtrl = -1;
int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
int isOk = 0;
int i, n2;
const char *zCmd = 0;
if( !ShellHasFlag(p,SHFLG_TestingMode) ){
utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n",
"testctrl");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
zCmd = nArg>=2 ? azArg[1] : "help";
/* The argument can optionally begin with "-" or "--" */
if( zCmd[0]=='-' && zCmd[1] ){
zCmd++;
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
|
| ︙ | ︙ | |||
26637 26638 26639 26640 26641 26642 26643 |
free(home_dir);
home_dir = 0;
return 0;
}
if( home_dir ) return home_dir;
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \
| | | 26962 26963 26964 26965 26966 26967 26968 26969 26970 26971 26972 26973 26974 26975 26976 |
free(home_dir);
home_dir = 0;
return 0;
}
if( home_dir ) return home_dir;
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \
&& !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI)
{
struct passwd *pwent;
uid_t uid = getuid();
if( (pwent=getpwuid(uid)) != NULL) {
home_dir = pwent->pw_dir;
}
}
|
| ︙ | ︙ | |||
26776 26777 26778 26779 26780 26781 26782 26783 26784 26785 26786 26787 26788 26789 | sqlite3_free(zBuf); } /* ** Show available command line options */ static const char zOptions[] = #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) " -A ARGS... run \".archive ARGS\" and exit\n" #endif " -append append the database to the end of the file\n" " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" | > | 27101 27102 27103 27104 27105 27106 27107 27108 27109 27110 27111 27112 27113 27114 27115 | sqlite3_free(zBuf); } /* ** Show available command line options */ static const char zOptions[] = " -- treat no subsequent arguments as options\n" #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) " -A ARGS... run \".archive ARGS\" and exit\n" #endif " -append append the database to the end of the file\n" " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" |
| ︙ | ︙ | |||
26827 26828 26829 26830 26831 26832 26833 26834 26835 26836 26837 26838 26839 26840 26841 26842 26843 26844 |
" -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"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
" -vfstrace enable tracing of all VFS calls\n"
#endif
#ifdef SQLITE_HAVE_ZLIB
" -zip open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
utf8_printf(stderr,
| > > > > | | | 27153 27154 27155 27156 27157 27158 27159 27160 27161 27162 27163 27164 27165 27166 27167 27168 27169 27170 27171 27172 27173 27174 27175 27176 27177 27178 27179 27180 27181 27182 27183 27184 |
" -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"
" -unsafe-testing allow unsafe commands and modes for testing\n"
#if SHELL_WIN_UTF8_OPT
" -utf8 setup interactive console code page for UTF-8\n"
#endif
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
" -vfstrace enable tracing of all VFS calls\n"
#endif
#ifdef SQLITE_HAVE_ZLIB
" -zip open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
utf8_printf(stderr,
"Usage: %s [OPTIONS] [FILENAME [SQL]]\n"
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
utf8_printf(stderr, "OPTIONS include:\n%s", zOptions);
}else{
raw_printf(stderr, "Use the -help option for additional information\n");
}
exit(1);
}
|
| ︙ | ︙ | |||
26872 26873 26874 26875 26876 26877 26878 26879 26880 | data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; data->pAuxDb = &data->aAuxDb[0]; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_URI, 1); | > > > < | 27202 27203 27204 27205 27206 27207 27208 27209 27210 27211 27212 27213 27214 27215 27216 27217 27218 27219 27220 | data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; data->pAuxDb = &data->aAuxDb[0]; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); #if !defined(SQLITE_SHELL_FIDDLE) verify_uninitialized(); #endif sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); } /* ** Output text to the console in a font that attracts extra attention. |
| ︙ | ︙ | |||
26916 26917 26918 26919 26920 26921 26922 26923 26924 26925 26926 26927 26928 26929 |
if( i==argc ){
utf8_printf(stderr, "%s: Error: missing argument to %s\n",
argv[0], argv[argc-1]);
exit(1);
}
return argv[i];
}
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
| > > > > | 27248 27249 27250 27251 27252 27253 27254 27255 27256 27257 27258 27259 27260 27261 27262 27263 27264 27265 |
if( i==argc ){
utf8_printf(stderr, "%s: Error: missing argument to %s\n",
argv[0], argv[argc-1]);
exit(1);
}
return argv[i];
}
static void sayAbnormalExit(void){
if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
}
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
|
| ︙ | ︙ | |||
26937 26938 26939 26940 26941 26942 26943 |
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char **argv;
#endif
#ifdef SQLITE_DEBUG
| | > < < > | > > > > > > > > > > > > > > | 27273 27274 27275 27276 27277 27278 27279 27280 27281 27282 27283 27284 27285 27286 27287 27288 27289 27290 27291 27292 27293 27294 27295 27296 27297 27298 27299 27300 27301 27302 27303 27304 27305 27306 27307 27308 27309 27310 27311 27312 27313 27314 27315 27316 27317 27318 27319 27320 27321 27322 27323 27324 27325 27326 27327 27328 27329 27330 27331 27332 27333 27334 27335 27336 27337 27338 27339 27340 27341 27342 27343 27344 27345 27346 27347 27348 27349 27350 27351 |
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char **argv;
#endif
#ifdef SQLITE_DEBUG
sqlite3_int64 mem_main_enter = 0;
#endif
char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
# define data shellState
#else
ShellState data;
#endif
const char *zInitFile = 0;
int i;
int rc = 0;
int warnInmemoryDb = 0;
int readStdin = 1;
int nCmd = 0;
int nOptsEnd = argc;
char **azCmd = 0;
const char *zVfs = 0; /* Value of -vfs command-line option */
#if !SQLITE_SHELL_IS_UTF8
char **argvToFree = 0;
int argcToFree = 0;
#endif
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
#ifdef SQLITE_SHELL_FIDDLE
stdin_is_interactive = 0;
stdout_is_console = 1;
data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
#endif
#if SHELL_WIN_UTF8_OPT
atexit(console_restore); /* Needs revision for CLI as library call */
#endif
atexit(sayAbnormalExit);
#ifdef SQLITE_DEBUG
mem_main_enter = sqlite3_memory_used();
#endif
#if !defined(_WIN32_WCE)
if( getenv("SQLITE_DEBUG_BREAK") ){
if( isatty(0) && isatty(2) ){
fprintf(stderr,
"attach debugger to process %d and press any key to continue.\n",
GETPID());
fgetc(stdin);
}else{
#if defined(_WIN32) || defined(WIN32)
#if SQLITE_OS_WINRT
__debugbreak();
#else
DebugBreak();
#endif
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
}
#endif
/* Register a valid signal handler early, before much else is done. */
#ifdef SIGINT
signal(SIGINT, interrupt_handler);
#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){
fprintf(stderr, "No ^C handler.\n");
}
#endif
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
|
| ︙ | ︙ | |||
27028 27029 27030 27031 27032 27033 27034 | } sqlite3_shutdown(); #endif assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; | < < < < < < < < < > > | > > > > | | 27378 27379 27380 27381 27382 27383 27384 27385 27386 27387 27388 27389 27390 27391 27392 27393 27394 27395 27396 27397 27398 27399 27400 27401 27402 27403 27404 27405 27406 27407 27408 27409 27410 27411 27412 27413 27414 27415 27416 27417 27418 27419 27420 27421 27422 27423 27424 27425 27426 27427 27428 27429 27430 27431 27432 27433 |
}
sqlite3_shutdown();
#endif
assert( argc>=1 && argv && argv[0] );
Argv0 = argv[0];
#ifdef SQLITE_SHELL_DBNAME_PROC
{
/* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
** of a C-function that will provide the name of the database file. Use
** this compile-time option to embed this shell program in larger
** applications. */
extern void SQLITE_SHELL_DBNAME_PROC(const char**);
SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
warnInmemoryDb = 0;
}
#endif
/* Do an initial pass through the command-line argument to locate
** the name of the database file, the name of the initialization file,
** the size of the alternative malloc heap,
** and the first command to execute.
*/
#ifndef SQLITE_SHELL_FIDDLE
verify_uninitialized();
#endif
for(i=1; i<argc; i++){
char *z;
z = argv[i];
if( z[0]!='-' || i>nOptsEnd ){
if( data.aAuxDb->zDbFilename==0 ){
data.aAuxDb->zDbFilename = z;
}else{
/* Excesss arguments are interpreted as SQL (or dot-commands) and
** mean that nothing is read from stdin */
readStdin = 0;
nCmd++;
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
shell_check_oom(azCmd);
azCmd[nCmd-1] = z;
}
continue;
}
if( z[1]=='-' ) z++;
if( cli_strcmp(z, "-")==0 ){
nOptsEnd = i;
continue;
}else if( cli_strcmp(z,"-separator")==0
|| cli_strcmp(z,"-nullvalue")==0
|| cli_strcmp(z,"-newline")==0
|| cli_strcmp(z,"-cmd")==0
){
(void)cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-init")==0 ){
zInitFile = cmdline_option_value(argc, argv, ++i);
|
| ︙ | ︙ | |||
27094 27095 27096 27097 27098 27099 27100 27101 27102 27103 27104 27105 27106 27107 27108 27109 27110 27111 27112 27113 27114 27115 27116 27117 27118 27119 27120 27121 27122 27123 27124 27125 27126 27127 27128 27129 27130 27131 27132 27133 27134 |
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
sqlite3_int64 szHeap;
zSize = cmdline_option_value(argc, argv, ++i);
szHeap = integerValue(zSize);
if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#else
(void)cmdline_option_value(argc, argv, ++i);
#endif
}else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>70000 ) sz = 70000;
if( sz<0 ) sz = 0;
n = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
n = 0xffffffffffffLL/sz;
}
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
data.shellFlgs |= SHFLG_Pagecache;
}else if( cli_strcmp(z,"-lookaside")==0 ){
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( cli_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( cli_strcmp(z,"-vfstrace")==0 ){
| > > > > | 27441 27442 27443 27444 27445 27446 27447 27448 27449 27450 27451 27452 27453 27454 27455 27456 27457 27458 27459 27460 27461 27462 27463 27464 27465 27466 27467 27468 27469 27470 27471 27472 27473 27474 27475 27476 27477 27478 27479 27480 27481 27482 27483 27484 27485 |
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
sqlite3_int64 szHeap;
zSize = cmdline_option_value(argc, argv, ++i);
szHeap = integerValue(zSize);
if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#else
(void)cmdline_option_value(argc, argv, ++i);
#endif
}else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>70000 ) sz = 70000;
if( sz<0 ) sz = 0;
n = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
n = 0xffffffffffffLL/sz;
}
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
data.shellFlgs |= SHFLG_Pagecache;
}else if( cli_strcmp(z,"-lookaside")==0 ){
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;
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
int n;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
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( cli_strcmp(z,"-vfstrace")==0 ){
|
| ︙ | ︙ | |||
27144 27145 27146 27147 27148 27149 27150 27151 |
#ifdef SQLITE_ENABLE_MULTIPLEX
}else if( cli_strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiple_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1);
#endif
}else if( cli_strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
| > | > | 27495 27496 27497 27498 27499 27500 27501 27502 27503 27504 27505 27506 27507 27508 27509 27510 27511 27512 27513 27514 |
#ifdef SQLITE_ENABLE_MULTIPLEX
}else if( cli_strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiple_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1);
#endif
}else if( cli_strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
#if defined(SQLITE_ENABLE_SORTER_REFERENCES)
}else if( cli_strcmp(z,"-sorterref")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
#endif
}else if( cli_strcmp(z,"-vfs")==0 ){
zVfs = cmdline_option_value(argc, argv, ++i);
#ifdef SQLITE_HAVE_ZLIB
}else if( cli_strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
|
| ︙ | ︙ | |||
27181 27182 27183 27184 27185 27186 27187 27188 27189 27190 27191 27192 27193 27194 27195 27196 27197 27198 27199 |
}else if( cli_strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(stderr);
}else if( cli_strcmp(z,"-bail")==0 ){
bail_on_error = 1;
}else if( cli_strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(argv[++i]);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
verify_uninitialized();
#ifdef SQLITE_SHELL_INIT_PROC
{
/* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
** of a C-function that will perform initialization actions on SQLite that
** occur just before or after sqlite3_initialize(). Use this compile-time
| > > > > | 27534 27535 27536 27537 27538 27539 27540 27541 27542 27543 27544 27545 27546 27547 27548 27549 27550 27551 27552 27553 27554 27555 27556 |
}else if( cli_strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(stderr);
}else if( cli_strcmp(z,"-bail")==0 ){
bail_on_error = 1;
}else if( cli_strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(argv[++i]);
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
ShellSetFlag(&data,SHFLG_TestingMode);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
#ifndef SQLITE_SHELL_FIDDLE
verify_uninitialized();
#endif
#ifdef SQLITE_SHELL_INIT_PROC
{
/* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
** of a C-function that will perform initialization actions on SQLite that
** occur just before or after sqlite3_initialize(). Use this compile-time
|
| ︙ | ︙ | |||
27249 27250 27251 27252 27253 27254 27255 |
/* Make a second pass through the command-line argument and set
** options. This second pass is delayed until after the initialization
** file is processed so that the command-line arguments will override
** settings in the initialization file.
*/
for(i=1; i<argc; i++){
char *z = argv[i];
| | | 27606 27607 27608 27609 27610 27611 27612 27613 27614 27615 27616 27617 27618 27619 27620 |
/* Make a second pass through the command-line argument and set
** options. This second pass is delayed until after the initialization
** file is processed so that the command-line arguments will override
** settings in the initialization file.
*/
for(i=1; i<argc; i++){
char *z = argv[i];
if( z[0]!='-' || i>=nOptsEnd ) continue;
if( z[1]=='-' ){ z++; }
if( cli_strcmp(z,"-init")==0 ){
i++;
}else if( cli_strcmp(z,"-html")==0 ){
data.mode = MODE_Html;
}else if( cli_strcmp(z,"-list")==0 ){
data.mode = MODE_List;
|
| ︙ | ︙ | |||
27341 27342 27343 27344 27345 27346 27347 27348 27349 27350 27351 27352 27353 27354 |
}else if( cli_strcmp(z,"-version")==0 ){
printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
return 0;
}else if( cli_strcmp(z,"-interactive")==0 ){
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( cli_strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
| > > > > | 27698 27699 27700 27701 27702 27703 27704 27705 27706 27707 27708 27709 27710 27711 27712 27713 27714 27715 |
}else if( cli_strcmp(z,"-version")==0 ){
printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
return 0;
}else if( cli_strcmp(z,"-interactive")==0 ){
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-utf8")==0 ){
#if SHELL_WIN_UTF8_OPT
console_utf8 = 1;
#endif /* SHELL_WIN_UTF8_OPT */
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( cli_strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
|
| ︙ | ︙ | |||
27411 27412 27413 27414 27415 27416 27417 27418 27419 27420 27421 27422 27423 27424 27425 27426 27427 27428 27429 27430 27431 27432 27433 27434 27435 27436 27437 27438 27439 27440 27441 27442 27443 27444 27445 27446 |
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
#endif
}else if( cli_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;
}
if( !readStdin ){
/* Run all arguments that do not begin with '-' as if they were separate
** command-line inputs, except for the argToSkip argument which contains
** the database filename.
*/
for(i=0; i<nCmd; i++){
if( azCmd[i][0]=='.' ){
rc = do_meta_command(azCmd[i], &data);
if( rc ){
free(azCmd);
return rc==2 ? 0 : rc;
}
}else{
open_db(&data, 0);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
utf8_printf(stderr,"Error: %s\n", zErrMsg);
}else{
utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
}
| > > > > > > > > > > > | 27772 27773 27774 27775 27776 27777 27778 27779 27780 27781 27782 27783 27784 27785 27786 27787 27788 27789 27790 27791 27792 27793 27794 27795 27796 27797 27798 27799 27800 27801 27802 27803 27804 27805 27806 27807 27808 27809 27810 27811 27812 27813 27814 27815 27816 27817 27818 |
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
#endif
}else if( cli_strcmp(z,"-safe")==0 ){
data.bSafeMode = data.bSafeModePersist = 1;
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
/* Acted upon in first pass. */
}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;
}
#if SHELL_WIN_UTF8_OPT
if( console_utf8 && stdin_is_interactive ){
console_prepare();
}else{
setBinaryMode(stdin, 0);
console_utf8 = 0;
}
#endif
if( !readStdin ){
/* Run all arguments that do not begin with '-' as if they were separate
** command-line inputs, except for the argToSkip argument which contains
** the database filename.
*/
for(i=0; i<nCmd; i++){
if( azCmd[i][0]=='.' ){
rc = do_meta_command(azCmd[i], &data);
if( rc ){
free(azCmd);
return rc==2 ? 0 : rc;
}
}else{
open_db(&data, 0);
echo_group_input(&data, azCmd[i]);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
utf8_printf(stderr,"Error: %s\n", zErrMsg);
}else{
utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
}
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | 1 2 3 4 5 6 7 8 9 10 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.42.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other |
| ︙ | ︙ | |||
119 120 121 122 123 124 125 126 127 128 129 130 131 132 | #endif /* defined(_MSC_VER) */ #if defined(_MSC_VER) && !defined(_WIN64) #undef SQLITE_4_BYTE_ALIGNED_MALLOC #define SQLITE_4_BYTE_ALIGNED_MALLOC #endif /* defined(_MSC_VER) && !defined(_WIN64) */ #endif /* SQLITE_MSVC_H */ /************** End of msvc.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Special setup for VxWorks | > > > > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #endif /* defined(_MSC_VER) */ #if defined(_MSC_VER) && !defined(_WIN64) #undef SQLITE_4_BYTE_ALIGNED_MALLOC #define SQLITE_4_BYTE_ALIGNED_MALLOC #endif /* defined(_MSC_VER) && !defined(_WIN64) */ #if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 #define HAVE_LOG2 0 #endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */ #endif /* SQLITE_MSVC_H */ /************** End of msvc.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Special setup for VxWorks |
| ︙ | ︙ | |||
448 449 450 451 452 453 454 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.42.0" #define SQLITE_VERSION_NUMBER 3042000 #define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" /* ** 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 |
| ︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 | ** file to the database file. ** ** <li>[[SQLITE_FCNTL_CKPT_DONE]] ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. | < < > | | | | | 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 | ** file to the database file. ** ** <li>[[SQLITE_FCNTL_CKPT_DONE]] ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** ** <li>[[SQLITE_FCNTL_EXTERNAL_READER]] ** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ** whether or not there is a database client in another process with a wal-mode ** transaction open on the database or not. It is only available on unix.The ** (void*) argument passed with this file-control should be a pointer to a ** value of type (int). The integer value is set to 1 if the database is a wal ** mode database and there exists at least one client in another process that ** currently has an SQL transaction open on the database. It is set to 0 if ** the database is not a wal-mode db, or if there is no such connection in any ** other process. This opcode cannot be used to detect transactions opened ** by clients within the current process, only within other processes. ** ** <li>[[SQLITE_FCNTL_CKSM_FILE]] ** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the ** [checksum VFS shim] only. ** ** <li>[[SQLITE_FCNTL_RESET_CACHE]] ** If there is currently no transaction open on the database, and the ** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control ** purges the contents of the in-memory page cache. If there is an open ** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 #define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 #define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 |
| ︙ | ︙ | |||
1958 1959 1960 1961 1962 1963 1964 | ** applications and so this routine is usually not necessary. It is ** provided to support rare applications with unusual needs. ** ** <b>The sqlite3_config() interface is not threadsafe. The application ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running.</b> ** | < < < < < < < < > > > > > > > > > > > | 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 | ** applications and so this routine is usually not necessary. It is ** provided to support rare applications with unusual needs. ** ** <b>The sqlite3_config() interface is not threadsafe. The application ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running.</b> ** ** The first argument to sqlite3_config() is an integer ** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [configuration option] ** in the first argument. ** ** For most configuration options, the sqlite3_config() interface ** may only be invoked prior to library initialization using ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. ** The exceptional configuration options that may be invoked at any time ** are called "anytime configuration options". ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ** [sqlite3_shutdown()] with a first argument that is not an anytime ** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. ** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ SQLITE_API int sqlite3_config(int, ...); |
| ︙ | ︙ | |||
2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 |
/*
** CAPI3REF: Configuration Options
** KEYWORDS: {configuration option}
**
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
** the call worked. The [sqlite3_config()] interface will return a
** non-zero [error code] if a discontinued or unsupported configuration option
** is invoked.
| > > > > > > > > > > > > > > > > > | 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 |
/*
** CAPI3REF: Configuration Options
** KEYWORDS: {configuration option}
**
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
** Most of the configuration options for sqlite3_config()
** will only work if invoked prior to [sqlite3_initialize()] or after
** [sqlite3_shutdown()]. The few exceptions to this rule are called
** "anytime configuration options".
** ^Calling [sqlite3_config()] with a first argument that is not an
** anytime configuration option in between calls to [sqlite3_initialize()] and
** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
**
** The set of anytime configuration options can change (by insertions
** and/or deletions) from one release of SQLite to the next.
** As of SQLite version 3.42.0, the complete set of anytime configuration
** options is:
** <ul>
** <li> SQLITE_CONFIG_LOG
** <li> SQLITE_CONFIG_PCACHE_HDRSZ
** </ul>
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
** the call worked. The [sqlite3_config()] interface will return a
** non-zero [error code] if a discontinued or unsupported configuration option
** is invoked.
|
| ︙ | ︙ | |||
2425 2426 2427 2428 2429 2430 2431 | ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ | | | | | | | | | | | | | | | | | | | | | | | 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 | ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* no-op */ #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ |
| ︙ | ︙ | |||
2681 2682 2683 2684 2685 2686 2687 | ** behaves as it did prior to [version 3.24.0] (2018-06-04). See the ** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for ** additional information. This feature can also be turned on and off ** using the [PRAGMA legacy_alter_table] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DML]] | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** behaves as it did prior to [version 3.24.0] (2018-06-04). See the ** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for ** additional information. This feature can also be turned on and off ** using the [PRAGMA legacy_alter_table] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DML]] ** <dt>SQLITE_DBCONFIG_DQS_DML</dt> ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DML statements ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The ** default value of this setting is determined by the [-DSQLITE_DQS] ** compile-time option. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DDL]] ** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DDL statements, ** such as CREATE TABLE and CREATE INDEX. The ** default value of this setting is determined by the [-DSQLITE_DQS] ** compile-time option. ** </dd> ** ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to ** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite ** takes additional defensive steps to protect the application from harm ** including: ** <ul> ** <li> Prohibit the use of SQL functions inside triggers, views, ** CHECK constraints, DEFAULT clauses, expression indexes, ** partial indexes, or generated columns ** unless those functions are tagged with [SQLITE_INNOCUOUS]. ** <li> Prohibit the use of virtual tables inside of triggers or views ** unless those virtual tables are tagged with [SQLITE_VTAB_INNOCUOUS]. ** </ul> ** This setting defaults to "on" for legacy compatibility, however ** all applications are advised to turn it off if possible. This setting ** can also be controlled using the [PRAGMA trusted_schema] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database file to have a schema format version number (the 4-byte ** integer found at offset 44 into the database header) of 1. This in turn ** means that the resulting database file will be readable and writable by ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there ** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little ** practical use, but is provided so that SQLite can continue to claim the ** ability to generate new database files that are compatible with version ** 3.0.0. ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, ** the [VACUUM] command will fail with an obscure error when attempting to ** process a table with generated columns and a descending index. This is ** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** either generated columns or decending indexes. ** </dd> ** ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) ** by default. This option takes two arguments: an integer and a pointer to ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after ** processing the first argument is written into the integer that the second ** argument points to. ** </dd> ** ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ** same as setting [PRAGMA reverse_unordered_selects]. This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, ** then 0 or 1 is written into the integer that the second argument points to ** depending on if the reverse scan order flag is set after processing the ** first argument. ** </dd> ** ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ #define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */ #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
| ︙ | ︙ | |||
6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 | ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is | > > > > > > > | 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 | ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. ** ** If a negative argument is passed to sqlite3_sleep() the results vary by ** VFS and operating system. Some system treat a negative argument as an ** instruction to sleep forever. Others understand it to mean do not sleep ** at all. ^In SQLite version 3.42.0 and later, a negative ** argument passed into sqlite3_sleep() is changed to zero before it is relayed ** down into the xSleep method of the VFS. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is |
| ︙ | ︙ | |||
8131 8132 8133 8134 8135 8136 8137 | ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** | | | | | 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 | ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), ** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, ** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); |
| ︙ | ︙ | |||
9867 9868 9869 9870 9871 9872 9873 | ** prohibits that virtual table from being used from within triggers and ** views. ** </dd> ** ** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt> ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the | | > > > > > > > > > > | 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 |
** prohibits that virtual table from being used from within triggers and
** views.
** </dd>
**
** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
** identify that virtual table as being safe to use from within triggers
** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
** virtual table can do no serious harm even if it is controlled by a
** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
** flag unless absolutely necessary.
** </dd>
**
** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
** instruct the query planner to begin at least a read transaction on
** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
** virtual table is used.
** </dd>
** </dl>
*/
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
#define SQLITE_VTAB_INNOCUOUS 2
#define SQLITE_VTAB_DIRECTONLY 3
#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
/*
** CAPI3REF: Determine The Virtual Table Conflict Policy
**
** This function may only be called from within a call to the [xUpdate] method
** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
|
| ︙ | ︙ | |||
10108 10109 10110 10111 10112 10113 10114 | ** ** These interfaces are only useful from within the ** [xFilter|xFilter() method] of a [virtual table] implementation. ** The result of invoking these interfaces from any other context ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or | | | < | | 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 |
**
** These interfaces are only useful from within the
** [xFilter|xFilter() method] of a [virtual table] implementation.
** The result of invoking these interfaces from any other context
** is undefined and probably harmful.
**
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
** xFilter method which invokes these routines, and specifically
** a parameter that was previously selected for all-at-once IN constraint
** processing use the [sqlite3_vtab_in()] interface in the
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
** an xFilter argument that was selected for all-at-once IN constraint
** processing, then these routines return [SQLITE_ERROR].)^
**
** ^(Use these routines to access all values on the right-hand side
** of the IN constraint using code like the following:
**
** <blockquote><pre>
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
** rc==SQLITE_OK && pVal;
** rc=sqlite3_vtab_in_next(pList, &pVal)
** ){
** // do something with pVal
** }
** if( rc!=SQLITE_OK ){
** // an error has occurred
** }
|
| ︙ | ︙ | |||
10256 10257 10258 10259 10260 10261 10262 | ** description for the X-th loop. ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt> ** <dd>^The "int" variable pointed to by the V parameter will be set to the ** id for the X-th query plan element. The id value is unique within the ** statement. The select-id is the same value as is output in the first ** column of an [EXPLAIN QUERY PLAN] query. | < > | 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 | ** description for the X-th loop. ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt> ** <dd>^The "int" variable pointed to by the V parameter will be set to the ** id for the X-th query plan element. The id value is unique within the ** statement. The select-id is the same value as is output in the first ** column of an [EXPLAIN QUERY PLAN] query. ** ** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt> ** <dd>The "int" variable pointed to by the V parameter will be set to the ** the id of the parent of the current query element, if applicable, or ** to zero if the query element has no parent. This is the same value as ** returned in the second column of an [EXPLAIN QUERY PLAN] query. ** ** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt> ** <dd>The sqlite3_int64 output value is set to the number of cycles, ** according to the processor time-stamp counter, that elapsed while the ** query element was being processed. This value is not available for ** all query elements - if it is unavailable the output variable is ** set to -1. ** </dl> */ #define SQLITE_SCANSTAT_NLOOP 0 #define SQLITE_SCANSTAT_NVISIT 1 #define SQLITE_SCANSTAT_EST 2 #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 |
| ︙ | ︙ | |||
10426 10427 10428 10429 10430 10431 10432 | ** or any operation on a WITHOUT ROWID table, the value of the sixth ** parameter is undefined. For an INSERT or UPDATE on a rowid table the ** seventh parameter is the final rowid value of the row being inserted ** or updated. The value of the seventh parameter passed to the callback ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** | | | 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 | ** or any operation on a WITHOUT ROWID table, the value of the sixth ** parameter is undefined. For an INSERT or UPDATE on a rowid table the ** seventh parameter is the final rowid value of the row being inserted ** or updated. The value of the seventh parameter passed to the callback ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** ** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from ** the previous call on the same [database connection] D, or NULL for ** the first call on D. ** ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines ** may only be called from within a preupdate callback. Invoking any of |
| ︙ | ︙ | |||
10835 10836 10837 10838 10839 10840 10841 10842 10843 10844 10845 10846 10847 10848 | ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if 0 } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ /******** Begin file sqlite3rtree.h *********/ /* | > > > > > > > > > > > > > | 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 | ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 # undef SQLITE_OMIT_WAL # define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ # ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION # endif # ifndef SQLITE_THREADSAFE # define SQLITE_THREADSAFE 0 # endif #endif #if 0 } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ /******** Begin file sqlite3rtree.h *********/ /* |
| ︙ | ︙ | |||
11041 11042 11043 11044 11045 11046 11047 | ** Session objects must be deleted before the database handle to which they ** are attached is closed. Refer to the documentation for ** [sqlite3session_create()] for details. */ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* | | | | > > > > | | | | | > | > > > > > > > | > | 11127 11128 11129 11130 11131 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 | ** Session objects must be deleted before the database handle to which they ** are attached is closed. Refer to the documentation for ** [sqlite3session_create()] for details. */ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* ** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been ** created. At present the only valid values for the second parameter are ** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** */ SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); /* ** CAPI3REF: Options for sqlite3session_object_config ** ** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> ** This option is used to set, clear or query the flag that enables ** the [sqlite3session_changeset_size()] API. Because it imposes some ** computational overhead, this API is disabled by default. Argument ** pArg must point to a value of type (int). If the value is initially ** 0, then the sqlite3session_changeset_size() API is disabled. If it ** is greater than 0, then the same API is enabled. Or, if the initial ** value is less than zero, no change is made. In all cases the (int) ** variable is set to 1 if the sqlite3session_changeset_size() API is ** enabled following the current call, or 0 otherwise. ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. ** ** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> ** This option is used to set, clear or query the flag that enables ** collection of data for tables with no explicit PRIMARY KEY. ** ** Normally, tables with no explicit PRIMARY KEY are simply ignored ** by the sessions module. However, if this flag is set, it behaves ** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted ** as their leftmost columns. ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. */ #define SQLITE_SESSION_OBJCONFIG_SIZE 1 #define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object ** METHOD: sqlite3_session ** ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When |
| ︙ | ︙ | |||
12204 12205 12206 12207 12208 12209 12210 12211 12212 12213 12214 12215 12216 12217 12218 12219 12220 | ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. ** ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 /* ** CAPI3REF: Constants Passed To The Conflict Handler ** ** Values that may be passed as the second argument to a conflict-handler. ** ** <dl> | > > > > > > > > > > > > > > | 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 12327 12328 12329 12330 12331 12332 12333 | ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. ** ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. ** ** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd> ** Do not invoke the conflict handler callback for any changes that ** would not actually modify the database even if they were applied. ** Specifically, this means that the conflict handler is not invoked ** for: ** <ul> ** <li>a delete change if the row being deleted cannot be found, ** <li>an update change if the modified fields are already set to ** their new values in the conflicting row, or ** <li>an insert change if all fields of the conflicting row match ** the row being inserted. ** </ul> */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 /* ** CAPI3REF: Constants Passed To The Conflict Handler ** ** Values that may be passed as the second argument to a conflict-handler. ** ** <dl> |
| ︙ | ︙ | |||
13503 13504 13505 13506 13507 13508 13509 | #pragma warn -ccc /* Condition is always true or false */ #pragma warn -aus /* Assigned value is never used */ #pragma warn -csu /* Comparing signed and unsigned */ #pragma warn -spa /* Suspicious pointer arithmetic */ #endif /* | | | | 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 | #pragma warn -ccc /* Condition is always true or false */ #pragma warn -aus /* Assigned value is never used */ #pragma warn -csu /* Comparing signed and unsigned */ #pragma warn -spa /* Suspicious pointer arithmetic */ #endif /* ** A few places in the code require atomic load/store of aligned ** integer values. */ #ifndef __has_extension # define __has_extension(x) 0 /* compatibility with non-clang compilers */ #endif #if GCC_VERSION>=4007000 || __has_extension(c_atomic) # define SQLITE_ATOMIC_INTRINSICS 1 # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED) |
| ︙ | ︙ | |||
13560 13561 13562 13563 13564 13565 13566 | # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* | | > > > > > > > | 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 13688 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700 13701 13702 | # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* ** Macros to hint to the compiler that a function should or should not be ** inlined. */ #if defined(__GNUC__) # define SQLITE_NOINLINE __attribute__((noinline)) # define SQLITE_INLINE __attribute__((always_inline)) inline #elif defined(_MSC_VER) && _MSC_VER>=1310 # define SQLITE_NOINLINE __declspec(noinline) # define SQLITE_INLINE __forceinline #else # define SQLITE_NOINLINE # define SQLITE_INLINE #endif #if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) # undef SQLITE_INLINE # define SQLITE_INLINE #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. */ |
| ︙ | ︙ | |||
14386 14387 14388 14389 14390 14391 14392 | ** is 0x00000000ffffffff. But because of quirks of some compilers, we ** have to specify the value in the less intuitive manner shown: */ #define SQLITE_MAX_U32 ((((u64)1)<<32)-1) /* ** The datatype used to store estimates of the number of rows in a | | < < < | < < < | 14506 14507 14508 14509 14510 14511 14512 14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 | ** is 0x00000000ffffffff. But because of quirks of some compilers, we ** have to specify the value in the less intuitive manner shown: */ #define SQLITE_MAX_U32 ((((u64)1)<<32)-1) /* ** The datatype used to store estimates of the number of rows in a ** table or index. */ typedef u64 tRowcnt; /* ** Estimated quantities used for query planning are stored as 16-bit ** logarithms. For quantity X, the value stored is 10*log2(X). This ** gives a possible range of values of approximately 1.0e986 to 1e-986. ** But the allowed values are "grainy". Not every value is representable. ** For example, quantities 16 and 17 are both represented by a LogEst |
| ︙ | ︙ | |||
16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 | # define sqlite3VdbeScanStatusRange(a,b,c,d) # define sqlite3VdbeScanStatusCounters(a,b,c,d) #endif #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pcache.h in the middle of sqliteInt.h ****************/ /************** Begin file pcache.h ******************************************/ | > > > > | 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 | # define sqlite3VdbeScanStatusRange(a,b,c,d) # define sqlite3VdbeScanStatusCounters(a,b,c,d) #endif #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif #if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr); #endif #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pcache.h in the middle of sqliteInt.h ****************/ /************** Begin file pcache.h ******************************************/ |
| ︙ | ︙ | |||
16583 16584 16585 16586 16587 16588 16589 | u16 flags; /* PGHDR flags defined below */ /********************************************************************** ** Elements above, except pCache, are public. All that follow are ** private to pcache.c and should not be accessed by other modules. ** pCache is grouped with the public elements for efficiency. */ | | | 16701 16702 16703 16704 16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715 |
u16 flags; /* PGHDR flags defined below */
/**********************************************************************
** Elements above, except pCache, are public. All that follow are
** private to pcache.c and should not be accessed by other modules.
** pCache is grouped with the public elements for efficiency.
*/
i64 nRef; /* Number of users of this page */
PgHdr *pDirtyNext; /* Next element in list of dirty pages */
PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */
/* NB: pDirtyNext and pDirtyPrev are undefined if the
** PgHdr object is not dirty */
};
/* Bit values for PgHdr.flags */
|
| ︙ | ︙ | |||
16664 16665 16666 16667 16668 16669 16670 | /* Clear flags from pages of the page cache */ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); /* Discard the contents of the cache */ SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ | | | | 16782 16783 16784 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 | /* Clear flags from pages of the page cache */ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); /* Discard the contents of the cache */ SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*); /* Increment the reference count of an existing page */ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* Iterate through all dirty pages currently stored in the cache. This ** interface is only available if SQLITE_CHECK_PAGES is defined when the |
| ︙ | ︙ | |||
17244 17245 17246 17247 17248 17249 17250 |
#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
#define SQLITE_TrustedSchema 0x00000080 /* Allow unsafe functions and
** vtabs in the schema definition */
#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
#define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */
| | | 17362 17363 17364 17365 17366 17367 17368 17369 17370 17371 17372 17373 17374 17375 17376 |
#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
#define SQLITE_TrustedSchema 0x00000080 /* Allow unsafe functions and
** vtabs in the schema definition */
#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
#define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */
#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */
#define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */
#define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */
#define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */
#define SQLITE_ForeignKeys 0x00004000 /* Enforce foreign key constraints */
#define SQLITE_AutoIndex 0x00008000 /* Enable automatic indexes */
#define SQLITE_LoadExtension 0x00010000 /* Enable load_extension */
#define SQLITE_LoadExtFunc 0x00020000 /* Enable load_extension() SQL func */
|
| ︙ | ︙ | |||
17270 17271 17272 17273 17274 17275 17276 17277 17278 17279 17280 17281 17282 17283 |
#define SQLITE_DqsDDL 0x20000000 /* dbl-quoted strings allowed in DDL*/
#define SQLITE_DqsDML 0x40000000 /* dbl-quoted strings allowed in DML*/
#define SQLITE_EnableView 0x80000000 /* Enable the use of views */
#define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
#define SQLITE_VdbeListing HI(0x0200000) /* Debug listings of VDBE progs */
#define SQLITE_VdbeTrace HI(0x0400000) /* True to trace VDBE execution */
#define SQLITE_VdbeAddopTrace HI(0x0800000) /* Trace sqlite3VdbeAddOp() calls */
| > | 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 |
#define SQLITE_DqsDDL 0x20000000 /* dbl-quoted strings allowed in DDL*/
#define SQLITE_DqsDML 0x40000000 /* dbl-quoted strings allowed in DML*/
#define SQLITE_EnableView 0x80000000 /* Enable the use of views */
#define SQLITE_CountRows HI(0x00001) /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
#define SQLITE_VdbeListing HI(0x0200000) /* Debug listings of VDBE progs */
#define SQLITE_VdbeTrace HI(0x0400000) /* True to trace VDBE execution */
#define SQLITE_VdbeAddopTrace HI(0x0800000) /* Trace sqlite3VdbeAddOp() calls */
|
| ︙ | ︙ | |||
17326 17327 17328 17329 17330 17331 17332 17333 17334 17335 17336 17337 17338 17339 | #define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ #define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ #define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ #define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) #define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) | > | 17445 17446 17447 17448 17449 17450 17451 17452 17453 17454 17455 17456 17457 17458 17459 | #define SQLITE_BloomPulldown 0x00100000 /* Run Bloom filters early */ #define SQLITE_BalancedMerge 0x00200000 /* Balance multi-way merges */ #define SQLITE_ReleaseReg 0x00400000 /* Use OP_ReleaseReg for testing */ #define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ #define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ #define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* ** Macros for testing whether or not optimizations are enabled or disabled. */ #define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) #define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) |
| ︙ | ︙ | |||
17797 17798 17799 17800 17801 17802 17803 17804 17805 17806 17807 17808 17809 17810 |
*/
struct VTable {
sqlite3 *db; /* Database connection associated with this table */
Module *pMod; /* Pointer to module implementation */
sqlite3_vtab *pVtab; /* Pointer to vtab instance */
int nRef; /* Number of pointers to this structure */
u8 bConstraint; /* True if constraints are supported */
u8 eVtabRisk; /* Riskiness of allowing hacker access */
int iSavepoint; /* Depth of the SAVEPOINT stack */
VTable *pNext; /* Next in linked list (see above) */
};
/* Allowed values for VTable.eVtabRisk
*/
| > | 17917 17918 17919 17920 17921 17922 17923 17924 17925 17926 17927 17928 17929 17930 17931 |
*/
struct VTable {
sqlite3 *db; /* Database connection associated with this table */
Module *pMod; /* Pointer to module implementation */
sqlite3_vtab *pVtab; /* Pointer to vtab instance */
int nRef; /* Number of pointers to this structure */
u8 bConstraint; /* True if constraints are supported */
u8 bAllSchemas; /* True if might use any attached schema */
u8 eVtabRisk; /* Riskiness of allowing hacker access */
int iSavepoint; /* Depth of the SAVEPOINT stack */
VTable *pNext; /* Next in linked list (see above) */
};
/* Allowed values for VTable.eVtabRisk
*/
|
| ︙ | ︙ | |||
18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 18188 18189 18190 |
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
IndexSample *aSample; /* Samples of the left-most key */
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
#endif
Bitmask colNotIdxed; /* Unindexed columns in pTab */
| > | 18298 18299 18300 18301 18302 18303 18304 18305 18306 18307 18308 18309 18310 18311 18312 |
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
int mxSample; /* Number of slots allocated to aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
IndexSample *aSample; /* Samples of the left-most key */
tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
#endif
Bitmask colNotIdxed; /* Unindexed columns in pTab */
|
| ︙ | ︙ | |||
18828 18829 18830 18831 18832 18833 18834 | #define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ #define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ #define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ #define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ #define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ #define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ #define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ | | | 18950 18951 18952 18953 18954 18955 18956 18957 18958 18959 18960 18961 18962 18963 18964 | #define NC_AllowAgg 0x000001 /* Aggregate functions are allowed here */ #define NC_PartIdx 0x000002 /* True if resolving a partial index WHERE */ #define NC_IsCheck 0x000004 /* True if resolving a CHECK constraint */ #define NC_GenCol 0x000008 /* True for a GENERATED ALWAYS AS clause */ #define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ #define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ #define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ #define NC_Subquery 0x000040 /* A subquery has been seen */ #define NC_UEList 0x000080 /* True if uNC.pEList is used */ #define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ #define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ #define NC_UBaseReg 0x000400 /* True if uNC.iBaseReg is used */ #define NC_MinMaxAgg 0x001000 /* min/max aggregates seen. See note above */ #define NC_Complex 0x002000 /* True if a function or subquery seen */ #define NC_AllowWin 0x004000 /* Window functions are allowed here */ |
| ︙ | ︙ | |||
19147 19148 19149 19150 19151 19152 19153 19154 19155 19156 19157 19158 19159 19160 |
*/
struct IndexedExpr {
Expr *pExpr; /* The expression contained in the index */
int iDataCur; /* The data cursor associated with the index */
int iIdxCur; /* The index cursor */
int iIdxCol; /* The index column that contains value of pExpr */
u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */
IndexedExpr *pIENext; /* Next in a list of all indexed expressions */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
const char *zIdxName; /* Name of index, used only for bytecode comments */
#endif
};
/*
| > | 19269 19270 19271 19272 19273 19274 19275 19276 19277 19278 19279 19280 19281 19282 19283 |
*/
struct IndexedExpr {
Expr *pExpr; /* The expression contained in the index */
int iDataCur; /* The data cursor associated with the index */
int iIdxCur; /* The index cursor */
int iIdxCol; /* The index column that contains value of pExpr */
u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */
u8 aff; /* Affinity of the pExpr expression */
IndexedExpr *pIENext; /* Next in a list of all indexed expressions */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
const char *zIdxName; /* Name of index, used only for bytecode comments */
#endif
};
/*
|
| ︙ | ︙ | |||
19199 19200 19201 19202 19203 19204 19205 19206 19207 19208 19209 19210 19211 19212 | u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table associated with an index on expr, or negative | > > > | 19322 19323 19324 19325 19326 19327 19328 19329 19330 19331 19332 19333 19334 19335 19336 19337 19338 | u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif #ifdef SQLITE_DEBUG u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table associated with an index on expr, or negative |
| ︙ | ︙ | |||
19659 19660 19661 19662 19663 19664 19665 19666 19667 19668 19669 19670 19671 19672 |
struct WindowRewrite *pRewrite; /* Window rewrite context */
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
SrcItem *pSrcItem; /* A single FROM clause item */
DbFixer *pFix; /* See sqlite3FixSelect() */
} u;
};
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
** explicit.
| > | 19785 19786 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 19797 19798 19799 |
struct WindowRewrite *pRewrite; /* Window rewrite context */
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
SrcItem *pSrcItem; /* A single FROM clause item */
DbFixer *pFix; /* See sqlite3FixSelect() */
Mem *aMem; /* See sqlite3BtreeCursorHint() */
} u;
};
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
** explicit.
|
| ︙ | ︙ | |||
19928 19929 19930 19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 | # define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01) # define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06) # define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) # define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) # define sqlite3Isspace(x) isspace((unsigned char)(x)) # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) # define sqlite3Tolower(x) tolower((unsigned char)(x)) # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') #endif SQLITE_PRIVATE int sqlite3IsIdChar(u8); /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*); | > > > > | 20055 20056 20057 20058 20059 20060 20061 20062 20063 20064 20065 20066 20067 20068 20069 20070 20071 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 | # define sqlite3Isspace(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x01) # define sqlite3Isalnum(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x06) # define sqlite3Isalpha(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x02) # define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04) # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) # define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) # define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) # define sqlite3Isspace(x) isspace((unsigned char)(x)) # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) # define sqlite3Tolower(x) tolower((unsigned char)(x)) # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') # define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') # define sqlite3JsonId2(x) sqlite3IsIdChar(x) #endif SQLITE_PRIVATE int sqlite3IsIdChar(u8); /* ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*); |
| ︙ | ︙ | |||
20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 | SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); #endif 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*); | > > > > | 20261 20262 20263 20264 20265 20266 20267 20268 20269 20270 20271 20272 20273 20274 20275 20276 20277 20278 | SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); SQLITE_PRIVATE void sqlite3FinishCoding(Parse*); SQLITE_PRIVATE int sqlite3GetTempReg(Parse*); SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int); #if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int); #endif #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); #endif 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*); |
| ︙ | ︙ | |||
20280 20281 20282 20283 20284 20285 20286 |
Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
| | | 20415 20416 20417 20418 20419 20420 20421 20422 20423 20424 20425 20426 20427 20428 20429 |
Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
|
| ︙ | ︙ | |||
20369 20370 20371 20372 20373 20374 20375 | SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); | | | 20504 20505 20506 20507 20508 20509 20510 20511 20512 20513 20514 20515 20516 20517 20518 | SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,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*); |
| ︙ | ︙ | |||
20817 20818 20819 20820 20821 20822 20823 | SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse*, Token*); SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); | < < | < | 20952 20953 20954 20955 20956 20957 20958 20959 20960 20961 20962 20963 20964 20965 20966 | SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse*, Token*); SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*); SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE void sqlite3ParseObjectInit(Parse*,sqlite3*); SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse*); SQLITE_PRIVATE void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*); #ifdef SQLITE_ENABLE_NORMALIZE |
| ︙ | ︙ | |||
21066 21067 21068 21069 21070 21071 21072 21073 21074 21075 21076 21077 21078 21079 | #endif #if defined(VDBE_PROFILE) \ || defined(SQLITE_PERFORMANCE_TRACE) \ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); #endif #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ /************** Begin file os_common.h ***************************************/ /* ** 2004 May 22 | > > > > > > | 21198 21199 21200 21201 21202 21203 21204 21205 21206 21207 21208 21209 21210 21211 21212 21213 21214 21215 21216 21217 | #endif #if defined(VDBE_PROFILE) \ || defined(SQLITE_PERFORMANCE_TRACE) \ || defined(SQLITE_ENABLE_STMT_SCANSTATUS) SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); #endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS # define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus) #else # define IS_STMT_SCANSTATUS(db) 0 #endif #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ /************** Begin file os_common.h ***************************************/ /* ** 2004 May 22 |
| ︙ | ︙ | |||
22062 22063 22064 22065 22066 22067 22068 | ** ** isspace() 0x01 ** isalpha() 0x02 ** isdigit() 0x04 ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 | | | 22200 22201 22202 22203 22204 22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 | ** ** isspace() 0x01 ** isalpha() 0x02 ** isdigit() 0x04 ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 ** SQLite identifier character 0x40 $, _, or non-ascii ** Quote character 0x80 ** ** Bit 0x20 is set if the mapped character requires translation to upper ** case. i.e. if the character is a lower-case ASCII character. ** If x is a lower-case ASCII character, then its upper-case equivalent ** is (x - 0x20). Therefore toupper() can be implemented as: ** |
| ︙ | ︙ | |||
22256 22257 22258 22259 22260 22261 22262 | #endif 0, /* bLocaltimeFault */ 0, /* xAltLocaltime */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ #ifdef SQLITE_DEBUG | | | 22394 22395 22396 22397 22398 22399 22400 22401 22402 22403 22404 22405 22406 22407 22408 |
#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
0, /* iPrngSeed */
#ifdef SQLITE_DEBUG
{0,0,0,0,0,0}, /* aTune */
#endif
};
/*
** Hash table for global functions - functions common to all
** database connections. After initialization, this table is
** read-only.
|
| ︙ | ︙ | |||
23038 23039 23040 23041 23042 23043 23044 23045 23046 23047 23048 23049 23050 23051 | SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *); SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *); SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*); #else # define sqlite3VdbeIncrWriteCounter(V,C) # define sqlite3VdbeAssertAbortable(V) #endif | > > | 23176 23177 23178 23179 23180 23181 23182 23183 23184 23185 23186 23187 23188 23189 23190 23191 | SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *); SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *); SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*); #else # define sqlite3VdbeIncrWriteCounter(V,C) # define sqlite3VdbeAssertAbortable(V) #endif |
| ︙ | ︙ | |||
23553 23554 23555 23556 23557 23558 23559 23560 23561 23562 23563 23564 23565 23566 | char validJD; /* True (1) if iJD is valid */ char rawS; /* Raw numeric value stored in s */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ char validTZ; /* True (1) if tz is valid */ char tzSet; /* Timezone was set explicitly */ char isError; /* An overflow has occurred */ }; /* ** Convert zDate into one or more integers according to the conversion ** specifier zFormat. ** | > | 23693 23694 23695 23696 23697 23698 23699 23700 23701 23702 23703 23704 23705 23706 23707 | char validJD; /* True (1) if iJD is valid */ char rawS; /* Raw numeric value stored in s */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ char validTZ; /* True (1) if tz is valid */ char tzSet; /* Timezone was set explicitly */ char isError; /* An overflow has occurred */ char useSubsec; /* Display subsecond precision */ }; /* ** Convert zDate into one or more integers according to the conversion ** specifier zFormat. ** |
| ︙ | ︙ | |||
23867 23868 23869 23870 23871 23872 23873 23874 23875 23876 23877 23878 23879 23880 |
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
return setDateTimeToCurrent(context, p);
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
setRawDateNumber(p, r);
return 0;
}
return 1;
}
/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999.
** Multiplying this by 86400000 gives 464269060799999 as the maximum value
** for DateTime.iJD.
| > > > > > | 24008 24009 24010 24011 24012 24013 24014 24015 24016 24017 24018 24019 24020 24021 24022 24023 24024 24025 24026 |
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){
return setDateTimeToCurrent(context, p);
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){
setRawDateNumber(p, r);
return 0;
}else if( (sqlite3StrICmp(zDate,"subsec")==0
|| sqlite3StrICmp(zDate,"subsecond")==0)
&& sqlite3NotPureFunc(context) ){
p->useSubsec = 1;
return setDateTimeToCurrent(context, p);
}
return 1;
}
/* The julian day number for 9999-12-31 23:59:59.999 is 5373484.4999999.
** Multiplying this by 86400000 gives 464269060799999 as the maximum value
** for DateTime.iJD.
|
| ︙ | ︙ | |||
24225 24226 24227 24228 24229 24230 24231 |
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
if( p->tzSet==0 ){
i64 iOrigJD; /* Original localtime */
i64 iGuess; /* Guess at the corresponding utc time */
int cnt = 0; /* Safety to prevent infinite loop */
| | | 24371 24372 24373 24374 24375 24376 24377 24378 24379 24380 24381 24382 24383 24384 24385 |
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
if( p->tzSet==0 ){
i64 iOrigJD; /* Original localtime */
i64 iGuess; /* Guess at the corresponding utc time */
int cnt = 0; /* Safety to prevent infinite loop */
i64 iErr; /* Guess is off by this much */
computeJD(p);
iGuess = iOrigJD = p->iJD;
iErr = 0;
do{
DateTime new;
memset(&new, 0, sizeof(new));
|
| ︙ | ︙ | |||
24281 24282 24283 24284 24285 24286 24287 24288 |
}
case 's': {
/*
** start of TTTTT
**
** Move the date backwards to the beginning of the current day,
** or month or year.
*/
| > > > > > > | > > > > > > > > | 24427 24428 24429 24430 24431 24432 24433 24434 24435 24436 24437 24438 24439 24440 24441 24442 24443 24444 24445 24446 24447 24448 24449 24450 24451 24452 24453 24454 24455 24456 |
}
case 's': {
/*
** start of TTTTT
**
** Move the date backwards to the beginning of the current day,
** or month or year.
**
** subsecond
** subsec
**
** Show subsecond precision in the output of datetime() and
** unixepoch() and strftime('%s').
*/
if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){
if( sqlite3_stricmp(z, "subsec")==0
|| sqlite3_stricmp(z, "subsecond")==0
){
p->useSubsec = 1;
rc = 0;
}
break;
}
if( !p->validJD && !p->validYMD && !p->validHMS ) break;
z += 9;
computeYMD(p);
p->validHMS = 1;
p->h = p->m = 0;
p->s = 0.0;
p->rawS = 0;
|
| ︙ | ︙ | |||
24480 24481 24482 24483 24484 24485 24486 |
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
computeJD(&x);
| > > > | > | | > > > > > > > > > > > | | | | > > | | | > > > > > > > > > > > | | | | > > | | 24640 24641 24642 24643 24644 24645 24646 24647 24648 24649 24650 24651 24652 24653 24654 24655 24656 24657 24658 24659 24660 24661 24662 24663 24664 24665 24666 24667 24668 24669 24670 24671 24672 24673 24674 24675 24676 24677 24678 24679 24680 24681 24682 24683 24684 24685 24686 24687 24688 24689 24690 24691 24692 24693 24694 24695 24696 24697 24698 24699 24700 24701 24702 24703 24704 24705 24706 24707 24708 24709 24710 24711 24712 24713 24714 24715 24716 24717 24718 24719 24720 24721 24722 24723 24724 24725 24726 24727 24728 24729 24730 24731 24732 24733 24734 24735 24736 24737 24738 24739 24740 24741 24742 24743 24744 24745 24746 24747 24748 24749 24750 24751 24752 24753 24754 24755 24756 24757 24758 24759 24760 |
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
computeJD(&x);
if( x.useSubsec ){
sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0);
}else{
sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000);
}
}
}
/*
** datetime( TIMESTRING, MOD, MOD, ...)
**
** Return YYYY-MM-DD HH:MM:SS
*/
static void datetimeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
int Y, s, n;
char zBuf[32];
computeYMD_HMS(&x);
Y = x.Y;
if( Y<0 ) Y = -Y;
zBuf[1] = '0' + (Y/1000)%10;
zBuf[2] = '0' + (Y/100)%10;
zBuf[3] = '0' + (Y/10)%10;
zBuf[4] = '0' + (Y)%10;
zBuf[5] = '-';
zBuf[6] = '0' + (x.M/10)%10;
zBuf[7] = '0' + (x.M)%10;
zBuf[8] = '-';
zBuf[9] = '0' + (x.D/10)%10;
zBuf[10] = '0' + (x.D)%10;
zBuf[11] = ' ';
zBuf[12] = '0' + (x.h/10)%10;
zBuf[13] = '0' + (x.h)%10;
zBuf[14] = ':';
zBuf[15] = '0' + (x.m/10)%10;
zBuf[16] = '0' + (x.m)%10;
zBuf[17] = ':';
if( x.useSubsec ){
s = (int)1000.0*x.s;
zBuf[18] = '0' + (s/10000)%10;
zBuf[19] = '0' + (s/1000)%10;
zBuf[20] = '.';
zBuf[21] = '0' + (s/100)%10;
zBuf[22] = '0' + (s/10)%10;
zBuf[23] = '0' + (s)%10;
zBuf[24] = 0;
n = 24;
}else{
s = (int)x.s;
zBuf[18] = '0' + (s/10)%10;
zBuf[19] = '0' + (s)%10;
zBuf[20] = 0;
n = 20;
}
if( x.Y<0 ){
zBuf[0] = '-';
sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
}else{
sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT);
}
}
}
/*
** time( TIMESTRING, MOD, MOD, ...)
**
** Return HH:MM:SS
*/
static void timeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
DateTime x;
if( isDate(context, argc, argv, &x)==0 ){
int s, n;
char zBuf[16];
computeHMS(&x);
zBuf[0] = '0' + (x.h/10)%10;
zBuf[1] = '0' + (x.h)%10;
zBuf[2] = ':';
zBuf[3] = '0' + (x.m/10)%10;
zBuf[4] = '0' + (x.m)%10;
zBuf[5] = ':';
if( x.useSubsec ){
s = (int)1000.0*x.s;
zBuf[6] = '0' + (s/10000)%10;
zBuf[7] = '0' + (s/1000)%10;
zBuf[8] = '.';
zBuf[9] = '0' + (s/100)%10;
zBuf[10] = '0' + (s/10)%10;
zBuf[11] = '0' + (s)%10;
zBuf[12] = 0;
n = 12;
}else{
s = (int)x.s;
zBuf[6] = '0' + (s/10)%10;
zBuf[7] = '0' + (s)%10;
zBuf[8] = 0;
n = 8;
}
sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
}
}
/*
** date( TIMESTRING, MOD, MOD, ...)
**
** Return YYYY-MM-DD
|
| ︙ | ︙ | |||
24687 24688 24689 24690 24691 24692 24693 |
break;
}
case 'M': {
sqlite3_str_appendf(&sRes,"%02d",x.m);
break;
}
case 's': {
| > > > > | | > | 24877 24878 24879 24880 24881 24882 24883 24884 24885 24886 24887 24888 24889 24890 24891 24892 24893 24894 24895 24896 24897 |
break;
}
case 'M': {
sqlite3_str_appendf(&sRes,"%02d",x.m);
break;
}
case 's': {
if( x.useSubsec ){
sqlite3_str_appendf(&sRes,"%.3f",
(x.iJD - 21086676*(i64)10000000)/1000.0);
}else{
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': {
|
| ︙ | ︙ | |||
30059 30060 30061 30062 30063 30064 30065 30066 30067 30068 30069 30070 30071 30072 |
d = digit;
digit += '0';
*val = (*val - d)*10.0;
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);
| > > > > > > > > > > > > > > | 30254 30255 30256 30257 30258 30259 30260 30261 30262 30263 30264 30265 30266 30267 30268 30269 30270 30271 30272 30273 30274 30275 30276 30277 30278 30279 30280 30281 |
d = digit;
digit += '0';
*val = (*val - d)*10.0;
return (char)digit;
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** "*val" is a u64. *msd is a divisor used to extract the
** most significant digit of *val. Extract that most significant
** digit and return it.
*/
static char et_getdigit_int(u64 *val, u64 *msd){
u64 x = (*val)/(*msd);
*val -= x*(*msd);
if( *msd>=10 ) *msd /= 10;
return '0' + (char)(x & 15);
}
#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);
|
| ︙ | ︙ | |||
30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 | etByte done; /* Loop termination flag */ etByte cThousand; /* Thousands separator for %d and %u */ etByte xtype = etINVALID; /* Conversion paradigm */ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ LONGDOUBLE_TYPE realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ #ifndef SQLITE_OMIT_FLOATING_POINT int exp, e2; /* exponent of real numbers */ int nsd; /* Number of significant digits returned */ | > > | 30360 30361 30362 30363 30364 30365 30366 30367 30368 30369 30370 30371 30372 30373 30374 30375 |
etByte done; /* Loop termination flag */
etByte cThousand; /* Thousands separator for %d and %u */
etByte xtype = etINVALID; /* Conversion paradigm */
u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
LONGDOUBLE_TYPE realvalue; /* Value for real types */
sqlite_uint64 msd; /* Divisor to get most-significant-digit
** of longvalue */
const et_info *infop; /* Pointer to the appropriate info structure */
char *zOut; /* Rendering buffer */
int nOut; /* Size of the rendering buffer */
char *zExtra = 0; /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
int nsd; /* Number of significant digits returned */
|
| ︙ | ︙ | |||
30457 30458 30459 30460 30461 30462 30463 30464 30465 |
#endif
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
}else{
prefix = flag_prefix;
}
if( xtype==etGENERIC && precision>0 ) precision--;
testcase( precision>0xfff );
| > > > > > > > > > > > > > > > | | | | | | | | | | | | < < | > > > > | | > | | > > | | | | | | | | | > > > > | | | | | | | > > > > > > | < < < < > > > < > > > > > > > | > > > > | > | 30668 30669 30670 30671 30672 30673 30674 30675 30676 30677 30678 30679 30680 30681 30682 30683 30684 30685 30686 30687 30688 30689 30690 30691 30692 30693 30694 30695 30696 30697 30698 30699 30700 30701 30702 30703 30704 30705 30706 30707 30708 30709 30710 30711 30712 30713 30714 30715 30716 30717 30718 30719 30720 30721 30722 30723 30724 30725 30726 30727 30728 30729 30730 30731 30732 30733 30734 30735 30736 30737 30738 30739 30740 30741 30742 30743 30744 30745 30746 30747 30748 30749 30750 30751 30752 30753 30754 30755 30756 30757 30758 30759 30760 30761 30762 30763 30764 30765 30766 30767 30768 30769 30770 30771 30772 30773 30774 30775 30776 30777 30778 30779 30780 30781 30782 30783 30784 30785 30786 30787 30788 30789 30790 30791 30792 30793 30794 30795 30796 30797 30798 30799 30800 30801 30802 30803 30804 30805 30806 30807 30808 30809 30810 30811 30812 30813 30814 30815 30816 30817 30818 30819 |
#endif
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
}else{
prefix = flag_prefix;
}
exp = 0;
if( xtype==etGENERIC && precision>0 ) precision--;
testcase( precision>0xfff );
if( realvalue<1.0e+16
&& realvalue==(LONGDOUBLE_TYPE)(longvalue = (u64)realvalue)
){
/* Number is a pure integer that can be represented as u64 */
for(msd=1; msd*10<=longvalue; msd *= 10, exp++){}
if( exp>precision && xtype!=etFLOAT ){
u64 rnd = msd/2;
int kk = precision;
while( kk-- > 0 ){ rnd /= 10; }
longvalue += rnd;
}
}else{
msd = 0;
longvalue = 0; /* To prevent a compiler warning */
idx = precision & 0xfff;
rounder = arRound[idx%10];
while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; }
if( xtype==etFLOAT ){
double rx = (double)realvalue;
sqlite3_uint64 u;
int ex;
memcpy(&u, &rx, sizeof(u));
ex = -1023 + (int)((u>>52)&0x7ff);
if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16;
realvalue += rounder;
}
if( sqlite3IsNaN((double)realvalue) ){
if( flag_zeropad ){
bufpt = "null";
length = 4;
}else{
bufpt = "NaN";
length = 3;
}
break;
}
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
if( ALWAYS(realvalue>0.0) ){
LONGDOUBLE_TYPE scale = 1.0;
while( realvalue>=1e100*scale && exp<=350){ scale*=1e100;exp+=100;}
while( realvalue>=1e10*scale && exp<=350 ){ scale*=1e10; exp+=10; }
while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
if( flag_zeropad ){
realvalue = 9.0;
exp = 999;
}else{
bufpt = buf;
buf[0] = prefix;
memcpy(buf+(prefix!=0),"Inf",4);
length = 3+(prefix!=0);
break;
}
}
if( xtype!=etFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
}
}
}
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
if( xtype==etGENERIC ){
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
}else{
precision = precision - exp;
xtype = etFLOAT;
}
}else{
flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
}else{
e2 = exp;
}
nsd = 16 + flag_altform2*10;
bufpt = buf;
{
i64 szBufNeeded; /* Size of a temporary buffer needed */
szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15;
if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3;
if( szBufNeeded > etBUFSIZE ){
bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded);
if( bufpt==0 ) return;
}
}
zOut = bufpt;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
*(bufpt++) = prefix;
}
/* Digits prior to the decimal point */
if( e2<0 ){
*(bufpt++) = '0';
}else if( msd>0 ){
for(; e2>=0; e2--){
*(bufpt++) = et_getdigit_int(&longvalue,&msd);
if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
}
}else{
for(; e2>=0; e2--){
*(bufpt++) = et_getdigit(&realvalue,&nsd);
if( cThousand && (e2%3)==0 && e2>1 ) *(bufpt++) = ',';
}
}
/* The decimal point */
if( flag_dp ){
*(bufpt++) = '.';
}
/* "0" digits after the decimal point but before the first
** significant digit of the number */
for(e2++; e2<0; precision--, e2++){
assert( precision>0 );
*(bufpt++) = '0';
}
/* Significant digits after the decimal point */
if( msd>0 ){
while( (precision--)>0 ){
*(bufpt++) = et_getdigit_int(&longvalue,&msd);
}
}else{
while( (precision--)>0 ){
*(bufpt++) = et_getdigit(&realvalue,&nsd);
}
}
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
assert( bufpt>zOut );
if( bufpt[-1]=='.' ){
if( flag_altform2 ){
|
| ︙ | ︙ | |||
31235 31236 31237 31238 31239 31240 31241 |
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
sqlite3_str_vappendf(&acc, zFormat, ap);
zBuf[acc.nChar] = 0;
return zBuf;
}
SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
| | > > > > > > > > > | > | | 31486 31487 31488 31489 31490 31491 31492 31493 31494 31495 31496 31497 31498 31499 31500 31501 31502 31503 31504 31505 31506 31507 31508 31509 31510 31511 31512 31513 31514 31515 |
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
sqlite3_str_vappendf(&acc, zFormat, ap);
zBuf[acc.nChar] = 0;
return zBuf;
}
SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
StrAccum acc;
va_list ap;
if( n<=0 ) return zBuf;
#ifdef SQLITE_ENABLE_API_ARMOR
if( zBuf==0 || zFormat==0 ) {
(void)SQLITE_MISUSE_BKPT;
if( zBuf ) zBuf[0] = 0;
return zBuf;
}
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
va_start(ap,zFormat);
sqlite3_str_vappendf(&acc, zFormat, ap);
va_end(ap);
zBuf[acc.nChar] = 0;
return zBuf;
}
/*
** This is the routine that actually formats the sqlite3_log() message.
** We house it in a separate routine from sqlite3_log() to avoid using
** stack space on small-stack systems when logging is disabled.
**
|
| ︙ | ︙ | |||
31540 31541 31542 31543 31544 31545 31546 31547 31548 31549 31550 31551 31552 31553 |
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " ON");
}
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
if( pItem->pSelect ) n++;
if( pItem->fg.isTabFunc ) n++;
if( pItem->fg.isUsing ) n++;
if( pItem->fg.isUsing ){
| > > > > > > > | 31801 31802 31803 31804 31805 31806 31807 31808 31809 31810 31811 31812 31813 31814 31815 31816 31817 31818 31819 31820 31821 |
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " ON");
}
if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte");
if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom");
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
if( pItem->pSelect ) n++;
if( pItem->fg.isTabFunc ) n++;
if( pItem->fg.isUsing ) n++;
if( pItem->fg.isUsing ){
|
| ︙ | ︙ | |||
34263 34264 34265 34266 34267 34268 34269 |
if( v<0 ){
x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v;
}else{
x = v;
}
i = sizeof(zTemp)-2;
zTemp[sizeof(zTemp)-1] = 0;
| < > | | > > | | | | 34531 34532 34533 34534 34535 34536 34537 34538 34539 34540 34541 34542 34543 34544 34545 34546 34547 34548 34549 34550 34551 34552 34553 |
if( v<0 ){
x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v;
}else{
x = v;
}
i = sizeof(zTemp)-2;
zTemp[sizeof(zTemp)-1] = 0;
while( 1 /*exit-by-break*/ ){
zTemp[i] = (x%10) + '0';
x = x/10;
if( x==0 ) break;
i--;
};
if( v<0 ) zTemp[--i] = '-';
memcpy(zOut, &zTemp[i], sizeof(zTemp)-i);
return sizeof(zTemp)-1-i;
}
/*
** Compare the 19-character string zNum against the text representation
** value 2^63: 9223372036854775808. Return negative, zero, or positive
** if zNum is less than, equal to, or greater than the string.
** Note that zNum must contain exactly 19 characters.
|
| ︙ | ︙ | |||
34434 34435 34436 34437 34438 34439 34440 |
u64 u = 0;
int i, k;
for(i=2; z[i]=='0'; i++){}
for(k=i; sqlite3Isxdigit(z[k]); k++){
u = u*16 + sqlite3HexToInt(z[k]);
}
memcpy(pOut, &u, 8);
| > > | | 34704 34705 34706 34707 34708 34709 34710 34711 34712 34713 34714 34715 34716 34717 34718 34719 34720 |
u64 u = 0;
int i, k;
for(i=2; z[i]=='0'; i++){}
for(k=i; sqlite3Isxdigit(z[k]); k++){
u = u*16 + sqlite3HexToInt(z[k]);
}
memcpy(pOut, &u, 8);
if( k-i>16 ) return 2;
if( z[k]!=0 ) return 1;
return 0;
}else
#endif /* SQLITE_OMIT_HEX_INTEGER */
{
return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
}
}
|
| ︙ | ︙ | |||
34470 34471 34472 34473 34474 34475 34476 |
else if( zNum[0]=='0'
&& (zNum[1]=='x' || zNum[1]=='X')
&& sqlite3Isxdigit(zNum[2])
){
u32 u = 0;
zNum += 2;
while( zNum[0]=='0' ) zNum++;
| | | 34742 34743 34744 34745 34746 34747 34748 34749 34750 34751 34752 34753 34754 34755 34756 |
else if( zNum[0]=='0'
&& (zNum[1]=='x' || zNum[1]=='X')
&& sqlite3Isxdigit(zNum[2])
){
u32 u = 0;
zNum += 2;
while( zNum[0]=='0' ) zNum++;
for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){
u = u*16 + sqlite3HexToInt(zNum[i]);
}
if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
memcpy(pValue, &u, 4);
return 1;
}else{
return 0;
|
| ︙ | ︙ | |||
36966 36967 36968 36969 36970 36971 36972 | # define SQLITE_ENABLE_LOCKING_STYLE 1 # else # define SQLITE_ENABLE_LOCKING_STYLE 0 # endif #endif /* Use pread() and pwrite() if they are available */ | | | 37238 37239 37240 37241 37242 37243 37244 37245 37246 37247 37248 37249 37250 37251 37252 | # define SQLITE_ENABLE_LOCKING_STYLE 1 # else # define SQLITE_ENABLE_LOCKING_STYLE 0 # endif #endif /* Use pread() and pwrite() if they are available */ #if defined(__APPLE__) || defined(__linux__) # define HAVE_PREAD 1 # define HAVE_PWRITE 1 #endif #if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64) # undef USE_PREAD # define USE_PREAD64 1 #elif defined(HAVE_PREAD) && defined(HAVE_PWRITE) |
| ︙ | ︙ | |||
36989 36990 36991 36992 36993 36994 36995 | #include <sys/stat.h> /* amalgamator: keep */ #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> /* amalgamator: keep */ /* #include <time.h> */ #include <sys/time.h> /* amalgamator: keep */ #include <errno.h> | | > | 37261 37262 37263 37264 37265 37266 37267 37268 37269 37270 37271 37272 37273 37274 37275 37276 | #include <sys/stat.h> /* amalgamator: keep */ #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> /* amalgamator: keep */ /* #include <time.h> */ #include <sys/time.h> /* amalgamator: keep */ #include <errno.h> #if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ && !defined(SQLITE_WASI) # include <sys/mman.h> #endif #if SQLITE_ENABLE_LOCKING_STYLE /* # include <sys/ioctl.h> */ # include <sys/file.h> # include <sys/param.h> |
| ︙ | ︙ | |||
37077 37078 37079 37080 37081 37082 37083 37084 37085 | #define MAX_PATHNAME 512 /* ** Maximum supported symbolic links */ #define SQLITE_MAX_SYMLINKS 100 /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 37350 37351 37352 37353 37354 37355 37356 37357 37358 37359 37360 37361 37362 37363 37364 37365 37366 37367 37368 37369 37370 37371 37372 37373 37374 37375 37376 37377 37378 37379 37380 37381 37382 37383 37384 37385 37386 37387 37388 37389 37390 37391 37392 37393 37394 37395 37396 37397 37398 37399 37400 37401 37402 37403 | #define MAX_PATHNAME 512 /* ** Maximum supported symbolic links */ #define SQLITE_MAX_SYMLINKS 100 /* ** Remove and stub certain info for WASI (WebAssembly System ** Interface) builds. */ #ifdef SQLITE_WASI # undef HAVE_FCHMOD # undef HAVE_FCHOWN # undef HAVE_MREMAP # define HAVE_MREMAP 0 # ifndef SQLITE_DEFAULT_UNIX_VFS # define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile" /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */ # endif # ifndef F_RDLCK # define F_RDLCK 0 # define F_WRLCK 1 # define F_UNLCK 2 # if __LONG_MAX == 0x7fffffffL # define F_GETLK 12 # define F_SETLK 13 # define F_SETLKW 14 # else # define F_GETLK 5 # define F_SETLK 6 # define F_SETLKW 7 # endif # endif #else /* !SQLITE_WASI */ # ifndef HAVE_FCHMOD # define HAVE_FCHMOD # endif #endif /* SQLITE_WASI */ #ifdef SQLITE_WASI # define osGetpid(X) (pid_t)1 #else /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ # define osGetpid(X) (pid_t)getpid() #endif /* ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) |
| ︙ | ︙ | |||
37351 37352 37353 37354 37355 37356 37357 37358 37359 37360 37361 37362 37363 37364 37365 |
{ "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 },
#else
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
{ "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
#else
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
#endif
| > > > > | 37661 37662 37663 37664 37665 37666 37667 37668 37669 37670 37671 37672 37673 37674 37675 37676 37677 37678 37679 |
{ "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 },
#else
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
#if defined(HAVE_FCHMOD)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#else
{ "fchmod", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
{ "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
#else
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
#endif
|
| ︙ | ︙ | |||
37387 37388 37389 37390 37391 37392 37393 |
#if defined(HAVE_FCHOWN)
{ "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
#else
{ "geteuid", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
| | > | > | 37701 37702 37703 37704 37705 37706 37707 37708 37709 37710 37711 37712 37713 37714 37715 37716 37717 37718 37719 37720 37721 37722 37723 37724 |
#if defined(HAVE_FCHOWN)
{ "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
#else
{ "geteuid", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
&& !defined(SQLITE_WASI)
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
#else
{ "mmap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \
&& !defined(SQLITE_WASI)
{ "munmap", (sqlite3_syscall_ptr)munmap, 0 },
#else
{ "munmap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMunmap ((int(*)(void*,size_t))aSyscall[23].pCurrent)
#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
|
| ︙ | ︙ | |||
38545 38546 38547 38548 38549 38550 38551 | ** are inserted in between. The locking might fail on one of the later ** transitions leaving the lock state different from what it started but ** still short of its goal. The following chart shows the allowed ** transitions and the inserted intermediate states: ** ** UNLOCKED -> SHARED ** SHARED -> RESERVED | | | 38861 38862 38863 38864 38865 38866 38867 38868 38869 38870 38871 38872 38873 38874 38875 |
** are inserted in between. The locking might fail on one of the later
** transitions leaving the lock state different from what it started but
** still short of its goal. The following chart shows the allowed
** transitions and the inserted intermediate states:
**
** UNLOCKED -> SHARED
** SHARED -> RESERVED
** SHARED -> EXCLUSIVE
** RESERVED -> (PENDING) -> EXCLUSIVE
** PENDING -> EXCLUSIVE
**
** This routine will only increase a lock. Use the sqlite3OsUnlock()
** routine to lower a locking level.
*/
static int unixLock(sqlite3_file *id, int eFileLock){
|
| ︙ | ︙ | |||
38578 38579 38580 38581 38582 38583 38584 | ** lack of shared-locks on Windows95 lives on, for backwards ** compatibility.) ** ** A process may only obtain a RESERVED lock after it has a SHARED lock. ** A RESERVED lock is implemented by grabbing a write-lock on the ** 'reserved byte'. ** | | | | > | < < < > | | > | | > | | 38894 38895 38896 38897 38898 38899 38900 38901 38902 38903 38904 38905 38906 38907 38908 38909 38910 38911 38912 38913 38914 38915 38916 38917 38918 38919 38920 38921 | ** lack of shared-locks on Windows95 lives on, for backwards ** compatibility.) ** ** A process may only obtain a RESERVED lock after it has a SHARED lock. ** A RESERVED lock is implemented by grabbing a write-lock on the ** 'reserved byte'. ** ** An EXCLUSIVE lock may only be requested after either a SHARED or ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining ** a write-lock on the entire 'shared byte range'. Since all other locks ** require a read-lock on one of the bytes within this range, this ensures ** that no other locks are held on the database. ** ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then ** a PENDING lock is obtained first. A PENDING lock is implemented by ** obtaining a write-lock on the 'pending byte'. This ensures that no new ** SHARED locks can be obtained, but existing SHARED locks are allowed to ** persist. If the call to this function fails to obtain the EXCLUSIVE ** lock in this case, it holds the PENDING lock intead. The client may ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED ** locks have cleared. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int tErrno = 0; |
| ︙ | ︙ | |||
38661 38662 38663 38664 38665 38666 38667 | /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK | | > > > | 38978 38979 38980 38981 38982 38983 38984 38985 38986 38987 38988 38989 38990 38991 38992 38993 38994 38995 38996 38997 38998 38999 39000 39001 39002 39003 39004 39005 |
/* A PENDING lock is needed before acquiring a SHARED lock and before
** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
** be released.
*/
lock.l_len = 1L;
lock.l_whence = SEEK_SET;
if( eFileLock==SHARED_LOCK
|| (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock==RESERVED_LOCK)
){
lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK);
lock.l_start = PENDING_BYTE;
if( unixFileLock(pFile, &lock) ){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( rc!=SQLITE_BUSY ){
storeLastErrno(pFile, tErrno);
}
goto end_lock;
}else if( eFileLock==EXCLUSIVE_LOCK ){
pFile->eFileLock = PENDING_LOCK;
pInode->eFileLock = PENDING_LOCK;
}
}
/* If control gets to this point, then actually go ahead and make
** operating system calls for the specified lock.
*/
|
| ︙ | ︙ | |||
38759 38760 38761 38762 38763 38764 38765 |
){
pFile->transCntrChng = 0;
pFile->dbUpdate = 0;
pFile->inNormalWrite = 1;
}
#endif
| < < < < | 39079 39080 39081 39082 39083 39084 39085 39086 39087 39088 39089 39090 39091 39092 39093 39094 39095 |
){
pFile->transCntrChng = 0;
pFile->dbUpdate = 0;
pFile->inNormalWrite = 1;
}
#endif
if( rc==SQLITE_OK ){
pFile->eFileLock = eFileLock;
pInode->eFileLock = eFileLock;
}
end_lock:
sqlite3_mutex_leave(pInode->pLockMutex);
OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock),
rc==SQLITE_OK ? "ok" : "failed"));
return rc;
|
| ︙ | ︙ | |||
40172 40173 40174 40175 40176 40177 40178 | ** are gather together into this division. */ /* ** Seek to the offset passed as the second argument, then read cnt ** bytes into pBuf. Return the number of bytes actually read. ** | < < < < < < | 40488 40489 40490 40491 40492 40493 40494 40495 40496 40497 40498 40499 40500 40501 |
** are gather together into this division.
*/
/*
** Seek to the offset passed as the second argument, then read cnt
** bytes into pBuf. Return the number of bytes actually read.
**
** To avoid stomping the errno value on a failed read the lastErrno value
** is set before returning.
*/
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
int prior = 0;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
|
| ︙ | ︙ | |||
50204 50205 50206 50207 50208 50209 50210 |
dwShareMode,
dwCreationDisposition,
&extendedParameters);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
| | | | | 50514 50515 50516 50517 50518 50519 50520 50521 50522 50523 50524 50525 50526 50527 50528 50529 50530 50531 50532 50533 50534 50535 50536 50537 50538 50539 50540 50541 50542 50543 50544 50545 50546 50547 50548 50549 50550 50551 50552 50553 50554 50555 50556 50557 50558 50559 50560 50561 50562 50563 50564 50565 |
dwShareMode,
dwCreationDisposition,
&extendedParameters);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
}while( winRetryIoerr(&cnt, &lastErrno) );
#else
do{
h = osCreateFileW((LPCWSTR)zConverted,
dwDesiredAccess,
dwShareMode, NULL,
dwCreationDisposition,
dwFlagsAndAttributes,
NULL);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
}while( winRetryIoerr(&cnt, &lastErrno) );
#endif
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
do{
h = osCreateFileA((LPCSTR)zConverted,
dwDesiredAccess,
dwShareMode, NULL,
dwCreationDisposition,
dwFlagsAndAttributes,
NULL);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
int rc2, isRO = 0;
sqlite3BeginBenignMalloc();
rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
}while( winRetryIoerr(&cnt, &lastErrno) );
}
#endif
winLogIoerr(cnt, __LINE__);
|
| ︙ | ︙ | |||
50463 50464 50465 50466 50467 50468 50469 50470 50471 50472 50473 50474 50475 50476 |
DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM_BKPT;
}
if( osIsNT() ){
| > > > > > > > | 50773 50774 50775 50776 50777 50778 50779 50780 50781 50782 50783 50784 50785 50786 50787 50788 50789 50790 50791 50792 50793 |
DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
if( zFilename==0 ){
*pResOut = 0;
OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
zFilename, pResOut, *pResOut));
return SQLITE_OK;
}
zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM_BKPT;
}
if( osIsNT() ){
|
| ︙ | ︙ | |||
52591 52592 52593 52594 52595 52596 52597 |
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
** (so that the right page to eject can be found by following pDirtyPrev
** pointers).
*/
struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
| | | 52908 52909 52910 52911 52912 52913 52914 52915 52916 52917 52918 52919 52920 52921 52922 |
** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
** (so that the right page to eject can be found by following pDirtyPrev
** pointers).
*/
struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
i64 nRefSum; /* Sum of ref counts over all pages */
int szCache; /* Configured cache size */
int szSpill; /* Size before spilling occurs */
int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */
u8 bPurgeable; /* True if pages are on backing store */
u8 eCreate; /* eCreate value for for xFetch() */
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
|
| ︙ | ︙ | |||
52620 52621 52622 52623 52624 52625 52626 |
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){
PgHdr *pPg;
unsigned char *a;
int j;
| > > > | | | | | > < | | 52937 52938 52939 52940 52941 52942 52943 52944 52945 52946 52947 52948 52949 52950 52951 52952 52953 52954 52955 52956 52957 52958 52959 52960 52961 52962 52963 52964 52965 52966 52967 52968 52969 52970 52971 52972 52973 |
int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
static void pcachePageTrace(int i, sqlite3_pcache_page *pLower){
PgHdr *pPg;
unsigned char *a;
int j;
if( pLower==0 ){
printf("%3d: NULL\n", i);
}else{
pPg = (PgHdr*)pLower->pExtra;
printf("%3d: nRef %2lld flgs %02x data ", i, pPg->nRef, pPg->flags);
a = (unsigned char *)pLower->pBuf;
for(j=0; j<12; j++) printf("%02x", a[j]);
printf(" ptr %p\n", pPg);
}
}
static void pcacheDump(PCache *pCache){
int N;
int i;
sqlite3_pcache_page *pLower;
if( sqlite3PcacheTrace<2 ) return;
if( pCache->pCache==0 ) return;
N = sqlite3PcachePagecount(pCache);
if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
for(i=1; i<=N; i++){
pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
pcachePageTrace(i, pLower);
if( pLower && ((PgHdr*)pLower)->pPage==0 ){
sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
}
}
}
#else
# define pcacheTrace(X)
# define pcachePageTrace(PGNO, X)
|
| ︙ | ︙ | |||
53365 53366 53367 53368 53369 53370 53371 | /* ** Return the total number of references to all pages held by the cache. ** ** This is not the total number of pages referenced, but the sum of the ** reference count for all pages. */ | | | | 53685 53686 53687 53688 53689 53690 53691 53692 53693 53694 53695 53696 53697 53698 53699 53700 53701 53702 53703 53704 53705 53706 |
/*
** Return the total number of references to all pages held by the cache.
**
** This is not the total number of pages referenced, but the sum of the
** reference count for all pages.
*/
SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache *pCache){
return pCache->nRefSum;
}
/*
** Return the number of references to the page supplied as an argument.
*/
SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr *p){
return p->nRef;
}
/*
** Return the total number of pages in the cache.
*/
SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
|
| ︙ | ︙ | |||
58027 58028 58029 58030 58031 58032 58033 58034 58035 58036 58037 58038 58039 58040 |
** If successful, return SQLITE_OK. If an IO error occurs while modifying
** the database file, return the error code to the caller.
*/
static int pager_truncate(Pager *pPager, Pgno nPage){
int rc = SQLITE_OK;
assert( pPager->eState!=PAGER_ERROR );
assert( pPager->eState!=PAGER_READER );
if( isOpen(pPager->fd)
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
int szPage = pPager->pageSize;
assert( pPager->eLock==EXCLUSIVE_LOCK );
| > > | 58347 58348 58349 58350 58351 58352 58353 58354 58355 58356 58357 58358 58359 58360 58361 58362 |
** If successful, return SQLITE_OK. If an IO error occurs while modifying
** the database file, return the error code to the caller.
*/
static int pager_truncate(Pager *pPager, Pgno nPage){
int rc = SQLITE_OK;
assert( pPager->eState!=PAGER_ERROR );
assert( pPager->eState!=PAGER_READER );
PAGERTRACE(("Truncate %d npage %u\n", PAGERID(pPager), nPage));
if( isOpen(pPager->fd)
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
int szPage = pPager->pageSize;
assert( pPager->eLock==EXCLUSIVE_LOCK );
|
| ︙ | ︙ | |||
58357 58358 58359 58360 58361 58362 58363 |
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && zSuper[0] && res ){
/* If there was a super-journal and this routine will return success,
** see if it is possible to delete the super-journal.
*/
assert( zSuper==&pPager->pTmpSpace[4] );
| | | 58679 58680 58681 58682 58683 58684 58685 58686 58687 58688 58689 58690 58691 58692 58693 |
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && zSuper[0] && res ){
/* If there was a super-journal and this routine will return success,
** see if it is possible to delete the super-journal.
*/
assert( zSuper==&pPager->pTmpSpace[4] );
memset(pPager->pTmpSpace, 0, 4);
rc = pager_delsuper(pPager, zSuper);
testcase( rc!=SQLITE_OK );
}
if( isHot && nPlayback ){
sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
nPlayback, pPager->zJournal);
}
|
| ︙ | ︙ | |||
58978 58979 58980 58981 58982 58983 58984 | ** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the ** synchronous=FULL versus synchronous=NORMAL setting determines when ** the xSync primitive is called and is relevant to all platforms. ** ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ | < | 59300 59301 59302 59303 59304 59305 59306 59307 59308 59309 59310 59311 59312 59313 |
** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the
** synchronous=FULL versus synchronous=NORMAL setting determines when
** the xSync primitive is called and is relevant to all platforms.
**
** Numeric values associated with these states are OFF==1, NORMAL=2,
** and FULL=3.
*/
SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
unsigned pgFlags /* Various flags */
){
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
if( pPager->tempFile ){
pPager->noSync = 1;
|
| ︙ | ︙ | |||
59013 59014 59015 59016 59017 59018 59019 |
}
if( pgFlags & PAGER_CACHESPILL ){
pPager->doNotSpill &= ~SPILLFLAG_OFF;
}else{
pPager->doNotSpill |= SPILLFLAG_OFF;
}
}
| < | 59334 59335 59336 59337 59338 59339 59340 59341 59342 59343 59344 59345 59346 59347 |
}
if( pgFlags & PAGER_CACHESPILL ){
pPager->doNotSpill &= ~SPILLFLAG_OFF;
}else{
pPager->doNotSpill |= SPILLFLAG_OFF;
}
}
/*
** The following global variable is incremented whenever the library
** attempts to open a temporary file. This information is used for
** testing and analysis only.
*/
#ifdef SQLITE_TEST
|
| ︙ | ︙ | |||
60115 60116 60117 60118 60119 60120 60121 | char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ const char *zUri = 0; /* URI args to copy */ int nUriByte = 1; /* Number of bytes of URI args at *zUri */ | < | 60435 60436 60437 60438 60439 60440 60441 60442 60443 60444 60445 60446 60447 60448 | char *zPathname = 0; /* Full path to database file */ int nPathname = 0; /* Number of bytes in zPathname */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ const char *zUri = 0; /* URI args to copy */ int nUriByte = 1; /* Number of bytes of URI args at *zUri */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). */ journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; |
| ︙ | ︙ | |||
60163 60164 60165 60166 60167 60168 60169 |
}
}
nPathname = sqlite3Strlen30(zPathname);
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
while( *z ){
z += strlen(z)+1;
z += strlen(z)+1;
| < | 60482 60483 60484 60485 60486 60487 60488 60489 60490 60491 60492 60493 60494 60495 |
}
}
nPathname = sqlite3Strlen30(zPathname);
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
while( *z ){
z += strlen(z)+1;
z += strlen(z)+1;
}
nUriByte = (int)(&z[1] - zUri);
assert( nUriByte>=1 );
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
** the database being opened will be more than pVfs->mxPathname
** bytes in length. This means the database cannot be opened,
|
| ︙ | ︙ | |||
60419 60420 60421 60422 60423 60424 60425 |
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
pPager->exclusiveMode = (u8)tempFile;
pPager->changeCountDone = pPager->tempFile;
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
| < | < < < < < < < < < < | 60737 60738 60739 60740 60741 60742 60743 60744 60745 60746 60747 60748 60749 60750 60751 |
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
pPager->exclusiveMode = (u8)tempFile;
pPager->changeCountDone = pPager->tempFile;
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
sqlite3PagerSetFlags(pPager, (SQLITE_DEFAULT_SYNCHRONOUS+1)|PAGER_CACHESPILL);
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
pPager->nExtra = (u16)nExtra;
pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
assert( isOpen(pPager->fd) || tempFile );
setSectorSize(pPager);
|
| ︙ | ︙ | |||
60959 60960 60961 60962 60963 60964 60965 60966 60967 60968 60969 60970 60971 60972 |
pPg->pPager = pPager;
assert( !isOpen(pPager->fd) || !MEMDB );
if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
goto pager_acquire_err;
}
if( noContent ){
/* Failure to set the bits in the InJournal bit-vectors is benign.
** It merely means that we might do some extra work to journal a
** page that does not need to be journaled. Nevertheless, be sure
** to test the case where a malloc error occurs while trying to set
| > > > > | 61266 61267 61268 61269 61270 61271 61272 61273 61274 61275 61276 61277 61278 61279 61280 61281 61282 61283 |
pPg->pPager = pPager;
assert( !isOpen(pPager->fd) || !MEMDB );
if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
if( pgno<=pPager->dbSize ){
sqlite3PcacheRelease(pPg);
pPg = 0;
}
goto pager_acquire_err;
}
if( noContent ){
/* Failure to set the bits in the InJournal bit-vectors is benign.
** It merely means that we might do some extra work to journal a
** page that does not need to be journaled. Nevertheless, be sure
** to test the case where a malloc error occurs while trying to set
|
| ︙ | ︙ | |||
61123 61124 61125 61126 61127 61128 61129 | if( pPage==0 ) return 0; return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); } /* ** Release a page reference. ** | | | | > > | | 61434 61435 61436 61437 61438 61439 61440 61441 61442 61443 61444 61445 61446 61447 61448 61449 61450 61451 61452 61453 61454 61455 61456 61457 61458 61459 61460 61461 61462 61463 61464 61465 61466 61467 61468 61469 |
if( pPage==0 ) return 0;
return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
}
/*
** Release a page reference.
**
** The sqlite3PagerUnref() and sqlite3PagerUnrefNotNull() may only be used
** if we know that the page being released is not the last reference to page1.
** The btree layer always holds page1 open until the end, so these first
** two routines can be used to release any page other than BtShared.pPage1.
** The assert() at tag-20230419-2 proves that this constraint is always
** honored.
**
** Use sqlite3PagerUnrefPageOne() to release page1. This latter routine
** checks the total number of outstanding pages and if the number of
** pages reaches zero it drops the database lock.
*/
SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){
TESTONLY( Pager *pPager = pPg->pPager; )
assert( pPg!=0 );
if( pPg->flags & PGHDR_MMAP ){
assert( pPg->pgno!=1 ); /* Page1 is never memory mapped */
pagerReleaseMapPage(pPg);
}else{
sqlite3PcacheRelease(pPg);
}
/* Do not use this routine to release the last reference to page1 */
assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); /* tag-20230419-2 */
}
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
if( pPg ) sqlite3PagerUnrefNotNull(pPg);
}
SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){
Pager *pPager;
assert( pPg!=0 );
|
| ︙ | ︙ | |||
61691 61692 61693 61694 61695 61696 61697 | # define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else # define DIRECT_MODE isDirectMode #endif | | | 62004 62005 62006 62007 62008 62009 62010 62011 62012 62013 62014 62015 62016 62017 62018 |
# define DIRECT_MODE 0
assert( isDirectMode==0 );
UNUSED_PARAMETER(isDirectMode);
#else
# define DIRECT_MODE isDirectMode
#endif
if( !pPager->changeCountDone && pPager->dbSize>0 ){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
/* Open page 1 of the file for writing. */
rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
assert( pPgHdr==0 || rc==SQLITE_OK );
|
| ︙ | ︙ | |||
62902 62903 62904 62905 62906 62907 62908 62909 |
/*
** Attempt to take an exclusive lock on the database file. If a PENDING lock
** is obtained instead, immediately release it.
*/
static int pagerExclusiveLock(Pager *pPager){
int rc; /* Return code */
| > | > | | 63215 63216 63217 63218 63219 63220 63221 63222 63223 63224 63225 63226 63227 63228 63229 63230 63231 63232 63233 63234 63235 63236 63237 |
/*
** Attempt to take an exclusive lock on the database file. If a PENDING lock
** is obtained instead, immediately release it.
*/
static int pagerExclusiveLock(Pager *pPager){
int rc; /* Return code */
u8 eOrigLock; /* Original lock */
assert( pPager->eLock>=SHARED_LOCK );
eOrigLock = pPager->eLock;
rc = pagerLockDb(pPager, EXCLUSIVE_LOCK);
if( rc!=SQLITE_OK ){
/* If the attempt to grab the exclusive lock failed, release the
** pending lock that may have been obtained instead. */
pagerUnlockDb(pPager, eOrigLock);
}
return rc;
}
/*
** Call sqlite3WalOpen() to open the WAL handle. If the pager is in
|
| ︙ | ︙ | |||
63913 63914 63915 63916 63917 63918 63919 63920 |
}else{
s1 = s2 = 0;
}
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
assert( nByte<=65536 );
| > | < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > | 64228 64229 64230 64231 64232 64233 64234 64235 64236 64237 64238 64239 64240 64241 64242 64243 64244 64245 64246 64247 64248 64249 64250 64251 64252 64253 64254 64255 64256 64257 64258 64259 64260 64261 64262 64263 64264 64265 64266 64267 64268 64269 64270 64271 64272 64273 64274 64275 |
}else{
s1 = s2 = 0;
}
assert( nByte>=8 );
assert( (nByte&0x00000007)==0 );
assert( nByte<=65536 );
assert( nByte%4==0 );
if( !nativeCksum ){
do {
s1 += BYTESWAP32(aData[0]) + s2;
s2 += BYTESWAP32(aData[1]) + s1;
aData += 2;
}while( aData<aEnd );
}else if( nByte%64==0 ){
do {
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
s1 += *aData++ + s2;
s2 += *aData++ + s1;
}while( aData<aEnd );
}else{
do {
s1 += *aData++ + s2;
s2 += *aData++ + s1;
}while( aData<aEnd );
}
assert( aData==aEnd );
aOut[0] = s1;
aOut[1] = s2;
}
/*
** If there is the possibility of concurrent access to the SHM file
|
| ︙ | ︙ | |||
66856 66857 66858 66859 66860 66861 66862 |
** https://sqlite.org/src/info/ff5be73dee
*/
if( pWal->syncHeader ){
rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
if( rc ) return rc;
}
}
| | > > | 67192 67193 67194 67195 67196 67197 67198 67199 67200 67201 67202 67203 67204 67205 67206 67207 67208 |
** https://sqlite.org/src/info/ff5be73dee
*/
if( pWal->syncHeader ){
rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
if( rc ) return rc;
}
}
if( (int)pWal->szPage!=szPage ){
return SQLITE_CORRUPT_BKPT; /* TH3 test case: cov1/corrupt155.test */
}
/* Setup information needed to write frames into the WAL */
w.pWal = pWal;
w.pFd = pWal->pWalFd;
w.iSyncPoint = 0;
w.syncFlags = sync_flags;
w.szPage = szPage;
|
| ︙ | ︙ | |||
67516 67517 67518 67519 67520 67521 67522 | ** contiguous or in order, but cell pointers are contiguous and in order. ** ** Cell content makes use of variable length integers. A variable ** length integer is 1 to 9 bytes where the lower 7 bits of each ** byte are used. The integer consists of all bytes that have bit 8 set and ** the first byte with bit 8 clear. The most significant byte of the integer ** appears first. A variable-length integer may not be more than 9 bytes long. | | | 67854 67855 67856 67857 67858 67859 67860 67861 67862 67863 67864 67865 67866 67867 67868 | ** contiguous or in order, but cell pointers are contiguous and in order. ** ** Cell content makes use of variable length integers. A variable ** length integer is 1 to 9 bytes where the lower 7 bits of each ** byte are used. The integer consists of all bytes that have bit 8 set and ** the first byte with bit 8 clear. The most significant byte of the integer ** appears first. A variable-length integer may not be more than 9 bytes long. ** As a special case, all 8 bits of the 9th byte are used as data. This ** allows a 64-bit integer to be encoded in 9 bytes. ** ** 0x00 becomes 0x00000000 ** 0x7f becomes 0x0000007f ** 0x81 0x00 becomes 0x00000080 ** 0x82 0x00 becomes 0x00000100 ** 0x80 0x7f becomes 0x0000007f |
| ︙ | ︙ | |||
67900 67901 67902 67903 67904 67905 67906 | /* ** Legal values for BtCursor.curFlags */ #define BTCF_WriteFlag 0x01 /* True if a write cursor */ #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ | | | 68238 68239 68240 68241 68242 68243 68244 68245 68246 68247 68248 68249 68250 68251 68252 | /* ** Legal values for BtCursor.curFlags */ #define BTCF_WriteFlag 0x01 /* True if a write cursor */ #define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */ #define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */ #define BTCF_AtLast 0x08 /* Cursor is pointing to the last entry */ #define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */ #define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */ #define BTCF_Pinned 0x40 /* Cursor is busy and cannot be moved */ /* ** Potential values for BtCursor.eState. ** |
| ︙ | ︙ | |||
68045 68046 68047 68048 68049 68050 68051 | u8 *aPgRef; /* 1 bit per page in the db (see above) */ Pgno nPage; /* Number of pages in the database */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ u32 nStep; /* Number of steps into the integrity_check process */ const char *zPfx; /* Error message prefix */ | | > | | 68383 68384 68385 68386 68387 68388 68389 68390 68391 68392 68393 68394 68395 68396 68397 68398 68399 | u8 *aPgRef; /* 1 bit per page in the db (see above) */ Pgno nPage; /* Number of pages in the database */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ u32 nStep; /* Number of steps into the integrity_check process */ const char *zPfx; /* Error message prefix */ Pgno v0; /* Value for first %u substitution in zPfx (root page) */ Pgno v1; /* Value for second %u substitution in zPfx (current pg) */ int v2; /* Value for third %d substitution in zPfx */ StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ }; /* ** Routines to read or write a two- and four-byte big-endian integer values. |
| ︙ | ︙ | |||
68509 68510 68511 68512 68513 68514 68515 |
** normally produced as a side-effect of SQLITE_CORRUPT_BKPT is augmented
** with the page number and filename associated with the (MemPage*).
*/
#ifdef SQLITE_DEBUG
int corruptPageError(int lineno, MemPage *p){
char *zMsg;
sqlite3BeginBenignMalloc();
| | | | 68848 68849 68850 68851 68852 68853 68854 68855 68856 68857 68858 68859 68860 68861 68862 68863 |
** normally produced as a side-effect of SQLITE_CORRUPT_BKPT is augmented
** with the page number and filename associated with the (MemPage*).
*/
#ifdef SQLITE_DEBUG
int corruptPageError(int lineno, MemPage *p){
char *zMsg;
sqlite3BeginBenignMalloc();
zMsg = sqlite3_mprintf("database corruption page %u of %s",
p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0)
);
sqlite3EndBenignMalloc();
if( zMsg ){
sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg);
}
sqlite3_free(zMsg);
return SQLITE_CORRUPT_BKPT;
|
| ︙ | ︙ | |||
69319 69320 69321 69322 69323 69324 69325 |
/*
** Provide hints to the cursor. The particular hint given (and the type
** and number of the varargs parameters) is determined by the eHintType
** parameter. See the definitions of the BTREE_HINT_* macros for details.
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
/* Used only by system that substitute their own storage engine */
| > > > > > > > > > > > > > > | | > > > | 69658 69659 69660 69661 69662 69663 69664 69665 69666 69667 69668 69669 69670 69671 69672 69673 69674 69675 69676 69677 69678 69679 69680 69681 69682 69683 69684 69685 69686 69687 69688 69689 69690 |
/*
** Provide hints to the cursor. The particular hint given (and the type
** and number of the varargs parameters) is determined by the eHintType
** parameter. See the definitions of the BTREE_HINT_* macros for details.
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
/* Used only by system that substitute their own storage engine */
#ifdef SQLITE_DEBUG
if( ALWAYS(eHintType==BTREE_HINT_RANGE) ){
va_list ap;
Expr *pExpr;
Walker w;
memset(&w, 0, sizeof(w));
w.xExprCallback = sqlite3CursorRangeHintExprCheck;
va_start(ap, eHintType);
pExpr = va_arg(ap, Expr*);
w.u.aMem = va_arg(ap, Mem*);
va_end(ap);
assert( pExpr!=0 );
assert( w.u.aMem!=0 );
sqlite3WalkExpr(&w, pExpr);
}
#endif /* SQLITE_DEBUG */
}
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/*
** Provide flag hints to the cursor.
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
pCur->hints = x;
|
| ︙ | ︙ | |||
69405 69406 69407 69408 69409 69410 69411 |
*pRC = SQLITE_CORRUPT_BKPT;
goto ptrmap_exit;
}
assert( offset <= (int)pBt->usableSize-5 );
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
| | | 69761 69762 69763 69764 69765 69766 69767 69768 69769 69770 69771 69772 69773 69774 69775 |
*pRC = SQLITE_CORRUPT_BKPT;
goto ptrmap_exit;
}
assert( offset <= (int)pBt->usableSize-5 );
pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
TRACE(("PTRMAP_UPDATE: %u->(%u,%u)\n", key, eType, parent));
*pRC= rc = sqlite3PagerWrite(pDbPage);
if( rc==SQLITE_OK ){
pPtrmap[offset] = eType;
put4byte(&pPtrmap[offset+1], parent);
}
}
|
| ︙ | ︙ | |||
69604 69605 69606 69607 69608 69609 69610 |
**
** The code is inlined and the loop is unrolled for performance.
** This routine is a high-runner.
*/
iKey = *pIter;
if( iKey>=0x80 ){
u8 x;
| | | | | | | | | > > > > | 69960 69961 69962 69963 69964 69965 69966 69967 69968 69969 69970 69971 69972 69973 69974 69975 69976 69977 69978 69979 69980 69981 69982 69983 69984 69985 69986 69987 69988 69989 69990 69991 69992 69993 69994 69995 69996 69997 69998 |
**
** The code is inlined and the loop is unrolled for performance.
** This routine is a high-runner.
*/
iKey = *pIter;
if( iKey>=0x80 ){
u8 x;
iKey = (iKey<<7) ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ 0x10204000 ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<7) ^ 0x4000 ^ (x = *++pIter);
if( x>=0x80 ){
iKey = (iKey<<8) ^ 0x8000 ^ (*++pIter);
}
}
}
}
}
}else{
iKey ^= 0x204000;
}
}else{
iKey ^= 0x4000;
}
}
pIter++;
pInfo->nKey = *(i64*)&iKey;
pInfo->nPayload = nPayload;
pInfo->pPayload = pIter;
|
| ︙ | ︙ | |||
69701 69702 69703 69704 69705 69706 69707 | ** Compute the total number of bytes that a Cell needs in the cell ** data area of the btree-page. The return number includes the cell ** data header and the local payload, but not any overflow page or ** the space used by the cell pointer. ** ** cellSizePtrNoPayload() => table internal nodes ** cellSizePtrTableLeaf() => table leaf nodes | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 70061 70062 70063 70064 70065 70066 70067 70068 70069 70070 70071 70072 70073 70074 70075 70076 70077 70078 70079 70080 70081 70082 70083 70084 70085 70086 70087 70088 70089 70090 70091 70092 70093 70094 70095 70096 70097 70098 70099 70100 70101 70102 70103 70104 70105 70106 70107 70108 70109 70110 70111 70112 70113 70114 70115 70116 70117 70118 70119 70120 70121 70122 70123 70124 70125 70126 70127 70128 70129 70130 70131 70132 70133 70134 |
** Compute the total number of bytes that a Cell needs in the cell
** data area of the btree-page. The return number includes the cell
** data header and the local payload, but not any overflow page or
** the space used by the cell pointer.
**
** cellSizePtrNoPayload() => table internal nodes
** cellSizePtrTableLeaf() => table leaf nodes
** cellSizePtr() => index internal nodes
** cellSizeIdxLeaf() => index leaf nodes
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
u8 *pIter = pCell + 4; /* For looping over bytes of pCell */
u8 *pEnd; /* End mark for a varint */
u32 nSize; /* Size value to return */
#ifdef SQLITE_DEBUG
/* The value returned by this function should always be the same as
** the (CellInfo.nSize) value found by doing a full parse of the
** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
** this function verifies that this invariant is not violated. */
CellInfo debuginfo;
pPage->xParseCell(pPage, pCell, &debuginfo);
#endif
assert( pPage->childPtrSize==4 );
nSize = *pIter;
if( nSize>=0x80 ){
pEnd = &pIter[8];
nSize &= 0x7f;
do{
nSize = (nSize<<7) | (*++pIter & 0x7f);
}while( *(pIter)>=0x80 && pIter<pEnd );
}
pIter++;
testcase( nSize==pPage->maxLocal );
testcase( nSize==(u32)pPage->maxLocal+1 );
if( nSize<=pPage->maxLocal ){
nSize += (u32)(pIter - pCell);
assert( nSize>4 );
}else{
int minLocal = pPage->minLocal;
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
testcase( nSize==pPage->maxLocal );
testcase( nSize==(u32)pPage->maxLocal+1 );
if( nSize>pPage->maxLocal ){
nSize = minLocal;
}
nSize += 4 + (u16)(pIter - pCell);
}
assert( nSize==debuginfo.nSize || CORRUPT_DB );
return (u16)nSize;
}
static u16 cellSizePtrIdxLeaf(MemPage *pPage, u8 *pCell){
u8 *pIter = pCell; /* For looping over bytes of pCell */
u8 *pEnd; /* End mark for a varint */
u32 nSize; /* Size value to return */
#ifdef SQLITE_DEBUG
/* The value returned by this function should always be the same as
** the (CellInfo.nSize) value found by doing a full parse of the
** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
** this function verifies that this invariant is not violated. */
CellInfo debuginfo;
pPage->xParseCell(pPage, pCell, &debuginfo);
#endif
assert( pPage->childPtrSize==0 );
nSize = *pIter;
if( nSize>=0x80 ){
pEnd = &pIter[8];
nSize &= 0x7f;
do{
nSize = (nSize<<7) | (*++pIter & 0x7f);
}while( *(pIter)>=0x80 && pIter<pEnd );
|
| ︙ | ︙ | |||
69953 69954 69955 69956 69957 69958 69959 |
pAddr = &data[cellOffset + i*2];
pc = get2byte(pAddr);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
/* These conditions have already been verified in btreeInitPage()
** if PRAGMA cell_size_check=ON.
*/
| | | | 70357 70358 70359 70360 70361 70362 70363 70364 70365 70366 70367 70368 70369 70370 70371 70372 70373 70374 |
pAddr = &data[cellOffset + i*2];
pc = get2byte(pAddr);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
/* These conditions have already been verified in btreeInitPage()
** if PRAGMA cell_size_check=ON.
*/
if( pc>iCellLast ){
return SQLITE_CORRUPT_PAGE(pPage);
}
assert( pc>=0 && pc<=iCellLast );
size = pPage->xCellSize(pPage, &src[pc]);
cbrk -= size;
if( cbrk<iCellStart || pc+size>usableSize ){
return SQLITE_CORRUPT_PAGE(pPage);
}
assert( cbrk+size<=usableSize && cbrk>=iCellStart );
testcase( cbrk+size==usableSize );
|
| ︙ | ︙ | |||
70071 70072 70073 70074 70075 70076 70077 | ** The caller guarantees that there is sufficient space to make the ** allocation. This routine might need to defragment in order to bring ** all the space together, however. This routine will avoid using ** the first two bytes past the cell pointer area since presumably this ** allocation is being made in order to insert a new cell, so we will ** also end up needing a new cell pointer. */ | | | 70475 70476 70477 70478 70479 70480 70481 70482 70483 70484 70485 70486 70487 70488 70489 |
** The caller guarantees that there is sufficient space to make the
** allocation. This routine might need to defragment in order to bring
** all the space together, however. This routine will avoid using
** the first two bytes past the cell pointer area since presumably this
** allocation is being made in order to insert a new cell, so we will
** also end up needing a new cell pointer.
*/
static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
int top; /* First byte of cell content area */
int rc = SQLITE_OK; /* Integer return code */
u8 *pTmp; /* Temp ptr into data[] */
int gap; /* First byte of gap between cell pointers and cell content */
|
| ︙ | ︙ | |||
70097 70098 70099 70100 70101 70102 70103 | /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size ** and the reserved space is zero (the usual value for reserved space) ** then the cell content offset of an empty page wants to be 65536. ** However, that integer is too large to be stored in a 2-byte unsigned ** integer, so a value of 0 is used in its place. */ pTmp = &data[hdr+5]; top = get2byte(pTmp); | < > > | 70501 70502 70503 70504 70505 70506 70507 70508 70509 70510 70511 70512 70513 70514 70515 70516 70517 70518 70519 70520 70521 70522 |
/* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size
** and the reserved space is zero (the usual value for reserved space)
** then the cell content offset of an empty page wants to be 65536.
** However, that integer is too large to be stored in a 2-byte unsigned
** integer, so a value of 0 is used in its place. */
pTmp = &data[hdr+5];
top = get2byte(pTmp);
if( gap>top ){
if( top==0 && pPage->pBt->usableSize==65536 ){
top = 65536;
}else{
return SQLITE_CORRUPT_PAGE(pPage);
}
}else if( top>(int)pPage->pBt->usableSize ){
return SQLITE_CORRUPT_PAGE(pPage);
}
/* If there is enough space between gap and top for one more cell pointer,
** and if the freelist is not empty, then search the
** freelist looking for a slot big enough to satisfy the request.
*/
testcase( gap+2==top );
|
| ︙ | ︙ | |||
70186 70187 70188 70189 70190 70191 70192 | assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize ); assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( iSize>=4 ); /* Minimum cell size is 4 */ | | | 70591 70592 70593 70594 70595 70596 70597 70598 70599 70600 70601 70602 70603 70604 70605 |
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( iSize>=4 ); /* Minimum cell size is 4 */
assert( CORRUPT_DB || iStart<=pPage->pBt->usableSize-4 );
/* The list of freeblocks must be in ascending order. Find the
** spot on the list where iStart should be inserted.
*/
hdr = pPage->hdrOffset;
iPtr = hdr + 1;
if( data[iPtr+1]==0 && data[iPtr]==0 ){
|
| ︙ | ︙ | |||
70243 70244 70245 70246 70247 70248 70249 70250 70251 70252 70253 70254 70255 70256 70257 70258 70259 70260 |
}
}
if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
data[hdr+7] -= nFrag;
}
pTmp = &data[hdr+5];
x = get2byte(pTmp);
if( iStart<=x ){
/* The new freeblock is at the beginning of the cell content area,
** so just extend the cell content area rather than create another
** freelist entry */
if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
put2byte(&data[hdr+1], iFreeBlk);
put2byte(&data[hdr+5], iEnd);
}else{
/* Insert the new freeblock into the freelist */
put2byte(&data[iPtr], iStart);
| > > > > > < < < < < < | | > | 70648 70649 70650 70651 70652 70653 70654 70655 70656 70657 70658 70659 70660 70661 70662 70663 70664 70665 70666 70667 70668 70669 70670 70671 70672 70673 70674 70675 70676 70677 70678 70679 70680 |
}
}
if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
data[hdr+7] -= nFrag;
}
pTmp = &data[hdr+5];
x = get2byte(pTmp);
if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
/* Overwrite deleted information with zeros when the secure_delete
** option is enabled */
memset(&data[iStart], 0, iSize);
}
if( iStart<=x ){
/* The new freeblock is at the beginning of the cell content area,
** so just extend the cell content area rather than create another
** freelist entry */
if( iStart<x ) return SQLITE_CORRUPT_PAGE(pPage);
if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PAGE(pPage);
put2byte(&data[hdr+1], iFreeBlk);
put2byte(&data[hdr+5], iEnd);
}else{
/* Insert the new freeblock into the freelist */
put2byte(&data[iPtr], iStart);
put2byte(&data[iStart], iFreeBlk);
put2byte(&data[iStart+2], iSize);
}
pPage->nFree += iOrigSize;
return SQLITE_OK;
}
/*
** Decode the flags byte (the first byte of the header) for a page
** and initialize fields of the MemPage structure accordingly.
|
| ︙ | ︙ | |||
70298 70299 70300 70301 70302 70303 70304 |
pPage->xParseCell = btreeParseCellPtr;
pPage->intKey = 1;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
| | | | 70703 70704 70705 70706 70707 70708 70709 70710 70711 70712 70713 70714 70715 70716 70717 70718 70719 70720 70721 70722 70723 70724 |
pPage->xParseCell = btreeParseCellPtr;
pPage->intKey = 1;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==(PTF_ZERODATA | PTF_LEAF) ){
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtrIdxLeaf;
pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
pPage->intKey = 0;
pPage->intKeyLeaf = 0;
pPage->xCellSize = cellSizePtrIdxLeaf;
pPage->xParseCell = btreeParseCellPtrIndex;
return SQLITE_CORRUPT_PAGE(pPage);
}
}else{
pPage->childPtrSize = 4;
pPage->leaf = 0;
if( flagByte==(PTF_ZERODATA) ){
|
| ︙ | ︙ | |||
72171 72172 72173 72174 72175 72176 72177 |
assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pDbPage->pBt==pBt );
if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT;
/* Move page iDbPage from its current location to page number iFreePage */
| | | 72576 72577 72578 72579 72580 72581 72582 72583 72584 72585 72586 72587 72588 72589 72590 |
assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pDbPage->pBt==pBt );
if( iDbPage<3 ) return SQLITE_CORRUPT_BKPT;
/* Move page iDbPage from its current location to page number iFreePage */
TRACE(("AUTOVACUUM: Moving %u to free page %u (ptr page %u type %u)\n",
iDbPage, iFreePage, iPtrPage, eType));
rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage, isCommit);
if( rc!=SQLITE_OK ){
return rc;
}
pDbPage->pgno = iFreePage;
|
| ︙ | ︙ | |||
74457 74458 74459 74460 74461 74462 74463 |
pCur->eState = CURSOR_VALID;
if( pCur->skipNext>0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
idx = ++pCur->ix;
| | > | 74862 74863 74864 74865 74866 74867 74868 74869 74870 74871 74872 74873 74874 74875 74876 74877 |
pCur->eState = CURSOR_VALID;
if( pCur->skipNext>0 ) return SQLITE_OK;
}
}
pPage = pCur->pPage;
idx = ++pCur->ix;
if( sqlite3FaultSim(412) ) pPage->isInit = 0;
if( !pPage->isInit ){
return SQLITE_CORRUPT_BKPT;
}
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
if( rc ) return rc;
|
| ︙ | ︙ | |||
74720 74721 74722 74723 74724 74725 74726 |
if( rc ){
goto end_allocate_page;
}
*pPgno = iTrunk;
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
*ppPage = pTrunk;
pTrunk = 0;
| | | 75126 75127 75128 75129 75130 75131 75132 75133 75134 75135 75136 75137 75138 75139 75140 |
if( rc ){
goto end_allocate_page;
}
*pPgno = iTrunk;
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
*ppPage = pTrunk;
pTrunk = 0;
TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1));
}else if( k>(u32)(pBt->usableSize/4 - 2) ){
/* Value of k is out of range. Database corruption */
rc = SQLITE_CORRUPT_PGNO(iTrunk);
goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
}else if( searchList
&& (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
|
| ︙ | ︙ | |||
74786 74787 74788 74789 74790 74791 74792 |
if( rc ){
goto end_allocate_page;
}
put4byte(&pPrevTrunk->aData[0], iNewTrunk);
}
}
pTrunk = 0;
| | | 75192 75193 75194 75195 75196 75197 75198 75199 75200 75201 75202 75203 75204 75205 75206 |
if( rc ){
goto end_allocate_page;
}
put4byte(&pPrevTrunk->aData[0], iNewTrunk);
}
}
pTrunk = 0;
TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1));
#endif
}else if( k>0 ){
/* Extract a leaf from the trunk */
u32 closest;
Pgno iPage;
unsigned char *aData = pTrunk->aData;
if( nearby>0 ){
|
| ︙ | ︙ | |||
74831 74832 74833 74834 74835 74836 74837 |
}
testcase( iPage==mxPage );
if( !searchList
|| (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
){
int noContent;
*pPgno = iPage;
| | | | 75237 75238 75239 75240 75241 75242 75243 75244 75245 75246 75247 75248 75249 75250 75251 75252 |
}
testcase( iPage==mxPage );
if( !searchList
|| (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
){
int noContent;
*pPgno = iPage;
TRACE(("ALLOCATE: %u was leaf %u of %u on trunk %u"
": %u more free pages\n",
*pPgno, closest+1, k, pTrunk->pgno, n-1));
rc = sqlite3PagerWrite(pTrunk->pDbPage);
if( rc ) goto end_allocate_page;
if( closest<k-1 ){
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
|
| ︙ | ︙ | |||
74888 74889 74890 74891 74892 74893 74894 |
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){
/* If *pPgno refers to a pointer-map page, allocate two new pages
** at the end of the file instead of one. The first allocated page
** becomes a new pointer-map page, the second is used by the caller.
*/
MemPage *pPg = 0;
| | | 75294 75295 75296 75297 75298 75299 75300 75301 75302 75303 75304 75305 75306 75307 75308 |
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){
/* If *pPgno refers to a pointer-map page, allocate two new pages
** at the end of the file instead of one. The first allocated page
** becomes a new pointer-map page, the second is used by the caller.
*/
MemPage *pPg = 0;
TRACE(("ALLOCATE: %u from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
}
if( rc ) return rc;
|
| ︙ | ︙ | |||
74911 74912 74913 74914 74915 74916 74917 |
rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
*ppPage = 0;
}
| | | 75317 75318 75319 75320 75321 75322 75323 75324 75325 75326 75327 75328 75329 75330 75331 |
rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
*ppPage = 0;
}
TRACE(("ALLOCATE: %u from end of file\n", *pPgno));
}
assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) );
end_allocate_page:
releasePage(pTrunk);
releasePage(pPrevTrunk);
|
| ︙ | ︙ | |||
75039 75040 75041 75042 75043 75044 75045 |
put4byte(&pTrunk->aData[4], nLeaf+1);
put4byte(&pTrunk->aData[8+nLeaf*4], iPage);
if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){
sqlite3PagerDontWrite(pPage->pDbPage);
}
rc = btreeSetHasContent(pBt, iPage);
}
| | | | 75445 75446 75447 75448 75449 75450 75451 75452 75453 75454 75455 75456 75457 75458 75459 75460 75461 75462 75463 75464 75465 75466 75467 75468 75469 75470 75471 75472 75473 75474 75475 75476 75477 75478 75479 75480 |
put4byte(&pTrunk->aData[4], nLeaf+1);
put4byte(&pTrunk->aData[8+nLeaf*4], iPage);
if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){
sqlite3PagerDontWrite(pPage->pDbPage);
}
rc = btreeSetHasContent(pBt, iPage);
}
TRACE(("FREE-PAGE: %u leaf on trunk page %u\n",pPage->pgno,pTrunk->pgno));
goto freepage_out;
}
}
/* If control flows to this point, then it was not possible to add the
** the page being freed as a leaf page of the first trunk in the free-list.
** Possibly because the free-list is empty, or possibly because the
** first trunk in the free-list is full. Either way, the page being freed
** will become the new first trunk page in the free-list.
*/
if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){
goto freepage_out;
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
goto freepage_out;
}
put4byte(pPage->aData, iTrunk);
put4byte(&pPage->aData[4], 0);
put4byte(&pPage1->aData[32], iPage);
TRACE(("FREE-PAGE: %u new trunk page replacing %u\n", pPage->pgno, iTrunk));
freepage_out:
if( pPage ){
pPage->isInit = 0;
}
releasePage(pPage);
releasePage(pTrunk);
|
| ︙ | ︙ | |||
75419 75420 75421 75422 75423 75424 75425 75426 75427 75428 75429 75430 75431 75432 | ** If the cell content will fit on the page, then put it there. If it ** will not fit, then make a copy of the cell content into pTemp if ** pTemp is not null. Regardless of pTemp, allocate a new entry ** in pPage->apOvfl[] and make it point to the cell content (either ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. */ static int insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ int sz, /* Bytes of content in pCell */ u8 *pTemp, /* Temp storage space for pCell, if needed */ | > > > > > > > > | 75825 75826 75827 75828 75829 75830 75831 75832 75833 75834 75835 75836 75837 75838 75839 75840 75841 75842 75843 75844 75845 75846 | ** If the cell content will fit on the page, then put it there. If it ** will not fit, then make a copy of the cell content into pTemp if ** pTemp is not null. Regardless of pTemp, allocate a new entry ** in pPage->apOvfl[] and make it point to the cell content (either ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. ** ** The insertCellFast() routine below works exactly the same as ** insertCell() except that it lacks the pTemp and iChild parameters ** which are assumed zero. Other than that, the two routines are the ** same. ** ** Fixes or enhancements to this routine should be reflected in ** insertCellFast()! */ static int insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ int sz, /* Bytes of content in pCell */ u8 *pTemp, /* Temp storage space for pCell, if needed */ |
| ︙ | ︙ | |||
75441 75442 75443 75444 75445 75446 75447 75448 75449 75450 75451 75452 |
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
assert( pPage->nFree>=0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
| > < | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 75855 75856 75857 75858 75859 75860 75861 75862 75863 75864 75865 75866 75867 75868 75869 75870 75871 75872 75873 75874 75875 75876 75877 75878 75879 75880 75881 75882 75883 75884 75885 75886 75887 75888 75889 75890 75891 75892 75893 75894 75895 75896 75897 75898 75899 75900 75901 75902 75903 75904 75905 75906 75907 75908 75909 75910 75911 75912 75913 75914 75915 75916 75917 75918 75919 75920 75921 75922 75923 75924 75925 75926 75927 75928 75929 75930 75931 75932 75933 75934 75935 75936 75937 75938 75939 75940 75941 75942 75943 75944 75945 75946 75947 75948 75949 75950 75951 75952 75953 75954 75955 75956 75957 75958 75959 75960 75961 75962 75963 75964 75965 |
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
assert( pPage->nFree>=0 );
assert( iChild>0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
put4byte(pCell, iChild);
j = pPage->nOverflow++;
/* Comparison against ArraySize-1 since we hold back one extra slot
** as a contingency. In other words, never need more than 3 overflow
** slots but 4 are allocated, just to be safe. */
assert( j < ArraySize(pPage->apOvfl)-1 );
pPage->apOvfl[j] = pCell;
pPage->aiOvfl[j] = (u16)i;
/* When multiple overflows occur, they are always sequential and in
** sorted order. This invariants arise because multiple overflows can
** only occur when inserting divider cells into the parent page during
** balancing, and the dividers are adjacent and sorted.
*/
assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( NEVER(rc!=SQLITE_OK) ){
return rc;
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
/* In a corrupt database where an entry in the cell index section of
** a btree page has a value of 3 or less, the pCell value might point
** as many as 4 bytes in front of the start of the aData buffer for
** the source page. Make sure this does not cause problems by not
** reading the first 4 bytes */
memcpy(&data[idx+4], pCell+4, sz-4);
put4byte(&data[idx], iChild);
pIns = pPage->aCellIdx + i*2;
memmove(pIns+2, pIns, 2*(pPage->nCell - i));
put2byte(pIns, idx);
pPage->nCell++;
/* increment the cell count */
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
int rc2 = SQLITE_OK;
/* The cell may contain a pointer to an overflow page. If so, write
** the entry for the overflow page into the pointer map.
*/
ptrmapPutOvflPtr(pPage, pPage, pCell, &rc2);
if( rc2 ) return rc2;
}
#endif
}
return SQLITE_OK;
}
/*
** This variant of insertCell() assumes that the pTemp and iChild
** parameters are both zero. Use this variant in sqlite3BtreeInsert()
** for performance improvement, and also so that this variant is only
** called from that one place, and is thus inlined, and thus runs must
** faster.
**
** Fixes or enhancements to this routine should be reflected into
** the insertCell() routine.
*/
static int insertCellFast(
MemPage *pPage, /* Page into which we are copying */
int i, /* New cell becomes the i-th cell of the page */
u8 *pCell, /* Content of the new cell */
int sz /* Bytes of content in pCell */
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
u8 *data; /* The content of the whole page */
u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
assert( pPage->nFree>=0 );
assert( pPage->nOverflow==0 );
if( sz+2>pPage->nFree ){
j = pPage->nOverflow++;
/* Comparison against ArraySize-1 since we hold back one extra slot
** as a contingency. In other words, never need more than 3 overflow
** slots but 4 are allocated, just to be safe. */
assert( j < ArraySize(pPage->apOvfl)-1 );
pPage->apOvfl[j] = pCell;
pPage->aiOvfl[j] = (u16)i;
|
| ︙ | ︙ | |||
75480 75481 75482 75483 75484 75485 75486 |
if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
| < < < < < < < < < | < | 75983 75984 75985 75986 75987 75988 75989 75990 75991 75992 75993 75994 75995 75996 75997 |
if( rc ){ return rc; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx], pCell, sz);
pIns = pPage->aCellIdx + i*2;
memmove(pIns+2, pIns, 2*(pPage->nCell - i));
put2byte(pIns, idx);
pPage->nCell++;
/* increment the cell count */
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell || CORRUPT_DB );
|
| ︙ | ︙ | |||
75675 75676 75677 75678 75679 75680 75681 | u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager); u8 *pData; int k; /* Current slot in pCArray->apEnd[] */ u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ assert( i<iEnd ); j = get2byte(&aData[hdr+5]); | | | 76168 76169 76170 76171 76172 76173 76174 76175 76176 76177 76178 76179 76180 76181 76182 |
u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager);
u8 *pData;
int k; /* Current slot in pCArray->apEnd[] */
u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
if( NEVER(j>(u32)usableSize) ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; pCArray->ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
pSrcEnd = pCArray->apEnd[k];
pData = pEnd;
while( 1/*exit by break*/ ){
|
| ︙ | ︙ | |||
75819 75820 75821 75822 75823 75824 75825 |
int nCell, /* Cells to delete */
CellArray *pCArray /* Array of cells */
){
u8 * const aData = pPg->aData;
u8 * const pEnd = &aData[pPg->pBt->usableSize];
u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
int nRet = 0;
| | | | > > > | > > > > > > > > > > | > | | | < | < < < < < | | > | | < | | 76312 76313 76314 76315 76316 76317 76318 76319 76320 76321 76322 76323 76324 76325 76326 76327 76328 76329 76330 76331 76332 76333 76334 76335 76336 76337 76338 76339 76340 76341 76342 76343 76344 76345 76346 76347 76348 76349 76350 76351 76352 76353 76354 76355 76356 76357 76358 76359 76360 76361 76362 76363 76364 76365 76366 76367 76368 76369 |
int nCell, /* Cells to delete */
CellArray *pCArray /* Array of cells */
){
u8 * const aData = pPg->aData;
u8 * const pEnd = &aData[pPg->pBt->usableSize];
u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
int nRet = 0;
int i, j;
int iEnd = iFirst + nCell;
int nFree = 0;
int aOfst[10];
int aAfter[10];
for(i=iFirst; i<iEnd; i++){
u8 *pCell = pCArray->apCell[i];
if( SQLITE_WITHIN(pCell, pStart, pEnd) ){
int sz;
int iAfter;
int iOfst;
/* No need to use cachedCellSize() here. The sizes of all cells that
** are to be freed have already been computing while deciding which
** cells need freeing */
sz = pCArray->szCell[i]; assert( sz>0 );
iOfst = (u16)(pCell - aData);
iAfter = iOfst+sz;
for(j=0; j<nFree; j++){
if( aOfst[j]==iAfter ){
aOfst[j] = iOfst;
break;
}else if( aAfter[j]==iOfst ){
aAfter[j] = iAfter;
break;
}
}
if( j>=nFree ){
if( nFree>=(int)(sizeof(aOfst)/sizeof(aOfst[0])) ){
for(j=0; j<nFree; j++){
freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]);
}
nFree = 0;
}
aOfst[nFree] = iOfst;
aAfter[nFree] = iAfter;
if( &aData[iAfter]>pEnd ) return 0;
nFree++;
}
nRet++;
}
}
for(j=0; j<nFree; j++){
freeSpace(pPg, aOfst[j], aAfter[j]-aOfst[j]);
}
return nRet;
}
/*
** pCArray contains pointers to and sizes of all cells in the page being
** balanced. The current page, pPg, has pPg->nCell cells starting with
|
| ︙ | ︙ | |||
75907 75908 75909 75910 75911 75912 75913 |
}
if( iNewEnd < iOldEnd ){
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
assert( nCell>=nTail );
nCell -= nTail;
}
| | | | 76408 76409 76410 76411 76412 76413 76414 76415 76416 76417 76418 76419 76420 76421 76422 76423 76424 |
}
if( iNewEnd < iOldEnd ){
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
assert( nCell>=nTail );
nCell -= nTail;
}
pData = &aData[get2byte(&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;
|
| ︙ | ︙ | |||
76646 76647 76648 76649 76650 76651 76652 | ** must be true: ** (1) We found one or more cells (cntNew[0])>0), or ** (2) pPage is a virtual root page. A virtual root page is when ** the real root page is page 1 and we are the only child of ** that page. */ assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB); | | | 77147 77148 77149 77150 77151 77152 77153 77154 77155 77156 77157 77158 77159 77160 77161 |
** must be true:
** (1) We found one or more cells (cntNew[0])>0), or
** (2) pPage is a virtual root page. A virtual root page is when
** the real root page is page 1 and we are the only child of
** that page.
*/
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB);
TRACE(("BALANCE: old: %u(nc=%u) %u(nc=%u) %u(nc=%u)\n",
apOld[0]->pgno, apOld[0]->nCell,
nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0,
nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0
));
/*
** Allocate k new pages. Reuse old pages where possible.
|
| ︙ | ︙ | |||
76730 76731 76732 76733 76734 76735 76736 |
sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA);
sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB);
apNew[i]->pgno = pgnoB;
apNew[iB]->pgno = pgnoA;
}
}
| | | | 77231 77232 77233 77234 77235 77236 77237 77238 77239 77240 77241 77242 77243 77244 77245 77246 |
sqlite3PagerRekey(apNew[iB]->pDbPage, pgnoA, fgA);
sqlite3PagerRekey(apNew[i]->pDbPage, pgnoB, fgB);
apNew[i]->pgno = pgnoB;
apNew[iB]->pgno = pgnoA;
}
}
TRACE(("BALANCE: new: %u(%u nc=%u) %u(%u nc=%u) %u(%u nc=%u) "
"%u(%u nc=%u) %u(%u nc=%u)\n",
apNew[0]->pgno, szNew[0], cntNew[0],
nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0,
nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0,
nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0,
nNew>=3 ? cntNew[2] - cntNew[1] - !leafData : 0,
nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0,
nNew>=4 ? cntNew[3] - cntNew[2] - !leafData : 0,
|
| ︙ | ︙ | |||
76976 76977 76978 76979 76980 76981 76982 |
for(i=0; i<nNew; i++){
u32 key = get4byte(&apNew[i]->aData[8]);
ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc);
}
}
assert( pParent->isInit );
| | | 77477 77478 77479 77480 77481 77482 77483 77484 77485 77486 77487 77488 77489 77490 77491 |
for(i=0; i<nNew; i++){
u32 key = get4byte(&apNew[i]->aData[8]);
ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc);
}
}
assert( pParent->isInit );
TRACE(("BALANCE: finished: old=%u new=%u cells=%u\n",
nOld, nNew, b.nCell));
/* Free any old pages that were not reused as new pages.
*/
for(i=nNew; i<nOld; i++){
freePage(apOld[i], &rc);
}
|
| ︙ | ︙ | |||
77061 77062 77063 77064 77065 77066 77067 |
releasePage(pChild);
return rc;
}
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
assert( pChild->nCell==pRoot->nCell || CORRUPT_DB );
| | | 77562 77563 77564 77565 77566 77567 77568 77569 77570 77571 77572 77573 77574 77575 77576 |
releasePage(pChild);
return rc;
}
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
assert( pChild->nCell==pRoot->nCell || CORRUPT_DB );
TRACE(("BALANCE: copy root %u into %u\n", pRoot->pgno, pChild->pgno));
/* Copy the overflow cells from pRoot to pChild */
memcpy(pChild->aiOvfl, pRoot->aiOvfl,
pRoot->nOverflow*sizeof(pRoot->aiOvfl[0]));
memcpy(pChild->apOvfl, pRoot->apOvfl,
pRoot->nOverflow*sizeof(pRoot->apOvfl[0]));
pChild->nOverflow = pRoot->nOverflow;
|
| ︙ | ︙ | |||
77544 77545 77546 77547 77548 77549 77550 |
x2.nData = pX->nKey;
x2.nZero = 0;
return btreeOverwriteCell(pCur, &x2);
}
}
}
assert( pCur->eState==CURSOR_VALID
| | | | 78045 78046 78047 78048 78049 78050 78051 78052 78053 78054 78055 78056 78057 78058 78059 78060 78061 78062 78063 78064 78065 78066 78067 78068 78069 78070 78071 78072 78073 78074 |
x2.nData = pX->nKey;
x2.nZero = 0;
return btreeOverwriteCell(pCur, &x2);
}
}
}
assert( pCur->eState==CURSOR_VALID
|| (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB );
pPage = pCur->pPage;
assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
assert( pPage->leaf || !pPage->intKey );
if( pPage->nFree<0 ){
if( NEVER(pCur->eState>CURSOR_INVALID) ){
/* ^^^^^--- due to the moveToRoot() call above */
rc = SQLITE_CORRUPT_BKPT;
}else{
rc = btreeComputeFreeSpace(pPage);
}
if( rc ) return rc;
}
TRACE(("INSERT: table=%u nkey=%lld ndata=%u page=%u %s\n",
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit || CORRUPT_DB );
newCell = p->pBt->pTmpSpace;
assert( newCell!=0 );
assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
if( flags & BTREE_PREFORMAT ){
|
| ︙ | ︙ | |||
77586 77587 77588 77589 77590 77591 77592 77593 77594 77595 77596 77597 77598 77599 |
}else{
rc = fillInCell(pPage, newCell, pX, &szNew);
if( rc ) goto end_insert;
}
assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(p->pBt) );
idx = pCur->ix;
if( loc==0 ){
CellInfo info;
assert( idx>=0 );
if( idx>=pPage->nCell ){
return SQLITE_CORRUPT_BKPT;
}
rc = sqlite3PagerWrite(pPage->pDbPage);
| > | 78087 78088 78089 78090 78091 78092 78093 78094 78095 78096 78097 78098 78099 78100 78101 |
}else{
rc = fillInCell(pPage, newCell, pX, &szNew);
if( rc ) goto end_insert;
}
assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(p->pBt) );
idx = pCur->ix;
pCur->info.nSize = 0;
if( loc==0 ){
CellInfo info;
assert( idx>=0 );
if( idx>=pPage->nCell ){
return SQLITE_CORRUPT_BKPT;
}
rc = sqlite3PagerWrite(pPage->pDbPage);
|
| ︙ | ︙ | |||
77634 77635 77636 77637 77638 77639 77640 |
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->leaf );
idx = ++pCur->ix;
pCur->curFlags &= ~BTCF_ValidNKey;
}else{
assert( pPage->leaf );
}
| | | 78136 78137 78138 78139 78140 78141 78142 78143 78144 78145 78146 78147 78148 78149 78150 |
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->leaf );
idx = ++pCur->ix;
pCur->curFlags &= ~BTCF_ValidNKey;
}else{
assert( pPage->leaf );
}
rc = insertCellFast(pPage, idx, newCell, szNew);
assert( pPage->nOverflow==0 || rc==SQLITE_OK );
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
/* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move
** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables.
|
| ︙ | ︙ | |||
77658 77659 77660 77661 77662 77663 77664 | ** happen while processing an "INSERT INTO ... SELECT" statement), it ** is advantageous to leave the cursor pointing to the last entry in ** the b-tree if possible. If the cursor is left pointing to the last ** entry in the table, and the next row inserted has an integer key ** larger than the largest existing key, it is possible to insert the ** row without seeking the cursor. This can be a big performance boost. */ | < | 78160 78161 78162 78163 78164 78165 78166 78167 78168 78169 78170 78171 78172 78173 |
** happen while processing an "INSERT INTO ... SELECT" statement), it
** is advantageous to leave the cursor pointing to the last entry in
** the b-tree if possible. If the cursor is left pointing to the last
** entry in the table, and the next row inserted has an integer key
** larger than the largest existing key, it is possible to insert the
** row without seeking the cursor. This can be a big performance boost.
*/
if( pPage->nOverflow ){
assert( rc==SQLITE_OK );
pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
** fails. Internal data structure corruption will result otherwise.
|
| ︙ | ︙ | |||
77858 77859 77860 77861 77862 77863 77864 77865 77866 77867 77868 77869 77870 77871 |
pPage = pCur->pPage;
if( pPage->nCell<=iCellIdx ){
return SQLITE_CORRUPT_BKPT;
}
pCell = findCell(pPage, iCellIdx);
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
return SQLITE_CORRUPT_BKPT;
}
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
** be preserved following this delete operation. If the current delete
** will cause a b-tree rebalance, then this is done by saving the cursor
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
** returning.
| > > > | 78359 78360 78361 78362 78363 78364 78365 78366 78367 78368 78369 78370 78371 78372 78373 78374 78375 |
pPage = pCur->pPage;
if( pPage->nCell<=iCellIdx ){
return SQLITE_CORRUPT_BKPT;
}
pCell = findCell(pPage, iCellIdx);
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
return SQLITE_CORRUPT_BKPT;
}
if( pCell<&pPage->aCellIdx[pPage->nCell] ){
return SQLITE_CORRUPT_BKPT;
}
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
** be preserved following this delete operation. If the current delete
** will cause a b-tree rebalance, then this is done by saving the cursor
** key and leaving the cursor in CURSOR_REQUIRESEEK state before
** returning.
|
| ︙ | ︙ | |||
78607 78608 78609 78610 78611 78612 78613 |
pCheck->mxErr--;
pCheck->nErr++;
va_start(ap, zFormat);
if( pCheck->errMsg.nChar ){
sqlite3_str_append(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
| | > | 79111 79112 79113 79114 79115 79116 79117 79118 79119 79120 79121 79122 79123 79124 79125 79126 |
pCheck->mxErr--;
pCheck->nErr++;
va_start(ap, zFormat);
if( pCheck->errMsg.nChar ){
sqlite3_str_append(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx,
pCheck->v0, pCheck->v1, pCheck->v2);
}
sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
if( pCheck->errMsg.accError==SQLITE_NOMEM ){
checkOom(pCheck);
}
}
|
| ︙ | ︙ | |||
78647 78648 78649 78650 78651 78652 78653 |
** Return 1 if there are 2 or more references to the page and 0 if
** if this is the first reference to the page.
**
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
if( iPage>pCheck->nPage || iPage==0 ){
| | | | 79152 79153 79154 79155 79156 79157 79158 79159 79160 79161 79162 79163 79164 79165 79166 79167 79168 79169 79170 |
** Return 1 if there are 2 or more references to the page and 0 if
** if this is the first reference to the page.
**
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
if( iPage>pCheck->nPage || iPage==0 ){
checkAppendMsg(pCheck, "invalid page number %u", iPage);
return 1;
}
if( getPageReferenced(pCheck, iPage) ){
checkAppendMsg(pCheck, "2nd reference to page %u", iPage);
return 1;
}
setPageReferenced(pCheck, iPage);
return 0;
}
#ifndef SQLITE_OMIT_AUTOVACUUM
|
| ︙ | ︙ | |||
78677 78678 78679 78680 78681 78682 78683 |
int rc;
u8 ePtrmapType;
Pgno iPtrmapParent;
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck);
| | | | 79182 79183 79184 79185 79186 79187 79188 79189 79190 79191 79192 79193 79194 79195 79196 79197 79198 79199 79200 79201 79202 |
int rc;
u8 ePtrmapType;
Pgno iPtrmapParent;
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) checkOom(pCheck);
checkAppendMsg(pCheck, "Failed to read ptrmap key=%u", iChild);
return;
}
if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
checkAppendMsg(pCheck,
"Bad ptr map entry key=%u expected=(%u,%u) got=(%u,%u)",
iChild, eType, iParent, ePtrmapType, iPtrmapParent);
}
}
#endif
/*
** Check the integrity of the freelist or of an overflow page list.
|
| ︙ | ︙ | |||
78708 78709 78710 78711 78712 78713 78714 |
int nErrAtStart = pCheck->nErr;
while( iPage!=0 && pCheck->mxErr ){
DbPage *pOvflPage;
unsigned char *pOvflData;
if( checkRef(pCheck, iPage) ) break;
N--;
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
| | | | 79213 79214 79215 79216 79217 79218 79219 79220 79221 79222 79223 79224 79225 79226 79227 79228 79229 79230 79231 79232 79233 79234 79235 79236 79237 79238 79239 79240 |
int nErrAtStart = pCheck->nErr;
while( iPage!=0 && pCheck->mxErr ){
DbPage *pOvflPage;
unsigned char *pOvflData;
if( checkRef(pCheck, iPage) ) break;
N--;
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
checkAppendMsg(pCheck, "failed to get page %u", iPage);
break;
}
pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
if( isFreeList ){
u32 n = (u32)get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0);
}
#endif
if( n>pCheck->pBt->usableSize/4-2 ){
checkAppendMsg(pCheck,
"freelist leaf count too big on page %u", iPage);
N--;
}else{
for(i=0; i<(int)n; i++){
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0);
|
| ︙ | ︙ | |||
78753 78754 78755 78756 78757 78758 78759 |
}
#endif
iPage = get4byte(pOvflData);
sqlite3PagerUnref(pOvflPage);
}
if( N && nErrAtStart==pCheck->nErr ){
checkAppendMsg(pCheck,
| | | 79258 79259 79260 79261 79262 79263 79264 79265 79266 79267 79268 79269 79270 79271 79272 |
}
#endif
iPage = get4byte(pOvflData);
sqlite3PagerUnref(pOvflPage);
}
if( N && nErrAtStart==pCheck->nErr ){
checkAppendMsg(pCheck,
"%s is %u but should be %u",
isFreeList ? "size" : "overflow list length",
expected-N, expected);
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
|
| ︙ | ︙ | |||
78783 78784 78785 78786 78787 78788 78789 |
**
** This heap is used for cell overlap and coverage testing. Each u32
** entry represents the span of a cell or freeblock on a btree page.
** The upper 16 bits are the index of the first byte of a range and the
** lower 16 bits are the index of the last byte of that range.
*/
static void btreeHeapInsert(u32 *aHeap, u32 x){
| > > | | 79288 79289 79290 79291 79292 79293 79294 79295 79296 79297 79298 79299 79300 79301 79302 79303 79304 |
**
** This heap is used for cell overlap and coverage testing. Each u32
** entry represents the span of a cell or freeblock on a btree page.
** The upper 16 bits are the index of the first byte of a range and the
** lower 16 bits are the index of the last byte of that range.
*/
static void btreeHeapInsert(u32 *aHeap, u32 x){
u32 j, i;
assert( aHeap!=0 );
i = ++aHeap[0];
aHeap[i] = x;
while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){
x = aHeap[j];
aHeap[j] = aHeap[i];
aHeap[i] = x;
i = j;
}
|
| ︙ | ︙ | |||
78866 78867 78868 78869 78870 78871 78872 | */ checkProgress(pCheck); if( pCheck->mxErr==0 ) goto end_of_check; pBt = pCheck->pBt; usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage) ) return 0; | | | | 79373 79374 79375 79376 79377 79378 79379 79380 79381 79382 79383 79384 79385 79386 79387 79388 |
*/
checkProgress(pCheck);
if( pCheck->mxErr==0 ) goto end_of_check;
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage) ) return 0;
pCheck->zPfx = "Tree %u page %u: ";
pCheck->v0 = pCheck->v1 = iPage;
if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
goto end_of_check;
}
/* Clear MemPage.isInit to make sure the corruption detection code in
|
| ︙ | ︙ | |||
78893 78894 78895 78896 78897 78898 78899 |
checkAppendMsg(pCheck, "free space corruption", rc);
goto end_of_check;
}
data = pPage->aData;
hdr = pPage->hdrOffset;
/* Set up for cell analysis */
| | | | 79400 79401 79402 79403 79404 79405 79406 79407 79408 79409 79410 79411 79412 79413 79414 79415 79416 79417 79418 79419 79420 79421 79422 79423 79424 79425 79426 79427 79428 79429 79430 79431 79432 79433 79434 |
checkAppendMsg(pCheck, "free space corruption", rc);
goto end_of_check;
}
data = pPage->aData;
hdr = pPage->hdrOffset;
/* Set up for cell analysis */
pCheck->zPfx = "Tree %u page %u cell %u: ";
contentOffset = get2byteNotZero(&data[hdr+5]);
assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
/* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
** number of cells on the page. */
nCell = get2byte(&data[hdr+3]);
assert( pPage->nCell==nCell );
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
** immediately follows the b-tree page header. */
cellStart = hdr + 12 - 4*pPage->leaf;
assert( pPage->aCellIdx==&data[cellStart] );
pCellIdx = &data[cellStart + 2*(nCell-1)];
if( !pPage->leaf ){
/* Analyze the right-child page of internal pages */
pgno = get4byte(&data[hdr+8]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
pCheck->zPfx = "Tree %u page %u right child: ";
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
keyCanBeEqual = 0;
}else{
/* For leaf pages, the coverage check will occur in the same loop
|
| ︙ | ︙ | |||
78937 78938 78939 78940 78941 78942 78943 |
/* Check cell size */
pCheck->v2 = i;
assert( pCellIdx==&data[cellStart + i*2] );
pc = get2byteAligned(pCellIdx);
pCellIdx -= 2;
if( pc<contentOffset || pc>usableSize-4 ){
| | | 79444 79445 79446 79447 79448 79449 79450 79451 79452 79453 79454 79455 79456 79457 79458 |
/* Check cell size */
pCheck->v2 = i;
assert( pCellIdx==&data[cellStart + i*2] );
pc = get2byteAligned(pCellIdx);
pCellIdx -= 2;
if( pc<contentOffset || pc>usableSize-4 ){
checkAppendMsg(pCheck, "Offset %u out of range %u..%u",
pc, contentOffset, usableSize-4);
doCoverageCheck = 0;
continue;
}
pCell = &data[pc];
pPage->xParseCell(pPage, pCell, &info);
if( pc+info.nSize>usableSize ){
|
| ︙ | ︙ | |||
79069 79070 79071 79072 79073 79074 79075 |
/* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
** is stored in the fifth field of the b-tree page header.
** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
** number of fragmented free bytes within the cell content area.
*/
if( heap[0]==0 && nFrag!=data[hdr+7] ){
checkAppendMsg(pCheck,
| | | 79576 79577 79578 79579 79580 79581 79582 79583 79584 79585 79586 79587 79588 79589 79590 |
/* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
** is stored in the fifth field of the b-tree page header.
** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
** number of fragmented free bytes within the cell content area.
*/
if( heap[0]==0 && nFrag!=data[hdr+7] ){
checkAppendMsg(pCheck,
"Fragmentation of %u bytes reported as %u on page %u",
nFrag, data[hdr+7], iPage);
}
}
end_of_check:
if( !doCoverageCheck ) pPage->isInit = savedIsInit;
releasePage(pPage);
|
| ︙ | ︙ | |||
79166 79167 79168 79169 79170 79171 79172 |
i = PENDING_BYTE_PAGE(pBt);
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
if( bCkFreelist ){
| | | | 79673 79674 79675 79676 79677 79678 79679 79680 79681 79682 79683 79684 79685 79686 79687 79688 79689 79690 79691 79692 79693 79694 79695 79696 79697 79698 79699 79700 79701 79702 79703 79704 |
i = PENDING_BYTE_PAGE(pBt);
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
if( bCkFreelist ){
sCheck.zPfx = "Freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
get4byte(&pBt->pPage1->aData[36]));
sCheck.zPfx = 0;
}
/* Check all the tables.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
if( !bPartial ){
if( pBt->autoVacuum ){
Pgno mx = 0;
Pgno mxInHdr;
for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
mxInHdr = get4byte(&pBt->pPage1->aData[52]);
if( mx!=mxInHdr ){
checkAppendMsg(&sCheck,
"max rootpage (%u) disagrees with header (%u)",
mx, mxInHdr
);
}
}else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
checkAppendMsg(&sCheck,
"incremental_vacuum enabled with a max rootpage of zero"
);
|
| ︙ | ︙ | |||
79214 79215 79216 79217 79218 79219 79220 |
/* Make sure every page in the file is referenced
*/
if( !bPartial ){
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
| | | | | 79721 79722 79723 79724 79725 79726 79727 79728 79729 79730 79731 79732 79733 79734 79735 79736 79737 79738 79739 79740 79741 79742 79743 79744 79745 79746 79747 |
/* Make sure every page in the file is referenced
*/
if( !bPartial ){
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, "Page %u: never used", i);
}
#else
/* If the database supports auto-vacuum, make sure no tables contain
** references to pointer-map pages.
*/
if( getPageReferenced(&sCheck, i)==0 &&
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
checkAppendMsg(&sCheck, "Page %u: never used", i);
}
if( getPageReferenced(&sCheck, i)!=0 &&
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
checkAppendMsg(&sCheck, "Page %u: pointer map referenced", i);
}
#endif
}
}
/* Clean up and report errors.
*/
|
| ︙ | ︙ | |||
79788 79789 79790 79791 79792 79793 79794 | i64 iOff; assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 ); assert( p->bDestLocked ); assert( !isFatalError(p->rc) ); assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ); assert( zSrcData ); | < < < < | < < | 80295 80296 80297 80298 80299 80300 80301 80302 80303 80304 80305 80306 80307 80308 80309 |
i64 iOff;
assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
assert( zSrcData );
assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 );
/* This loop runs once for each destination page spanned by the source
** page. For each iteration, variable iOff is set to the byte offset
** of the destination page.
*/
for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
DbPage *pDestPg = 0;
|
| ︙ | ︙ | |||
79927 79928 79929 79930 79931 79932 79933 |
}
/* Do not allow backup if the destination database is in WAL mode
** and the page sizes are different between source and destination */
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
pgszDest = sqlite3BtreeGetPageSize(p->pDest);
destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
| | > > > | 80428 80429 80430 80431 80432 80433 80434 80435 80436 80437 80438 80439 80440 80441 80442 80443 80444 80445 |
}
/* Do not allow backup if the destination database is in WAL mode
** and the page sizes are different between source and destination */
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
pgszDest = sqlite3BtreeGetPageSize(p->pDest);
destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
if( SQLITE_OK==rc
&& (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager))
&& pgszSrc!=pgszDest
){
rc = SQLITE_READONLY;
}
/* Now that there is a read-lock on the source database, query the
** source pager for the number of pages in the database.
*/
nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc);
|
| ︙ | ︙ | |||
80476 80477 80478 80479 80480 80481 80482 80483 80484 80485 80486 80487 80488 80489 |
*/
SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
Mem tmp;
char zBuf[100];
char *z;
int i, j, incr;
if( (p->flags & MEM_Str)==0 ) return 1;
if( p->flags & MEM_Term ){
/* Insure that the string is properly zero-terminated. Pay particular
** attention to the case where p->n is odd */
if( p->szMalloc>0 && p->z==p->zMalloc ){
assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 );
assert( p->enc!=SQLITE_UTF8 || p->szMalloc >= p->n+1 );
}
| > | 80980 80981 80982 80983 80984 80985 80986 80987 80988 80989 80990 80991 80992 80993 80994 |
*/
SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){
Mem tmp;
char zBuf[100];
char *z;
int i, j, incr;
if( (p->flags & MEM_Str)==0 ) return 1;
if( p->db && p->db->mallocFailed ) return 1;
if( p->flags & MEM_Term ){
/* Insure that the string is properly zero-terminated. Pay particular
** attention to the case where p->n is odd */
if( p->szMalloc>0 && p->z==p->zMalloc ){
assert( p->enc==SQLITE_UTF8 || p->szMalloc >= ((p->n+1)&~1)+2 );
assert( p->enc!=SQLITE_UTF8 || p->szMalloc >= p->n+1 );
}
|
| ︙ | ︙ | |||
80758 80759 80760 80761 80762 80763 80764 |
if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
pMem->enc = 0;
return SQLITE_NOMEM_BKPT;
}
vdbeMemRenderNum(nByte, pMem->z, pMem);
assert( pMem->z!=0 );
| | | 81263 81264 81265 81266 81267 81268 81269 81270 81271 81272 81273 81274 81275 81276 81277 |
if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
pMem->enc = 0;
return SQLITE_NOMEM_BKPT;
}
vdbeMemRenderNum(nByte, pMem->z, pMem);
assert( pMem->z!=0 );
assert( pMem->n==(int)sqlite3Strlen30NN(pMem->z) );
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
sqlite3VdbeChangeEncoding(pMem, enc);
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
81802 81803 81804 81805 81806 81807 81808 81809 81810 81811 81812 81813 81814 81815 |
assert( pCtx!=0 );
assert( (p->flags & EP_TokenOnly)==0 );
assert( ExprUseXList(p) );
pList = p->x.pList;
if( pList ) nVal = pList->nExpr;
assert( !ExprHasProperty(p, EP_IntValue) );
pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0);
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
|| (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
){
return SQLITE_OK;
}
| > > > | 82307 82308 82309 82310 82311 82312 82313 82314 82315 82316 82317 82318 82319 82320 82321 82322 82323 |
assert( pCtx!=0 );
assert( (p->flags & EP_TokenOnly)==0 );
assert( ExprUseXList(p) );
pList = p->x.pList;
if( pList ) nVal = pList->nExpr;
assert( !ExprHasProperty(p, EP_IntValue) );
pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0);
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
if( pFunc==0 ) return SQLITE_OK;
#endif
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
|| (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
){
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
81827 81828 81829 81830 81831 81832 81833 |
pVal = valueNew(db, pCtx);
if( pVal==0 ){
rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
| < < | < > | 82335 82336 82337 82338 82339 82340 82341 82342 82343 82344 82345 82346 82347 82348 82349 82350 82351 82352 82353 82354 82355 82356 82357 82358 82359 82360 82361 82362 82363 82364 82365 82366 82367 82368 82369 82370 |
pVal = valueNew(db, pCtx);
if( pVal==0 ){
rc = SQLITE_NOMEM_BKPT;
goto value_from_function_out;
}
memset(&ctx, 0, sizeof(ctx));
ctx.pOut = pVal;
ctx.pFunc = pFunc;
ctx.enc = ENC(db);
pFunc->xSFunc(&ctx, nVal, apVal);
if( ctx.isError ){
rc = ctx.isError;
sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
}else{
sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8);
assert( rc==SQLITE_OK );
rc = sqlite3VdbeChangeEncoding(pVal, enc);
if( NEVER(rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal)) ){
rc = SQLITE_TOOBIG;
pCtx->pParse->nErr++;
}
}
value_from_function_out:
if( rc!=SQLITE_OK ){
pVal = 0;
pCtx->pParse->rc = rc;
}
if( apVal ){
for(i=0; i<nVal; i++){
sqlite3ValueFree(apVal[i]);
}
sqlite3DbFreeNN(db, apVal);
}
|
| ︙ | ︙ | |||
81908 81909 81910 81911 81912 81913 81914 81915 81916 81917 81918 81919 81920 81921 |
if( op==TK_CAST ){
u8 aff;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
aff = sqlite3AffinityType(pExpr->u.zToken,0);
rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
testcase( rc!=SQLITE_OK );
if( *ppVal ){
sqlite3VdbeMemCast(*ppVal, aff, enc);
sqlite3ValueApplyAffinity(*ppVal, affinity, enc);
}
return rc;
}
/* Handle negative integers in a single step. This is needed in the
| > > > > > > > | 82414 82415 82416 82417 82418 82419 82420 82421 82422 82423 82424 82425 82426 82427 82428 82429 82430 82431 82432 82433 82434 |
if( op==TK_CAST ){
u8 aff;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
aff = sqlite3AffinityType(pExpr->u.zToken,0);
rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
testcase( rc!=SQLITE_OK );
if( *ppVal ){
#ifdef SQLITE_ENABLE_STAT4
rc = ExpandBlob(*ppVal);
#else
/* zero-blobs only come from functions, not literal values. And
** functions are only processed under STAT4 */
assert( (ppVal[0][0].flags & MEM_Zero)==0 );
#endif
sqlite3VdbeMemCast(*ppVal, aff, enc);
sqlite3ValueApplyAffinity(*ppVal, affinity, enc);
}
return rc;
}
/* Handle negative integers in a single step. This is needed in the
|
| ︙ | ︙ | |||
82754 82755 82756 82757 82758 82759 82760 |
** Add a new OP_Explain opcode.
**
** If the bPush flag is true, then make this opcode the parent for
** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/
SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
int addr = 0;
| | | | 83267 83268 83269 83270 83271 83272 83273 83274 83275 83276 83277 83278 83279 83280 83281 83282 83283 83284 |
** Add a new OP_Explain opcode.
**
** If the bPush flag is true, then make this opcode the parent for
** subsequent Explains until sqlite3VdbeExplainPop() is called.
*/
SQLITE_PRIVATE int sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
int addr = 0;
#if !defined(SQLITE_DEBUG)
/* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined.
** But omit them (for performance) during production builds */
if( pParse->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
char *zMsg;
Vdbe *v;
va_list ap;
int iThis;
va_start(ap, zFmt);
|
| ︙ | ︙ | |||
83131 83132 83133 83134 83135 83136 83137 83138 83139 83140 83141 83142 83143 83144 |
** coordinated with changes to mkopcodeh.tcl.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
Parse *pParse = p->pParse;
int *aLabel = pParse->aLabel;
p->readOnly = 1;
p->bIsReader = 0;
pOp = &p->aOp[p->nOp-1];
assert( p->aOp[0].opcode==OP_Init );
while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){
/* Only JUMP opcodes and the short list of special opcodes in the switch
** below need to be considered. The mkopcodeh.tcl generator script groups
| > > | 83644 83645 83646 83647 83648 83649 83650 83651 83652 83653 83654 83655 83656 83657 83658 83659 |
** coordinated with changes to mkopcodeh.tcl.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
Parse *pParse = p->pParse;
int *aLabel = pParse->aLabel;
assert( pParse->db->mallocFailed==0 ); /* tag-20230419-1 */
p->readOnly = 1;
p->bIsReader = 0;
pOp = &p->aOp[p->nOp-1];
assert( p->aOp[0].opcode==OP_Init );
while( 1 /* Loop termates when it reaches the OP_Init opcode */ ){
/* Only JUMP opcodes and the short list of special opcodes in the switch
** below need to be considered. The mkopcodeh.tcl generator script groups
|
| ︙ | ︙ | |||
83190 83191 83192 83193 83194 83195 83196 83197 83198 83199 83200 83201 83202 83203 |
default: {
if( pOp->p2<0 ){
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
assert( ADDR(pOp->p2)<-pParse->nLabel );
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
break;
}
}
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
| > | 83705 83706 83707 83708 83709 83710 83711 83712 83713 83714 83715 83716 83717 83718 83719 |
default: {
if( pOp->p2<0 ){
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
** have non-negative values for P2. */
assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 );
assert( ADDR(pOp->p2)<-pParse->nLabel );
assert( aLabel!=0 ); /* True because of tag-20230419-1 */
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
break;
}
}
/* The mkopcodeh.tcl script has so arranged things that the only
** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to
|
| ︙ | ︙ | |||
83433 83434 83435 83436 83437 83438 83439 |
Vdbe *p, /* VM to add scanstatus() to */
int addrExplain, /* Address of OP_Explain (or 0) */
int addrLoop, /* Address of loop counter */
int addrVisit, /* Address of rows visited counter */
LogEst nEst, /* Estimated number of output rows */
const char *zName /* Name of table or index being scanned */
){
| > | | | | | | | | | | | | > > | | | | | | | | | | | | | | > > | | | | | | | | | | | | > | | 83949 83950 83951 83952 83953 83954 83955 83956 83957 83958 83959 83960 83961 83962 83963 83964 83965 83966 83967 83968 83969 83970 83971 83972 83973 83974 83975 83976 83977 83978 83979 83980 83981 83982 83983 83984 83985 83986 83987 83988 83989 83990 83991 83992 83993 83994 83995 83996 83997 83998 83999 84000 84001 84002 84003 84004 84005 84006 84007 84008 84009 84010 84011 84012 84013 84014 84015 84016 84017 84018 84019 84020 84021 84022 84023 84024 84025 84026 84027 84028 84029 84030 84031 84032 84033 84034 84035 84036 84037 84038 84039 |
Vdbe *p, /* VM to add scanstatus() to */
int addrExplain, /* Address of OP_Explain (or 0) */
int addrLoop, /* Address of loop counter */
int addrVisit, /* Address of rows visited counter */
LogEst nEst, /* Estimated number of output rows */
const char *zName /* Name of table or index being scanned */
){
if( IS_STMT_SCANSTATUS(p->db) ){
sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus);
ScanStatus *aNew;
aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
if( aNew ){
ScanStatus *pNew = &aNew[p->nScan++];
memset(pNew, 0, sizeof(ScanStatus));
pNew->addrExplain = addrExplain;
pNew->addrLoop = addrLoop;
pNew->addrVisit = addrVisit;
pNew->nEst = nEst;
pNew->zName = sqlite3DbStrDup(p->db, zName);
p->aScan = aNew;
}
}
}
/*
** Add the range of instructions from addrStart to addrEnd (inclusive) to
** the set of those corresponding to the sqlite3_stmt_scanstatus() counters
** associated with the OP_Explain instruction at addrExplain. The
** sum of the sqlite3Hwtime() values for each of these instructions
** will be returned for SQLITE_SCANSTAT_NCYCLE requests.
*/
SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(
Vdbe *p,
int addrExplain,
int addrStart,
int addrEnd
){
if( IS_STMT_SCANSTATUS(p->db) ){
ScanStatus *pScan = 0;
int ii;
for(ii=p->nScan-1; ii>=0; ii--){
pScan = &p->aScan[ii];
if( pScan->addrExplain==addrExplain ) break;
pScan = 0;
}
if( pScan ){
if( addrEnd<0 ) addrEnd = sqlite3VdbeCurrentAddr(p)-1;
for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
if( pScan->aAddrRange[ii]==0 ){
pScan->aAddrRange[ii] = addrStart;
pScan->aAddrRange[ii+1] = addrEnd;
break;
}
}
}
}
}
/*
** Set the addresses for the SQLITE_SCANSTAT_NLOOP and SQLITE_SCANSTAT_NROW
** counters for the query element associated with the OP_Explain at
** addrExplain.
*/
SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(
Vdbe *p,
int addrExplain,
int addrLoop,
int addrVisit
){
if( IS_STMT_SCANSTATUS(p->db) ){
ScanStatus *pScan = 0;
int ii;
for(ii=p->nScan-1; ii>=0; ii--){
pScan = &p->aScan[ii];
if( pScan->addrExplain==addrExplain ) break;
pScan = 0;
}
if( pScan ){
pScan->addrLoop = addrLoop;
pScan->addrVisit = addrVisit;
}
}
}
#endif /* defined(SQLITE_ENABLE_STMT_SCANSTATUS) */
/*
** Change the value of the opcode, or P1, P2, P3, or P5 operands
** for a specific instruction.
*/
SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){
|
| ︙ | ︙ | |||
83927 83928 83929 83930 83931 83932 83933 |
}else{
return &p->aOp[addr];
}
}
/* Return the most recently added opcode
*/
| | | 84449 84450 84451 84452 84453 84454 84455 84456 84457 84458 84459 84460 84461 84462 84463 |
}else{
return &p->aOp[addr];
}
}
/* Return the most recently added opcode
*/
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetLastOp(Vdbe *p){
return sqlite3VdbeGetOp(p, p->nOp - 1);
}
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
/*
** Return an integer value for one of the parameters to the opcode pOp
** determined by character c.
|
| ︙ | ︙ | |||
85631 85632 85633 85634 85635 85636 85637 85638 85639 85640 85641 85642 85643 85644 |
p->nChange = 0;
}else{
db->nDeferredCons = 0;
db->nDeferredImmCons = 0;
db->flags &= ~(u64)SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else{
sqlite3RollbackAll(db, SQLITE_OK);
p->nChange = 0;
}
db->nStatement = 0;
}else if( eStatementOp==0 ){
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
| > > | 86153 86154 86155 86156 86157 86158 86159 86160 86161 86162 86163 86164 86165 86166 86167 86168 |
p->nChange = 0;
}else{
db->nDeferredCons = 0;
db->nDeferredImmCons = 0;
db->flags &= ~(u64)SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else if( p->rc==SQLITE_SCHEMA && db->nVdbeActive>1 ){
p->nChange = 0;
}else{
sqlite3RollbackAll(db, SQLITE_OK);
p->nChange = 0;
}
db->nStatement = 0;
}else if( eStatementOp==0 ){
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
|
| ︙ | ︙ | |||
85949 85950 85951 85952 85953 85954 85955 |
if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree);
}
vdbeFreeOpArray(db, p->aOp, p->nOp);
if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql);
#ifdef SQLITE_ENABLE_NORMALIZE
sqlite3DbFree(db, p->zNormSql);
{
| | | | | 86473 86474 86475 86476 86477 86478 86479 86480 86481 86482 86483 86484 86485 86486 86487 86488 86489 |
if( p->pFree ) sqlite3DbNNFreeNN(db, p->pFree);
}
vdbeFreeOpArray(db, p->aOp, p->nOp);
if( p->zSql ) sqlite3DbNNFreeNN(db, p->zSql);
#ifdef SQLITE_ENABLE_NORMALIZE
sqlite3DbFree(db, p->zNormSql);
{
DblquoteStr *pThis, *pNxt;
for(pThis=p->pDblStr; pThis; pThis=pNxt){
pNxt = pThis->pNextStr;
sqlite3DbFree(db, pThis);
}
}
#endif
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
{
int i;
|
| ︙ | ︙ | |||
87578 87579 87580 87581 87582 87583 87584 87585 87586 87587 87588 87589 87590 87591 |
sqlite3_result_error(pCtx, zMsg, -1);
sqlite3_free(zMsg);
return 0;
}
return 1;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
** in memory obtained from sqlite3DbMalloc).
*/
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
| > > > > > > > > > > > > > > | 88102 88103 88104 88105 88106 88107 88108 88109 88110 88111 88112 88113 88114 88115 88116 88117 88118 88119 88120 88121 88122 88123 88124 88125 88126 88127 88128 88129 |
sqlite3_result_error(pCtx, zMsg, -1);
sqlite3_free(zMsg);
return 0;
}
return 1;
}
#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG)
/*
** This Walker callback is used to help verify that calls to
** sqlite3BtreeCursorHint() with opcode BTREE_HINT_RANGE have
** byte-code register values correctly initialized.
*/
SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_REGISTER ){
assert( (pWalker->u.aMem[pExpr->iTable].flags & MEM_Undefined)==0 );
}
return WRC_Continue;
}
#endif /* SQLITE_ENABLE_CURSOR_HINTS && SQLITE_DEBUG */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
** in memory obtained from sqlite3DbMalloc).
*/
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
|
| ︙ | ︙ | |||
87640 87641 87642 87643 87644 87645 87646 87647 87648 87649 87650 87651 87652 87653 87654 87655 87656 87657 87658 87659 87660 87661 87662 |
int iBlobWrite
){
sqlite3 *db = v->db;
i64 iKey2;
PreUpdate preupdate;
const char *zTbl = pTab->zName;
static const u8 fakeSortOrder = 0;
assert( db->pPreUpdate==0 );
memset(&preupdate, 0, sizeof(PreUpdate));
if( HasRowid(pTab)==0 ){
iKey1 = iKey2 = 0;
preupdate.pPk = sqlite3PrimaryKeyIndex(pTab);
}else{
if( op==SQLITE_UPDATE ){
iKey2 = v->aMem[iReg].u.i;
}else{
iKey2 = iKey1;
}
}
assert( pCsr!=0 );
assert( pCsr->eCurType==CURTYPE_BTREE );
| > > > > > > > > > > | | | 88178 88179 88180 88181 88182 88183 88184 88185 88186 88187 88188 88189 88190 88191 88192 88193 88194 88195 88196 88197 88198 88199 88200 88201 88202 88203 88204 88205 88206 88207 88208 88209 88210 88211 88212 88213 88214 88215 88216 88217 88218 88219 |
int iBlobWrite
){
sqlite3 *db = v->db;
i64 iKey2;
PreUpdate preupdate;
const char *zTbl = pTab->zName;
static const u8 fakeSortOrder = 0;
#ifdef SQLITE_DEBUG
int nRealCol;
if( pTab->tabFlags & TF_WithoutRowid ){
nRealCol = sqlite3PrimaryKeyIndex(pTab)->nColumn;
}else if( pTab->tabFlags & TF_HasVirtual ){
nRealCol = pTab->nNVCol;
}else{
nRealCol = pTab->nCol;
}
#endif
assert( db->pPreUpdate==0 );
memset(&preupdate, 0, sizeof(PreUpdate));
if( HasRowid(pTab)==0 ){
iKey1 = iKey2 = 0;
preupdate.pPk = sqlite3PrimaryKeyIndex(pTab);
}else{
if( op==SQLITE_UPDATE ){
iKey2 = v->aMem[iReg].u.i;
}else{
iKey2 = iKey1;
}
}
assert( pCsr!=0 );
assert( pCsr->eCurType==CURTYPE_BTREE );
assert( pCsr->nField==nRealCol
|| (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
);
preupdate.v = v;
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
preupdate.keyinfo.db = db;
|
| ︙ | ︙ | |||
87964 87965 87966 87967 87968 87969 87970 |
SQLITE_NULL, /* 0x1b (not possible) */
SQLITE_INTEGER, /* 0x1c (not possible) */
SQLITE_NULL, /* 0x1d (not possible) */
SQLITE_INTEGER, /* 0x1e (not possible) */
SQLITE_NULL, /* 0x1f (not possible) */
SQLITE_FLOAT, /* 0x20 INTREAL */
SQLITE_NULL, /* 0x21 (not possible) */
| | | 88512 88513 88514 88515 88516 88517 88518 88519 88520 88521 88522 88523 88524 88525 88526 |
SQLITE_NULL, /* 0x1b (not possible) */
SQLITE_INTEGER, /* 0x1c (not possible) */
SQLITE_NULL, /* 0x1d (not possible) */
SQLITE_INTEGER, /* 0x1e (not possible) */
SQLITE_NULL, /* 0x1f (not possible) */
SQLITE_FLOAT, /* 0x20 INTREAL */
SQLITE_NULL, /* 0x21 (not possible) */
SQLITE_FLOAT, /* 0x22 INTREAL + TEXT */
SQLITE_NULL, /* 0x23 (not possible) */
SQLITE_FLOAT, /* 0x24 (not possible) */
SQLITE_NULL, /* 0x25 (not possible) */
SQLITE_FLOAT, /* 0x26 (not possible) */
SQLITE_NULL, /* 0x27 (not possible) */
SQLITE_FLOAT, /* 0x28 (not possible) */
SQLITE_NULL, /* 0x29 (not possible) */
|
| ︙ | ︙ | |||
88574 88575 88576 88577 88578 88579 88580 88581 88582 88583 88584 88585 88586 88587 88588 88589 88590 88591 88592 88593 88594 88595 |
** performance by substituting a NULL result, or some other light-weight
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
assert( p );
return sqlite3_value_nochange(p->pOut);
}
/*
** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
** sqlite3_vtab_in_next() (if bNext!=0).
*/
static int valueFromValueList(
sqlite3_value *pVal, /* Pointer to the ValueList object */
sqlite3_value **ppOut, /* Store the next value from the list here */
int bNext /* 1 for _next(). 0 for _first() */
){
int rc;
ValueList *pRhs;
*ppOut = 0;
if( pVal==0 ) return SQLITE_MISUSE;
| > > > > > > > > > > > > > > > > > > | < > | 89122 89123 89124 89125 89126 89127 89128 89129 89130 89131 89132 89133 89134 89135 89136 89137 89138 89139 89140 89141 89142 89143 89144 89145 89146 89147 89148 89149 89150 89151 89152 89153 89154 89155 89156 89157 89158 89159 89160 89161 89162 89163 89164 89165 89166 89167 89168 89169 89170 |
** performance by substituting a NULL result, or some other light-weight
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
assert( p );
return sqlite3_value_nochange(p->pOut);
}
/*
** The destructor function for a ValueList object. This needs to be
** a separate function, unknowable to the application, to ensure that
** calls to sqlite3_vtab_in_first()/sqlite3_vtab_in_next() that are not
** preceeded by activation of IN processing via sqlite3_vtab_int() do not
** try to access a fake ValueList object inserted by a hostile extension.
*/
SQLITE_PRIVATE void sqlite3VdbeValueListFree(void *pToDelete){
sqlite3_free(pToDelete);
}
/*
** Implementation of sqlite3_vtab_in_first() (if bNext==0) and
** sqlite3_vtab_in_next() (if bNext!=0).
*/
static int valueFromValueList(
sqlite3_value *pVal, /* Pointer to the ValueList object */
sqlite3_value **ppOut, /* Store the next value from the list here */
int bNext /* 1 for _next(). 0 for _first() */
){
int rc;
ValueList *pRhs;
*ppOut = 0;
if( pVal==0 ) return SQLITE_MISUSE;
if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
return SQLITE_ERROR;
}else{
assert( (pVal->flags&(MEM_TypeMask|MEM_Term|MEM_Subtype)) ==
(MEM_Null|MEM_Term|MEM_Subtype) );
assert( pVal->eSubtype=='p' );
assert( pVal->u.zPType!=0 && strcmp(pVal->u.zPType,"ValueList")==0 );
pRhs = (ValueList*)pVal->z;
}
if( bNext ){
rc = sqlite3BtreeNext(pRhs->pCsr, 0);
}else{
int dummy = 0;
rc = sqlite3BtreeFirst(pRhs->pCsr, &dummy);
assert( rc==SQLITE_OK || sqlite3BtreeEof(pRhs->pCsr) );
if( sqlite3BtreeEof(pRhs->pCsr) ) rc = SQLITE_DONE;
|
| ︙ | ︙ | |||
89012 89013 89014 89015 89016 89017 89018 89019 89020 |
#endif
ret = 0;
p = (Vdbe *)pStmt;
db = p->db;
assert( db!=0 );
n = sqlite3_column_count(pStmt);
if( N<n && N>=0 ){
N += useType*n;
sqlite3_mutex_enter(db->mutex);
| > < > | | 89578 89579 89580 89581 89582 89583 89584 89585 89586 89587 89588 89589 89590 89591 89592 89593 89594 89595 89596 89597 89598 89599 89600 89601 89602 89603 89604 89605 89606 89607 |
#endif
ret = 0;
p = (Vdbe *)pStmt;
db = p->db;
assert( db!=0 );
n = sqlite3_column_count(pStmt);
if( N<n && N>=0 ){
u8 prior_mallocFailed = db->mallocFailed;
N += useType*n;
sqlite3_mutex_enter(db->mutex);
#ifndef SQLITE_OMIT_UTF16
if( useUtf16 ){
ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]);
}else
#endif
{
ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]);
}
/* A malloc may have failed inside of the _text() call. If this
** is the case, clear the mallocFailed flag and return NULL.
*/
assert( db->mallocFailed==0 || db->mallocFailed==1 );
if( db->mallocFailed > prior_mallocFailed ){
sqlite3OomClear(db);
ret = 0;
}
sqlite3_mutex_leave(db->mutex);
}
return ret;
}
|
| ︙ | ︙ | |||
89813 89814 89815 89816 89817 89818 89819 |
sqlite3_stmt *pStmt, /* Prepared statement being queried */
int iScan, /* Index of loop to report on */
int iScanStatusOp, /* Which metric to return */
int flags,
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
| > > | > > > > > > > | | | 90380 90381 90382 90383 90384 90385 90386 90387 90388 90389 90390 90391 90392 90393 90394 90395 90396 90397 90398 90399 90400 90401 90402 90403 90404 90405 90406 90407 90408 90409 90410 90411 |
sqlite3_stmt *pStmt, /* Prepared statement being queried */
int iScan, /* Index of loop to report on */
int iScanStatusOp, /* Which metric to return */
int flags,
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
VdbeOp *aOp = p->aOp;
int nOp = p->nOp;
ScanStatus *pScan = 0;
int idx;
if( p->pFrame ){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
aOp = pFrame->aOp;
nOp = pFrame->nOp;
}
if( iScan<0 ){
int ii;
if( iScanStatusOp==SQLITE_SCANSTAT_NCYCLE ){
i64 res = 0;
for(ii=0; ii<nOp; ii++){
res += aOp[ii].nCycle;
}
*(i64*)pOut = res;
return 0;
}
return 1;
}
if( flags & SQLITE_SCANSTAT_COMPLEX ){
|
| ︙ | ︙ | |||
89847 89848 89849 89850 89851 89852 89853 |
}
}
if( idx>=p->nScan ) return 1;
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
if( pScan->addrLoop>0 ){
| | | | 90423 90424 90425 90426 90427 90428 90429 90430 90431 90432 90433 90434 90435 90436 90437 90438 90439 90440 90441 90442 90443 90444 90445 |
}
}
if( idx>=p->nScan ) return 1;
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
if( pScan->addrLoop>0 ){
*(sqlite3_int64*)pOut = aOp[pScan->addrLoop].nExec;
}else{
*(sqlite3_int64*)pOut = -1;
}
break;
}
case SQLITE_SCANSTAT_NVISIT: {
if( pScan->addrVisit>0 ){
*(sqlite3_int64*)pOut = aOp[pScan->addrVisit].nExec;
}else{
*(sqlite3_int64*)pOut = -1;
}
break;
}
case SQLITE_SCANSTAT_EST: {
double r = 1.0;
|
| ︙ | ︙ | |||
89877 89878 89879 89880 89881 89882 89883 |
}
case SQLITE_SCANSTAT_NAME: {
*(const char**)pOut = pScan->zName;
break;
}
case SQLITE_SCANSTAT_EXPLAIN: {
if( pScan->addrExplain ){
| | | | | | | | | 90453 90454 90455 90456 90457 90458 90459 90460 90461 90462 90463 90464 90465 90466 90467 90468 90469 90470 90471 90472 90473 90474 90475 90476 90477 90478 90479 90480 90481 90482 90483 90484 90485 90486 90487 90488 90489 90490 90491 90492 90493 90494 90495 90496 90497 90498 90499 90500 90501 90502 90503 90504 90505 90506 90507 90508 90509 90510 90511 90512 |
}
case SQLITE_SCANSTAT_NAME: {
*(const char**)pOut = pScan->zName;
break;
}
case SQLITE_SCANSTAT_EXPLAIN: {
if( pScan->addrExplain ){
*(const char**)pOut = aOp[ pScan->addrExplain ].p4.z;
}else{
*(const char**)pOut = 0;
}
break;
}
case SQLITE_SCANSTAT_SELECTID: {
if( pScan->addrExplain ){
*(int*)pOut = aOp[ pScan->addrExplain ].p1;
}else{
*(int*)pOut = -1;
}
break;
}
case SQLITE_SCANSTAT_PARENTID: {
if( pScan->addrExplain ){
*(int*)pOut = aOp[ pScan->addrExplain ].p2;
}else{
*(int*)pOut = -1;
}
break;
}
case SQLITE_SCANSTAT_NCYCLE: {
i64 res = 0;
if( pScan->aAddrRange[0]==0 ){
res = -1;
}else{
int ii;
for(ii=0; ii<ArraySize(pScan->aAddrRange); ii+=2){
int iIns = pScan->aAddrRange[ii];
int iEnd = pScan->aAddrRange[ii+1];
if( iIns==0 ) break;
if( iIns>0 ){
while( iIns<=iEnd ){
res += aOp[iIns].nCycle;
iIns++;
}
}else{
int iOp;
for(iOp=0; iOp<nOp; iOp++){
Op *pOp = &aOp[iOp];
if( pOp->p1!=iEnd ) continue;
if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_NCYCLE)==0 ){
continue;
}
res += aOp[iOp].nCycle;
}
}
}
}
*(i64*)pOut = res;
break;
}
|
| ︙ | ︙ | |||
90845 90846 90847 90848 90849 90850 90851 |
for(i=pOp->p3, mx=i+pOp->p4.i; i<mx; i++){
const Mem *p = &aMem[i];
if( p->flags & (MEM_Int|MEM_IntReal) ){
h += p->u.i;
}else if( p->flags & MEM_Real ){
h += sqlite3VdbeIntValue(p);
}else if( p->flags & (MEM_Str|MEM_Blob) ){
| | > > | | 91421 91422 91423 91424 91425 91426 91427 91428 91429 91430 91431 91432 91433 91434 91435 91436 91437 91438 |
for(i=pOp->p3, mx=i+pOp->p4.i; i<mx; i++){
const Mem *p = &aMem[i];
if( p->flags & (MEM_Int|MEM_IntReal) ){
h += p->u.i;
}else if( p->flags & MEM_Real ){
h += sqlite3VdbeIntValue(p);
}else if( p->flags & (MEM_Str|MEM_Blob) ){
/* All strings have the same hash and all blobs have the same hash,
** though, at least, those hashes are different from each other and
** from NULL. */
h += 4093 + (p->flags & (MEM_Str|MEM_Blob));
}
}
return h;
}
/*
** Return the symbolic name for the data type of a pMem
|
| ︙ | ︙ | |||
90896 90897 90898 90899 90900 90901 90902 90903 90904 90905 90906 90907 90908 90909 |
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
u64 *pnCycle = 0;
#endif
/*** INSERT STACK UNION HERE ***/
assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */
if( DbMaskNonZero(p->lockMask) ){
sqlite3VdbeEnter(p);
}
| > | 91474 91475 91476 91477 91478 91479 91480 91481 91482 91483 91484 91485 91486 91487 91488 |
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE)
u64 *pnCycle = 0;
int bStmtScanStatus = IS_STMT_SCANSTATUS(db)!=0;
#endif
/*** INSERT STACK UNION HERE ***/
assert( p->eVdbeState==VDBE_RUN_STATE ); /* sqlite3_step() verifies this */
if( DbMaskNonZero(p->lockMask) ){
sqlite3VdbeEnter(p);
}
|
| ︙ | ︙ | |||
90960 90961 90962 90963 90964 90965 90966 |
for(pOp=&aOp[p->pc]; 1; pOp++){
/* Errors are detected by individual opcodes, with an immediate
** jumps to abort_due_to_error. */
assert( rc==SQLITE_OK );
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
nVmStep++;
| > | < | > | > > > | 91539 91540 91541 91542 91543 91544 91545 91546 91547 91548 91549 91550 91551 91552 91553 91554 91555 91556 91557 91558 91559 91560 91561 91562 91563 |
for(pOp=&aOp[p->pc]; 1; pOp++){
/* Errors are detected by individual opcodes, with an immediate
** jumps to abort_due_to_error. */
assert( rc==SQLITE_OK );
assert( pOp>=aOp && pOp<&aOp[p->nOp]);
nVmStep++;
#if defined(VDBE_PROFILE)
pOp->nExec++;
pnCycle = &pOp->nCycle;
if( sqlite3NProfileCnt==0 ) *pnCycle -= sqlite3Hwtime();
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( bStmtScanStatus ){
pOp->nExec++;
pnCycle = &pOp->nCycle;
*pnCycle -= sqlite3Hwtime();
}
#endif
/* Only allow tracing if SQLITE_DEBUG is defined.
*/
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeTrace ){
sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp);
|
| ︙ | ︙ | |||
91314 91315 91316 91317 91318 91319 91320 |
int pcx;
#ifdef SQLITE_DEBUG
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
#endif
/* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates
| | | 91897 91898 91899 91900 91901 91902 91903 91904 91905 91906 91907 91908 91909 91910 91911 |
int pcx;
#ifdef SQLITE_DEBUG
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
#endif
/* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates
** something is wrong with the code generator. Raise an assertion in order
** to bring this to the attention of fuzzers and other testing tools. */
assert( pOp->p1!=SQLITE_INTERNAL );
if( p->pFrame && pOp->p1==SQLITE_OK ){
/* Halt the sub-program. Return control to the parent frame. */
pFrame = p->pFrame;
p->pFrame = pFrame->pParent;
|
| ︙ | ︙ | |||
92356 92357 92358 92359 92360 92361 92362 |
/* Neither operand is NULL and we couldn't do the special high-speed
** integer comparison case. So do a general-case comparison. */
affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity>=SQLITE_AFF_NUMERIC ){
if( (flags1 | flags3)&MEM_Str ){
if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
| | | 92939 92940 92941 92942 92943 92944 92945 92946 92947 92948 92949 92950 92951 92952 92953 |
/* Neither operand is NULL and we couldn't do the special high-speed
** integer comparison case. So do a general-case comparison. */
affinity = pOp->p5 & SQLITE_AFF_MASK;
if( affinity>=SQLITE_AFF_NUMERIC ){
if( (flags1 | flags3)&MEM_Str ){
if( (flags1 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn1,0);
assert( flags3==pIn3->flags || CORRUPT_DB );
flags3 = pIn3->flags;
}
if( (flags3 & (MEM_Int|MEM_IntReal|MEM_Real|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3,0);
}
}
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
|
| ︙ | ︙ | |||
92554 92555 92556 92557 92558 92559 92560 | assert( pOp[1].opcode==OP_Jump ); break; } /* Opcode: Jump P1 P2 P3 * * ** ** Jump to the instruction at address P1, P2, or P3 depending on whether | | | 93137 93138 93139 93140 93141 93142 93143 93144 93145 93146 93147 93148 93149 93150 93151 |
assert( pOp[1].opcode==OP_Jump );
break;
}
/* Opcode: Jump P1 P2 P3 * *
**
** Jump to the instruction at address P1, P2, or P3 depending on whether
** in the most recent OP_Compare instruction the P1 vector was less than,
** equal to, or greater than the P2 vector, respectively.
**
** This opcode must immediately follow an OP_Compare opcode.
*/
case OP_Jump: { /* jump */
assert( pOp>aOp && pOp[-1].opcode==OP_Compare );
assert( iCompareIsInit );
|
| ︙ | ︙ | |||
92781 92782 92783 92784 92785 92786 92787 92788 92789 92790 92791 92792 92793 92794 |
** If P1 is -1, then P3 is a register number and the datatype is taken
** from the value in that register.
**
** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant
** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04.
** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10.
**
** Take the jump to address P2 if and only if the datatype of the
** value determined by P1 and P3 corresponds to one of the bits in the
** P5 bitmask.
**
*/
case OP_IsType: { /* jump */
VdbeCursor *pC;
| > > > > > > | 93364 93365 93366 93367 93368 93369 93370 93371 93372 93373 93374 93375 93376 93377 93378 93379 93380 93381 93382 93383 |
** If P1 is -1, then P3 is a register number and the datatype is taken
** from the value in that register.
**
** P5 is a bitmask of data types. SQLITE_INTEGER is the least significant
** (0x01) bit. SQLITE_FLOAT is the 0x02 bit. SQLITE_TEXT is 0x04.
** SQLITE_BLOB is 0x08. SQLITE_NULL is 0x10.
**
** WARNING: This opcode does not reliably distinguish between NULL and REAL
** when P1>=0. If the database contains a NaN value, this opcode will think
** that the datatype is REAL when it should be NULL. When P1<0 and the value
** is already stored in register P3, then this opcode does reliably
** distinguish between NULL and REAL. The problem only arises then P1>=0.
**
** Take the jump to address P2 if and only if the datatype of the
** value determined by P1 and P3 corresponds to one of the bits in the
** P5 bitmask.
**
*/
case OP_IsType: { /* jump */
VdbeCursor *pC;
|
| ︙ | ︙ | |||
92894 92895 92896 92897 92898 92899 92900 |
**
** If P1 is not an open cursor, then this opcode is a no-op.
*/
case OP_IfNullRow: { /* jump */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
| | | 93483 93484 93485 93486 93487 93488 93489 93490 93491 93492 93493 93494 93495 93496 93497 |
**
** If P1 is not an open cursor, then this opcode is a no-op.
*/
case OP_IfNullRow: { /* jump */
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
if( pC && pC->nullRow ){
sqlite3VdbeMemSetNull(aMem + pOp->p3);
goto jump_to_p2;
}
break;
}
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
| ︙ | ︙ | |||
93389 93390 93391 93392 93393 93394 93395 |
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;
| | | 93978 93979 93980 93981 93982 93983 93984 93985 93986 93987 93988 93989 93990 93991 93992 |
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|MEM_Str);
}
}
REGISTER_TRACE((int)(pIn1-aMem), pIn1);
zAffinity++;
if( zAffinity[0]==0 ) break;
pIn1++;
}
|
| ︙ | ︙ | |||
95128 95129 95130 95131 95132 95133 95134 95135 95136 95137 95138 95139 95140 95141 |
printf("... fall through after %d steps\n", pOp->p1);
}
#endif
VdbeBranchTaken(0,3);
break;
}
nStep--;
rc = sqlite3BtreeNext(pC->uc.pCursor, 0);
if( rc ){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
goto seekscan_search_fail;
}else{
goto abort_due_to_error;
| > | 95717 95718 95719 95720 95721 95722 95723 95724 95725 95726 95727 95728 95729 95730 95731 |
printf("... fall through after %d steps\n", pOp->p1);
}
#endif
VdbeBranchTaken(0,3);
break;
}
nStep--;
pC->cacheStatus = CACHE_STALE;
rc = sqlite3BtreeNext(pC->uc.pCursor, 0);
if( rc ){
if( rc==SQLITE_DONE ){
rc = SQLITE_OK;
goto seekscan_search_fail;
}else{
goto abort_due_to_error;
|
| ︙ | ︙ | |||
97780 97781 97782 97783 97784 97785 97786 97787 97788 97789 97790 97791 97792 97793 |
if( rc ){
sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
goto abort_due_to_error;
}
sqlite3VdbeChangeEncoding(pMem, encoding);
UPDATE_MAX_BLOBSIZE(pMem);
break;
}
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 P2 P3 * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
| > | 98370 98371 98372 98373 98374 98375 98376 98377 98378 98379 98380 98381 98382 98383 98384 |
if( rc ){
sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
goto abort_due_to_error;
}
sqlite3VdbeChangeEncoding(pMem, encoding);
UPDATE_MAX_BLOBSIZE(pMem);
REGISTER_TRACE((int)(pMem-aMem), pMem);
break;
}
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 P2 P3 * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
|
| ︙ | ︙ | |||
98193 98194 98195 98196 98197 98198 98199 | pC = p->apCsr[pOp->p1]; pRhs = sqlite3_malloc64( sizeof(*pRhs) ); if( pRhs==0 ) goto no_mem; pRhs->pCsr = pC->uc.pCursor; pRhs->pOut = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Null; | | | 98784 98785 98786 98787 98788 98789 98790 98791 98792 98793 98794 98795 98796 98797 98798 | pC = p->apCsr[pOp->p1]; pRhs = sqlite3_malloc64( sizeof(*pRhs) ); if( pRhs==0 ) goto no_mem; pRhs->pCsr = pC->uc.pCursor; pRhs->pOut = &aMem[pOp->p3]; pOut = out2Prerelease(p, pOp); pOut->flags = MEM_Null; sqlite3VdbeMemSetPointer(pOut, pRhs, "ValueList", sqlite3VdbeValueListFree); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VFilter P1 P2 P3 P4 * |
| ︙ | ︙ | |||
98918 98919 98920 98921 98922 98923 98924 |
*****************************************************************************/
}
#if defined(VDBE_PROFILE)
*pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
pnCycle = 0;
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
| > | | > | 99509 99510 99511 99512 99513 99514 99515 99516 99517 99518 99519 99520 99521 99522 99523 99524 99525 99526 |
*****************************************************************************/
}
#if defined(VDBE_PROFILE)
*pnCycle += sqlite3NProfileCnt ? sqlite3NProfileCnt : sqlite3Hwtime();
pnCycle = 0;
#elif defined(SQLITE_ENABLE_STMT_SCANSTATUS)
if( pnCycle ){
*pnCycle += sqlite3Hwtime();
pnCycle = 0;
}
#endif
/* The following code adds nothing to the actual functionality
** of the program. It is only here for testing and debugging.
** On the other hand, it does burn CPU cycles every time through
** the evaluator loop. So we can leave it out when NDEBUG is defined.
*/
|
| ︙ | ︙ | |||
99398 99399 99400 99401 99402 99403 99404 |
blob_open_out:
if( rc==SQLITE_OK && db->mallocFailed==0 ){
*ppBlob = (sqlite3_blob *)pBlob;
}else{
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
| | | 99991 99992 99993 99994 99995 99996 99997 99998 99999 100000 100001 100002 100003 100004 100005 |
blob_open_out:
if( rc==SQLITE_OK && db->mallocFailed==0 ){
*ppBlob = (sqlite3_blob *)pBlob;
}else{
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
sqlite3DbFree(db, zErr);
sqlite3ParseObjectReset(&sParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
|
| ︙ | ︙ | |||
99557 99558 99559 99560 99561 99562 99563 |
*/
rc = SQLITE_ABORT;
}else{
char *zErr;
((Vdbe*)p->pStmt)->rc = SQLITE_OK;
rc = blobSeekToRow(p, iRow, &zErr);
if( rc!=SQLITE_OK ){
| | | 100150 100151 100152 100153 100154 100155 100156 100157 100158 100159 100160 100161 100162 100163 100164 |
*/
rc = SQLITE_ABORT;
}else{
char *zErr;
((Vdbe*)p->pStmt)->rc = SQLITE_OK;
rc = blobSeekToRow(p, iRow, &zErr);
if( rc!=SQLITE_OK ){
sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : (char*)0), zErr);
sqlite3DbFree(db, zErr);
}
assert( rc!=SQLITE_SCHEMA );
}
rc = sqlite3ApiExit(db, rc);
assert( rc==SQLITE_OK || p->pStmt==0 );
|
| ︙ | ︙ | |||
103679 103680 103681 103682 103683 103684 103685 103686 103687 103688 103689 103690 103691 103692 |
pNew->iColumn = iColumn;
pNew->y.pTab = pMatch->pTab;
assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 );
ExprSetProperty(pNew, EP_CanBeNull);
*ppList = sqlite3ExprListAppend(pParse, *ppList, pNew);
}
}
/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
** expression node refer back to that source column. The following changes
** are made to pExpr:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > | 104272 104273 104274 104275 104276 104277 104278 104279 104280 104281 104282 104283 104284 104285 104286 104287 104288 104289 104290 104291 104292 104293 104294 104295 104296 104297 104298 104299 104300 104301 104302 104303 104304 104305 104306 104307 104308 104309 104310 104311 |
pNew->iColumn = iColumn;
pNew->y.pTab = pMatch->pTab;
assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 );
ExprSetProperty(pNew, EP_CanBeNull);
*ppList = sqlite3ExprListAppend(pParse, *ppList, pNew);
}
}
/*
** Return TRUE (non-zero) if zTab is a valid name for the schema table pTab.
*/
static SQLITE_NOINLINE int isValidSchemaTableName(
const char *zTab, /* Name as it appears in the SQL */
Table *pTab, /* The schema table we are trying to match */
Schema *pSchema /* non-NULL if a database qualifier is present */
){
const char *zLegacy;
assert( pTab!=0 );
assert( pTab->tnum==1 );
if( sqlite3StrNICmp(zTab, "sqlite_", 7)!=0 ) return 0;
zLegacy = pTab->zName;
if( strcmp(zLegacy+7, &LEGACY_TEMP_SCHEMA_TABLE[7])==0 ){
if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){
return 1;
}
if( pSchema==0 ) return 0;
if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1;
if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
}else{
if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
}
return 0;
}
/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
** expression node refer back to that source column. The following changes
** are made to pExpr:
**
|
| ︙ | ︙ | |||
103833 103834 103835 103836 103837 103838 103839 |
hit = 1;
if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
}
assert( zDb==0 || zTab!=0 );
if( zTab ){
| < | < | | > > > > | 104452 104453 104454 104455 104456 104457 104458 104459 104460 104461 104462 104463 104464 104465 104466 104467 104468 104469 104470 104471 104472 104473 104474 104475 104476 |
hit = 1;
if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
}
assert( zDb==0 || zTab!=0 );
if( zTab ){
if( zDb ){
if( pTab->pSchema!=pSchema ) continue;
if( pSchema==0 && strcmp(zDb,"*")!=0 ) continue;
}
if( pItem->zAlias!=0 ){
if( sqlite3StrICmp(zTab, pItem->zAlias)!=0 ){
continue;
}
}else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){
if( pTab->tnum!=1 ) continue;
if( !isValidSchemaTableName(zTab, pTab, pSchema) ) continue;
}
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT && pItem->zAlias ){
sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab);
}
}
hCol = sqlite3StrIHash(zCol);
|
| ︙ | ︙ | |||
103917 103918 103919 103920 103921 103922 103923 |
pTab = 0;
#ifndef SQLITE_OMIT_TRIGGER
if( pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
if( pParse->bReturning ){
if( (pNC->ncFlags & NC_UBaseReg)!=0
| > | | 104538 104539 104540 104541 104542 104543 104544 104545 104546 104547 104548 104549 104550 104551 104552 104553 |
pTab = 0;
#ifndef SQLITE_OMIT_TRIGGER
if( pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
if( pParse->bReturning ){
if( (pNC->ncFlags & NC_UBaseReg)!=0
&& ALWAYS(zTab==0
|| sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0)
){
pExpr->iTable = op!=TK_DELETE;
pTab = pParse->pTriggerTab;
}
}else if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
pTab = pParse->pTriggerTab;
|
| ︙ | ︙ | |||
104397 104398 104399 104400 104401 104402 104403 |
for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
testcase( ExprHasProperty(pExpr, EP_OuterON) );
assert( !ExprHasProperty(pExpr, EP_IntValue) );
| | | < < | < | < | 105019 105020 105021 105022 105023 105024 105025 105026 105027 105028 105029 105030 105031 105032 105033 105034 105035 105036 |
for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
testcase( ExprHasProperty(pExpr, EP_OuterON) );
assert( !ExprHasProperty(pExpr, EP_IntValue) );
pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
pExpr->flags |= EP_IntValue;
pExpr->op = TK_INTEGER;
for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
p->nRef = anRef[i];
}
sqlite3ExprDelete(pParse->db, pExpr->pLeft);
pExpr->pLeft = 0;
}
return WRC_Prune;
|
| ︙ | ︙ | |||
104706 104707 104708 104709 104710 104711 104712 |
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
}
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
| < > | 105324 105325 105326 105327 105328 105329 105330 105331 105332 105333 105334 105335 105336 105337 105338 105339 |
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
}
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
}
pNC->ncFlags |= NC_Subquery;
}
break;
}
case TK_VARIABLE: {
testcase( pNC->ncFlags & NC_IsCheck );
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
|
| ︙ | ︙ | |||
105895 105896 105897 105898 105899 105900 105901 |
}
if( p->flags & EP_Collate ){
if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){
p = p->pLeft;
}else{
Expr *pNext = p->pRight;
/* The Expr.x union is never used at the same time as Expr.pRight */
| < | | | | 106513 106514 106515 106516 106517 106518 106519 106520 106521 106522 106523 106524 106525 106526 106527 106528 106529 106530 |
}
if( p->flags & EP_Collate ){
if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){
p = p->pLeft;
}else{
Expr *pNext = p->pRight;
/* The Expr.x union is never used at the same time as Expr.pRight */
assert( !ExprUseXList(p) || p->x.pList==0 || p->pRight==0 );
if( ExprUseXList(p) && p->x.pList!=0 && !db->mallocFailed ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
pNext = p->x.pList->a[i].pExpr;
break;
}
}
}
p = pNext;
|
| ︙ | ︙ | |||
106731 106732 106733 106734 106735 106736 106737 | return pRet; } /* ** Join two expressions using an AND operator. If either expression is ** NULL, then just return the other expression. ** | | | | | > > | | | | | | | > | 107348 107349 107350 107351 107352 107353 107354 107355 107356 107357 107358 107359 107360 107361 107362 107363 107364 107365 107366 107367 107368 107369 107370 107371 107372 107373 107374 107375 107376 107377 107378 107379 107380 107381 107382 |
return pRet;
}
/*
** Join two expressions using an AND operator. If either expression is
** NULL, then just return the other expression.
**
** If one side or the other of the AND is known to be false, and neither side
** is part of an ON clause, then instead of returning an AND expression,
** just return a constant expression with a value of false.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){
sqlite3 *db = pParse->db;
if( pLeft==0 ){
return pRight;
}else if( pRight==0 ){
return pLeft;
}else{
u32 f = pLeft->flags | pRight->flags;
if( (f&(EP_OuterON|EP_InnerON|EP_IsFalse))==EP_IsFalse
&& !IN_RENAME_OBJECT
){
sqlite3ExprDeferredDelete(pParse, pLeft);
sqlite3ExprDeferredDelete(pParse, pRight);
return sqlite3Expr(db, TK_INTEGER, "0");
}else{
return sqlite3PExpr(pParse, TK_AND, pLeft, pRight);
}
}
}
/*
** Construct a new expression node for a function with multiple
** arguments.
*/
|
| ︙ | ︙ | |||
107993 107994 107995 107996 107997 107998 107999 |
** table other than iCur.
*/
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
return exprIsConst(p, 3, iCur);
}
/*
| | > > > > > | | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | 108613 108614 108615 108616 108617 108618 108619 108620 108621 108622 108623 108624 108625 108626 108627 108628 108629 108630 108631 108632 108633 108634 108635 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 108684 108685 108686 108687 108688 108689 108690 108691 108692 108693 |
** table other than iCur.
*/
SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
return exprIsConst(p, 3, iCur);
}
/*
** Check pExpr to see if it is an constraint on the single data source
** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr
** constrains pSrc but does not depend on any other tables or data
** sources anywhere else in the query. Return true (non-zero) if pExpr
** is a constraint on pSrc only.
**
** This is an optimization. False negatives will perhaps cause slower
** queries, but false positives will yield incorrect answers. So when in
** doubt, return 0.
**
** To be an single-source constraint, the following must be true:
**
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
**
** (2) pExpr cannot use subqueries or non-deterministic functions.
**
** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
** (Is there some way to relax this constraint?)
**
** (4) If pSrc is the right operand of a LEFT JOIN, then...
** (4a) pExpr must come from an ON clause..
** (4b) and specifically the ON clause associated with the LEFT JOIN.
**
** (5) If pSrc is not the right operand of a LEFT JOIN or the left
** operand of a RIGHT JOIN, then pExpr must be from the WHERE
** clause, not an ON clause.
**
** (6) Either:
**
** (6a) pExpr does not originate in an ON or USING clause, or
**
** (6b) The ON or USING clause from which pExpr is derived is
** not to the left of a RIGHT JOIN (or FULL JOIN).
**
** Without this restriction, accepting pExpr as a single-table
** constraint might move the the ON/USING filter expression
** from the left side of a RIGHT JOIN over to the right side,
** which leads to incorrect answers. See also restriction (9)
** on push-down.
*/
SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
Expr *pExpr, /* The constraint */
const SrcList *pSrcList, /* Complete FROM clause */
int iSrc /* Which element of pSrcList to use */
){
const SrcItem *pSrc = &pSrcList->a[iSrc];
if( pSrc->fg.jointype & JT_LTORJ ){
return 0; /* rule (3) */
}
if( pSrc->fg.jointype & JT_LEFT ){
if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */
if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */
}else{
if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */
}
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) /* (6a) */
&& (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (6b) */
){
int jj;
for(jj=0; jj<iSrc; jj++){
if( pExpr->w.iJoin==pSrcList->a[jj].iCursor ){
if( (pSrcList->a[jj].fg.jointype & JT_LTORJ)!=0 ){
return 0; /* restriction (6) */
}
break;
}
}
}
return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
}
/*
** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy().
|
| ︙ | ︙ | |||
108267 108268 108269 108270 108271 108272 108273 | return 0; } /* ** 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, | | | 108923 108924 108925 108926 108927 108928 108929 108930 108931 108932 108933 108934 108935 108936 108937 |
return 0;
}
/*
** 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 materialized into a transient
** table, then return NULL.
*/
#ifndef SQLITE_OMIT_SUBQUERY
static Select *isCandidateForInOpt(const Expr *pX){
Select *p;
SrcList *pSrc;
ExprList *pEList;
|
| ︙ | ︙ | |||
108553 108554 108555 108556 108557 108558 108559 |
colUsed = 0; /* Columns of index used so far */
for(i=0; i<nExpr; i++){
Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
Expr *pRhs = pEList->a[i].pExpr;
CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
int j;
| < | 109209 109210 109211 109212 109213 109214 109215 109216 109217 109218 109219 109220 109221 109222 |
colUsed = 0; /* Columns of index used so far */
for(i=0; i<nExpr; i++){
Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
Expr *pRhs = pEList->a[i].pExpr;
CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
int j;
for(j=0; j<nExpr; j++){
if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
assert( pIdx->azColl[j] );
if( pReq!=0 && sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ){
continue;
}
break;
|
| ︙ | ︙ | |||
109456 109457 109458 109459 109460 109461 109462 109463 109464 109465 109466 109467 109468 109469 109470 109471 109472 109473 109474 109475 109476 109477 109478 109479 109480 109481 109482 109483 109484 109485 109486 109487 109488 109489 109490 109491 109492 109493 109494 109495 109496 109497 |
Parse *pParse, /* Parsing context */
Table *pTab, /* Table containing the generated column */
Column *pCol, /* The generated column */
int regOut /* Put the result in this register */
){
int iAddr;
Vdbe *v = pParse->pVdbe;
assert( v!=0 );
assert( pParse->iSelfTab!=0 );
if( pParse->iSelfTab>0 ){
iAddr = sqlite3VdbeAddOp3(v, OP_IfNullRow, pParse->iSelfTab-1, 0, regOut);
}else{
iAddr = 0;
}
sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut);
if( pCol->affinity>=SQLITE_AFF_TEXT ){
sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
}
if( iAddr ) sqlite3VdbeJumpHere(v, iAddr);
}
#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
/*
** Generate code to extract the value of the iCol-th column of a table.
*/
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
Vdbe *v, /* Parsing context */
Table *pTab, /* The table containing the value */
int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
int iCol, /* Index of the column to extract */
int regOut /* Extract the value into this register */
){
Column *pCol;
assert( v!=0 );
assert( pTab!=0 );
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
VdbeComment((v, "%s.rowid", pTab->zName));
}else{
int op;
int x;
if( IsVirtual(pTab) ){
| > > > | 110111 110112 110113 110114 110115 110116 110117 110118 110119 110120 110121 110122 110123 110124 110125 110126 110127 110128 110129 110130 110131 110132 110133 110134 110135 110136 110137 110138 110139 110140 110141 110142 110143 110144 110145 110146 110147 110148 110149 110150 110151 110152 110153 110154 110155 |
Parse *pParse, /* Parsing context */
Table *pTab, /* Table containing the generated column */
Column *pCol, /* The generated column */
int regOut /* Put the result in this register */
){
int iAddr;
Vdbe *v = pParse->pVdbe;
int nErr = pParse->nErr;
assert( v!=0 );
assert( pParse->iSelfTab!=0 );
if( pParse->iSelfTab>0 ){
iAddr = sqlite3VdbeAddOp3(v, OP_IfNullRow, pParse->iSelfTab-1, 0, regOut);
}else{
iAddr = 0;
}
sqlite3ExprCodeCopy(pParse, sqlite3ColumnExpr(pTab,pCol), regOut);
if( pCol->affinity>=SQLITE_AFF_TEXT ){
sqlite3VdbeAddOp4(v, OP_Affinity, regOut, 1, 0, &pCol->affinity, 1);
}
if( iAddr ) sqlite3VdbeJumpHere(v, iAddr);
if( pParse->nErr>nErr ) pParse->db->errByteOffset = -1;
}
#endif /* SQLITE_OMIT_GENERATED_COLUMNS */
/*
** Generate code to extract the value of the iCol-th column of a table.
*/
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
Vdbe *v, /* Parsing context */
Table *pTab, /* The table containing the value */
int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
int iCol, /* Index of the column to extract */
int regOut /* Extract the value into this register */
){
Column *pCol;
assert( v!=0 );
assert( pTab!=0 );
assert( iCol!=XN_EXPR );
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
VdbeComment((v, "%s.rowid", pTab->zName));
}else{
int op;
int x;
if( IsVirtual(pTab) ){
|
| ︙ | ︙ | |||
109720 109721 109722 109723 109724 109725 109726 |
}
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.
*/
| | > > > | 110378 110379 110380 110381 110382 110383 110384 110385 110386 110387 110388 110389 110390 110391 110392 110393 110394 110395 110396 110397 110398 |
}
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", "flexnum" };
char aff;
assert( nFarg==1 );
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
assert( aff<=SQLITE_AFF_NONE
|| (aff>=SQLITE_AFF_BLOB && aff<=SQLITE_AFF_FLEXNUM) );
sqlite3VdbeLoadString(v, target,
(aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
break;
}
#endif /* !defined(SQLITE_UNTESTABLE) */
}
return target;
|
| ︙ | ︙ | |||
109747 109748 109749 109750 109751 109752 109753 109754 109755 109756 109757 109758 109759 109760 109761 109762 109763 109764 109765 109766 109767 |
Parse *pParse, /* The parsing context */
Expr *pExpr, /* The expression to potentially bypass */
int target /* Where to store the result of the expression */
){
IndexedExpr *p;
Vdbe *v;
for(p=pParse->pIdxEpr; p; p=p->pIENext){
int iDataCur = p->iDataCur;
if( iDataCur<0 ) continue;
if( pParse->iSelfTab ){
if( p->iDataCur!=pParse->iSelfTab-1 ) continue;
iDataCur = -1;
}
if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue;
v = pParse->pVdbe;
assert( v!=0 );
if( p->bMaybeNullRow ){
/* If the index is on a NULL row due to an outer join, then we
** cannot extract the value from the index. The value must be
** computed using the original expression. */
int addr = sqlite3VdbeCurrentAddr(v);
| > > > > > > > > > > > | 110408 110409 110410 110411 110412 110413 110414 110415 110416 110417 110418 110419 110420 110421 110422 110423 110424 110425 110426 110427 110428 110429 110430 110431 110432 110433 110434 110435 110436 110437 110438 110439 |
Parse *pParse, /* The parsing context */
Expr *pExpr, /* The expression to potentially bypass */
int target /* Where to store the result of the expression */
){
IndexedExpr *p;
Vdbe *v;
for(p=pParse->pIdxEpr; p; p=p->pIENext){
u8 exprAff;
int iDataCur = p->iDataCur;
if( iDataCur<0 ) continue;
if( pParse->iSelfTab ){
if( p->iDataCur!=pParse->iSelfTab-1 ) continue;
iDataCur = -1;
}
if( sqlite3ExprCompare(0, pExpr, p->pExpr, iDataCur)!=0 ) continue;
assert( p->aff>=SQLITE_AFF_BLOB && p->aff<=SQLITE_AFF_NUMERIC );
exprAff = sqlite3ExprAffinity(pExpr);
if( (exprAff<=SQLITE_AFF_BLOB && p->aff!=SQLITE_AFF_BLOB)
|| (exprAff==SQLITE_AFF_TEXT && p->aff!=SQLITE_AFF_TEXT)
|| (exprAff>=SQLITE_AFF_NUMERIC && p->aff!=SQLITE_AFF_NUMERIC)
){
/* Affinity mismatch on a generated column */
continue;
}
v = pParse->pVdbe;
assert( v!=0 );
if( p->bMaybeNullRow ){
/* If the index is on a NULL row due to an outer join, then we
** cannot extract the value from the index. The value must be
** computed using the original expression. */
int addr = sqlite3VdbeCurrentAddr(v);
|
| ︙ | ︙ | |||
109822 109823 109824 109825 109826 109827 109828 |
op = pExpr->op;
}
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
struct AggInfo_col *pCol;
assert( pAggInfo!=0 );
| | > > > > > > > > > > > > | 110494 110495 110496 110497 110498 110499 110500 110501 110502 110503 110504 110505 110506 110507 110508 110509 110510 110511 110512 110513 110514 110515 110516 110517 110518 110519 110520 |
op = pExpr->op;
}
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
struct AggInfo_col *pCol;
assert( pAggInfo!=0 );
assert( pExpr->iAgg>=0 );
if( pExpr->iAgg>=pAggInfo->nColumn ){
/* Happens when the left table of a RIGHT JOIN is null and
** is using an expression index */
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
#ifdef SQLITE_VDBE_COVERAGE
/* Verify that the OP_Null above is exercised by tests
** tag-20230325-2 */
sqlite3VdbeAddOp2(v, OP_NotNull, target, 1);
VdbeCoverageNeverTaken(v);
#endif
break;
}
pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
return AggInfoColumnReg(pAggInfo, pExpr->iAgg);
}else if( pAggInfo->useSortingIdx ){
Table *pTab = pCol->pTab;
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
|
| ︙ | ︙ | |||
109997 109998 109999 110000 110001 110002 110003 |
}
case TK_REGISTER: {
return pExpr->iTable;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
| | | < < < | 110681 110682 110683 110684 110685 110686 110687 110688 110689 110690 110691 110692 110693 110694 110695 110696 |
}
case TK_REGISTER: {
return pExpr->iTable;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
sqlite3ExprCode(pParse, pExpr->pLeft, target);
assert( inReg==target );
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeAddOp2(v, OP_Cast, target,
sqlite3AffinityType(pExpr->u.zToken, 0));
return inReg;
}
#endif /* SQLITE_OMIT_CAST */
case TK_IS:
|
| ︙ | ︙ | |||
110333 110334 110335 110336 110337 110338 110339 |
** Z is stored in pExpr->pList->a[1].pExpr.
*/
case TK_BETWEEN: {
exprCodeBetween(pParse, pExpr, target, 0, 0);
return target;
}
case TK_COLLATE: {
| | | > > > > | < | < < < < | | | 111014 111015 111016 111017 111018 111019 111020 111021 111022 111023 111024 111025 111026 111027 111028 111029 111030 111031 111032 111033 111034 111035 111036 111037 |
** Z is stored in pExpr->pList->a[1].pExpr.
*/
case TK_BETWEEN: {
exprCodeBetween(pParse, pExpr, target, 0, 0);
return target;
}
case TK_COLLATE: {
if( !ExprHasProperty(pExpr, EP_Collate) ){
/* A TK_COLLATE Expr node without the EP_Collate tag is a so-called
** "SOFT-COLLATE" that is added to constraints that are pushed down
** from outer queries into sub-queries by the push-down optimization.
** Clear subtypes as subtypes may not cross a subquery boundary.
*/
assert( pExpr->pLeft );
sqlite3ExprCode(pParse, pExpr->pLeft, target);
sqlite3VdbeAddOp1(v, OP_ClrSubtype, target);
return target;
}else{
pExpr = pExpr->pLeft;
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */
}
}
case TK_SPAN:
case TK_UPLUS: {
|
| ︙ | ︙ | |||
110444 110445 110446 110447 110448 110449 110450 |
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pAggInfo->aCol[pExpr->iAgg].iSorterColumn,
target);
inReg = target;
break;
}
}
| | > > > | < < > | > | | > < | 111124 111125 111126 111127 111128 111129 111130 111131 111132 111133 111134 111135 111136 111137 111138 111139 111140 111141 111142 111143 111144 111145 111146 111147 111148 111149 111150 |
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pAggInfo->aCol[pExpr->iAgg].iSorterColumn,
target);
inReg = target;
break;
}
}
addrINR = sqlite3VdbeAddOp3(v, OP_IfNullRow, pExpr->iTable, 0, target);
/* The OP_IfNullRow opcode above can overwrite the result register with
** NULL. So we have to ensure that the result register is not a value
** that is suppose to be a constant. Two defenses are needed:
** (1) Temporarily disable factoring of constant expressions
** (2) Make sure the computed value really is stored in register
** "target" and not someplace else.
*/
pParse->okConstFactor = 0; /* note (1) above */
sqlite3ExprCode(pParse, pExpr->pLeft, target);
assert( target==inReg );
pParse->okConstFactor = okConstFactor;
sqlite3VdbeJumpHere(v, addrINR);
break;
}
/*
** Form A:
** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
**
|
| ︙ | ︙ | |||
110690 110691 110692 110693 110694 110695 110696 |
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;
| | > > | 111373 111374 111375 111376 111377 111378 111379 111380 111381 111382 111383 111384 111385 111386 111387 111388 111389 |
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) || pExpr->op==TK_REGISTER)
){
op = OP_Copy;
}else{
op = OP_SCopy;
}
sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
}
}
|
| ︙ | ︙ | |||
111875 111876 111877 111878 111879 111880 111881 111882 |
if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced))
&& pExpr->pAggInfo!=0
){
AggInfo *pAggInfo = pExpr->pAggInfo;
int iAgg = pExpr->iAgg;
Parse *pParse = pWalker->pParse;
sqlite3 *db = pParse->db;
if( pExpr->op!=TK_AGG_FUNCTION ){
| > | | > | | > | 112560 112561 112562 112563 112564 112565 112566 112567 112568 112569 112570 112571 112572 112573 112574 112575 112576 112577 112578 112579 112580 112581 112582 112583 112584 112585 112586 112587 112588 112589 |
if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced))
&& pExpr->pAggInfo!=0
){
AggInfo *pAggInfo = pExpr->pAggInfo;
int iAgg = pExpr->iAgg;
Parse *pParse = pWalker->pParse;
sqlite3 *db = pParse->db;
assert( iAgg>=0 );
if( pExpr->op!=TK_AGG_FUNCTION ){
if( iAgg<pAggInfo->nColumn
&& pAggInfo->aCol[iAgg].pCExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
if( pExpr ){
pAggInfo->aCol[iAgg].pCExpr = pExpr;
sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}else{
assert( pExpr->op==TK_AGG_FUNCTION );
if( ALWAYS(iAgg<pAggInfo->nFunc)
&& pAggInfo->aFunc[iAgg].pFExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
if( pExpr ){
pAggInfo->aFunc[iAgg].pFExpr = pExpr;
sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}
|
| ︙ | ︙ | |||
112037 112038 112039 112040 112041 112042 112043 |
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
int iDataCur = pIEpr->iDataCur;
if( iDataCur<0 ) continue;
if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
}
if( pIEpr==0 ) break;
if( NEVER(!ExprUseYTab(pExpr)) ) break;
| > > > > | > > > > | 112725 112726 112727 112728 112729 112730 112731 112732 112733 112734 112735 112736 112737 112738 112739 112740 112741 112742 112743 112744 112745 112746 112747 112748 112749 112750 112751 112752 112753 112754 112755 112756 112757 |
for(pIEpr=pParse->pIdxEpr; pIEpr; pIEpr=pIEpr->pIENext){
int iDataCur = pIEpr->iDataCur;
if( iDataCur<0 ) continue;
if( sqlite3ExprCompare(0, pExpr, pIEpr->pExpr, iDataCur)==0 ) break;
}
if( pIEpr==0 ) break;
if( NEVER(!ExprUseYTab(pExpr)) ) break;
for(i=0; i<pSrcList->nSrc; i++){
if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break;
}
if( i>=pSrcList->nSrc ) break;
if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */
if( pParse->nErr ){ return WRC_Abort; }
/* If we reach this point, it means that expression pExpr can be
** translated into a reference to an index column as described by
** pIEpr.
*/
memset(&tmp, 0, sizeof(tmp));
tmp.op = TK_AGG_COLUMN;
tmp.iTable = pIEpr->iIdxCur;
tmp.iColumn = pIEpr->iIdxCol;
findOrCreateAggInfoColumn(pParse, pAggInfo, &tmp);
if( pParse->nErr ){ return WRC_Abort; }
assert( pAggInfo->aCol!=0 );
assert( tmp.iAgg<pAggInfo->nColumn );
pAggInfo->aCol[tmp.iAgg].pCExpr = pExpr;
pExpr->pAggInfo = pAggInfo;
pExpr->iAgg = tmp.iAgg;
return WRC_Prune;
}
case TK_IF_NULL_ROW:
case TK_AGG_COLUMN:
|
| ︙ | ︙ | |||
112071 112072 112073 112074 112075 112076 112077 |
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
break;
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
}
| | | 112767 112768 112769 112770 112771 112772 112773 112774 112775 112776 112777 112778 112779 112780 112781 |
assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
findOrCreateAggInfoColumn(pParse, pAggInfo, pExpr);
break;
} /* endif pExpr->iTable==pItem->iCursor */
} /* end loop over pSrcList */
}
return WRC_Continue;
}
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
|
| ︙ | ︙ | |||
112224 112225 112226 112227 112228 112229 112230 112231 112232 112233 112234 112235 112236 112237 112238 112239 112240 112241 112242 112243 112244 112245 112246 112247 112248 112249 112250 112251 112252 112253 112254 112255 |
** invokes the sub/co-routine.
*/
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nTempReg = 0;
pParse->nRangeReg = 0;
}
/*
** Validate that no temporary register falls within the range of
** iFirst..iLast, inclusive. This routine is only call from within assert()
** statements.
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
int i;
if( pParse->nRangeReg>0
&& pParse->iRangeReg+pParse->nRangeReg > iFirst
&& pParse->iRangeReg <= iLast
){
return 0;
}
for(i=0; i<pParse->nTempReg; i++){
if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){
return 0;
}
}
return 1;
}
#endif /* SQLITE_DEBUG */
/************** End of expr.c ************************************************/
/************** Begin file alter.c *******************************************/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 112920 112921 112922 112923 112924 112925 112926 112927 112928 112929 112930 112931 112932 112933 112934 112935 112936 112937 112938 112939 112940 112941 112942 112943 112944 112945 112946 112947 112948 112949 112950 112951 112952 112953 112954 112955 112956 112957 112958 112959 112960 112961 112962 112963 112964 112965 112966 112967 112968 112969 112970 112971 112972 112973 112974 112975 112976 112977 112978 112979 112980 112981 112982 112983 112984 112985 112986 112987 112988 112989 112990 |
** invokes the sub/co-routine.
*/
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nTempReg = 0;
pParse->nRangeReg = 0;
}
/*
** Make sure sufficient registers have been allocated so that
** iReg is a valid register number.
*/
SQLITE_PRIVATE void sqlite3TouchRegister(Parse *pParse, int iReg){
if( pParse->nMem<iReg ) pParse->nMem = iReg;
}
#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG)
/*
** Return the latest reusable register in the set of all registers.
** The value returned is no less than iMin. If any register iMin or
** greater is in permanent use, then return one more than that last
** permanent register.
*/
SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){
const ExprList *pList = pParse->pConstExpr;
if( pList ){
int i;
for(i=0; i<pList->nExpr; i++){
if( pList->a[i].u.iConstExprReg>=iMin ){
iMin = pList->a[i].u.iConstExprReg + 1;
}
}
}
pParse->nTempReg = 0;
pParse->nRangeReg = 0;
return iMin;
}
#endif /* SQLITE_ENABLE_STAT4 || SQLITE_DEBUG */
/*
** Validate that no temporary register falls within the range of
** iFirst..iLast, inclusive. This routine is only call from within assert()
** statements.
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
int i;
if( pParse->nRangeReg>0
&& pParse->iRangeReg+pParse->nRangeReg > iFirst
&& pParse->iRangeReg <= iLast
){
return 0;
}
for(i=0; i<pParse->nTempReg; i++){
if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){
return 0;
}
}
if( pParse->pConstExpr ){
ExprList *pList = pParse->pConstExpr;
for(i=0; i<pList->nExpr; i++){
int iReg = pList->a[i].u.iConstExprReg;
if( iReg==0 ) continue;
if( iReg>=iFirst && iReg<=iLast ) return 0;
}
}
return 1;
}
#endif /* SQLITE_DEBUG */
/************** End of expr.c ************************************************/
/************** Begin file alter.c *******************************************/
|
| ︙ | ︙ | |||
113529 113530 113531 113532 113533 113534 113535 113536 113537 113538 113539 113540 113541 113542 |
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zQuot);
return rc;
}
/*
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
** it was read from the schema of database zDb. Return SQLITE_OK if
** successful. Otherwise, return an SQLite error code and leave an error
** message in the Parse object.
*/
| > > > > > > > > > > > > > | 114264 114265 114266 114267 114268 114269 114270 114271 114272 114273 114274 114275 114276 114277 114278 114279 114280 114281 114282 114283 114284 114285 114286 114287 114288 114289 114290 |
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zQuot);
return rc;
}
/*
** Set all pEList->a[].fg.eEName fields in the expression-list to val.
*/
static void renameSetENames(ExprList *pEList, int val){
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
pEList->a[i].fg.eEName = val;
}
}
}
/*
** Resolve all symbols in the trigger at pParse->pNewTrigger, assuming
** it was read from the schema of database zDb. Return SQLITE_OK if
** successful. Otherwise, return an SQLite error code and leave an error
** message in the Parse object.
*/
|
| ︙ | ︙ | |||
113577 113578 113579 113580 113581 113582 113583 113584 113585 113586 113587 113588 113589 113590 113591 |
pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
);
if( pSel==0 ){
pStep->pExprList = 0;
pSrc = 0;
rc = SQLITE_NOMEM;
}else{
sqlite3SelectPrep(pParse, pSel, 0);
rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
assert( pSrc==pSel->pSrc );
if( pStep->pExprList ) pSel->pEList = 0;
pSel->pSrc = 0;
sqlite3SelectDelete(db, pSel);
}
| > > > > > > > > > > | 114325 114326 114327 114328 114329 114330 114331 114332 114333 114334 114335 114336 114337 114338 114339 114340 114341 114342 114343 114344 114345 114346 114347 114348 114349 |
pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0
);
if( pSel==0 ){
pStep->pExprList = 0;
pSrc = 0;
rc = SQLITE_NOMEM;
}else{
/* pStep->pExprList contains an expression-list used for an UPDATE
** statement. So the a[].zEName values are the RHS of the
** "<col> = <expr>" clauses of the UPDATE statement. So, before
** running SelectPrep(), change all the eEName values in
** pStep->pExprList to ENAME_SPAN (from their current value of
** ENAME_NAME). This is to prevent any ids in ON() clauses that are
** part of pSrc from being incorrectly resolved against the
** a[].zEName values as if they were column aliases. */
renameSetENames(pStep->pExprList, ENAME_SPAN);
sqlite3SelectPrep(pParse, pSel, 0);
renameSetENames(pStep->pExprList, ENAME_NAME);
rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList );
assert( pSrc==pSel->pSrc );
if( pStep->pExprList ) pSel->pEList = 0;
pSel->pSrc = 0;
sqlite3SelectDelete(db, pSel);
}
|
| ︙ | ︙ | |||
115526 115527 115528 115529 115530 115531 115532 115533 115534 115535 115536 | int regRowid = iMem++; /* Rowid argument passed to stat_push() */ int regTemp = iMem++; /* Temporary use register */ int regTemp2 = iMem++; /* Second temporary use register */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ int regPrev = iMem; /* MUST BE LAST (see below) */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK Table *pStat1 = 0; #endif | > > > | > | 116284 116285 116286 116287 116288 116289 116290 116291 116292 116293 116294 116295 116296 116297 116298 116299 116300 116301 116302 116303 116304 116305 116306 |
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
int regTemp = iMem++; /* Temporary use register */
int regTemp2 = iMem++; /* Second temporary use register */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
int regPrev = iMem; /* MUST BE LAST (see below) */
#ifdef SQLITE_ENABLE_STAT4
int doOnce = 1; /* Flag for a one-time computation */
#endif
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
Table *pStat1 = 0;
#endif
sqlite3TouchRegister(pParse, iMem);
assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) );
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
return;
}
if( !IsOrdinaryTable(pTab) ){
/* Do not gather statistics on views or virtual tables */
return;
|
| ︙ | ︙ | |||
115636 115637 115638 115639 115640 115641 115642 |
** end_of_scan:
*/
/* Make sure there are enough memory cells allocated to accommodate
** the regPrev array and a trailing rowid (the rowid slot is required
** when building a record to insert into the sample column of
** the sqlite_stat4 table. */
| | | 116398 116399 116400 116401 116402 116403 116404 116405 116406 116407 116408 116409 116410 116411 116412 |
** end_of_scan:
*/
/* Make sure there are enough memory cells allocated to accommodate
** the regPrev array and a trailing rowid (the rowid slot is required
** when building a record to insert into the sample column of
** the sqlite_stat4 table. */
sqlite3TouchRegister(pParse, regPrev+nColTest);
/* Open a read-only cursor on the index being analyzed. */
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
|
| ︙ | ︙ | |||
115808 115809 115810 115811 115812 115813 115814 |
int regSample = regStat1+3;
int regCol = regStat1+4;
int regSampleRowid = regCol + nCol;
int addrNext;
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
| > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | 116570 116571 116572 116573 116574 116575 116576 116577 116578 116579 116580 116581 116582 116583 116584 116585 116586 116587 116588 116589 116590 116591 116592 116593 116594 116595 116596 116597 116598 116599 116600 116601 116602 116603 116604 116605 116606 116607 116608 116609 116610 116611 116612 |
int regSample = regStat1+3;
int regCol = regStat1+4;
int regSampleRowid = regCol + nCol;
int addrNext;
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
if( doOnce ){
int mxCol = nCol;
Index *pX;
/* Compute the maximum number of columns in any index */
for(pX=pTab->pIndex; pX; pX=pX->pNext){
int nColX; /* Number of columns in pX */
if( !HasRowid(pTab) && IsPrimaryKeyIndex(pX) ){
nColX = pX->nKeyCol;
}else{
nColX = pX->nColumn;
}
if( nColX>mxCol ) mxCol = nColX;
}
/* Allocate space to compute results for the largest index */
sqlite3TouchRegister(pParse, regCol+mxCol);
doOnce = 0;
#ifdef SQLITE_DEBUG
/* Verify that the call to sqlite3ClearTempRegCache() below
** really is needed.
** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25)
*/
testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) );
#endif
sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */
assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) );
}
assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) );
addrNext = sqlite3VdbeCurrentAddr(v);
callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
VdbeCoverage(v);
callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
|
| ︙ | ︙ | |||
115889 115890 115891 115892 115893 115894 115895 115896 115897 115898 115899 115900 115901 115902 |
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
iTab = pParse->nTab;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
}
loadAnalysis(pParse, iDb);
}
/*
** Generate code that will do an analysis of a single table in
** a database. If pOnlyIdx is not NULL then it is a single index
| > > > > > | 116679 116680 116681 116682 116683 116684 116685 116686 116687 116688 116689 116690 116691 116692 116693 116694 116695 116696 116697 |
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
iTab = pParse->nTab;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
#ifdef SQLITE_ENABLE_STAT4
iMem = sqlite3FirstAvailableRegister(pParse, iMem);
#else
assert( iMem==sqlite3FirstAvailableRegister(pParse,iMem) );
#endif
}
loadAnalysis(pParse, iDb);
}
/*
** Generate code that will do an analysis of a single table in
** a database. If pOnlyIdx is not NULL then it is a single index
|
| ︙ | ︙ | |||
116129 116130 116131 116132 116133 116134 116135 116136 116137 116138 116139 116140 116141 116142 116143 116144 |
}
/*
** If the Index.aSample variable is not NULL, delete the aSample[] array
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
sqlite3DbFree(db, p->p);
}
sqlite3DbFree(db, pIdx->aSample);
}
| > > | | 116924 116925 116926 116927 116928 116929 116930 116931 116932 116933 116934 116935 116936 116937 116938 116939 116940 116941 116942 116943 116944 116945 116946 116947 116948 116949 |
}
/*
** If the Index.aSample variable is not NULL, delete the aSample[] array
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
assert( db!=0 );
assert( pIdx!=0 );
#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
sqlite3DbFree(db, p->p);
}
sqlite3DbFree(db, pIdx->aSample);
}
if( db->pnBytesFreed==0 ){
pIdx->nSample = 0;
pIdx->aSample = 0;
}
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
#endif /* SQLITE_ENABLE_STAT4 */
|
| ︙ | ︙ | |||
116274 116275 116276 116277 116278 116279 116280 116281 116282 116283 116284 116285 116286 116287 116288 116289 116290 116291 116292 116293 116294 |
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
assert( pIdx==0 || pIdx->nSample==0 );
if( pIdx==0 ) continue;
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
nIdxCol = pIdx->nKeyCol;
}else{
nIdxCol = pIdx->nColumn;
}
pIdx->nSampleCol = nIdxCol;
nByte = sizeof(IndexSample) * nSample;
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
sqlite3_finalize(pStmt);
| > > > > > | 117071 117072 117073 117074 117075 117076 117077 117078 117079 117080 117081 117082 117083 117084 117085 117086 117087 117088 117089 117090 117091 117092 117093 117094 117095 117096 |
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
assert( pIdx==0 || pIdx->nSample==0 );
if( pIdx==0 ) continue;
if( pIdx->aSample!=0 ){
/* The same index appears in sqlite_stat4 under multiple names */
continue;
}
assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
nIdxCol = pIdx->nKeyCol;
}else{
nIdxCol = pIdx->nColumn;
}
pIdx->nSampleCol = nIdxCol;
pIdx->mxSample = nSample;
nByte = sizeof(IndexSample) * nSample;
nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
116320 116321 116322 116323 116324 116325 116326 116327 116328 116329 116330 116331 116332 116333 |
Index *pIdx; /* Pointer to the index object */
int nCol = 1; /* Number of columns in index */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
/* This next condition is true if data has already been loaded from
** the sqlite_stat4 table. */
nCol = pIdx->nSampleCol;
if( pIdx!=pPrevIdx ){
initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
}
| > > > > > | 117122 117123 117124 117125 117126 117127 117128 117129 117130 117131 117132 117133 117134 117135 117136 117137 117138 117139 117140 |
Index *pIdx; /* Pointer to the index object */
int nCol = 1; /* Number of columns in index */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
if( pIdx->nSample>=pIdx->mxSample ){
/* Too many slots used because the same index appears in
** sqlite_stat4 using multiple names */
continue;
}
/* This next condition is true if data has already been loaded from
** the sqlite_stat4 table. */
nCol = pIdx->nSampleCol;
if( pIdx!=pPrevIdx ){
initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
}
|
| ︙ | ︙ | |||
116363 116364 116365 116366 116367 116368 116369 |
** 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 );
| > | | | 117170 117171 117172 117173 117174 117175 117176 117177 117178 117179 117180 117181 117182 117183 117184 117185 117186 117187 117188 117189 |
** 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( OptimizationEnabled(db, SQLITE_Stat4)
&& (pStat4 = sqlite3FindTable(db, "sqlite_stat4", zDb))!=0
&& IsOrdinaryTable(pStat4)
){
rc = loadStatTbl(db,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx COLLATE nocase",
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
zDb
);
}
return rc;
}
#endif /* SQLITE_ENABLE_STAT4 */
|
| ︙ | ︙ | |||
116557 116558 116559 116560 116561 116562 116563 | sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; char *zPath = 0; char *zErr = 0; unsigned int flags; Db *aNew; /* New array of Db pointers */ | | > > > > > > > > | | | | > | > > > > | 117365 117366 117367 117368 117369 117370 117371 117372 117373 117374 117375 117376 117377 117378 117379 117380 117381 117382 117383 117384 117385 117386 117387 117388 117389 117390 117391 117392 117393 117394 117395 117396 117397 117398 117399 117400 117401 117402 117403 117404 117405 117406 117407 117408 117409 117410 117411 117412 117413 117414 117415 117416 117417 117418 |
sqlite3 *db = sqlite3_context_db_handle(context);
const char *zName;
const char *zFile;
char *zPath = 0;
char *zErr = 0;
unsigned int flags;
Db *aNew; /* New array of Db pointers */
Db *pNew = 0; /* Db object for the newly attached database */
char *zErrDyn = 0;
sqlite3_vfs *pVfs;
UNUSED_PARAMETER(NotUsed);
zFile = (const char *)sqlite3_value_text(argv[0]);
zName = (const char *)sqlite3_value_text(argv[1]);
if( zFile==0 ) zFile = "";
if( zName==0 ) zName = "";
#ifndef SQLITE_OMIT_DESERIALIZE
# define REOPEN_AS_MEMDB(db) (db->init.reopenMemdb)
#else
# define REOPEN_AS_MEMDB(db) (0)
#endif
if( REOPEN_AS_MEMDB(db) ){
/* This is not a real ATTACH. Instead, this routine is being called
** from sqlite3_deserialize() to close database db->init.iDb and
** reopen it as a MemDB */
Btree *pNewBt = 0;
pVfs = sqlite3_vfs_find("memdb");
if( pVfs==0 ) return;
rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB);
if( rc==SQLITE_OK ){
Schema *pNewSchema = sqlite3SchemaGet(db, pNewBt);
if( pNewSchema ){
/* Both the Btree and the new Schema were allocated successfully.
** Close the old db and update the aDb[] slot with the new memdb
** values. */
pNew = &db->aDb[db->init.iDb];
if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt);
pNew->pBt = pNewBt;
pNew->pSchema = pNewSchema;
}else{
sqlite3BtreeClose(pNewBt);
rc = SQLITE_NOMEM;
}
}
if( rc ) goto attach_error;
}else{
/* This is a real ATTACH
**
** Check for the following errors:
**
** * Too many attached databases,
** * Transaction currently open
|
| ︙ | ︙ | |||
116696 116697 116698 116699 116700 116701 116702 |
rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
if( newAuth<db->auth.authLevel ){
rc = SQLITE_AUTH_USER;
}
}
#endif
if( rc ){
| | | 117517 117518 117519 117520 117521 117522 117523 117524 117525 117526 117527 117528 117529 117530 117531 |
rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
if( newAuth<db->auth.authLevel ){
rc = SQLITE_AUTH_USER;
}
}
#endif
if( rc ){
if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
if( db->aDb[iDb].pBt ){
sqlite3BtreeClose(db->aDb[iDb].pBt);
db->aDb[iDb].pBt = 0;
db->aDb[iDb].pSchema = 0;
}
|
| ︙ | ︙ | |||
116812 116813 116814 116815 116816 116817 116818 116819 116820 116821 116822 116823 116824 116825 |
Expr *pKey /* Database key for encryption extension */
){
int rc;
NameContext sName;
Vdbe *v;
sqlite3* db = pParse->db;
int regArgs;
if( pParse->nErr ) goto attach_end;
memset(&sName, 0, sizeof(NameContext));
sName.pParse = pParse;
if(
SQLITE_OK!=resolveAttachExpr(&sName, pFilename) ||
| > > | 117633 117634 117635 117636 117637 117638 117639 117640 117641 117642 117643 117644 117645 117646 117647 117648 |
Expr *pKey /* Database key for encryption extension */
){
int rc;
NameContext sName;
Vdbe *v;
sqlite3* db = pParse->db;
int regArgs;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto attach_end;
if( pParse->nErr ) goto attach_end;
memset(&sName, 0, sizeof(NameContext));
sName.pParse = pParse;
if(
SQLITE_OK!=resolveAttachExpr(&sName, pFilename) ||
|
| ︙ | ︙ | |||
118196 118197 118198 118199 118200 118201 118202 |
}
sqlite3FreeIndex(db, pIndex);
}
if( IsOrdinaryTable(pTable) ){
sqlite3FkDelete(db, pTable);
}
| | | 119019 119020 119021 119022 119023 119024 119025 119026 119027 119028 119029 119030 119031 119032 119033 |
}
sqlite3FreeIndex(db, pIndex);
}
if( IsOrdinaryTable(pTable) ){
sqlite3FkDelete(db, pTable);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( IsVirtual(pTable) ){
sqlite3VtabClear(db, pTable);
}
#endif
else{
assert( IsView(pTable) );
sqlite3SelectDelete(db, pTable->u.view.pSelect);
|
| ︙ | ︙ | |||
118799 118800 118801 118802 118803 118804 118805 |
SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
Returning *pRet;
Hash *pHash;
sqlite3 *db = pParse->db;
if( pParse->pNewTrigger ){
sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
}else{
| | | 119622 119623 119624 119625 119626 119627 119628 119629 119630 119631 119632 119633 119634 119635 119636 |
SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
Returning *pRet;
Hash *pHash;
sqlite3 *db = pParse->db;
if( pParse->pNewTrigger ){
sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
}else{
assert( pParse->bReturning==0 || pParse->ifNotExists );
}
pParse->bReturning = 1;
pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
if( pRet==0 ){
sqlite3ExprListDelete(db, pList);
return;
}
|
| ︙ | ︙ | |||
118825 118826 118827 118828 118829 118830 118831 | pRet->retTrig.pSchema = db->aDb[1].pSchema; pRet->retTrig.pTabSchema = db->aDb[1].pSchema; pRet->retTrig.step_list = &pRet->retTStep; pRet->retTStep.op = TK_RETURNING; pRet->retTStep.pTrig = &pRet->retTrig; pRet->retTStep.pExprList = pList; pHash = &(db->aDb[1].pSchema->trigHash); | | > | 119648 119649 119650 119651 119652 119653 119654 119655 119656 119657 119658 119659 119660 119661 119662 119663 |
pRet->retTrig.pSchema = db->aDb[1].pSchema;
pRet->retTrig.pTabSchema = db->aDb[1].pSchema;
pRet->retTrig.step_list = &pRet->retTStep;
pRet->retTStep.op = TK_RETURNING;
pRet->retTStep.pTrig = &pRet->retTrig;
pRet->retTStep.pExprList = pList;
pHash = &(db->aDb[1].pSchema->trigHash);
assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0
|| pParse->nErr || pParse->ifNotExists );
if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
==&pRet->retTrig ){
sqlite3OomFault(db);
}
}
/*
|
| ︙ | ︙ | |||
119360 119361 119362 119363 119364 119365 119366 119367 119368 119369 119370 119371 119372 119373 |
if( ALWAYS(pExpr) && pExpr->op==TK_ID ){
/* The value of a generated column needs to be a real expression, not
** just a reference to another column, in order for covering index
** optimizations to work correctly. So if the value is not an expression,
** turn it into one by adding a unary "+" operator. */
pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0);
}
sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr);
pExpr = 0;
goto generated_done;
generated_error:
sqlite3ErrorMsg(pParse, "error in generated column \"%s\"",
pCol->zCnName);
| > | 120184 120185 120186 120187 120188 120189 120190 120191 120192 120193 120194 120195 120196 120197 120198 |
if( ALWAYS(pExpr) && pExpr->op==TK_ID ){
/* The value of a generated column needs to be a real expression, not
** just a reference to another column, in order for covering index
** optimizations to work correctly. So if the value is not an expression,
** turn it into one by adding a unary "+" operator. */
pExpr = sqlite3PExpr(pParse, TK_UPLUS, pExpr, 0);
}
if( pExpr && pExpr->op!=TK_RAISE ) pExpr->affExpr = pCol->affinity;
sqlite3ColumnSetExpr(pParse, pTab, pCol, pExpr);
pExpr = 0;
goto generated_done;
generated_error:
sqlite3ErrorMsg(pParse, "error in generated column \"%s\"",
pCol->zCnName);
|
| ︙ | ︙ | |||
123226 123227 123228 123229 123230 123231 123232 123233 123234 123235 123236 123237 123238 123239 |
SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
db->enc = enc;
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
** strings is BINARY.
*/
db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
}
/*
** This function is responsible for invoking the collation factory callback
** or substituting a collation sequence of a different encoding when the
** requested collation sequence is not available in the desired encoding.
**
| > | 124051 124052 124053 124054 124055 124056 124057 124058 124059 124060 124061 124062 124063 124064 124065 |
SQLITE_PRIVATE void sqlite3SetTextEncoding(sqlite3 *db, u8 enc){
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
db->enc = enc;
/* EVIDENCE-OF: R-08308-17224 The default collating function for all
** strings is BINARY.
*/
db->pDfltColl = sqlite3FindCollSeq(db, enc, sqlite3StrBINARY, 0);
sqlite3ExpirePreparedStatements(db, 1);
}
/*
** This function is responsible for invoking the collation factory callback
** or substituting a collation sequence of a different encoding when the
** requested collation sequence is not available in the desired encoding.
**
|
| ︙ | ︙ | |||
123697 123698 123699 123700 123701 123702 123703 | /* ** Check to make sure the given table is writable. ** ** If pTab is not writable -> generate an error message and return 1. ** If pTab is writable but other errors have occurred -> return 1. ** If pTab is writable and no prior errors -> return 0; */ | | | > > | 124523 124524 124525 124526 124527 124528 124529 124530 124531 124532 124533 124534 124535 124536 124537 124538 124539 124540 124541 124542 124543 124544 124545 |
/*
** Check to make sure the given table is writable.
**
** If pTab is not writable -> generate an error message and return 1.
** If pTab is writable but other errors have occurred -> return 1.
** If pTab is writable and no prior errors -> return 0;
*/
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, Trigger *pTrigger){
if( tabIsReadOnly(pParse, pTab) ){
sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
return 1;
}
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab)
&& (pTrigger==0 || (pTrigger->bReturning && pTrigger->pNext==0))
){
sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
return 1;
}
#endif
return 0;
}
|
| ︙ | ︙ | |||
123957 123958 123959 123960 123961 123962 123963 |
/* If pTab is really a view, make sure it has been initialized.
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto delete_from_cleanup;
}
| | | 124785 124786 124787 124788 124789 124790 124791 124792 124793 124794 124795 124796 124797 124798 124799 |
/* If pTab is really a view, make sure it has been initialized.
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto delete_from_cleanup;
}
if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto delete_from_cleanup;
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
db->aDb[iDb].zDbSName);
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
|
| ︙ | ︙ | |||
124066 124067 124068 124069 124070 124071 124072 |
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
{
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
| | | 124894 124895 124896 124897 124898 124899 124900 124901 124902 124903 124904 124905 124906 124907 124908 |
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
{
u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK;
if( sNC.ncFlags & NC_Subquery ) bComplex = 1;
wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
if( HasRowid(pTab) ){
/* For a rowid table, initialize the RowSet to an empty set */
pPk = 0;
nPk = 1;
iRowSet = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
|
| ︙ | ︙ | |||
125824 125825 125826 125827 125828 125829 125830 125831 125832 125833 125834 125835 125836 125837 |
*(z++) = hexdigits[(c>>4)&0xf];
*(z++) = hexdigits[c&0xf];
}
*z = 0;
sqlite3_result_text(context, zHex, n*2, sqlite3_free);
}
}
/*
** The zeroblob(N) function returns a zero-filled blob of size N bytes.
*/
static void zeroblobFunc(
sqlite3_context *context,
int argc,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 126652 126653 126654 126655 126656 126657 126658 126659 126660 126661 126662 126663 126664 126665 126666 126667 126668 126669 126670 126671 126672 126673 126674 126675 126676 126677 126678 126679 126680 126681 126682 126683 126684 126685 126686 126687 126688 126689 126690 126691 126692 126693 126694 126695 126696 126697 126698 126699 126700 126701 126702 126703 126704 126705 126706 126707 126708 126709 126710 126711 126712 126713 126714 126715 126716 126717 126718 126719 126720 126721 126722 126723 126724 126725 126726 126727 126728 126729 126730 126731 126732 126733 126734 126735 126736 126737 126738 126739 126740 126741 126742 126743 126744 126745 126746 126747 126748 126749 126750 126751 126752 126753 126754 126755 |
*(z++) = hexdigits[(c>>4)&0xf];
*(z++) = hexdigits[c&0xf];
}
*z = 0;
sqlite3_result_text(context, zHex, n*2, sqlite3_free);
}
}
/*
** Buffer zStr contains nStr bytes of utf-8 encoded text. Return 1 if zStr
** contains character ch, or 0 if it does not.
*/
static int strContainsChar(const u8 *zStr, int nStr, u32 ch){
const u8 *zEnd = &zStr[nStr];
const u8 *z = zStr;
while( z<zEnd ){
u32 tst = Utf8Read(z);
if( tst==ch ) return 1;
}
return 0;
}
/*
** The unhex() function. This function may be invoked with either one or
** two arguments. In both cases the first argument is interpreted as text
** a text value containing a set of pairs of hexadecimal digits which are
** decoded and returned as a blob.
**
** If there is only a single argument, then it must consist only of an
** even number of hexadeximal digits. Otherwise, return NULL.
**
** Or, if there is a second argument, then any character that appears in
** the second argument is also allowed to appear between pairs of hexadecimal
** digits in the first argument. If any other character appears in the
** first argument, or if one of the allowed characters appears between
** two hexadecimal digits that make up a single byte, NULL is returned.
**
** The following expressions are all true:
**
** unhex('ABCD') IS x'ABCD'
** unhex('AB CD') IS NULL
** unhex('AB CD', ' ') IS x'ABCD'
** unhex('A BCD', ' ') IS NULL
*/
static void unhexFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
const u8 *zPass = (const u8*)"";
int nPass = 0;
const u8 *zHex = sqlite3_value_text(argv[0]);
int nHex = sqlite3_value_bytes(argv[0]);
#ifdef SQLITE_DEBUG
const u8 *zEnd = zHex ? &zHex[nHex] : 0;
#endif
u8 *pBlob = 0;
u8 *p = 0;
assert( argc==1 || argc==2 );
if( argc==2 ){
zPass = sqlite3_value_text(argv[1]);
nPass = sqlite3_value_bytes(argv[1]);
}
if( !zHex || !zPass ) return;
p = pBlob = contextMalloc(pCtx, (nHex/2)+1);
if( pBlob ){
u8 c; /* Most significant digit of next byte */
u8 d; /* Least significant digit of next byte */
while( (c = *zHex)!=0x00 ){
while( !sqlite3Isxdigit(c) ){
u32 ch = Utf8Read(zHex);
assert( zHex<=zEnd );
if( !strContainsChar(zPass, nPass, ch) ) goto unhex_null;
c = *zHex;
if( c==0x00 ) goto unhex_done;
}
zHex++;
assert( *zEnd==0x00 );
assert( zHex<=zEnd );
d = *(zHex++);
if( !sqlite3Isxdigit(d) ) goto unhex_null;
*(p++) = (sqlite3HexToInt(c)<<4) | sqlite3HexToInt(d);
}
}
unhex_done:
sqlite3_result_blob(pCtx, pBlob, (p - pBlob), sqlite3_free);
return;
unhex_null:
sqlite3_free(pBlob);
return;
}
/*
** The zeroblob(N) function returns a zero-filled blob of size N bytes.
*/
static void zeroblobFunc(
sqlite3_context *context,
int argc,
|
| ︙ | ︙ | |||
126030 126031 126032 126033 126034 126035 126036 | } #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION /* ** The "unknown" function is automatically substituted in place of ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN | | | 126948 126949 126950 126951 126952 126953 126954 126955 126956 126957 126958 126959 126960 126961 126962 | } #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION /* ** The "unknown" function is automatically substituted in place of ** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN ** when the SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION compile-time option is used. ** When the "sqlite3" command-line shell is built using this functionality, ** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries ** involving application-defined functions to be examined in a generic ** sqlite3 shell. */ static void unknownFunc( sqlite3_context *context, |
| ︙ | ︙ | |||
126672 126673 126674 126675 126676 126677 126678 126679 126680 126681 126682 126683 126684 126685 |
/*
** On some systems, ceil() and floor() are intrinsic function. You are
** unable to take a pointer to these functions. Hence, we here wrap them
** in our own actual functions.
*/
static double xCeil(double x){ return ceil(x); }
static double xFloor(double x){ return floor(x); }
/*
** Implementation of SQL functions:
**
** ln(X) - natural logarithm
** log(X) - log X base 10
** log10(X) - log X base 10
| > > > > > > > > > > > > | 127590 127591 127592 127593 127594 127595 127596 127597 127598 127599 127600 127601 127602 127603 127604 127605 127606 127607 127608 127609 127610 127611 127612 127613 127614 127615 |
/*
** On some systems, ceil() and floor() are intrinsic function. You are
** unable to take a pointer to these functions. Hence, we here wrap them
** in our own actual functions.
*/
static double xCeil(double x){ return ceil(x); }
static double xFloor(double x){ return floor(x); }
/*
** Some systems do not have log2() and log10() in their standard math
** libraries.
*/
#if defined(HAVE_LOG10) && HAVE_LOG10==0
# define log10(X) (0.4342944819032517867*log(X))
#endif
#if defined(HAVE_LOG2) && HAVE_LOG2==0
# define log2(X) (1.442695040888963456*log(X))
#endif
/*
** Implementation of SQL functions:
**
** ln(X) - natural logarithm
** log(X) - log X base 10
** log10(X) - log X base 10
|
| ︙ | ︙ | |||
126889 126890 126891 126892 126893 126894 126895 126896 126897 126898 126899 126900 126901 126902 |
#ifndef SQLITE_OMIT_FLOATING_POINT
FUNCTION(round, 1, 0, 0, roundFunc ),
FUNCTION(round, 2, 0, 0, roundFunc ),
#endif
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(hex, 1, 0, 0, hexFunc ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
| > > | 127819 127820 127821 127822 127823 127824 127825 127826 127827 127828 127829 127830 127831 127832 127833 127834 |
#ifndef SQLITE_OMIT_FLOATING_POINT
FUNCTION(round, 1, 0, 0, roundFunc ),
FUNCTION(round, 2, 0, 0, roundFunc ),
#endif
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(hex, 1, 0, 0, hexFunc ),
FUNCTION(unhex, 1, 0, 0, unhexFunc ),
FUNCTION(unhex, 2, 0, 0, unhexFunc ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
|
| ︙ | ︙ | |||
128319 128320 128321 128322 128323 128324 128325 |
sqlite3DbFree(db, aiCol);
zFrom = pFKey->pFrom->zName;
nFrom = sqlite3Strlen30(zFrom);
if( action==OE_Restrict ){
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
| | < < < < < < > > > > > > | | 129251 129252 129253 129254 129255 129256 129257 129258 129259 129260 129261 129262 129263 129264 129265 129266 129267 129268 129269 129270 129271 129272 129273 129274 129275 129276 129277 129278 129279 129280 |
sqlite3DbFree(db, aiCol);
zFrom = pFKey->pFrom->zName;
nFrom = sqlite3Strlen30(zFrom);
if( action==OE_Restrict ){
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
SrcList *pSrc;
Expr *pRaise;
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
pRaise->affExpr = OE_Abort;
}
pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
if( pSrc ){
assert( pSrc->nSrc==1 );
pSrc->a[0].zName = sqlite3DbStrDup(db, zFrom);
pSrc->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
}
pSelect = sqlite3SelectNew(pParse,
sqlite3ExprListAppend(pParse, 0, pRaise),
pSrc,
pWhere,
0, 0, 0, 0, 0
);
pWhere = 0;
}
/* Disable lookaside memory allocation */
|
| ︙ | ︙ | |||
128550 128551 128552 128553 128554 128555 128556 | ** An extra 'D' is appended to the end of the string to cover the ** rowid that appears as the last column in every index. ** ** Memory for the buffer containing the column index affinity string ** is managed along with the rest of the Index structure. It will be ** released when sqlite3DeleteIndex() is called. */ | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > > | 129482 129483 129484 129485 129486 129487 129488 129489 129490 129491 129492 129493 129494 129495 129496 129497 129498 129499 129500 129501 129502 129503 129504 129505 129506 129507 129508 129509 129510 129511 129512 129513 129514 129515 129516 129517 129518 129519 129520 129521 129522 129523 129524 129525 129526 129527 129528 129529 129530 129531 129532 129533 129534 129535 129536 |
** An extra 'D' is appended to the end of the string to cover the
** rowid that appears as the last column in every index.
**
** Memory for the buffer containing the column index affinity string
** is managed along with the rest of the Index structure. It will be
** released when sqlite3DeleteIndex() is called.
*/
static SQLITE_NOINLINE const char *computeIndexAffStr(sqlite3 *db, Index *pIdx){
/* The first time a column affinity string for a particular index is
** required, it is allocated and populated here. It is then stored as
** a member of the Index structure for subsequent use.
**
** The column affinity string will eventually be deleted by
** sqliteDeleteIndex() when the Index structure itself is cleaned
** up.
*/
int n;
Table *pTab = pIdx->pTable;
pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
sqlite3OomFault(db);
return 0;
}
for(n=0; n<pIdx->nColumn; n++){
i16 x = pIdx->aiColumn[n];
char aff;
if( x>=0 ){
aff = pTab->aCol[x].affinity;
}else if( x==XN_ROWID ){
aff = SQLITE_AFF_INTEGER;
}else{
assert( x==XN_EXPR );
assert( pIdx->bHasExpr );
assert( pIdx->aColExpr!=0 );
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
}
if( aff<SQLITE_AFF_BLOB ) aff = SQLITE_AFF_BLOB;
if( aff>SQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC;
pIdx->zColAff[n] = aff;
}
pIdx->zColAff[n] = 0;
return pIdx->zColAff;
}
SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
if( !pIdx->zColAff ) return computeIndexAffStr(db, pIdx);
return pIdx->zColAff;
}
/*
** Compute an affinity string for a table. Space is obtained
** from sqlite3DbMalloc(). The caller is responsible for freeing
** the space when done.
*/
SQLITE_PRIVATE char *sqlite3TableAffinityStr(sqlite3 *db, const Table *pTab){
|
| ︙ | ︙ | |||
129274 129275 129276 129277 129278 129279 129280 |
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto insert_cleanup;
}
/* Cannot insert into a read-only table.
*/
| | | 130208 130209 130210 130211 130212 130213 130214 130215 130216 130217 130218 130219 130220 130221 130222 |
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto insert_cleanup;
}
/* Cannot insert into a read-only table.
*/
if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto insert_cleanup;
}
/* Allocate a VDBE
*/
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
|
| ︙ | ︙ | |||
129721 129722 129723 129724 129725 129726 129727 |
addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
/* Copy the new data already generated. */
| | | 130655 130656 130657 130658 130659 130660 130661 130662 130663 130664 130665 130666 130667 130668 130669 |
addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
/* Copy the new data already generated. */
assert( pTab->nNVCol>0 || pParse->nErr>0 );
sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
/* Compute the new value for generated columns after all other
** columns have already been computed. This must be done after
** computing the ROWID in case one of the generated columns
** refers to the ROWID. */
|
| ︙ | ︙ | |||
133084 133085 133086 133087 133088 133089 133090 | } zEntry = zProc ? zProc : "sqlite3_extension_init"; /* tag-20210611-1. Some dlopen() implementations will segfault if given ** an oversize filename. Most filesystems have a pathname limit of 4K, ** so limit the extension filename length to about twice that. | | > > > > > | > | 134018 134019 134020 134021 134022 134023 134024 134025 134026 134027 134028 134029 134030 134031 134032 134033 134034 134035 134036 134037 134038 134039 134040 134041 134042 134043 134044 134045 134046 |
}
zEntry = zProc ? zProc : "sqlite3_extension_init";
/* tag-20210611-1. Some dlopen() implementations will segfault if given
** an oversize filename. Most filesystems have a pathname limit of 4K,
** so limit the extension filename length to about twice that.
** https://sqlite.org/forum/forumpost/08a0d6d9bf
**
** Later (2023-03-25): Save an extra 6 bytes for the filename suffix.
** See https://sqlite.org/forum/forumpost/24083b579d.
*/
if( nMsg>SQLITE_MAX_PATHLEN ) goto extension_not_found;
handle = sqlite3OsDlOpen(pVfs, zFile);
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
if( nMsg+strlen(azEndings[ii])+1<=SQLITE_MAX_PATHLEN ){
handle = sqlite3OsDlOpen(pVfs, zAltFile);
}
sqlite3_free(zAltFile);
}
#endif
if( handle==0 ) goto extension_not_found;
xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
/* If no entry point was specified and the default legacy
|
| ︙ | ︙ | |||
135587 135588 135589 135590 135591 135592 135593 |
k = sqliteHashNext(k);
}
if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
| | | 136527 136528 136529 136530 136531 136532 136533 136534 136535 136536 136537 136538 136539 136540 136541 |
k = sqliteHashNext(k);
}
if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
zDb = db->aDb[iDb].zDbSName;
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
sqlite3TouchRegister(pParse, pTab->nCol+regRow);
sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
sqlite3VdbeLoadString(v, regResult, pTab->zName);
assert( IsOrdinaryTable(pTab) );
for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){
pParent = sqlite3FindTable(db, pFK->zTo, zDb);
if( pParent==0 ) continue;
pIdx = 0;
|
| ︙ | ︙ | |||
135628 135629 135630 135631 135632 135633 135634 |
}
addrOk = sqlite3VdbeMakeLabel(pParse);
/* Generate code to read the child key values into registers
** regRow..regRow+n. If any of the child key values are NULL, this
** row cannot cause an FK violation. Jump directly to addrOk in
** this case. */
| | | 136568 136569 136570 136571 136572 136573 136574 136575 136576 136577 136578 136579 136580 136581 136582 |
}
addrOk = sqlite3VdbeMakeLabel(pParse);
/* Generate code to read the child key values into registers
** regRow..regRow+n. If any of the child key values are NULL, this
** row cannot cause an FK violation. Jump directly to addrOk in
** this case. */
sqlite3TouchRegister(pParse, regRow + pFK->nCol);
for(j=0; j<pFK->nCol; j++){
int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom;
sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j);
sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v);
}
/* Generate code to query the parent index for a matching parent
|
| ︙ | ︙ | |||
135757 135758 135759 135760 135761 135762 135763 135764 135765 135766 135767 135768 135769 135770 |
int cnt = 0; /* Number of entries in aRoot[] */
int mxIdx = 0; /* Maximum number of indexes for any table */
if( OMIT_TEMPDB && i==1 ) continue;
if( iDb>=0 && i!=iDb ) continue;
sqlite3CodeVerifySchema(pParse, i);
/* Do an integrity check of the B-Tree
**
** Begin by finding the root pages numbers
** for all tables and indices in the database.
*/
assert( sqlite3SchemaMutexHeld(db, i, 0) );
| > | 136697 136698 136699 136700 136701 136702 136703 136704 136705 136706 136707 136708 136709 136710 136711 |
int cnt = 0; /* Number of entries in aRoot[] */
int mxIdx = 0; /* Maximum number of indexes for any table */
if( OMIT_TEMPDB && i==1 ) continue;
if( iDb>=0 && i!=iDb ) continue;
sqlite3CodeVerifySchema(pParse, i);
pParse->okConstFactor = 0; /* tag-20230327-1 */
/* Do an integrity check of the B-Tree
**
** Begin by finding the root pages numbers
** for all tables and indices in the database.
*/
assert( sqlite3SchemaMutexHeld(db, i, 0) );
|
| ︙ | ︙ | |||
135792 135793 135794 135795 135796 135797 135798 |
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
aRoot[++cnt] = pIdx->tnum;
}
}
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
| | | 136733 136734 136735 136736 136737 136738 136739 136740 136741 136742 136743 136744 136745 136746 136747 |
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
aRoot[++cnt] = pIdx->tnum;
}
}
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
sqlite3TouchRegister(pParse, 8+mxIdx);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
| ︙ | ︙ | |||
135848 135849 135850 135851 135852 135853 135854 |
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
/* Fetch the right-most column from the table. This will cause
** the entire record header to be parsed and sanity checked. It
** will also prepopulate the cursor column cache that is used
** by the OP_IsType code, so it is a required step.
*/
| > > | | | > | > > > > > > | | 136789 136790 136791 136792 136793 136794 136795 136796 136797 136798 136799 136800 136801 136802 136803 136804 136805 136806 136807 136808 136809 136810 136811 136812 136813 136814 136815 136816 136817 |
loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
/* Fetch the right-most column from the table. This will cause
** the entire record header to be parsed and sanity checked. It
** will also prepopulate the cursor column cache that is used
** by the OP_IsType code, so it is a required step.
*/
assert( !IsVirtual(pTab) );
if( HasRowid(pTab) ){
mxCol = -1;
for(j=0; j<pTab->nCol; j++){
if( (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)==0 ) mxCol++;
}
if( mxCol==pTab->iPKey ) mxCol--;
}else{
/* COLFLAG_VIRTUAL columns are not included in the WITHOUT ROWID
** PK index column-count, so there is no need to account for them
** in this case. */
mxCol = sqlite3PrimaryKeyIndex(pTab)->nColumn-1;
}
if( mxCol>=0 ){
sqlite3VdbeAddOp3(v, OP_Column, iDataCur, mxCol, 3);
sqlite3VdbeTypeofColumn(v, 3);
}
if( !isQuick ){
if( pPk ){
/* Verify WITHOUT ROWID keys are in ascending order */
int a1;
|
| ︙ | ︙ | |||
135933 135934 135935 135936 135937 135938 135939 135940 |
}
}
labelError = sqlite3VdbeMakeLabel(pParse);
labelOk = sqlite3VdbeMakeLabel(pParse);
if( pCol->notNull ){
/* (1) NOT NULL columns may not contain a NULL */
int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
| > > > | > > > > > > > > > | > > | 136883 136884 136885 136886 136887 136888 136889 136890 136891 136892 136893 136894 136895 136896 136897 136898 136899 136900 136901 136902 136903 136904 136905 136906 136907 136908 136909 136910 136911 136912 136913 136914 136915 136916 136917 136918 136919 |
}
}
labelError = sqlite3VdbeMakeLabel(pParse);
labelOk = sqlite3VdbeMakeLabel(pParse);
if( pCol->notNull ){
/* (1) NOT NULL columns may not contain a NULL */
int jmp3;
int jmp2 = sqlite3VdbeAddOp4Int(v, OP_IsType, p1, labelOk, p3, p4);
VdbeCoverage(v);
if( p1<0 ){
sqlite3VdbeChangeP5(v, 0x0f); /* INT, REAL, TEXT, or BLOB */
jmp3 = jmp2;
}else{
sqlite3VdbeChangeP5(v, 0x0d); /* INT, TEXT, or BLOB */
/* OP_IsType does not detect NaN values in the database file
** which should be treated as a NULL. So if the header type
** is REAL, we have to load the actual data using OP_Column
** to reliably determine if the value is a NULL. */
sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
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( doTypeCheck ){
sqlite3VdbeGoto(v, labelError);
sqlite3VdbeJumpHere(v, jmp2);
sqlite3VdbeJumpHere(v, jmp3);
}else{
/* VDBE byte code will fall thru */
}
}
if( bStrict && doTypeCheck ){
/* (2) Datatype must be exact for non-ANY columns in STRICT tables*/
static unsigned char aStdTypeMask[] = {
|
| ︙ | ︙ | |||
136040 136041 136042 136043 136044 136045 136046 136047 136048 136049 136050 136051 136052 136053 |
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
sqlite3VdbeLoadString(v, 4, " missing from index ");
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp4 = integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, jmp2);
/* Any indexed columns with non-BINARY collations must still hold
** the exact same text value as the table. */
label6 = 0;
for(kk=0; kk<pIdx->nKeyCol; kk++){
if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue;
if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse);
| > > > > > > > > > > > > > > > > > | 137004 137005 137006 137007 137008 137009 137010 137011 137012 137013 137014 137015 137016 137017 137018 137019 137020 137021 137022 137023 137024 137025 137026 137027 137028 137029 137030 137031 137032 137033 137034 |
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
sqlite3VdbeLoadString(v, 4, " missing from index ");
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
jmp4 = integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, jmp2);
/* The OP_IdxRowid opcode is an optimized version of OP_Column
** that extracts the rowid off the end of the index record.
** But it only works correctly if index record does not have
** any extra bytes at the end. Verify that this is the case. */
if( HasRowid(pTab) ){
int jmp7;
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur+j, 3);
jmp7 = sqlite3VdbeAddOp3(v, OP_Eq, 3, 0, r1+pIdx->nColumn-1);
VdbeCoverageNeverNull(v);
sqlite3VdbeLoadString(v, 3,
"rowid not at end-of-record for row ");
sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
sqlite3VdbeLoadString(v, 4, " of index ");
sqlite3VdbeGoto(v, jmp5-1);
sqlite3VdbeJumpHere(v, jmp7);
}
/* Any indexed columns with non-BINARY collations must still hold
** the exact same text value as the table. */
label6 = 0;
for(kk=0; kk<pIdx->nKeyCol; kk++){
if( pIdx->azColl[kk]==sqlite3StrBINARY ) continue;
if( label6==0 ) label6 = sqlite3VdbeMakeLabel(pParse);
|
| ︙ | ︙ | |||
137238 137239 137240 137241 137242 137243 137244 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
| > > > > > > | > | 138219 138220 138221 138222 138223 138224 138225 138226 138227 138228 138229 138230 138231 138232 138233 138234 138235 138236 138237 138238 138239 138240 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
if( db->nVdbeActive>0 && encoding!=ENC(db)
&& (db->mDbFlags & DBFLAG_Vacuum)==0
){
rc = SQLITE_LOCKED;
goto initone_error_out;
}else{
sqlite3SetTextEncoding(db, encoding);
}
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
sqlite3SetString(pzErrMsg, db, "attached databases must use the same"
" text encoding as main database");
rc = SQLITE_ERROR;
goto initone_error_out;
|
| ︙ | ︙ | |||
137627 137628 137629 137630 137631 137632 137633 | memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); sParse.pOuterParse = db->pParse; db->pParse = &sParse; sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); | > | > > > | 138615 138616 138617 138618 138619 138620 138621 138622 138623 138624 138625 138626 138627 138628 138629 138630 138631 138632 138633 |
memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ);
memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
sParse.pOuterParse = db->pParse;
db->pParse = &sParse;
sParse.db = db;
sParse.pReprepare = pReprepare;
assert( ppStmt && *ppStmt==0 );
if( db->mallocFailed ){
sqlite3ErrorMsg(&sParse, "out of memory");
db->errCode = rc = SQLITE_NOMEM;
goto end_prepare;
}
assert( sqlite3_mutex_held(db->mutex) );
/* For a long-term use prepared statement avoid the use of
** lookaside memory.
*/
if( prepFlags & SQLITE_PREPARE_PERSISTENT ){
sParse.disableLookaside++;
|
| ︙ | ︙ | |||
138716 138717 138718 138719 138720 138721 138722 | /* Three cases: ** (1) The data to be sorted has already been packed into a Record ** by a prior OP_MakeRecord. In this case nData==1 and regData ** will be completely unrelated to regOrigData. ** (2) All output columns are included in the sort record. In that ** case regData==regOrigData. ** (3) Some output columns are omitted from the sort record due to | | | 139708 139709 139710 139711 139712 139713 139714 139715 139716 139717 139718 139719 139720 139721 139722 | /* Three cases: ** (1) The data to be sorted has already been packed into a Record ** by a prior OP_MakeRecord. In this case nData==1 and regData ** will be completely unrelated to regOrigData. ** (2) All output columns are included in the sort record. In that ** case regData==regOrigData. ** (3) Some output columns are omitted from the sort record due to ** the SQLITE_ENABLE_SORTER_REFERENCES optimization, or due to the ** SQLITE_ECEL_OMITREF optimization, or due to the ** SortCtx.pDeferredRowLoad optimiation. In any of these cases ** regOrigData is 0 to prevent this routine from trying to copy ** values that might not yet exist. */ assert( nData==1 || regData==regOrigData || regOrigData==0 ); |
| ︙ | ︙ | |||
140317 140318 140319 140320 140321 140322 140323 | struct ExprList_item *a; NameContext sNC; assert( pSelect!=0 ); assert( (pSelect->selFlags & SF_Resolved)!=0 ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); | | < < > > > | | | | | | > | | | < < < | 141309 141310 141311 141312 141313 141314 141315 141316 141317 141318 141319 141320 141321 141322 141323 141324 141325 141326 141327 141328 141329 141330 141331 141332 141333 141334 141335 141336 141337 141338 141339 141340 141341 141342 141343 141344 141345 141346 141347 141348 141349 141350 141351 141352 141353 141354 141355 141356 141357 141358 141359 141360 141361 141362 141363 141364 141365 141366 141367 141368 141369 141370 141371 141372 141373 141374 141375 141376 141377 |
struct ExprList_item *a;
NameContext sNC;
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed || IN_RENAME_OBJECT ) return;
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
a = pSelect->pEList->a;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const char *zType;
i64 n;
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
if( pCol->affinity<=SQLITE_AFF_NONE ){
pCol->affinity = aff;
}
if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
int m = 0;
Select *pS2;
for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
}
if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
pCol->affinity = SQLITE_AFF_BLOB;
}else
if( pCol->affinity>=SQLITE_AFF_NUMERIC && (m&0x02)!=0 ){
pCol->affinity = SQLITE_AFF_BLOB;
}
if( pCol->affinity>=SQLITE_AFF_NUMERIC && p->op==TK_CAST ){
pCol->affinity = SQLITE_AFF_FLEXNUM;
}
}
zType = columnType(&sNC, p, 0, 0, 0);
if( zType==0 || pCol->affinity!=sqlite3AffinityType(zType, 0) ){
if( pCol->affinity==SQLITE_AFF_NUMERIC
|| pCol->affinity==SQLITE_AFF_FLEXNUM
){
zType = "NUM";
}else{
zType = 0;
for(j=1; j<SQLITE_N_STDTYPE; j++){
if( sqlite3StdTypeAffinity[j]==pCol->affinity ){
zType = sqlite3StdType[j];
break;
}
}
}
}
if( zType ){
i64 m = sqlite3Strlen30(zType);
n = sqlite3Strlen30(pCol->zCnName);
pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
if( pCol->zCnName ){
memcpy(&pCol->zCnName[n+1], zType, m+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl ){
assert( pTab->pIndex==0 );
sqlite3ColumnSetColl(db, pCol, pColl->zName);
}
|
| ︙ | ︙ | |||
141860 141861 141862 141863 141864 141865 141866 |
Expr ifNullRow;
assert( pSubst->pEList!=0 && iColumn<pSubst->pEList->nExpr );
assert( pExpr->pRight==0 );
if( sqlite3ExprIsVector(pCopy) ){
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
}else{
sqlite3 *db = pSubst->pParse->db;
| | > > | 142851 142852 142853 142854 142855 142856 142857 142858 142859 142860 142861 142862 142863 142864 142865 142866 142867 |
Expr ifNullRow;
assert( pSubst->pEList!=0 && iColumn<pSubst->pEList->nExpr );
assert( pExpr->pRight==0 );
if( sqlite3ExprIsVector(pCopy) ){
sqlite3VectorErrorMsg(pSubst->pParse, pCopy);
}else{
sqlite3 *db = pSubst->pParse->db;
if( pSubst->isOuterJoin
&& (pCopy->op!=TK_COLUMN || pCopy->iTable!=pSubst->iNewTable)
){
memset(&ifNullRow, 0, sizeof(ifNullRow));
ifNullRow.op = TK_IF_NULL_ROW;
ifNullRow.pLeft = pCopy;
ifNullRow.iTable = pSubst->iNewTable;
ifNullRow.iColumn = -99;
ifNullRow.flags = EP_IfNullRow;
pCopy = &ifNullRow;
|
| ︙ | ︙ | |||
142237 142238 142239 142240 142241 142242 142243 | ** (17d2) DISTINCT ** (17e) the subquery may not contain window functions, and ** (17f) the subquery must not be the RHS of a LEFT JOIN. ** (17g) either the subquery is the first element of the outer ** query or there are no RIGHT or FULL JOINs in any arm ** of the subquery. (This is a duplicate of condition (27b).) ** (17h) The corresponding result set expressions in all arms of the | | < | 143230 143231 143232 143233 143234 143235 143236 143237 143238 143239 143240 143241 143242 143243 143244 | ** (17d2) DISTINCT ** (17e) the subquery may not contain window functions, and ** (17f) the subquery must not be the RHS of a LEFT JOIN. ** (17g) either the subquery is the first element of the outer ** query or there are no RIGHT or FULL JOINs in any arm ** of the subquery. (This is a duplicate of condition (27b).) ** (17h) The corresponding result set expressions in all arms of the ** compound must have the same affinity. ** ** The parent and sub-query may contain WHERE clauses. Subject to ** rules (11), (13) and (14), they may also contain ORDER BY, ** LIMIT and OFFSET clauses. The subquery cannot use any compound ** operator other than UNION ALL because all the other compound ** operators have an implied DISTINCT which is disallowed by ** restriction (4). |
| ︙ | ︙ | |||
143106 143107 143108 143109 143110 143111 143112 | ** be materialized. (This restriction is implemented in the calling ** routine.) ** ** (8) If the subquery is a compound that uses UNION, INTERSECT, ** or EXCEPT, then all of the result set columns for all arms of ** the compound must use the BINARY collating sequence. ** | > > > > > | > | | > > > > > > > > > | > > | > > | > > < < < | 144098 144099 144100 144101 144102 144103 144104 144105 144106 144107 144108 144109 144110 144111 144112 144113 144114 144115 144116 144117 144118 144119 144120 144121 144122 144123 144124 144125 144126 144127 144128 144129 144130 144131 144132 144133 144134 144135 144136 144137 144138 144139 144140 144141 144142 144143 144144 144145 144146 144147 144148 144149 144150 144151 144152 144153 144154 144155 144156 144157 144158 144159 144160 144161 144162 144163 144164 144165 144166 |
** be materialized. (This restriction is implemented in the calling
** routine.)
**
** (8) If the subquery is a compound that uses UNION, INTERSECT,
** or EXCEPT, then all of the result set columns for all arms of
** the compound must use the BINARY collating sequence.
**
** (9) All three of the following are true:
**
** (9a) The WHERE clause expression originates in the ON or USING clause
** of a join (either an INNER or an OUTER join), and
**
** (9b) The subquery is to the right of the ON/USING clause
**
** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING
** clause and the subquery.
**
** Without this restriction, the push-down optimization might move
** the ON/USING filter expression from the left side of a RIGHT JOIN
** over to the right side, which leads to incorrect answers. See
** also restriction (6) in sqlite3ExprIsSingleTableConstraint().
**
** (10) The inner query is not the right-hand table of a RIGHT JOIN.
**
** (11) The subquery is not a VALUES clause
**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
static int pushDownWhereTerms(
Parse *pParse, /* Parse context (for malloc() and error reporting) */
Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
Expr *pWhere, /* The WHERE clause of the outer query */
SrcList *pSrcList, /* The complete from clause of the outer query */
int iSrc /* Which FROM clause term to try to push into */
){
Expr *pNew;
SrcItem *pSrc; /* The subquery FROM term into which WHERE is pushed */
int nChng = 0;
pSrc = &pSrcList->a[iSrc];
if( pWhere==0 ) return 0;
if( pSubq->selFlags & (SF_Recursive|SF_MultiPart) ){
return 0; /* restrictions (2) and (11) */
}
if( pSrc->fg.jointype & (JT_LTORJ|JT_RIGHT) ){
return 0; /* restrictions (10) */
}
if( pSubq->pPrior ){
Select *pSel;
int notUnionAll = 0;
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
u8 op = pSel->op;
assert( op==TK_ALL || op==TK_SELECT
|| op==TK_UNION || op==TK_INTERSECT || op==TK_EXCEPT );
if( op!=TK_ALL && op!=TK_SELECT ){
notUnionAll = 1;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pSel->pWin ) return 0; /* restriction (6b) */
#endif
}
if( notUnionAll ){
/* If any of the compound arms are connected using UNION, INTERSECT,
** or EXCEPT, then we must ensure that none of the columns use a
** non-BINARY collating sequence. */
for(pSel=pSubq; pSel; pSel=pSel->pPrior){
int ii;
const ExprList *pList = pSel->pEList;
|
| ︙ | ︙ | |||
143182 143183 143184 143185 143186 143187 143188 |
}
#endif
if( pSubq->pLimit!=0 ){
return 0; /* restriction (3) */
}
while( pWhere->op==TK_AND ){
| | | > > > > > > > > > > > > > > > > > | | 144192 144193 144194 144195 144196 144197 144198 144199 144200 144201 144202 144203 144204 144205 144206 144207 144208 144209 144210 144211 144212 144213 144214 144215 144216 144217 144218 144219 144220 144221 144222 144223 144224 144225 144226 144227 144228 144229 144230 144231 144232 144233 144234 144235 144236 144237 144238 144239 144240 144241 |
}
#endif
if( pSubq->pLimit!=0 ){
return 0; /* restriction (3) */
}
while( pWhere->op==TK_AND ){
nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrcList, iSrc);
pWhere = pWhere->pLeft;
}
#if 0 /* These checks now done by sqlite3ExprIsSingleTableConstraint() */
if( ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) /* (9a) */
&& (pSrcList->a[0].fg.jointype & JT_LTORJ)!=0 /* Fast pre-test of (9c) */
){
int jj;
for(jj=0; jj<iSrc; jj++){
if( pWhere->w.iJoin==pSrcList->a[jj].iCursor ){
/* If we reach this point, both (9a) and (9b) are satisfied.
** The following loop checks (9c):
*/
for(jj++; jj<iSrc; jj++){
if( (pSrcList->a[jj].fg.jointype & JT_RIGHT)!=0 ){
return 0; /* restriction (9) */
}
}
}
}
}
if( isLeftJoin
&& (ExprHasProperty(pWhere,EP_OuterON)==0
|| pWhere->w.iJoin!=iCursor)
){
return 0; /* restriction (4) */
}
if( ExprHasProperty(pWhere,EP_OuterON)
&& pWhere->w.iJoin!=iCursor
){
return 0; /* restriction (5) */
}
#endif
if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
while( pSubq ){
SubstContext x;
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
unsetJoinExpr(pNew, -1, 1);
x.pParse = pParse;
|
| ︙ | ︙ | |||
143233 143234 143235 143236 143237 143238 143239 143240 143241 143242 143243 143244 143245 143246 |
}
pSubq = pSubq->pPrior;
}
}
return nChng;
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
** The pFunc is the only aggregate function in the query. Check to see
** if the query is a candidate for the min/max optimization.
**
** If the query is a candidate for the min/max optimization, then set
** *ppMinMax to be an ORDER BY clause to be used for the optimization
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 144260 144261 144262 144263 144264 144265 144266 144267 144268 144269 144270 144271 144272 144273 144274 144275 144276 144277 144278 144279 144280 144281 144282 144283 144284 144285 144286 144287 144288 144289 144290 144291 144292 144293 144294 144295 144296 144297 144298 144299 144300 144301 144302 144303 144304 144305 144306 144307 144308 144309 144310 144311 144312 144313 144314 144315 144316 144317 144318 144319 144320 144321 144322 144323 144324 144325 144326 144327 144328 144329 144330 144331 144332 144333 144334 144335 144336 144337 144338 144339 144340 144341 144342 144343 144344 144345 |
}
pSubq = pSubq->pPrior;
}
}
return nChng;
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
/*
** Check to see if a subquery contains result-set columns that are
** never used. If it does, change the value of those result-set columns
** to NULL so that they do not cause unnecessary work to compute.
**
** Return the number of column that were changed to NULL.
*/
static int disableUnusedSubqueryResultColumns(SrcItem *pItem){
int nCol;
Select *pSub; /* The subquery to be simplified */
Select *pX; /* For looping over compound elements of pSub */
Table *pTab; /* The table that describes the subquery */
int j; /* Column number */
int nChng = 0; /* Number of columns converted to NULL */
Bitmask colUsed; /* Columns that may not be NULLed out */
assert( pItem!=0 );
if( pItem->fg.isCorrelated || pItem->fg.isCte ){
return 0;
}
assert( pItem->pTab!=0 );
pTab = pItem->pTab;
assert( pItem->pSelect!=0 );
pSub = pItem->pSelect;
assert( pSub->pEList->nExpr==pTab->nCol );
if( (pSub->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){
testcase( pSub->selFlags & SF_Distinct );
testcase( pSub->selFlags & SF_Aggregate );
return 0;
}
for(pX=pSub; pX; pX=pX->pPrior){
if( pX->pPrior && pX->op!=TK_ALL ){
/* This optimization does not work for compound subqueries that
** use UNION, INTERSECT, or EXCEPT. Only UNION ALL is allowed. */
return 0;
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pX->pWin ){
/* This optimization does not work for subqueries that use window
** functions. */
return 0;
}
#endif
}
colUsed = pItem->colUsed;
if( pSub->pOrderBy ){
ExprList *pList = pSub->pOrderBy;
for(j=0; j<pList->nExpr; j++){
u16 iCol = pList->a[j].u.x.iOrderByCol;
if( iCol>0 ){
iCol--;
colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol);
}
}
}
nCol = pTab->nCol;
for(j=0; j<nCol; j++){
Bitmask m = j<BMS-1 ? MASKBIT(j) : TOPBIT;
if( (m & colUsed)!=0 ) continue;
for(pX=pSub; pX; pX=pX->pPrior) {
Expr *pY = pX->pEList->a[j].pExpr;
if( pY->op==TK_NULL ) continue;
pY->op = TK_NULL;
ExprClearProperty(pY, EP_Skip|EP_Unlikely);
pX->selFlags |= SF_PushDown;
nChng++;
}
}
return nChng;
}
/*
** The pFunc is the only aggregate function in the query. Check to see
** if the query is a candidate for the min/max optimization.
**
** If the query is a candidate for the min/max optimization, then set
** *ppMinMax to be an ORDER BY clause to be used for the optimization
|
| ︙ | ︙ | |||
143625 143626 143627 143628 143629 143630 143631 |
if( pFrom->fg.isIndexedBy ){
sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy);
return 2;
}
pFrom->fg.isCte = 1;
pFrom->u2.pCteUse = pCteUse;
pCteUse->nUse++;
| < < < | 144724 144725 144726 144727 144728 144729 144730 144731 144732 144733 144734 144735 144736 144737 |
if( pFrom->fg.isIndexedBy ){
sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy);
return 2;
}
pFrom->fg.isCte = 1;
pFrom->u2.pCteUse = pCteUse;
pCteUse->nUse++;
/* Check if this is a recursive CTE. */
pRecTerm = pSel = pFrom->pSelect;
bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
while( bMayRecursive && pRecTerm->op==pSel->op ){
int i;
SrcList *pSrc = pRecTerm->pSrc;
|
| ︙ | ︙ | |||
144379 144380 144381 144382 144383 144384 144385 144386 144387 |
static void optimizeAggregateUseOfIndexedExpr(
Parse *pParse, /* Parsing context */
Select *pSelect, /* The SELECT statement being processed */
AggInfo *pAggInfo, /* The aggregate info */
NameContext *pNC /* Name context used to resolve agg-func args */
){
assert( pAggInfo->iFirstReg==0 );
pAggInfo->nColumn = pAggInfo->nAccumulator;
if( ALWAYS(pAggInfo->nSortingColumn>0) ){
| > > > > | < < < | > > | 145475 145476 145477 145478 145479 145480 145481 145482 145483 145484 145485 145486 145487 145488 145489 145490 145491 145492 145493 145494 145495 145496 145497 145498 145499 |
static void optimizeAggregateUseOfIndexedExpr(
Parse *pParse, /* Parsing context */
Select *pSelect, /* The SELECT statement being processed */
AggInfo *pAggInfo, /* The aggregate info */
NameContext *pNC /* Name context used to resolve agg-func args */
){
assert( pAggInfo->iFirstReg==0 );
assert( pSelect!=0 );
assert( pSelect->pGroupBy!=0 );
pAggInfo->nColumn = pAggInfo->nAccumulator;
if( ALWAYS(pAggInfo->nSortingColumn>0) ){
int mx = pSelect->pGroupBy->nExpr - 1;
int j, k;
for(j=0; j<pAggInfo->nColumn; j++){
k = pAggInfo->aCol[j].iSorterColumn;
if( k>mx ) mx = k;
}
pAggInfo->nSortingColumn = mx+1;
}
analyzeAggFuncArgs(pAggInfo, pNC);
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x20 ){
IndexedExpr *pIEpr;
TREETRACE(0x20, pParse, pSelect,
("AggInfo (possibly) adjusted for Indexed Exprs\n"));
|
| ︙ | ︙ | |||
144420 144421 144422 144423 144424 144425 144426 | struct AggInfo_col *pCol; UNUSED_PARAMETER(pWalker); if( pExpr->pAggInfo==0 ) return WRC_Continue; if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue; if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; pAggInfo = pExpr->pAggInfo; | > | > | 145519 145520 145521 145522 145523 145524 145525 145526 145527 145528 145529 145530 145531 145532 145533 145534 145535 145536 145537 145538 145539 | struct AggInfo_col *pCol; UNUSED_PARAMETER(pWalker); if( pExpr->pAggInfo==0 ) return WRC_Continue; if( pExpr->op==TK_AGG_COLUMN ) return WRC_Continue; if( pExpr->op==TK_AGG_FUNCTION ) return WRC_Continue; if( pExpr->op==TK_IF_NULL_ROW ) return WRC_Continue; pAggInfo = pExpr->pAggInfo; if( NEVER(pExpr->iAgg>=pAggInfo->nColumn) ) return WRC_Continue; assert( pExpr->iAgg>=0 ); pCol = &pAggInfo->aCol[pExpr->iAgg]; pExpr->op = TK_AGG_COLUMN; pExpr->iTable = pCol->iTable; pExpr->iColumn = pCol->iColumn; ExprClearProperty(pExpr, EP_Skip|EP_Collate); return WRC_Prune; } /* ** Convert every pAggInfo->aFunc[].pExpr such that any node within ** those expressions that has pAppInfo set is changed into a TK_AGG_COLUMN ** opcode. |
| ︙ | ︙ | |||
144778 144779 144780 144781 144782 144783 144784 |
*/
static void agginfoFree(sqlite3 *db, AggInfo *p){
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
}
| < | 145879 145880 145881 145882 145883 145884 145885 145886 145887 145888 145889 145890 145891 145892 |
*/
static void agginfoFree(sqlite3 *db, AggInfo *p){
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
}
/*
** Attempt to transform a query of the form
**
** SELECT count(*) FROM (SELECT x FROM t1 UNION ALL SELECT y FROM t2)
**
** Into this:
**
|
| ︙ | ︙ | |||
144806 144807 144808 144809 144810 144811 144812 144813 144814 144815 144816 144817 144818 144819 144820 144821 144822 | Select *pSub, *pPrior; Expr *pExpr; Expr *pCount; sqlite3 *db; if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */ if( p->pEList->nExpr!=1 ) return 0; /* Single result column */ if( p->pWhere ) return 0; if( p->pGroupBy ) return 0; pExpr = p->pEList->a[0].pExpr; if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */ assert( ExprUseUToken(pExpr) ); if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */ assert( ExprUseXList(pExpr) ); if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */ if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */ pSub = p->pSrc->a[0].pSelect; if( pSub==0 ) return 0; /* The FROM is a subquery */ | > > > | > > | | 145906 145907 145908 145909 145910 145911 145912 145913 145914 145915 145916 145917 145918 145919 145920 145921 145922 145923 145924 145925 145926 145927 145928 145929 145930 145931 145932 145933 145934 145935 145936 145937 145938 145939 145940 145941 |
Select *pSub, *pPrior;
Expr *pExpr;
Expr *pCount;
sqlite3 *db;
if( (p->selFlags & SF_Aggregate)==0 ) return 0; /* This is an aggregate */
if( p->pEList->nExpr!=1 ) return 0; /* Single result column */
if( p->pWhere ) return 0;
if( p->pHaving ) return 0;
if( p->pGroupBy ) return 0;
if( p->pOrderBy ) return 0;
pExpr = p->pEList->a[0].pExpr;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0; /* Result is an aggregate */
assert( ExprUseUToken(pExpr) );
if( sqlite3_stricmp(pExpr->u.zToken,"count") ) return 0; /* Is count() */
assert( ExprUseXList(pExpr) );
if( pExpr->x.pList!=0 ) return 0; /* Must be count(*) */
if( p->pSrc->nSrc!=1 ) return 0; /* One table in FROM */
if( ExprHasProperty(pExpr, EP_WinFunc) ) return 0;/* Not a window function */
pSub = p->pSrc->a[0].pSelect;
if( pSub==0 ) return 0; /* The FROM is a subquery */
if( pSub->pPrior==0 ) return 0; /* Must be a compound */
if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
do{
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
if( pSub->pWhere ) return 0; /* No WHERE clause */
if( pSub->pLimit ) return 0; /* No LIMIT clause */
if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
assert( pSub->pHaving==0 ); /* Due to the previous */
pSub = pSub->pPrior; /* Repeat over compound */
}while( pSub );
/* If we reach this point then it is OK to perform the transformation */
db = pParse->db;
pCount = pExpr;
pExpr = 0;
|
| ︙ | ︙ | |||
144865 144866 144867 144868 144869 144870 144871 |
if( sqlite3TreeTrace & 0x200 ){
TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
return 1;
}
| < | 145970 145971 145972 145973 145974 145975 145976 145977 145978 145979 145980 145981 145982 145983 |
if( sqlite3TreeTrace & 0x200 ){
TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
return 1;
}
/*
** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same
** as pSrcItem but has the same alias as p0, then return true.
** Otherwise return false.
*/
static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){
|
| ︙ | ︙ | |||
144910 144911 144912 144913 144914 144915 144916 | ** requires it to be the outer loop ** (c) All of the following are true: ** (i) The subquery is the left-most subquery in the FROM clause ** (ii) There is nothing that would prevent the subquery from ** being used as the outer loop if the sqlite3WhereBegin() ** routine nominates it to that position. ** (iii) The query is not a UPDATE ... FROM | | | > > | > > > > | | | 146014 146015 146016 146017 146018 146019 146020 146021 146022 146023 146024 146025 146026 146027 146028 146029 146030 146031 146032 146033 146034 146035 146036 146037 146038 146039 146040 146041 146042 146043 146044 146045 146046 146047 146048 146049 |
** requires it to be the outer loop
** (c) All of the following are true:
** (i) The subquery is the left-most subquery in the FROM clause
** (ii) There is nothing that would prevent the subquery from
** being used as the outer loop if the sqlite3WhereBegin()
** routine nominates it to that position.
** (iii) The query is not a UPDATE ... FROM
** (2) The subquery is not a CTE that should be materialized because
** (a) the AS MATERIALIZED keyword is used, or
** (b) the CTE is used multiple times and does not have the
** NOT MATERIALIZED keyword
** (3) The subquery is not part of a left operand for a RIGHT JOIN
** (4) The SQLITE_Coroutine optimization disable flag is not set
** (5) The subquery is not self-joined
*/
static int fromClauseTermCanBeCoroutine(
Parse *pParse, /* Parsing context */
SrcList *pTabList, /* FROM clause */
int i, /* Which term of the FROM clause holds the subquery */
int selFlags /* Flags on the SELECT statement */
){
SrcItem *pItem = &pTabList->a[i];
if( pItem->fg.isCte ){
const CteUse *pCteUse = pItem->u2.pCteUse;
if( pCteUse->eM10d==M10d_Yes ) return 0; /* (2a) */
if( pCteUse->nUse>=2 && pCteUse->eM10d!=M10d_No ) return 0; /* (2b) */
}
if( pTabList->a[0].fg.jointype & JT_LTORJ ) return 0; /* (3) */
if( OptimizationDisabled(pParse->db, SQLITE_Coroutines) ) return 0; /* (4) */
if( isSelfJoinView(pTabList, pItem, i+1, pTabList->nSrc)!=0 ){
return 0; /* (5) */
}
if( i==0 ){
if( pTabList->nSrc==1 ) return 1; /* (1a) */
if( pTabList->a[1].fg.jointype & JT_CROSS ) return 1; /* (1b) */
if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */
|
| ︙ | ︙ | |||
145115 145116 145117 145118 145119 145120 145121 |
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
assert( pItem->iCursor>=0 );
unsetJoinExpr(p->pWhere, pItem->iCursor,
pTabList->a[0].fg.jointype & JT_LTORJ);
}
| | | 146225 146226 146227 146228 146229 146230 146231 146232 146233 146234 146235 146236 146237 146238 146239 |
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
assert( pItem->iCursor>=0 );
unsetJoinExpr(p->pWhere, pItem->iCursor,
pTabList->a[0].fg.jointype & JT_LTORJ);
}
/* No futher action if this term of the FROM clause is not a subquery */
if( pSub==0 ) continue;
/* Catch mismatch in the declared columns of a view and the number of
** columns in the SELECT on the RHS */
if( pTab->nCol!=pSub->pEList->nExpr ){
sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d",
pTab->nCol, pTab->zName, pSub->pEList->nExpr);
|
| ︙ | ︙ | |||
145248 145249 145250 145251 145252 145253 145254 |
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
}
| < < < | 146358 146359 146360 146361 146362 146363 146364 146365 146366 146367 146368 146369 146370 146371 146372 146373 146374 146375 146376 146377 |
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}else{
TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n"));
}
if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView)
&& countOfViewOptimization(pParse, p)
){
if( db->mallocFailed ) goto select_end;
pTabList = p->pSrc;
}
/* For each term in the FROM clause, do two things:
** (1) Authorized unreferenced tables
** (2) Generate code for all sub-queries
*/
for(i=0; i<pTabList->nSrc; i++){
SrcItem *pItem = &pTabList->a[i];
|
| ︙ | ︙ | |||
145315 145316 145317 145318 145319 145320 145321 |
/* 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))
| | > > > > > > > > > > > > > > > > | 146422 146423 146424 146425 146426 146427 146428 146429 146430 146431 146432 146433 146434 146435 146436 146437 146438 146439 146440 146441 146442 146443 146444 146445 146446 146447 146448 146449 146450 146451 146452 146453 146454 146455 146456 146457 146458 146459 146460 146461 146462 146463 146464 |
/* 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, pTabList, i)
){
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x4000 ){
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
** expressions, to avoid unneeded searching and computation.
*/
if( OptimizationEnabled(db, SQLITE_NullUnusedCols)
&& disableUnusedSubqueryResultColumns(pItem)
){
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x4000 ){
TREETRACE(0x4000,pParse,p,
("Change unused result columns to NULL for subquery %d:\n",
pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
}
zSavedAuthContext = pParse->zAuthContext;
pParse->zAuthContext = pItem->zName;
/* Generate code to implement the subquery
*/
if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){
|
| ︙ | ︙ | |||
145871 145872 145873 145874 145875 145876 145877 |
sortOut = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd);
VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
pAggInfo->useSortingIdx = 1;
}
| | | 146994 146995 146996 146997 146998 146999 147000 147001 147002 147003 147004 147005 147006 147007 147008 |
sortOut = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd);
VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
pAggInfo->useSortingIdx = 1;
}
/* If there are entries in pAgggInfo->aFunc[] that contain subexpressions
** that are indexed (and that were previously identified and tagged
** in optimizeAggregateUseOfIndexedExpr()) then those subexpressions
** must now be converted into a TK_AGG_COLUMN node so that the value
** is correctly pulled from the index rather than being recomputed. */
if( pParse->pIdxEpr ){
aggregateConvertIndexedExprRefToColumn(pAggInfo);
#if TREETRACE_ENABLED
|
| ︙ | ︙ | |||
146615 146616 146617 146618 146619 146620 146621 146622 146623 146624 146625 146626 146627 146628 |
if( !IN_RENAME_OBJECT ){
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto trigger_cleanup;
}
}
/* Do not create a trigger on a system table */
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
| > | 147738 147739 147740 147741 147742 147743 147744 147745 147746 147747 147748 147749 147750 147751 147752 |
if( !IN_RENAME_OBJECT ){
if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
assert( !db->init.busy );
sqlite3CodeVerifySchema(pParse, iDb);
VVA_ONLY( pParse->ifNotExists = 1; )
}
goto trigger_cleanup;
}
}
/* Do not create a trigger on a system table */
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
|
| ︙ | ︙ | |||
147396 147397 147398 147399 147400 147401 147402 |
sqlite3SelectPrep(pParse, &sSelect, 0);
if( pParse->nErr==0 ){
assert( db->mallocFailed==0 );
sqlite3GenerateColumnNames(pParse, &sSelect);
}
sqlite3ExprListDelete(db, sSelect.pEList);
pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
| | | 148520 148521 148522 148523 148524 148525 148526 148527 148528 148529 148530 148531 148532 148533 148534 |
sqlite3SelectPrep(pParse, &sSelect, 0);
if( pParse->nErr==0 ){
assert( db->mallocFailed==0 );
sqlite3GenerateColumnNames(pParse, &sSelect);
}
sqlite3ExprListDelete(db, sSelect.pEList);
pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
if( pParse->nErr==0 ){
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
if( pReturning->nRetCol==0 ){
pReturning->nRetCol = pNew->nExpr;
pReturning->iRetCur = pParse->nTab++;
}
sNC.pParse = pParse;
|
| ︙ | ︙ | |||
147865 147866 147867 147868 147869 147870 147871 147872 147873 147874 147875 147876 147877 147878 |
int orconf /* Default ON CONFLICT policy for trigger steps */
){
const int op = pChanges ? TK_UPDATE : TK_DELETE;
u32 mask = 0;
Trigger *p;
assert( isNew==1 || isNew==0 );
for(p=pTrigger; p; p=p->pNext){
if( p->op==op
&& (tr_tm&p->tr_tm)
&& checkColumnOverlap(p->pColumns,pChanges)
){
if( p->bReturning ){
mask = 0xffffffff;
| > > > | 148989 148990 148991 148992 148993 148994 148995 148996 148997 148998 148999 149000 149001 149002 149003 149004 149005 |
int orconf /* Default ON CONFLICT policy for trigger steps */
){
const int op = pChanges ? TK_UPDATE : TK_DELETE;
u32 mask = 0;
Trigger *p;
assert( isNew==1 || isNew==0 );
if( IsView(pTab) ){
return 0xffffffff;
}
for(p=pTrigger; p; p=p->pNext){
if( p->op==op
&& (tr_tm&p->tr_tm)
&& checkColumnOverlap(p->pColumns,pChanges)
){
if( p->bReturning ){
mask = 0xffffffff;
|
| ︙ | ︙ | |||
148299 148300 148301 148302 148303 148304 148305 |
pLimit = 0;
}
#endif
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto update_cleanup;
}
| | | 149426 149427 149428 149429 149430 149431 149432 149433 149434 149435 149436 149437 149438 149439 149440 |
pLimit = 0;
}
#endif
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto update_cleanup;
}
if( sqlite3IsReadOnly(pParse, pTab, pTrigger) ){
goto update_cleanup;
}
/* Allocate a cursors for the main database table and for all indices.
** The index cursors might not be used, but if they are used they
** need to occur right after the database cursor. So go ahead and
** allocate enough space, just in case.
|
| ︙ | ︙ | |||
148618 148619 148620 148621 148622 148623 148624 |
eOnePass = ONEPASS_SINGLE;
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
bFinishSeek = 0;
}else{
/* Begin the database scan.
**
** Do not consider a single-pass strategy for a multi-row update if
| > > > | > | < < > > | > > > > > > | 149745 149746 149747 149748 149749 149750 149751 149752 149753 149754 149755 149756 149757 149758 149759 149760 149761 149762 149763 149764 149765 149766 149767 149768 149769 149770 149771 149772 149773 149774 |
eOnePass = ONEPASS_SINGLE;
sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
bFinishSeek = 0;
}else{
/* Begin the database scan.
**
** Do not consider a single-pass strategy for a multi-row update if
** there is anything that might disrupt the cursor being used to do
** the UPDATE:
** (1) This is a nested UPDATE
** (2) There are triggers
** (3) There are FOREIGN KEY constraints
** (4) There are REPLACE conflict handlers
** (5) There are subqueries in the WHERE clause
*/
flags = WHERE_ONEPASS_DESIRED;
if( !pParse->nested
&& !pTrigger
&& !hasFK
&& !chngKey
&& !bReplace
&& (sNC.ncFlags & NC_Subquery)==0
){
flags |= WHERE_ONEPASS_MULTIROW;
}
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,0,0,flags,iIdxCur);
if( pWInfo==0 ) goto update_cleanup;
/* A one-pass strategy that might update more than one row may not
** be used if any column of the index used for the scan is being
|
| ︙ | ︙ | |||
150189 150190 150191 150192 150193 150194 150195 |
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;
| < > | 151326 151327 151328 151329 151330 151331 151332 151333 151334 151335 151336 151337 151338 151339 151340 151341 151342 151343 |
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;
if( p ){
p->pModule->xDisconnect(p);
}
sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
sqlite3DbFree(db, pVTab);
}
}
/*
** Table p is a virtual table. This function moves all elements in the
** p->u.vtab.p list to the sqlite3.pDisconnect lists of their associated
|
| ︙ | ︙ | |||
150588 150589 150590 150591 150592 150593 150594 150595 150596 150597 150598 150599 150600 150601 150602 |
assert( &db->pVtabCtx );
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
sCtx.pPrior = db->pVtabCtx;
sCtx.bDeclared = 0;
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
if( zErr==0 ){
*pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName);
| > > | 151725 151726 151727 151728 151729 151730 151731 151732 151733 151734 151735 151736 151737 151738 151739 151740 151741 |
assert( &db->pVtabCtx );
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
sCtx.pPrior = db->pVtabCtx;
sCtx.bDeclared = 0;
db->pVtabCtx = &sCtx;
pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
if( zErr==0 ){
*pzErr = sqlite3MPrintf(db, "vtable constructor failed: %s", zModuleName);
|
| ︙ | ︙ | |||
151078 151079 151080 151081 151082 151083 151084 151085 151086 151087 151088 151089 151090 151091 151092 |
xMethod = pMod->xRollbackTo;
break;
default:
xMethod = pMod->xRelease;
break;
}
if( xMethod && pVTab->iSavepoint>iSavepoint ){
rc = xMethod(pVTab->pVtab, iSavepoint);
}
sqlite3VtabUnlock(pVTab);
}
}
}
return rc;
}
| > > > | 152217 152218 152219 152220 152221 152222 152223 152224 152225 152226 152227 152228 152229 152230 152231 152232 152233 152234 |
xMethod = pMod->xRollbackTo;
break;
default:
xMethod = pMod->xRelease;
break;
}
if( xMethod && pVTab->iSavepoint>iSavepoint ){
u64 savedFlags = (db->flags & SQLITE_Defensive);
db->flags &= ~(u64)SQLITE_Defensive;
rc = xMethod(pVTab->pVtab, iSavepoint);
db->flags |= savedFlags;
}
sqlite3VtabUnlock(pVTab);
}
}
}
return rc;
}
|
| ︙ | ︙ | |||
151306 151307 151308 151309 151310 151311 151312 151313 151314 151315 151316 151317 151318 151319 |
case SQLITE_VTAB_INNOCUOUS: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_Low;
break;
}
case SQLITE_VTAB_DIRECTONLY: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_High;
break;
}
default: {
rc = SQLITE_MISUSE_BKPT;
break;
}
}
va_end(ap);
| > > > > | 152448 152449 152450 152451 152452 152453 152454 152455 152456 152457 152458 152459 152460 152461 152462 152463 152464 152465 |
case SQLITE_VTAB_INNOCUOUS: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_Low;
break;
}
case SQLITE_VTAB_DIRECTONLY: {
p->pVTable->eVtabRisk = SQLITE_VTABRISK_High;
break;
}
case SQLITE_VTAB_USES_ALL_SCHEMAS: {
p->pVTable->bAllSchemas = 1;
break;
}
default: {
rc = SQLITE_MISUSE_BKPT;
break;
}
}
va_end(ap);
|
| ︙ | ︙ | |||
152080 152081 152082 152083 152084 152085 152086 |
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
| | | | | | | 153226 153227 153228 153229 153230 153231 153232 153233 153234 153235 153236 153237 153238 153239 153240 153241 153242 153243 153244 153245 153246 153247 153248 153249 153250 153251 153252 153253 153254 153255 |
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
** was defined at compile-time. If it is not a no-op, a single OP_Explain
** opcode is added to the output to describe the table scan strategy in pLevel.
**
** If an OP_Explain opcode is added to the VM, its address is returned.
** Otherwise, if no OP_Explain is coded, zero is returned.
*/
SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
int ret = 0;
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
| ︙ | ︙ | |||
152261 152262 152263 152264 152265 152266 152267 |
*/
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
Vdbe *v, /* Vdbe to add scanstatus entry to */
SrcList *pSrclist, /* FROM clause pLvl reads data from */
WhereLevel *pLvl, /* Level to add scanstatus() entry for */
int addrExplain /* Address of OP_Explain (or 0) */
){
| > | | | | | | | | | | | | | | | | | | | > | 153407 153408 153409 153410 153411 153412 153413 153414 153415 153416 153417 153418 153419 153420 153421 153422 153423 153424 153425 153426 153427 153428 153429 153430 153431 153432 153433 153434 153435 153436 153437 153438 153439 153440 153441 153442 153443 |
*/
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
Vdbe *v, /* Vdbe to add scanstatus entry to */
SrcList *pSrclist, /* FROM clause pLvl reads data from */
WhereLevel *pLvl, /* Level to add scanstatus() entry for */
int addrExplain /* Address of OP_Explain (or 0) */
){
if( IS_STMT_SCANSTATUS( sqlite3VdbeDb(v) ) ){
const char *zObj = 0;
WhereLoop *pLoop = pLvl->pWLoop;
int wsFlags = pLoop->wsFlags;
int viaCoroutine = 0;
if( (wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
zObj = pLoop->u.btree.pIndex->zName;
}else{
zObj = pSrclist->a[pLvl->iFrom].zName;
viaCoroutine = pSrclist->a[pLvl->iFrom].fg.viaCoroutine;
}
sqlite3VdbeScanStatus(
v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
);
if( viaCoroutine==0 ){
if( (wsFlags & (WHERE_MULTI_OR|WHERE_AUTO_INDEX))==0 ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
if( wsFlags & WHERE_INDEXED ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}
}
}
#endif
/*
|
| ︙ | ︙ | |||
152458 152459 152460 152461 152462 152463 152464 152465 152466 152467 |
static Expr *removeUnindexableInClauseTerms(
Parse *pParse, /* The parsing context */
int iEq, /* Look at loop terms starting here */
WhereLoop *pLoop, /* The current loop */
Expr *pX /* The IN expression to be reduced */
){
sqlite3 *db = pParse->db;
Expr *pNew;
pNew = sqlite3ExprDup(db, pX, 0);
if( db->mallocFailed==0 ){
| > > | | | | | < | | | | > | > | | | | | | | | > | | | | | > | > | | > | | | | | | | | | | < | | | | | | | | | | | | | | | | | > | 153606 153607 153608 153609 153610 153611 153612 153613 153614 153615 153616 153617 153618 153619 153620 153621 153622 153623 153624 153625 153626 153627 153628 153629 153630 153631 153632 153633 153634 153635 153636 153637 153638 153639 153640 153641 153642 153643 153644 153645 153646 153647 153648 153649 153650 153651 153652 153653 153654 153655 153656 153657 153658 153659 153660 153661 153662 153663 153664 153665 153666 153667 153668 153669 153670 153671 153672 153673 153674 153675 153676 153677 153678 153679 153680 153681 153682 153683 153684 153685 153686 153687 153688 |
static Expr *removeUnindexableInClauseTerms(
Parse *pParse, /* The parsing context */
int iEq, /* Look at loop terms starting here */
WhereLoop *pLoop, /* The current loop */
Expr *pX /* The IN expression to be reduced */
){
sqlite3 *db = pParse->db;
Select *pSelect; /* Pointer to the SELECT on the RHS */
Expr *pNew;
pNew = sqlite3ExprDup(db, pX, 0);
if( db->mallocFailed==0 ){
for(pSelect=pNew->x.pSelect; pSelect; pSelect=pSelect->pPrior){
ExprList *pOrigRhs; /* Original unmodified RHS */
ExprList *pOrigLhs = 0; /* Original unmodified LHS */
ExprList *pRhs = 0; /* New RHS after modifications */
ExprList *pLhs = 0; /* New LHS after mods */
int i; /* Loop counter */
assert( ExprUseXSelect(pNew) );
pOrigRhs = pSelect->pEList;
assert( pNew->pLeft!=0 );
assert( ExprUseXList(pNew->pLeft) );
if( pSelect==pNew->x.pSelect ){
pOrigLhs = pNew->pLeft->x.pList;
}
for(i=iEq; i<pLoop->nLTerm; i++){
if( pLoop->aLTerm[i]->pExpr==pX ){
int iField;
assert( (pLoop->aLTerm[i]->eOperator & (WO_OR|WO_AND))==0 );
iField = pLoop->aLTerm[i]->u.x.iField - 1;
if( pOrigRhs->a[iField].pExpr==0 ) continue; /* Duplicate PK column */
pRhs = sqlite3ExprListAppend(pParse, pRhs, pOrigRhs->a[iField].pExpr);
pOrigRhs->a[iField].pExpr = 0;
if( pOrigLhs ){
assert( pOrigLhs->a[iField].pExpr!=0 );
pLhs = sqlite3ExprListAppend(pParse,pLhs,pOrigLhs->a[iField].pExpr);
pOrigLhs->a[iField].pExpr = 0;
}
}
}
sqlite3ExprListDelete(db, pOrigRhs);
if( pOrigLhs ){
sqlite3ExprListDelete(db, pOrigLhs);
pNew->pLeft->x.pList = pLhs;
}
pSelect->pEList = pRhs;
if( pLhs && pLhs->nExpr==1 ){
/* Take care here not to generate a TK_VECTOR containing only a
** single value. Since the parser never creates such a vector, some
** of the subroutines do not handle this case. */
Expr *p = pLhs->a[0].pExpr;
pLhs->a[0].pExpr = 0;
sqlite3ExprDelete(db, pNew->pLeft);
pNew->pLeft = p;
}
if( pSelect->pOrderBy ){
/* If the SELECT statement has an ORDER BY clause, zero the
** iOrderByCol variables. These are set to non-zero when an
** ORDER BY term exactly matches one of the terms of the
** result-set. Since the result-set of the SELECT statement may
** have been modified or reordered, these variables are no longer
** set correctly. Since setting them is just an optimization,
** it's easiest just to zero them here. */
ExprList *pOrderBy = pSelect->pOrderBy;
for(i=0; i<pOrderBy->nExpr; i++){
pOrderBy->a[i].u.x.iOrderByCol = 0;
}
}
#if 0
printf("For indexing, change the IN expr:\n");
sqlite3TreeViewExpr(0, pX, 0);
printf("Into:\n");
sqlite3TreeViewExpr(0, pNew, 0);
#endif
}
}
return pNew;
}
/*
** Generate code for a single equality term of the WHERE clause. An equality
|
| ︙ | ︙ | |||
152971 152972 152973 152974 152975 152976 152977 152978 152979 152980 |
** Also, if the node is a TK_COLUMN that does access the table idenified
** by pCCurHint.iTabCur, and an index is being used (which we will
** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
** an access of the index rather than the original table.
*/
static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
int rc = WRC_Continue;
struct CCurHint *pHint = pWalker->u.pCCurHint;
if( pExpr->op==TK_COLUMN ){
if( pExpr->iTable!=pHint->iTabCur ){
| > | | | | | | < | > > | < | | 154126 154127 154128 154129 154130 154131 154132 154133 154134 154135 154136 154137 154138 154139 154140 154141 154142 154143 154144 154145 154146 154147 154148 154149 154150 154151 154152 154153 154154 154155 154156 154157 154158 154159 154160 154161 |
** Also, if the node is a TK_COLUMN that does access the table idenified
** by pCCurHint.iTabCur, and an index is being used (which we will
** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
** an access of the index rather than the original table.
*/
static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
int rc = WRC_Continue;
int reg;
struct CCurHint *pHint = pWalker->u.pCCurHint;
if( pExpr->op==TK_COLUMN ){
if( pExpr->iTable!=pHint->iTabCur ){
reg = ++pWalker->pParse->nMem; /* Register for column value */
reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg);
pExpr->op = TK_REGISTER;
pExpr->iTable = reg;
}else if( pHint->pIdx!=0 ){
pExpr->iTable = pHint->iIdxCur;
pExpr->iColumn = sqlite3TableColumnToIndex(pHint->pIdx, pExpr->iColumn);
assert( pExpr->iColumn>=0 );
}
}else if( pExpr->pAggInfo ){
rc = WRC_Prune;
reg = ++pWalker->pParse->nMem; /* Register for column value */
reg = sqlite3ExprCodeTarget(pWalker->pParse, pExpr, reg);
pExpr->op = TK_REGISTER;
pExpr->iTable = reg;
}else if( pExpr->op==TK_TRUEFALSE ){
/* Do not walk disabled expressions. tag-20230504-1 */
return WRC_Prune;
}
return rc;
}
/*
** Insert an OP_CursorHint instruction if it is appropriate to do so.
*/
|
| ︙ | ︙ | |||
153093 153094 153095 153096 153097 153098 153099 |
}
/* If we survive all prior tests, that means this term is worth hinting */
pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
}
if( pExpr!=0 ){
sWalker.xExprCallback = codeCursorHintFixExpr;
| | | 154249 154250 154251 154252 154253 154254 154255 154256 154257 154258 154259 154260 154261 154262 154263 |
}
/* If we survive all prior tests, that means this term is worth hinting */
pExpr = sqlite3ExprAnd(pParse, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
}
if( pExpr!=0 ){
sWalker.xExprCallback = codeCursorHintFixExpr;
if( pParse->nErr==0 ) sqlite3WalkExpr(&sWalker, pExpr);
sqlite3VdbeAddOp4(v, OP_CursorHint,
(sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
(const char*)pExpr, P4_EXPR);
}
}
#else
# define codeCursorHint(A,B,C,D) /* No-op */
|
| ︙ | ︙ | |||
153887 153888 153889 153890 153891 153892 153893 |
** should we try before giving up and going with a seek. The cost
** of a seek is proportional to the logarithm of the of the number
** of entries in the tree, so basing the number of steps to try
** on the estimated number of rows in the btree seems like a good
** guess. */
addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan,
(pIdx->aiRowLogEst[0]+9)/10);
| | | 155043 155044 155045 155046 155047 155048 155049 155050 155051 155052 155053 155054 155055 155056 155057 |
** should we try before giving up and going with a seek. The cost
** of a seek is proportional to the logarithm of the of the number
** of entries in the tree, so basing the number of steps to try
** on the estimated number of rows in the btree seems like a good
** guess. */
addrSeekScan = sqlite3VdbeAddOp1(v, OP_SeekScan,
(pIdx->aiRowLogEst[0]+9)/10);
if( pRangeStart || pRangeEnd ){
sqlite3VdbeChangeP5(v, 1);
sqlite3VdbeChangeP2(v, addrSeekScan, sqlite3VdbeCurrentAddr(v)+1);
addrSeekScan = 0;
}
VdbeCoverage(v);
}
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
|
| ︙ | ︙ | |||
153928 153929 153930 153931 153932 153933 153934 |
/* Load the value for the inequality constraint at the end of the
** range (if any).
*/
nConstraint = nEq;
assert( pLevel->p2==0 );
if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
| | < < < < < < < < < | 155084 155085 155086 155087 155088 155089 155090 155091 155092 155093 155094 155095 155096 155097 155098 |
/* Load the value for the inequality constraint at the end of the
** range (if any).
*/
nConstraint = nEq;
assert( pLevel->p2==0 );
if( pRangeEnd ){
Expr *pRight = pRangeEnd->pExpr->pRight;
assert( addrSeekScan==0 );
codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
if( (pRangeEnd->wtFlags & TERM_VNULL)==0
&& sqlite3ExprCanBeNull(pRight)
){
sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
VdbeCoverage(v);
|
| ︙ | ︙ | |||
153971 153972 153973 153974 153975 153976 153977 |
}
nConstraint++;
}
if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff);
if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff);
/* Top of the loop body */
| | | 155118 155119 155120 155121 155122 155123 155124 155125 155126 155127 155128 155129 155130 155131 155132 |
}
nConstraint++;
}
if( zStartAff ) sqlite3DbNNFreeNN(db, zStartAff);
if( zEndAff ) sqlite3DbNNFreeNN(db, zEndAff);
/* Top of the loop body */
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
/* Check if the index cursor is past the end of the range. */
if( nConstraint ){
if( regBignull ){
/* Except, skip the end-of-range check while doing the NULL-scan */
sqlite3VdbeAddOp2(v, OP_IfNot, regBignull, sqlite3VdbeCurrentAddr(v)+3);
VdbeComment((v, "If NULL-scan 2nd pass"));
|
| ︙ | ︙ | |||
155731 155732 155733 155734 155735 155736 155737 | ** ** If pExpr is a TK_COLUMN column reference, then this routine always returns ** true even if that particular column is not indexed, because the column ** might be added to an automatic index later. */ static SQLITE_NOINLINE int exprMightBeIndexed2( SrcList *pFrom, /* The FROM clause */ | < | > | | | | | | | | > > | | | | | | > < > > < | | > > > | > > > > | 156878 156879 156880 156881 156882 156883 156884 156885 156886 156887 156888 156889 156890 156891 156892 156893 156894 156895 156896 156897 156898 156899 156900 156901 156902 156903 156904 156905 156906 156907 156908 156909 156910 156911 156912 156913 156914 156915 156916 156917 156918 156919 156920 156921 156922 156923 156924 156925 156926 156927 156928 156929 156930 156931 156932 156933 156934 156935 156936 156937 156938 156939 156940 156941 156942 156943 156944 156945 156946 156947 156948 156949 156950 156951 |
**
** If pExpr is a TK_COLUMN column reference, then this routine always returns
** true even if that particular column is not indexed, because the column
** might be added to an automatic index later.
*/
static SQLITE_NOINLINE int exprMightBeIndexed2(
SrcList *pFrom, /* The FROM clause */
int *aiCurCol, /* Write the referenced table cursor and column here */
Expr *pExpr, /* An operand of a comparison operator */
int j /* Start looking with the j-th pFrom entry */
){
Index *pIdx;
int i;
int iCur;
do{
iCur = pFrom->a[j].iCursor;
for(pIdx=pFrom->a[j].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->aColExpr==0 ) continue;
for(i=0; i<pIdx->nKeyCol; i++){
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
assert( pIdx->bHasExpr );
if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
&& pExpr->op!=TK_STRING
){
aiCurCol[0] = iCur;
aiCurCol[1] = XN_EXPR;
return 1;
}
}
}
}while( ++j < pFrom->nSrc );
return 0;
}
static int exprMightBeIndexed(
SrcList *pFrom, /* The FROM clause */
int *aiCurCol, /* Write the referenced table cursor & column here */
Expr *pExpr, /* An operand of a comparison operator */
int op /* The specific comparison operator */
){
int i;
/* If this expression is a vector to the left or right of a
** inequality constraint (>, <, >= or <=), perform the processing
** on the first element of the vector. */
assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
assert( op<=TK_GE );
if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
assert( ExprUseXList(pExpr) );
pExpr = pExpr->x.pList->a[0].pExpr;
}
if( pExpr->op==TK_COLUMN ){
aiCurCol[0] = pExpr->iTable;
aiCurCol[1] = pExpr->iColumn;
return 1;
}
for(i=0; i<pFrom->nSrc; i++){
Index *pIdx;
for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->aColExpr ){
return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i);
}
}
}
return 0;
}
/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the
** subexpression and populate all the other fields of the WhereTerm
|
| ︙ | ︙ | |||
155906 155907 155908 155909 155910 155911 155912 |
if( pTerm->u.x.iField>0 ){
assert( op==TK_IN );
assert( pLeft->op==TK_VECTOR );
assert( ExprUseXList(pLeft) );
pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr;
}
| | | | 157063 157064 157065 157066 157067 157068 157069 157070 157071 157072 157073 157074 157075 157076 157077 157078 157079 157080 157081 157082 157083 157084 157085 |
if( pTerm->u.x.iField>0 ){
assert( op==TK_IN );
assert( pLeft->op==TK_VECTOR );
assert( ExprUseXList(pLeft) );
pLeft = pLeft->x.pList->a[pTerm->u.x.iField-1].pExpr;
}
if( exprMightBeIndexed(pSrc, aiCurCol, pLeft, op) ){
pTerm->leftCursor = aiCurCol[0];
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
pTerm->u.x.leftColumn = aiCurCol[1];
pTerm->eOperator = operatorMask(op) & opMask;
}
if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
if( pRight
&& exprMightBeIndexed(pSrc, aiCurCol, pRight, op)
&& !ExprHasProperty(pRight, EP_FixedCol)
){
WhereTerm *pNew;
Expr *pDup;
u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
assert( pTerm->u.x.iField==0 );
if( pTerm->leftCursor>=0 ){
|
| ︙ | ︙ | |||
155958 155959 155960 155961 155962 155963 155964 |
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
}else
if( op==TK_ISNULL
&& !ExprHasProperty(pExpr,EP_OuterON)
&& 0==sqlite3ExprCanBeNull(pLeft)
){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
| | | 157115 157116 157117 157118 157119 157120 157121 157122 157123 157124 157125 157126 157127 157128 157129 |
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
}else
if( op==TK_ISNULL
&& !ExprHasProperty(pExpr,EP_OuterON)
&& 0==sqlite3ExprCanBeNull(pLeft)
){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
pExpr->op = TK_TRUEFALSE; /* See tag-20230504-1 */
pExpr->u.zToken = "false";
ExprSetProperty(pExpr, EP_IsFalse);
pTerm->prereqAll = 0;
pTerm->eOperator = 0;
}
}
|
| ︙ | ︙ | |||
156125 156126 156127 156128 156129 156130 156131 |
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
pStr1);
transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
| < > | 157282 157283 157284 157285 157286 157287 157288 157289 157290 157291 157292 157293 157294 157295 157296 157297 157298 157299 157300 157301 157302 157303 |
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
pStr1);
transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
pStr2);
transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags);
testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew1);
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
if( isComplete ){
markTermAsChild(pWC, idxNew1, idxTerm);
markTermAsChild(pWC, idxNew2, idxTerm);
}
}
|
| ︙ | ︙ | |||
156189 156190 156191 156192 156193 156194 156195 | ** This only works if the RHS is a simple SELECT (not a compound) that does ** not use window functions. */ else if( pExpr->op==TK_IN && pTerm->u.x.iField==0 && pExpr->pLeft->op==TK_VECTOR && ALWAYS( ExprUseXSelect(pExpr) ) | | | 157346 157347 157348 157349 157350 157351 157352 157353 157354 157355 157356 157357 157358 157359 157360 |
** This only works if the RHS is a simple SELECT (not a compound) that does
** not use window functions.
*/
else if( pExpr->op==TK_IN
&& pTerm->u.x.iField==0
&& pExpr->pLeft->op==TK_VECTOR
&& ALWAYS( ExprUseXSelect(pExpr) )
&& (pExpr->x.pSelect->pPrior==0 || (pExpr->x.pSelect->selFlags & SF_Values))
#ifndef SQLITE_OMIT_WINDOWFUNC
&& pExpr->x.pSelect->pWin==0
#endif
&& pWC->op==TK_AND
){
int i;
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
|
| ︙ | ︙ | |||
156603 156604 156605 156606 156607 156608 156609 |
pColRef->iColumn = k++;
assert( ExprUseYTab(pColRef) );
pColRef->y.pTab = pTab;
pItem->colUsed |= sqlite3ExprColUsed(pColRef);
pRhs = sqlite3PExpr(pParse, TK_UPLUS,
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
| | > > > | 157760 157761 157762 157763 157764 157765 157766 157767 157768 157769 157770 157771 157772 157773 157774 157775 157776 157777 157778 157779 |
pColRef->iColumn = k++;
assert( ExprUseYTab(pColRef) );
pColRef->y.pTab = pTab;
pItem->colUsed |= sqlite3ExprColUsed(pColRef);
pRhs = sqlite3PExpr(pParse, TK_UPLUS,
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
if( pItem->fg.jointype & (JT_LEFT|JT_RIGHT) ){
testcase( pItem->fg.jointype & JT_LEFT ); /* testtag-20230227a */
testcase( pItem->fg.jointype & JT_RIGHT ); /* testtag-20230227b */
joinType = EP_OuterON;
}else{
testcase( pItem->fg.jointype & JT_LTORJ ); /* testtag-20230227c */
joinType = EP_InnerON;
}
sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType);
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
}
}
|
| ︙ | ︙ | |||
157448 157449 157450 157451 157452 157453 157454 |
*/
static void explainAutomaticIndex(
Parse *pParse,
Index *pIdx, /* Automatic index to explain */
int bPartial, /* True if pIdx is a partial index */
int *pAddrExplain /* OUT: Address of OP_Explain */
){
| | | 158608 158609 158610 158611 158612 158613 158614 158615 158616 158617 158618 158619 158620 158621 158622 |
*/
static void explainAutomaticIndex(
Parse *pParse,
Index *pIdx, /* Automatic index to explain */
int bPartial, /* True if pIdx is a partial index */
int *pAddrExplain /* OUT: Address of OP_Explain */
){
if( IS_STMT_SCANSTATUS(pParse->db) && pParse->explain!=2 ){
Table *pTab = pIdx->pTable;
const char *zSep = "";
char *zText = 0;
int ii = 0;
sqlite3_str *pStr = sqlite3_str_new(pParse->db);
sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName);
assert( pIdx->nColumn>1 );
|
| ︙ | ︙ | |||
157487 157488 157489 157490 157491 157492 157493 | /* ** Generate code to construct the Index object for an automatic index ** and to set up the WhereLevel object pLevel so that the code generator ** makes use of the automatic index. */ static SQLITE_NOINLINE void constructAutomaticIndex( Parse *pParse, /* The parsing context */ | | < | > > | > > | | 158647 158648 158649 158650 158651 158652 158653 158654 158655 158656 158657 158658 158659 158660 158661 158662 158663 158664 158665 158666 158667 158668 158669 158670 158671 158672 158673 158674 158675 158676 158677 158678 158679 158680 158681 158682 158683 158684 158685 158686 158687 158688 158689 158690 158691 158692 158693 158694 158695 158696 158697 158698 158699 158700 158701 158702 158703 158704 158705 158706 158707 158708 158709 158710 158711 158712 158713 158714 158715 |
/*
** Generate code to construct the Index object for an automatic index
** and to set up the WhereLevel object pLevel so that the code generator
** makes use of the automatic index.
*/
static SQLITE_NOINLINE void constructAutomaticIndex(
Parse *pParse, /* The parsing context */
WhereClause *pWC, /* The WHERE clause */
const Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
int nKeyCol; /* Number of columns in the constructed index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
WhereTerm *pWCEnd; /* End of pWC->a[] */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
int addrTop; /* Top of the index fill loop */
int regRecord; /* Register holding an index record */
int n; /* Column counter */
int i; /* Loop counter */
int mxBitCol; /* Maximum column in pSrc->colUsed */
CollSeq *pColl; /* Collating sequence to on a column */
WhereLoop *pLoop; /* The Loop object */
char *zNotUsed; /* Extra space on the end of pIdx */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
u8 sentWarning = 0; /* True if a warning has been issued */
u8 useBloomFilter = 0; /* True to also add a Bloom filter */
Expr *pPartial = 0; /* Partial Index Expression */
int iContinue = 0; /* Jump here to skip excluded rows */
SrcList *pTabList; /* The complete FROM clause */
SrcItem *pSrc; /* The FROM clause term to get the next index */
int addrCounter = 0; /* Address where integer counter is initialized */
int regBase; /* Array of registers where record is assembled */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
int addrExp = 0; /* Address of OP_Explain */
#endif
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
nKeyCol = 0;
pTabList = pWC->pWInfo->pTabList;
pSrc = &pTabList->a[pLevel->iFrom];
pTable = pSrc->pTab;
pWCEnd = &pWC->a[pWC->nTerm];
pLoop = pLevel->pWLoop;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
/* Make the automatic index a partial index if there are terms in the
** WHERE clause (or the ON clause of a LEFT join) that constrain which
** rows of the target table (pSrc) that can be used. */
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
&& sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
){
pPartial = sqlite3ExprAnd(pParse, pPartial,
sqlite3ExprDup(pParse->db, pExpr, 0));
}
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol;
Bitmask cMask;
|
| ︙ | ︙ | |||
157579 157580 157581 157582 157583 157584 157585 | ** covering index. A "covering index" is an index that contains all ** columns that are needed by the query. With a covering index, the ** original table never needs to be accessed. Automatic indices must ** be a covering index because the index will not be updated if the ** original table changes and the index and table cannot both be used ** if they go out of sync. */ | > > > | > | 158742 158743 158744 158745 158746 158747 158748 158749 158750 158751 158752 158753 158754 158755 158756 158757 158758 158759 158760 |
** covering index. A "covering index" is an index that contains all
** columns that are needed by the query. With a covering index, the
** original table never needs to be accessed. Automatic indices must
** be a covering index because the index will not be updated if the
** original table changes and the index and table cannot both be used
** if they go out of sync.
*/
if( IsView(pTable) ){
extraCols = ALLBITS;
}else{
extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
}
mxBitCol = MIN(BMS-1,pTable->nCol);
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
for(i=0; i<mxBitCol; i++){
if( extraCols & MASKBIT(i) ) nKeyCol++;
}
if( pSrc->colUsed & MASKBIT(BMS-1) ){
|
| ︙ | ︙ | |||
157615 157616 157617 157618 157619 157620 157621 157622 157623 157624 157625 157626 157627 157628 |
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.x.leftColumn;
pColl = sqlite3ExprCompareCollSeq(pParse, pX);
assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */
pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY;
n++;
}
}
}
assert( (u32)n==pLoop->u.btree.nEq );
/* Add additional columns needed to make the automatic index into
** a covering index */
| > > > > > > > > > > | 158782 158783 158784 158785 158786 158787 158788 158789 158790 158791 158792 158793 158794 158795 158796 158797 158798 158799 158800 158801 158802 158803 158804 158805 |
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.x.leftColumn;
pColl = sqlite3ExprCompareCollSeq(pParse, pX);
assert( pColl!=0 || pParse->nErr>0 ); /* TH3 collate01.800 */
pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY;
n++;
if( ALWAYS(pX->pLeft!=0)
&& sqlite3ExprAffinity(pX->pLeft)!=SQLITE_AFF_TEXT
){
/* TUNING: only use a Bloom filter on an automatic index
** if one or more key columns has the ability to hold numeric
** values, since strings all have the same hash in the Bloom
** filter implementation and hence a Bloom filter on a text column
** is not usually helpful. */
useBloomFilter = 1;
}
}
}
}
assert( (u32)n==pLoop->u.btree.nEq );
/* Add additional columns needed to make the automatic index into
** a covering index */
|
| ︙ | ︙ | |||
157647 157648 157649 157650 157651 157652 157653 | /* Create the automatic index */ explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp); assert( pLevel->iIdxCur>=0 ); pLevel->iIdxCur = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); | | > | | | | | | | | | 158824 158825 158826 158827 158828 158829 158830 158831 158832 158833 158834 158835 158836 158837 158838 158839 158840 158841 158842 158843 158844 158845 158846 158847 158848 158849 158850 158851 158852 158853 158854 158855 158856 158857 158858 158859 158860 158861 158862 158863 158864 158865 158866 158867 158868 158869 158870 158871 158872 158873 158874 158875 158876 158877 158878 158879 158880 |
/* Create the automatic index */
explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp);
assert( pLevel->iIdxCur>=0 );
pLevel->iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "for %s", pTable->zName));
if( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) && useBloomFilter ){
sqlite3WhereExplainBloomFilter(pParse, pWC->pWInfo, pLevel);
pLevel->regFilter = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Blob, 10000, pLevel->regFilter);
}
/* Fill the automatic index with content */
assert( pSrc == &pWC->pWInfo->pTabList->a[pLevel->iFrom] );
if( pSrc->fg.viaCoroutine ){
int regYield = pSrc->regReturn;
addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSrc->addrFillSub);
addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
VdbeCoverage(v);
VdbeComment((v, "next row of %s", pSrc->pTab->zName));
}else{
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
}
if( pPartial ){
iContinue = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL);
pLoop->wsFlags |= WHERE_PARTIALIDX;
}
regRecord = sqlite3GetTempReg(pParse);
regBase = sqlite3GenerateIndexKey(
pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
);
if( pLevel->regFilter ){
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0,
regBase, pLoop->u.btree.nEq);
}
sqlite3VdbeScanStatusCounters(v, addrExp, addrExp, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
if( pSrc->fg.viaCoroutine ){
sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
testcase( pParse->db->mallocFailed );
assert( pLevel->iIdxCur>0 );
translateColumnToCopy(pParse, addrTop, pLevel->iTabCur,
pSrc->regResult, pLevel->iIdxCur);
sqlite3VdbeGoto(v, addrTop);
pSrc->fg.viaCoroutine = 0;
}else{
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
}
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
|
| ︙ | ︙ | |||
157740 157741 157742 157743 157744 157745 157746 157747 157748 157749 157750 157751 157752 157753 157754 157755 157756 157757 157758 157759 157760 157761 157762 157763 157764 157765 157766 157767 157768 157769 |
int addrCont; /* Jump here to skip a row */
const WhereTerm *pTerm; /* For looping over WHERE clause terms */
const WhereTerm *pWCEnd; /* Last WHERE clause term */
Parse *pParse = pWInfo->pParse; /* Parsing context */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
int iCur; /* Cursor for table getting the filter */
assert( pLoop!=0 );
assert( v!=0 );
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
do{
const SrcItem *pItem;
const Table *pTab;
u64 sz;
sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
addrCont = sqlite3VdbeMakeLabel(pParse);
iCur = pLevel->iTabCur;
pLevel->regFilter = ++pParse->nMem;
/* The Bloom filter is a Blob held in a register. Initialize it
** to zero-filled blob of at least 80K bits, but maybe more if the
** estimated size of the table is larger. We could actually
** measure the size of the table at run-time using OP_Count with
** P3==1 and use that value to initialize the blob. But that makes
** testing complicated. By basing the blob size on the value in the
** sqlite_stat1 table, testing is much easier.
*/
| > > > > > > | > > | < | | 158918 158919 158920 158921 158922 158923 158924 158925 158926 158927 158928 158929 158930 158931 158932 158933 158934 158935 158936 158937 158938 158939 158940 158941 158942 158943 158944 158945 158946 158947 158948 158949 158950 158951 158952 158953 158954 158955 158956 158957 158958 158959 158960 158961 158962 158963 158964 158965 158966 158967 158968 158969 158970 158971 158972 158973 158974 158975 158976 158977 158978 158979 158980 158981 158982 158983 158984 158985 158986 158987 158988 158989 158990 158991 158992 158993 158994 158995 158996 158997 |
int addrCont; /* Jump here to skip a row */
const WhereTerm *pTerm; /* For looping over WHERE clause terms */
const WhereTerm *pWCEnd; /* Last WHERE clause term */
Parse *pParse = pWInfo->pParse; /* Parsing context */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
int iCur; /* Cursor for table getting the filter */
IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
saved_pIdxEpr = pParse->pIdxEpr;
pParse->pIdxEpr = 0;
assert( pLoop!=0 );
assert( v!=0 );
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
do{
const SrcList *pTabList;
const SrcItem *pItem;
const Table *pTab;
u64 sz;
int iSrc;
sqlite3WhereExplainBloomFilter(pParse, pWInfo, pLevel);
addrCont = sqlite3VdbeMakeLabel(pParse);
iCur = pLevel->iTabCur;
pLevel->regFilter = ++pParse->nMem;
/* The Bloom filter is a Blob held in a register. Initialize it
** to zero-filled blob of at least 80K bits, but maybe more if the
** estimated size of the table is larger. We could actually
** measure the size of the table at run-time using OP_Count with
** P3==1 and use that value to initialize the blob. But that makes
** testing complicated. By basing the blob size on the value in the
** sqlite_stat1 table, testing is much easier.
*/
pTabList = pWInfo->pTabList;
iSrc = pLevel->iFrom;
pItem = &pTabList->a[iSrc];
assert( pItem!=0 );
pTab = pItem->pTab;
assert( pTab!=0 );
sz = sqlite3LogEstToInt(pTab->nRowLogEst);
if( sz<10000 ){
sz = 10000;
}else if( sz>10000000 ){
sz = 10000000;
}
sqlite3VdbeAddOp2(v, OP_Blob, (int)sz, pLevel->regFilter);
addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
pWCEnd = &pWInfo->sWC.a[pWInfo->sWC.nTerm];
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
&& sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
){
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
}
}
if( pLoop->wsFlags & WHERE_IPK ){
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1);
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, 1);
sqlite3ReleaseTempReg(pParse, r1);
}else{
Index *pIdx = pLoop->u.btree.pIndex;
int n = pLoop->u.btree.nEq;
int r1 = sqlite3GetTempRange(pParse, n);
int jj;
for(jj=0; jj<n; jj++){
assert( pIdx->pTable==pItem->pTab );
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iCur, jj, r1+jj);
}
sqlite3VdbeAddOp4Int(v, OP_FilterAdd, pLevel->regFilter, 0, r1, n);
sqlite3ReleaseTempRange(pParse, r1, n);
}
sqlite3VdbeResolveLabel(v, addrCont);
sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
VdbeCoverage(v);
|
| ︙ | ︙ | |||
157829 157830 157831 157832 157833 157834 157835 157836 157837 157838 157839 157840 157841 157842 |
** not able to do early evaluation of bloom filters that make use of
** the IN operator */
break;
}
}
}while( iLevel < pWInfo->nLevel );
sqlite3VdbeJumpHere(v, addrOnce);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Allocate and populate an sqlite3_index_info structure. It is the
** responsibility of the caller to eventually release the structure
| > | 159014 159015 159016 159017 159018 159019 159020 159021 159022 159023 159024 159025 159026 159027 159028 |
** not able to do early evaluation of bloom filters that make use of
** the IN operator */
break;
}
}
}while( iLevel < pWInfo->nLevel );
sqlite3VdbeJumpHere(v, addrOnce);
pParse->pIdxEpr = saved_pIdxEpr;
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Allocate and populate an sqlite3_index_info structure. It is the
** responsibility of the caller to eventually release the structure
|
| ︙ | ︙ | |||
158084 158085 158086 158087 158088 158089 158090 158091 158092 158093 158094 158095 158096 158097 |
sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
}else{
sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg);
}
}
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
return rc;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
#ifdef SQLITE_ENABLE_STAT4
| > > > | 159270 159271 159272 159273 159274 159275 159276 159277 159278 159279 159280 159281 159282 159283 159284 159285 159286 |
sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
}else{
sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg);
}
}
if( pTab->u.vtab.p->bAllSchemas ){
sqlite3VtabUsesAllSchemas(pParse);
}
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
return rc;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
#ifdef SQLITE_ENABLE_STAT4
|
| ︙ | ︙ | |||
158127 158128 158129 158130 158131 158132 158133 158134 158135 158136 158137 158138 158139 158140 | #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif assert( pRec!=0 ); assert( pIdx->nSample>0 ); assert( pRec->nField>0 ); /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search ** is simply the aSample[] array. If the samples in aSample[] contain more ** than one fields, all fields following the first are ignored. ** ** If pRec contains N fields, where N is more than one, then as well as the | > | 159316 159317 159318 159319 159320 159321 159322 159323 159324 159325 159326 159327 159328 159329 159330 | #ifndef SQLITE_DEBUG UNUSED_PARAMETER( pParse ); #endif assert( pRec!=0 ); assert( pIdx->nSample>0 ); assert( pRec->nField>0 ); /* Do a binary search to find the first sample greater than or equal ** to pRec. If pRec contains a single field, the set of samples to search ** is simply the aSample[] array. If the samples in aSample[] contain more ** than one fields, all fields following the first are ignored. ** ** If pRec contains N fields, where N is more than one, then as well as the |
| ︙ | ︙ | |||
158172 158173 158174 158175 158176 158177 158178 | ** equal to the previous sample in the array. For example, in the above, ** sample 2 is the first sample of a block of N samples, so at first it ** appears that it should be 1 field in size. However, that would make it ** smaller than sample 1, so the binary search would not work. As a result, ** it is extended to two fields. The duplicates that this creates do not ** cause any problems. */ | > > > > > | | 159362 159363 159364 159365 159366 159367 159368 159369 159370 159371 159372 159373 159374 159375 159376 159377 159378 159379 159380 159381 |
** equal to the previous sample in the array. For example, in the above,
** sample 2 is the first sample of a block of N samples, so at first it
** appears that it should be 1 field in size. However, that would make it
** smaller than sample 1, so the binary search would not work. As a result,
** it is extended to two fields. The duplicates that this creates do not
** cause any problems.
*/
if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
nField = pIdx->nKeyCol;
}else{
nField = pIdx->nColumn;
}
nField = MIN(pRec->nField, nField);
iCol = 0;
iSample = pIdx->nSample * nField;
do{
int iSamp; /* Index in aSample[] of test sample */
int n; /* Number of fields in test sample */
iTest = (iMin+iSample)/2;
|
| ︙ | ︙ | |||
158238 158239 158240 158241 158242 158243 158244 |
/* if i==0 and iCol==0, then record pRec is smaller than all samples
** in the aSample[] array. Otherwise, if (iCol>0) then pRec must
** be greater than or equal to the (iCol) field prefix of sample i.
** If (i>0), then pRec must also be greater than sample (i-1). */
if( iCol>0 ){
pRec->nField = iCol;
assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0
| | | | 159433 159434 159435 159436 159437 159438 159439 159440 159441 159442 159443 159444 159445 159446 159447 159448 159449 159450 159451 159452 |
/* if i==0 and iCol==0, then record pRec is smaller than all samples
** in the aSample[] array. Otherwise, if (iCol>0) then pRec must
** be greater than or equal to the (iCol) field prefix of sample i.
** If (i>0), then pRec must also be greater than sample (i-1). */
if( iCol>0 ){
pRec->nField = iCol;
assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0
|| pParse->db->mallocFailed || CORRUPT_DB );
}
if( i>0 ){
pRec->nField = nField;
assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
|| pParse->db->mallocFailed || CORRUPT_DB );
}
}
}
#endif /* ifdef SQLITE_DEBUG */
if( res==0 ){
/* Record pRec is equal to sample i */
|
| ︙ | ︙ | |||
158608 158609 158610 158611 158612 158613 158614 |
}
}
#else
UNUSED_PARAMETER(pParse);
UNUSED_PARAMETER(pBuilder);
assert( pLower || pUpper );
#endif
| | | 159803 159804 159805 159806 159807 159808 159809 159810 159811 159812 159813 159814 159815 159816 159817 |
}
}
#else
UNUSED_PARAMETER(pParse);
UNUSED_PARAMETER(pBuilder);
assert( pLower || pUpper );
#endif
assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 || pParse->nErr>0 );
nNew = whereRangeAdjust(pLower, nOut);
nNew = whereRangeAdjust(pUpper, nNew);
/* TUNING: If there is both an upper and lower limit and neither limit
** has an application-defined likelihood(), assume the range is
** reduced by an additional 75%. This means that, by default, an open-ended
** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
|
| ︙ | ︙ | |||
158740 158741 158742 158743 158744 158745 158746 |
nEst = nRow0;
rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
nRowEst += nEst;
pBuilder->nRecValid = nRecValid;
}
if( rc==SQLITE_OK ){
| | | 159935 159936 159937 159938 159939 159940 159941 159942 159943 159944 159945 159946 159947 159948 159949 |
nEst = nRow0;
rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
nRowEst += nEst;
pBuilder->nRecValid = nRecValid;
}
if( rc==SQLITE_OK ){
if( nRowEst > (tRowcnt)nRow0 ) nRowEst = nRow0;
*pnRow = nRowEst;
WHERETRACE(0x20,("IN row estimate: est=%d\n", nRowEst));
}
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
#endif /* SQLITE_ENABLE_STAT4 */
|
| ︙ | ︙ | |||
160212 160213 160214 160215 160216 160217 160218 |
** will be more aggressive about generating automatic indexes for
** those objects, since there is no opportunity to add schema
** indexes on subqueries and views. */
pNew->rSetup = rLogSize + rSize;
if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){
pNew->rSetup += 28;
}else{
| | > | 161407 161408 161409 161410 161411 161412 161413 161414 161415 161416 161417 161418 161419 161420 161421 161422 |
** will be more aggressive about generating automatic indexes for
** those objects, since there is no opportunity to add schema
** indexes on subqueries and views. */
pNew->rSetup = rLogSize + rSize;
if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){
pNew->rSetup += 28;
}else{
pNew->rSetup -= 25; /* Greatly reduced setup cost for auto indexes
** on ephemeral materializations of views */
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
if( pNew->rSetup<0 ) pNew->rSetup = 0;
/* TUNING: Each index lookup yields 20 rows in the table. This
** is more than the usual guess of 10 rows, since we have no way
** of knowing how selective the index will ultimately be. It would
** not be unreasonable to make this value much larger. */
|
| ︙ | ︙ | |||
160708 160709 160710 160711 160712 160713 160714 |
*/
SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 );
return pHidden->eDistinct;
}
| < < | | < < | < | 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 |
*/
SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){
HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
assert( pHidden->eDistinct>=0 && pHidden->eDistinct<=3 );
return pHidden->eDistinct;
}
/*
** Cause the prepared statement that is associated with a call to
** xBestIndex to potentially use all schemas. If the statement being
** prepared is read-only, then just start read transactions on all
** schemas. But if this is a write operation, start writes on all
** schemas.
**
** This is used by the (built-in) sqlite_dbpage virtual table.
*/
SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse *pParse){
int nDb = pParse->db->nDb;
int i;
for(i=0; i<nDb; i++){
sqlite3CodeVerifySchema(pParse, i);
}
if( DbMaskNonZero(pParse->writeMask) ){
for(i=0; i<nDb; i++){
sqlite3BeginWriteOperation(pParse, 0, i);
}
}
}
/*
** Add all WhereLoop objects for a table of the join identified by
** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
**
** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and
** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause
|
| ︙ | ︙ | |||
161899 161900 161901 161902 161903 161904 161905 161906 161907 161908 161909 161910 161911 161912 |
pWInfo->bOrderedInnerLoop = 0;
if( pWInfo->pOrderBy ){
pWInfo->nOBSat = pFrom->isOrdered;
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
}else{
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
pWInfo->nOBSat = 0;
if( nLoop>0 ){
u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags;
if( (wsFlags & WHERE_ONEROW)==0
| > > > > | 163090 163091 163092 163093 163094 163095 163096 163097 163098 163099 163100 163101 163102 163103 163104 163105 163106 163107 |
pWInfo->bOrderedInnerLoop = 0;
if( pWInfo->pOrderBy ){
pWInfo->nOBSat = pFrom->isOrdered;
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
if( pWInfo->pSelect->pOrderBy
&& pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){
pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr;
}
}else{
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
pWInfo->nOBSat = 0;
if( nLoop>0 ){
u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags;
if( (wsFlags & WHERE_ONEROW)==0
|
| ︙ | ︙ | |||
162110 162111 162112 162113 162114 162115 162116 162117 162118 162119 162120 162121 162122 162123 | ** 1) The query must not be an aggregate. ** 2) The table must be the RHS of a LEFT JOIN. ** 3) Either the query must be DISTINCT, or else the ON or USING clause ** must contain a constraint that limits the scan of the table to ** at most a single row. ** 4) The table must not be referenced by any part of the query apart ** from its own USING or ON clause. ** ** For example, given: ** ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); ** | > > > > > > > | 163305 163306 163307 163308 163309 163310 163311 163312 163313 163314 163315 163316 163317 163318 163319 163320 163321 163322 163323 163324 163325 | ** 1) The query must not be an aggregate. ** 2) The table must be the RHS of a LEFT JOIN. ** 3) Either the query must be DISTINCT, or else the ON or USING clause ** must contain a constraint that limits the scan of the table to ** at most a single row. ** 4) The table must not be referenced by any part of the query apart ** from its own USING or ON clause. ** 5) The table must not have an inner-join ON or USING clause if there is ** a RIGHT JOIN anywhere in the query. Otherwise the ON/USING clause ** might move from the right side to the left side of the RIGHT JOIN. ** Note: Due to (2), this condition can only arise if the table is ** the right-most table of a subquery that was flattened into the ** main query and that subquery was the right-hand operand of an ** inner join that held an ON or USING clause. ** ** For example, given: ** ** CREATE TABLE t1(ipk INTEGER PRIMARY KEY, v1); ** CREATE TABLE t2(ipk INTEGER PRIMARY KEY, v2); ** CREATE TABLE t3(ipk INTEGER PRIMARY KEY, v3); ** |
| ︙ | ︙ | |||
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 |
*/
static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
WhereInfo *pWInfo,
Bitmask notReady
){
int i;
Bitmask tabUsed;
/* Preconditions checked by the caller */
assert( pWInfo->nLevel>=2 );
assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) );
/* These two preconditions checked by the caller combine to guarantee
** condition (1) of the header comment */
assert( pWInfo->pResultSet!=0 );
assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) );
tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet);
if( pWInfo->pOrderBy ){
tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
}
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
SrcItem *pItem;
WhereLoop *pLoop;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue;
| > > | 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 |
*/
static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
WhereInfo *pWInfo,
Bitmask notReady
){
int i;
Bitmask tabUsed;
int hasRightJoin;
/* Preconditions checked by the caller */
assert( pWInfo->nLevel>=2 );
assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_OmitNoopJoin) );
/* These two preconditions checked by the caller combine to guarantee
** condition (1) of the header comment */
assert( pWInfo->pResultSet!=0 );
assert( 0==(pWInfo->wctrlFlags & WHERE_AGG_DISTINCT) );
tabUsed = sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pResultSet);
if( pWInfo->pOrderBy ){
tabUsed |= sqlite3WhereExprListUsage(&pWInfo->sMaskSet, pWInfo->pOrderBy);
}
hasRightJoin = (pWInfo->pTabList->a[0].fg.jointype & JT_LTORJ)!=0;
for(i=pWInfo->nLevel-1; i>=1; i--){
WhereTerm *pTerm, *pEnd;
SrcItem *pItem;
WhereLoop *pLoop;
pLoop = pWInfo->a[i].pWLoop;
pItem = &pWInfo->pTabList->a[pLoop->iTab];
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))!=JT_LEFT ) continue;
|
| ︙ | ︙ | |||
162171 162172 162173 162174 162175 162176 162177 162178 162179 162180 162181 162182 162183 162184 |
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
}
if( pTerm<pEnd ) continue;
WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId));
notReady &= ~pLoop->maskSelf;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
pTerm->wtFlags |= TERM_CODED;
| > > > > > > | 163375 163376 163377 163378 163379 163380 163381 163382 163383 163384 163385 163386 163387 163388 163389 163390 163391 163392 163393 163394 |
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
&& pTerm->pExpr->w.iJoin==pItem->iCursor
){
break; /* restriction (5) */
}
}
if( pTerm<pEnd ) continue;
WHERETRACE(0xffffffff, ("-> drop loop %c not used\n", pLoop->cId));
notReady &= ~pLoop->maskSelf;
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
pTerm->wtFlags |= TERM_CODED;
|
| ︙ | ︙ | |||
162212 162213 162214 162215 162216 162217 162218 |
** WhereLoop. The implementation of the Bloom filter comes further
** down where the code for each WhereLoop is generated.
*/
static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
const WhereInfo *pWInfo
){
int i;
| | < | > > > > > | < < < | < < | 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 |
** WhereLoop. The implementation of the Bloom filter comes further
** down where the code for each WhereLoop is generated.
*/
static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
const WhereInfo *pWInfo
){
int i;
LogEst nSearch = 0;
assert( pWInfo->nLevel>=2 );
assert( OptimizationEnabled(pWInfo->pParse->db, SQLITE_BloomFilter) );
for(i=0; i<pWInfo->nLevel; i++){
WhereLoop *pLoop = pWInfo->a[i].pWLoop;
const unsigned int reqFlags = (WHERE_SELFCULL|WHERE_COLUMN_EQ);
SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
Table *pTab = pItem->pTab;
if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
pTab->tabFlags |= TF_StatsUsed;
if( i>=1
&& (pLoop->wsFlags & reqFlags)==reqFlags
/* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
&& ALWAYS((pLoop->wsFlags & (WHERE_IPK|WHERE_INDEXED))!=0)
){
if( nSearch > pTab->nRowLogEst ){
testcase( pItem->fg.jointype & JT_LEFT );
pLoop->wsFlags |= WHERE_BLOOMFILTER;
pLoop->wsFlags &= ~WHERE_IDX_ONLY;
WHERETRACE(0xffffffff, (
"-> use Bloom-filter on loop %c because there are ~%.1e "
"lookups into %s which has only ~%.1e rows\n",
pLoop->cId, (double)sqlite3LogEstToInt(nSearch), pTab->zName,
|
| ︙ | ︙ | |||
162311 162312 162313 162314 162315 162316 162317 162318 162319 162320 162321 162322 162323 162324 |
}
#endif
p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
p->iIdxCol = i;
p->bMaybeNullRow = bMaybeNullRow;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
p->zIdxName = pIdx->zName;
#endif
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
}
| > > > | 163520 163521 163522 163523 163524 163525 163526 163527 163528 163529 163530 163531 163532 163533 163534 163535 163536 |
}
#endif
p->pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
p->iIdxCol = i;
p->bMaybeNullRow = bMaybeNullRow;
if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){
p->aff = pIdx->zColAff[i];
}
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
p->zIdxName = pIdx->zName;
#endif
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
}
|
| ︙ | ︙ | |||
162568 162569 162570 162571 162572 162573 162574 |
/* Analyze all of the subexpressions. */
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
if( pSelect && pSelect->pLimit ){
sqlite3WhereAddLimit(&pWInfo->sWC, pSelect);
}
if( pParse->nErr ) goto whereBeginError;
| > > | | > | > > > > > | | > | | | > > > > > > | > > > > > | > > > | | 163780 163781 163782 163783 163784 163785 163786 163787 163788 163789 163790 163791 163792 163793 163794 163795 163796 163797 163798 163799 163800 163801 163802 163803 163804 163805 163806 163807 163808 163809 163810 163811 163812 163813 163814 163815 163816 163817 163818 163819 163820 163821 163822 163823 163824 163825 163826 163827 163828 163829 163830 163831 163832 |
/* Analyze all of the subexpressions. */
sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
if( pSelect && pSelect->pLimit ){
sqlite3WhereAddLimit(&pWInfo->sWC, pSelect);
}
if( pParse->nErr ) goto whereBeginError;
/* The False-WHERE-Term-Bypass optimization:
**
** If there are WHERE terms that are false, then no rows will be output,
** so skip over all of the code generated here.
**
** Conditions:
**
** (1) The WHERE term must not refer to any tables in the join.
** (2) The term must not come from an ON clause on the
** right-hand side of a LEFT or FULL JOIN.
** (3) The term must not come from an ON clause, or there must be
** no RIGHT or FULL OUTER joins in pTabList.
** (4) If the expression contains non-deterministic functions
** that are not within a sub-select. This is not required
** for correctness but rather to preserves SQLite's legacy
** behaviour in the following two cases:
**
** WHERE random()>0; -- eval random() once per row
** WHERE (SELECT random())>0; -- eval random() just once overall
**
** Note that the Where term need not be a constant in order for this
** optimization to apply, though it does need to be constant relative to
** the current subquery (condition 1). The term might include variables
** from outer queries so that the value of the term changes from one
** invocation of the current subquery to the next.
*/
for(ii=0; ii<sWLB.pWC->nBase; ii++){
WhereTerm *pT = &sWLB.pWC->a[ii]; /* A term of the WHERE clause */
Expr *pX; /* The expression of pT */
if( pT->wtFlags & TERM_VIRTUAL ) continue;
pX = pT->pExpr;
assert( pX!=0 );
assert( pT->prereqAll!=0 || !ExprHasProperty(pX, EP_OuterON) );
if( pT->prereqAll==0 /* Conditions (1) and (2) */
&& (nTabList==0 || exprIsDeterministic(pX)) /* Condition (4) */
&& !(ExprHasProperty(pX, EP_InnerON) /* Condition (3) */
&& (pTabList->a[0].fg.jointype & JT_LTORJ)!=0 )
){
sqlite3ExprIfFalse(pParse, pX, pWInfo->iBreak, SQLITE_JUMPIFNULL);
pT->wtFlags |= TERM_CODED;
}
}
if( wctrlFlags & WHERE_WANT_DISTINCT ){
if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
/* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
|
| ︙ | ︙ | |||
162826 162827 162828 162829 162830 162831 162832 |
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
| | | 164061 164062 164063 164064 164065 164066 164067 164068 164069 164070 164071 164072 164073 164074 164075 |
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
if( pLoop->u.btree.pIndex!=0 && (pTab->tabFlags & TF_WithoutRowid)==0 ){
sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete);
}else
#endif
{
sqlite3VdbeChangeP5(v, bFordelete);
}
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
|
| ︙ | ︙ | |||
162963 162964 162965 162966 162967 162968 162969 162970 162971 162972 |
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
}else{
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
sqlite3VdbeJumpHere(v, iOnce);
}
}
if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){
if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
| > | < | 164198 164199 164200 164201 164202 164203 164204 164205 164206 164207 164208 164209 164210 164211 164212 164213 164214 164215 164216 |
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
}else{
int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub);
sqlite3VdbeJumpHere(v, iOnce);
}
}
assert( pTabList == pWInfo->pTabList );
if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){
if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
constructAutomaticIndex(pParse, &pWInfo->sWC, notReady, pLevel);
#endif
}else{
sqlite3ConstructBloomFilter(pWInfo, ii, pLevel, notReady);
}
if( db->mallocFailed ) goto whereBeginError;
}
addrExplain = sqlite3WhereExplainOneScan(
|
| ︙ | ︙ | |||
163284 163285 163286 163287 163288 163289 163290 |
}
p = p->pIENext;
}
}
k = pLevel->addrBody + 1;
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeAddopTrace ){
| | > | 164519 164520 164521 164522 164523 164524 164525 164526 164527 164528 164529 164530 164531 164532 164533 164534 |
}
p = p->pIENext;
}
}
k = pLevel->addrBody + 1;
#ifdef SQLITE_DEBUG
if( db->flags & SQLITE_VdbeAddopTrace ){
printf("TRANSLATE cursor %d->%d in opcode range %d..%d\n",
pLevel->iTabCur, pLevel->iIdxCur, k, last-1);
}
/* Proof that the "+1" on the k value above is safe */
pOp = sqlite3VdbeGetOp(v, k - 1);
assert( pOp->opcode!=OP_Column || pOp->p1!=pLevel->iTabCur );
assert( pOp->opcode!=OP_Rowid || pOp->p1!=pLevel->iTabCur );
assert( pOp->opcode!=OP_IfNullRow || pOp->p1!=pLevel->iTabCur );
#endif
|
| ︙ | ︙ | |||
164159 164160 164161 164162 164163 164164 164165 164166 164167 164168 164169 164170 164171 164172 |
assert( pWin->pOwner==pExpr );
return WRC_Prune;
}
}
}
/* no break */ deliberate_fall_through
case TK_AGG_FUNCTION:
case TK_COLUMN: {
int iCol = -1;
if( pParse->db->mallocFailed ) return WRC_Abort;
if( p->pSub ){
int i;
for(i=0; i<p->pSub->nExpr; i++){
| > | 165395 165396 165397 165398 165399 165400 165401 165402 165403 165404 165405 165406 165407 165408 165409 |
assert( pWin->pOwner==pExpr );
return WRC_Prune;
}
}
}
/* no break */ deliberate_fall_through
case TK_IF_NULL_ROW:
case TK_AGG_FUNCTION:
case TK_COLUMN: {
int iCol = -1;
if( pParse->db->mallocFailed ) return WRC_Abort;
if( p->pSub ){
int i;
for(i=0; i<p->pSub->nExpr; i++){
|
| ︙ | ︙ | |||
164453 164454 164455 164456 164457 164458 164459 164460 164461 164462 164463 164464 164465 164466 |
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);
if( pTab2==0 ){
/* Might actually be some other kind of error, but in that case
** pParse->nErr will be set, so if SQLITE_NOMEM is set, we will get
| > | 165690 165691 165692 165693 165694 165695 165696 165697 165698 165699 165700 165701 165702 165703 165704 |
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;
p->pSrc->a[0].fg.isCorrelated = 1;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded|SF_OrderByReqd;
pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
pSub->selFlags |= (selFlags & SF_Aggregate);
if( pTab2==0 ){
/* Might actually be some other kind of error, but in that case
** pParse->nErr will be set, so if SQLITE_NOMEM is set, we will get
|
| ︙ | ︙ | |||
166986 166987 166988 166989 166990 166991 166992 | #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 | | | | | | | | | | | | | 168224 168225 168226 168227 168228 168229 168230 168231 168232 168233 168234 168235 168236 168237 168238 168239 168240 168241 168242 168243 168244 168245 168246 168247 168248 168249 | #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 575 #define YYNRULE 403 #define YYNRULE_WITH_ACTION 340 #define YYNTOKEN 185 #define YY_MAX_SHIFT 574 #define YY_MIN_SHIFTREDUCE 833 #define YY_MAX_SHIFTREDUCE 1235 #define YY_ERROR_ACTION 1236 #define YY_ACCEPT_ACTION 1237 #define YY_NO_ACTION 1238 #define YY_MIN_REDUCE 1239 #define YY_MAX_REDUCE 1641 /************* 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 |
| ︙ | ︙ | |||
167064 167065 167066 167067 167068 167069 167070 | ** 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 **********************************************/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 168302 168303 168304 168305 168306 168307 168308 168309 168310 168311 168312 168313 168314 168315 168316 168317 168318 168319 168320 168321 168322 168323 168324 168325 168326 168327 168328 168329 168330 168331 168332 168333 168334 168335 168336 168337 168338 168339 168340 168341 168342 168343 168344 168345 168346 168347 168348 168349 168350 168351 168352 168353 168354 168355 168356 168357 168358 168359 168360 168361 168362 168363 168364 168365 168366 168367 168368 168369 168370 168371 168372 168373 168374 168375 168376 168377 168378 168379 168380 168381 168382 168383 168384 168385 168386 168387 168388 168389 168390 168391 168392 168393 168394 168395 168396 168397 168398 168399 168400 168401 168402 168403 168404 168405 168406 168407 168408 168409 168410 168411 168412 168413 168414 168415 168416 168417 168418 168419 168420 168421 168422 168423 168424 168425 168426 168427 168428 168429 168430 168431 168432 168433 168434 168435 168436 168437 168438 168439 168440 168441 168442 168443 168444 168445 168446 168447 168448 168449 168450 168451 168452 168453 168454 168455 168456 168457 168458 168459 168460 168461 168462 168463 168464 168465 168466 168467 168468 168469 168470 168471 168472 168473 168474 168475 168476 168477 168478 168479 168480 168481 168482 168483 168484 168485 168486 168487 168488 168489 168490 168491 168492 168493 168494 168495 168496 168497 168498 168499 168500 168501 168502 168503 168504 168505 168506 168507 168508 168509 168510 168511 168512 168513 168514 168515 168516 168517 168518 168519 168520 168521 168522 168523 168524 168525 168526 168527 |
** 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 (2096)
static const YYACTIONTYPE yy_action[] = {
/* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229,
/* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409,
/* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71,
/* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970,
/* 40 */ 397, 71, 71, 125, 126, 80, 1212, 1212, 1047, 1050,
/* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409,
/* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229,
/* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323,
/* 80 */ 417, 523, 142, 125, 126, 80, 1212, 1212, 1047, 1050,
/* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115,
/* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120,
/* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442,
/* 120 */ 442, 1561, 376, 1563, 1188, 375, 1159, 565, 1159, 565,
/* 130 */ 409, 1561, 537, 259, 226, 444, 101, 145, 449, 316,
/* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120,
/* 150 */ 120, 119, 116, 444, 125, 126, 80, 1212, 1212, 1047,
/* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142,
/* 170 */ 294, 1188, 339, 448, 120, 120, 120, 119, 116, 444,
/* 180 */ 127, 1188, 1189, 1188, 148, 441, 440, 568, 119, 116,
/* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122,
/* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113,
/* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120,
/* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1188, 1189,
/* 230 */ 1188, 149, 1220, 409, 1220, 124, 124, 124, 124, 122,
/* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
/* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80,
/* 260 */ 1212, 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124,
/* 270 */ 124, 124, 1275, 522, 222, 1188, 568, 409, 224, 514,
/* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120,
/* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1188, 133,
/* 300 */ 133, 125, 126, 80, 1212, 1212, 1047, 1050, 1037, 1037,
/* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122,
/* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546,
/* 330 */ 1188, 373, 1188, 1189, 1188, 252, 1429, 399, 504, 501,
/* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340,
/* 350 */ 460, 328, 360, 394, 1233, 1188, 1189, 1188, 563, 568,
/* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119,
/* 370 */ 116, 444, 284, 284, 369, 1574, 1600, 441, 440, 154,
/* 380 */ 409, 445, 71, 71, 1282, 565, 1217, 1188, 1189, 1188,
/* 390 */ 85, 1219, 271, 557, 543, 515, 1555, 568, 98, 1218,
/* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1212, 1212, 1047,
/* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550,
/* 420 */ 13, 13, 1024, 507, 1220, 1188, 1220, 549, 109, 109,
/* 430 */ 222, 568, 1234, 175, 568, 427, 110, 197, 445, 569,
/* 440 */ 445, 430, 1546, 1014, 325, 551, 1188, 270, 287, 368,
/* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359,
/* 460 */ 316, 559, 1606, 122, 122, 122, 122, 121, 121, 120,
/* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27,
/* 480 */ 284, 284, 1188, 1189, 1188, 1154, 568, 1605, 409, 899,
/* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1154, 516,
/* 500 */ 413, 1154, 552, 1188, 1189, 1188, 568, 544, 1548, 51,
/* 510 */ 51, 214, 125, 126, 80, 1212, 1212, 1047, 1050, 1037,
/* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1188, 474, 135,
/* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120,
/* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 1555,
/* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1212, 1212,
/* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
/* 570 */ 1549, 122, 122, 122, 122, 121, 121, 120, 120, 120,
/* 580 */ 119, 116, 444, 485, 1188, 1189, 1188, 482, 281, 1263,
/* 590 */ 955, 252, 1188, 373, 504, 501, 500, 1188, 340, 570,
/* 600 */ 1188, 570, 409, 292, 499, 955, 874, 191, 480, 316,
/* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121,
/* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212,
/* 630 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 640 */ 124, 409, 394, 1132, 1188, 867, 100, 284, 284, 1188,
/* 650 */ 1189, 1188, 373, 1089, 1188, 1189, 1188, 1188, 1189, 1188,
/* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1212, 1212,
/* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
/* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121,
/* 690 */ 121, 120, 120, 120, 119, 116, 444, 1154, 228, 1188,
/* 700 */ 157, 1188, 1189, 1188, 1547, 13, 13, 301, 955, 1228,
/* 710 */ 1154, 153, 409, 1154, 373, 1577, 1172, 5, 369, 1574,
/* 720 */ 429, 1234, 3, 955, 122, 122, 122, 122, 121, 121,
/* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212,
/* 740 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 750 */ 124, 409, 208, 567, 1188, 1025, 1188, 1189, 1188, 1188,
/* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568,
/* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1212, 1212,
/* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
/* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121,
/* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453,
/* 810 */ 528, 1188, 1189, 1188, 13, 13, 1188, 1189, 1188, 1293,
/* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200,
/* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121,
/* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212,
/* 850 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 860 */ 124, 409, 227, 1069, 1154, 284, 284, 419, 312, 278,
/* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1154, 565, 568,
/* 880 */ 1154, 1191, 565, 1594, 565, 125, 126, 80, 1212, 1212,
/* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
/* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121,
/* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354,
/* 920 */ 1580, 574, 2, 1241, 838, 839, 840, 1556, 317, 1207,
/* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1191,
/* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121,
/* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1212,
/* 960 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 970 */ 124, 568, 284, 284, 568, 1208, 409, 573, 313, 1241,
/* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1637,
/* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240,
/* 1000 */ 1321, 104, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123,
/* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121,
/* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284,
/* 1030 */ 428, 448, 1519, 1208, 439, 284, 284, 1483, 1348, 311,
/* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532,
/* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122,
/* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
/* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568,
/* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322,
/* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491,
/* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016,
/* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1212,
/* 1120 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 1130 */ 124, 347, 409, 862, 1528, 1208, 125, 126, 80, 1212,
/* 1140 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 1150 */ 124, 1133, 1635, 474, 1635, 371, 125, 114, 80, 1212,
/* 1160 */ 1212, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
/* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121,
/* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568,
/* 1190 */ 1290, 862, 464, 1208, 436, 122, 122, 122, 122, 121,
/* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1636,
/* 1210 */ 539, 1636, 15, 15, 890, 122, 122, 122, 122, 121,
/* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538,
/* 1230 */ 1131, 1415, 1553, 1554, 1327, 409, 6, 6, 1165, 1264,
/* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457,
/* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407,
/* 1260 */ 126, 80, 1212, 1212, 1047, 1050, 1037, 1037, 123, 123,
/* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1188, 1415,
/* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1552, 847,
/* 1290 */ 1165, 407, 6, 568, 321, 1154, 470, 44, 44, 1551,
/* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1154, 431,
/* 1310 */ 568, 1154, 322, 17, 487, 1111, 58, 58, 122, 122,
/* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444,
/* 1330 */ 1112, 216, 481, 59, 59, 1188, 1189, 1188, 111, 560,
/* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437,
/* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091,
/* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60,
/* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62,
/* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49,
/* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63,
/* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534,
/* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024,
/* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66,
/* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177,
/* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471,
/* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407,
/* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52,
/* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1579, 1176, 447,
/* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391,
/* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466,
/* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323,
/* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163,
/* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76,
/* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483,
/* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557,
/* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161,
/* 1560 */ 161, 1568, 557, 535, 568, 319, 568, 348, 536, 1007,
/* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568,
/* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130,
/* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014,
/* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445,
/* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568,
/* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355,
/* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451,
/* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1176,
/* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392,
/* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258,
/* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261,
/* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74,
/* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972,
/* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342,
/* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249,
/* 1720 */ 1251, 445, 1587, 1339, 308, 276, 168, 309, 11, 141,
/* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219,
/* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110,
/* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365,
/* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109,
/* 1770 */ 204, 1590, 1228, 558, 265, 218, 110, 205, 445, 569,
/* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014,
/* 1790 */ 1016, 1017, 27, 230, 1525, 1225, 79, 560, 85, 4,
/* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461,
/* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27,
/* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36,
/* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246,
/* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350,
/* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4,
/* 1860 */ 1307, 1300, 93, 1604, 881, 1603, 224, 404, 434, 520,
/* 1870 */ 263, 435, 1573, 563, 1279, 1278, 364, 1024, 306, 1277,
/* 1880 */ 264, 1602, 1559, 109, 109, 370, 1299, 307, 1558, 438,
/* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10,
/* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314,
/* 1910 */ 1182, 530, 272, 274, 379, 210, 1331, 547, 385, 386,
/* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513,
/* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147,
/* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212,
/* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084,
/* 1960 */ 326, 180, 169, 1207, 182, 334, 238, 913, 241, 1100,
/* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90,
/* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247,
/* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1222, 489,
/* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19,
/* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159,
/* 2020 */ 513, 39, 95, 1170, 160, 1053, 964, 1139, 96, 174,
/* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1160, 1156, 260,
/* 2040 */ 21, 22, 23, 1158, 1164, 1163, 1144, 24, 33, 25,
/* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052,
/* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019,
/* 2070 */ 861, 112, 29, 564, 1178, 1177, 268, 176, 143, 923,
/* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
/* 2090 */ 1238, 1238, 1238, 1238, 269, 1595,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
/* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19,
/* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216,
/* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39,
/* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49,
|
| ︙ | ︙ | |||
167487 167488 167489 167490 167491 167492 167493 | /* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37, /* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34, /* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34, /* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23, /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23, /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135, /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, | | | | | | | | | | 168725 168726 168727 168728 168729 168730 168731 168732 168733 168734 168735 168736 168737 168738 168739 168740 168741 168742 168743 168744 168745 168746 168747 168748 168749 168750 168751 168752 168753 168754 168755 168756 168757 168758 168759 168760 168761 168762 168763 168764 168765 168766 168767 168768 168769 168770 168771 168772 168773 168774 168775 168776 168777 168778 168779 168780 168781 168782 168783 168784 168785 168786 168787 168788 168789 168790 168791 168792 168793 168794 168795 168796 168797 168798 168799 168800 168801 168802 168803 168804 168805 168806 168807 168808 168809 168810 168811 168812 168813 168814 168815 168816 168817 168818 168819 168820 168821 |
/* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37,
/* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34,
/* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34,
/* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23,
/* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23,
/* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135,
/* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2090 */ 319, 319, 319, 319, 141, 141, 319, 319, 319, 319,
/* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
/* 2280 */ 319,
};
#define YY_SHIFT_COUNT (574)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (2074)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837,
/* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837,
/* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1,
/* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693,
/* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093,
/* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
/* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662,
/* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
/* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430,
/* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533,
/* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113,
/* 160 */ 113, 22, 22, 2096, 2096, 328, 328, 328, 239, 468,
/* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533,
/* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
/* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969,
/* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822,
/* 210 */ 67, 1274, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 1307,
/* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700,
/* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
/* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533,
/* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565,
/* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533,
/* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076,
/* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649,
/* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181,
/* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368,
/* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712,
/* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1764, 1791,
/* 330 */ 1791, 1791, 1791, 1633, 1806, 1677, 1651, 1651, 1677, 1783,
/* 340 */ 1764, 1677, 1764, 1677, 1633, 1806, 1674, 1779, 1633, 1806,
/* 350 */ 1823, 1633, 1806, 1633, 1806, 1823, 1732, 1732, 1732, 1794,
/* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701,
/* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742,
/* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897,
/* 390 */ 1897, 1914, 1914, 1914, 2096, 2096, 2096, 2096, 2096, 2096,
/* 400 */ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 207,
/* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322,
/* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599,
/* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660,
/* 440 */ 1548, 1549, 1682, 1685, 1597, 742, 1941, 1945, 1927, 1787,
/* 450 */ 1937, 1940, 1934, 1936, 1821, 1810, 1832, 1938, 1938, 1942,
/* 460 */ 1822, 1947, 1824, 1949, 1968, 1828, 1841, 1938, 1842, 1912,
/* 470 */ 1939, 1938, 1826, 1921, 1922, 1925, 1926, 1850, 1865, 1948,
/* 480 */ 1843, 1982, 1980, 1964, 1872, 1827, 1928, 1970, 1929, 1923,
/* 490 */ 1958, 1848, 1885, 1977, 1983, 1985, 1871, 1880, 1984, 1943,
/* 500 */ 1986, 1987, 1988, 1990, 1946, 1955, 1991, 1911, 1989, 1994,
/* 510 */ 1951, 1992, 1996, 1873, 1998, 2000, 2001, 2002, 2003, 2004,
/* 520 */ 1999, 1933, 1890, 2009, 2010, 1910, 2005, 2012, 1892, 2011,
/* 530 */ 2006, 2007, 2008, 2013, 1950, 1962, 1957, 2014, 1969, 1952,
/* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031,
/* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044,
/* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954,
/* 570 */ 2052, 2055, 2053, 2073, 2074,
};
#define YY_REDUCE_COUNT (408)
#define YY_REDUCE_MIN (-271)
#define YY_REDUCE_MAX (1740)
static const short yy_reduce_ofst[] = {
/* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187,
/* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489,
|
| ︙ | ︙ | |||
167618 167619 167620 167621 167622 167623 167624 |
/* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560,
/* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657,
/* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718,
/* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716,
/* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740,
};
static const YYACTIONTYPE yy_default[] = {
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 168856 168857 168858 168859 168860 168861 168862 168863 168864 168865 168866 168867 168868 168869 168870 168871 168872 168873 168874 168875 168876 168877 168878 168879 168880 168881 168882 168883 168884 168885 168886 168887 168888 168889 168890 168891 168892 168893 168894 168895 168896 168897 168898 168899 168900 168901 168902 168903 168904 168905 168906 168907 168908 168909 168910 168911 168912 168913 168914 168915 168916 168917 168918 168919 168920 168921 168922 168923 168924 168925 168926 168927 |
/* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560,
/* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657,
/* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718,
/* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716,
/* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 1641, 1641, 1641, 1469, 1236, 1347, 1236, 1236, 1236, 1469,
/* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236,
/* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236,
/* 30 */ 1236, 1236, 1557, 1557, 1236, 1236, 1236, 1236, 1236, 1236,
/* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236,
/* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399,
/* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464,
/* 70 */ 1619, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236,
/* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436,
/* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236,
/* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427,
/* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1593, 1592, 1487, 1236,
/* 170 */ 1236, 1236, 1236, 1236, 1236, 1557, 1236, 1236, 1236, 1236,
/* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367,
/* 200 */ 1557, 1557, 1236, 1269, 1557, 1557, 1368, 1368, 1265, 1265,
/* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236,
/* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236,
/* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236,
/* 270 */ 1236, 1236, 1236, 1236, 1236, 1586, 1236, 1499, 1325, 1343,
/* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1633,
/* 290 */ 1403, 1392, 1344, 1392, 1630, 1390, 1403, 1403, 1390, 1403,
/* 300 */ 1344, 1630, 1286, 1608, 1281, 1377, 1377, 1377, 1367, 1367,
/* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1633, 1633,
/* 320 */ 1353, 1353, 1632, 1632, 1353, 1487, 1616, 1412, 1314, 1320,
/* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1616, 1616, 1390, 1412,
/* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1627, 1353, 1254,
/* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301,
/* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1575,
/* 370 */ 1236, 1481, 1481, 1477, 1353, 1567, 1567, 1380, 1380, 1385,
/* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1589,
/* 390 */ 1589, 1585, 1585, 1585, 1638, 1638, 1536, 1601, 1269, 1269,
/* 400 */ 1269, 1269, 1601, 1288, 1288, 1270, 1270, 1269, 1601, 1236,
/* 410 */ 1236, 1236, 1236, 1236, 1236, 1596, 1236, 1531, 1488, 1357,
/* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236,
/* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236,
/* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358,
/* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236,
/* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 480 */ 1629, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236,
/* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236,
/* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382,
/* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 540 */ 1236, 1236, 1236, 1236, 1572, 1372, 1236, 1236, 1236, 1236,
/* 550 */ 1620, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
/* 560 */ 1236, 1236, 1236, 1236, 1236, 1612, 1328, 1418, 1236, 1421,
/* 570 */ 1258, 1236, 1248, 1236, 1236,
};
/********** 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.
|
| ︙ | ︙ | |||
168472 168473 168474 168475 168476 168477 168478 | /* 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", | | | | | | | < | | | > | | | < | | > | | | | | | | < | > | | | | | | | | | | | < | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | < < | | | | | | | | | | | | | | | | | | | | | > > | | | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | < < | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | 169710 169711 169712 169713 169714 169715 169716 169717 169718 169719 169720 169721 169722 169723 169724 169725 169726 169727 169728 169729 169730 169731 169732 169733 169734 169735 169736 169737 169738 169739 169740 169741 169742 169743 169744 169745 169746 169747 169748 169749 169750 169751 169752 169753 169754 169755 169756 169757 169758 169759 169760 169761 169762 169763 169764 169765 169766 169767 169768 169769 169770 169771 169772 169773 169774 169775 169776 169777 169778 169779 169780 169781 169782 169783 169784 169785 169786 169787 169788 169789 169790 169791 169792 169793 169794 169795 169796 169797 169798 169799 169800 169801 169802 169803 169804 169805 169806 169807 169808 169809 169810 169811 169812 169813 169814 169815 169816 169817 169818 169819 169820 169821 169822 169823 169824 169825 169826 169827 169828 169829 169830 169831 169832 169833 169834 169835 169836 169837 169838 169839 169840 169841 169842 169843 169844 169845 169846 169847 169848 169849 169850 169851 169852 169853 169854 169855 169856 169857 169858 169859 169860 169861 169862 169863 169864 169865 169866 169867 169868 169869 169870 169871 169872 169873 169874 169875 169876 169877 169878 169879 169880 169881 169882 169883 169884 169885 169886 169887 169888 169889 169890 169891 169892 169893 169894 169895 169896 169897 169898 169899 169900 169901 169902 169903 169904 169905 169906 169907 169908 169909 169910 169911 169912 169913 169914 169915 169916 169917 169918 169919 169920 169921 169922 169923 169924 169925 169926 169927 169928 169929 169930 169931 169932 169933 169934 169935 169936 169937 169938 169939 169940 169941 169942 169943 169944 169945 169946 169947 169948 | /* 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|JOIN_KW", /* 179 */ "expr ::= nm DOT nm", /* 180 */ "expr ::= nm DOT nm DOT nm", /* 181 */ "term ::= NULL|FLOAT|BLOB", /* 182 */ "term ::= STRING", /* 183 */ "term ::= INTEGER", /* 184 */ "expr ::= VARIABLE", /* 185 */ "expr ::= expr COLLATE ID|STRING", /* 186 */ "expr ::= CAST LP expr AS typetoken RP", /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", /* 191 */ "term ::= CTIME_KW", /* 192 */ "expr ::= LP nexprlist COMMA expr RP", /* 193 */ "expr ::= expr AND expr", /* 194 */ "expr ::= expr OR expr", /* 195 */ "expr ::= expr LT|GT|GE|LE expr", /* 196 */ "expr ::= expr EQ|NE expr", /* 197 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", /* 198 */ "expr ::= expr PLUS|MINUS expr", /* 199 */ "expr ::= expr STAR|SLASH|REM expr", /* 200 */ "expr ::= expr CONCAT expr", /* 201 */ "likeop ::= NOT LIKE_KW|MATCH", /* 202 */ "expr ::= expr likeop expr", /* 203 */ "expr ::= expr likeop expr ESCAPE expr", /* 204 */ "expr ::= expr ISNULL|NOTNULL", /* 205 */ "expr ::= expr NOT NULL", /* 206 */ "expr ::= expr IS expr", /* 207 */ "expr ::= expr IS NOT expr", /* 208 */ "expr ::= expr IS NOT DISTINCT FROM expr", /* 209 */ "expr ::= expr IS DISTINCT FROM expr", /* 210 */ "expr ::= NOT expr", /* 211 */ "expr ::= BITNOT expr", /* 212 */ "expr ::= PLUS|MINUS expr", /* 213 */ "expr ::= expr PTR expr", /* 214 */ "between_op ::= BETWEEN", /* 215 */ "between_op ::= NOT BETWEEN", /* 216 */ "expr ::= expr between_op expr AND expr", /* 217 */ "in_op ::= IN", /* 218 */ "in_op ::= NOT IN", /* 219 */ "expr ::= expr in_op LP exprlist RP", /* 220 */ "expr ::= LP select RP", /* 221 */ "expr ::= expr in_op LP select RP", /* 222 */ "expr ::= expr in_op nm dbnm paren_exprlist", /* 223 */ "expr ::= EXISTS LP select RP", /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", /* 226 */ "case_exprlist ::= WHEN expr THEN expr", /* 227 */ "case_else ::= ELSE expr", /* 228 */ "case_else ::=", /* 229 */ "case_operand ::=", /* 230 */ "exprlist ::=", /* 231 */ "nexprlist ::= nexprlist COMMA expr", /* 232 */ "nexprlist ::= expr", /* 233 */ "paren_exprlist ::=", /* 234 */ "paren_exprlist ::= LP exprlist RP", /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", /* 236 */ "uniqueflag ::= UNIQUE", /* 237 */ "uniqueflag ::=", /* 238 */ "eidlist_opt ::=", /* 239 */ "eidlist_opt ::= LP eidlist RP", /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", /* 241 */ "eidlist ::= nm collate sortorder", /* 242 */ "collate ::=", /* 243 */ "collate ::= COLLATE ID|STRING", /* 244 */ "cmd ::= DROP INDEX ifexists fullname", /* 245 */ "cmd ::= VACUUM vinto", /* 246 */ "cmd ::= VACUUM nm vinto", /* 247 */ "vinto ::= INTO expr", /* 248 */ "vinto ::=", /* 249 */ "cmd ::= PRAGMA nm dbnm", /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", /* 258 */ "trigger_time ::= BEFORE|AFTER", /* 259 */ "trigger_time ::= INSTEAD OF", /* 260 */ "trigger_time ::=", /* 261 */ "trigger_event ::= DELETE|INSERT", /* 262 */ "trigger_event ::= UPDATE", /* 263 */ "trigger_event ::= UPDATE OF idlist", /* 264 */ "when_clause ::=", /* 265 */ "when_clause ::= WHEN expr", /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", /* 268 */ "trnm ::= nm DOT nm", /* 269 */ "tridxby ::= INDEXED BY nm", /* 270 */ "tridxby ::= NOT INDEXED", /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", /* 274 */ "trigger_cmd ::= scanpt select scanpt", /* 275 */ "expr ::= RAISE LP IGNORE RP", /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", /* 277 */ "raisetype ::= ROLLBACK", /* 278 */ "raisetype ::= ABORT", /* 279 */ "raisetype ::= FAIL", /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", /* 282 */ "cmd ::= DETACH database_kw_opt expr", /* 283 */ "key_opt ::=", /* 284 */ "key_opt ::= KEY expr", /* 285 */ "cmd ::= REINDEX", /* 286 */ "cmd ::= REINDEX nm dbnm", /* 287 */ "cmd ::= ANALYZE", /* 288 */ "cmd ::= ANALYZE nm dbnm", /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", /* 292 */ "add_column_fullname ::= fullname", /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", /* 294 */ "cmd ::= create_vtab", /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", /* 297 */ "vtabarg ::=", /* 298 */ "vtabargtoken ::= ANY", /* 299 */ "vtabargtoken ::= lp anylist RP", /* 300 */ "lp ::= LP", /* 301 */ "with ::= WITH wqlist", /* 302 */ "with ::= WITH RECURSIVE wqlist", /* 303 */ "wqas ::= AS", /* 304 */ "wqas ::= AS MATERIALIZED", /* 305 */ "wqas ::= AS NOT MATERIALIZED", /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", /* 307 */ "wqlist ::= wqitem", /* 308 */ "wqlist ::= wqlist COMMA wqitem", /* 309 */ "windowdefn_list ::= windowdefn", /* 310 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", /* 311 */ "windowdefn ::= nm AS LP window RP", /* 312 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", /* 313 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", /* 314 */ "window ::= ORDER BY sortlist frame_opt", /* 315 */ "window ::= nm ORDER BY sortlist frame_opt", /* 316 */ "window ::= frame_opt", /* 317 */ "window ::= nm frame_opt", /* 318 */ "frame_opt ::=", /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", /* 322 */ "frame_bound_s ::= frame_bound", /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", /* 324 */ "frame_bound_e ::= frame_bound", /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", /* 327 */ "frame_bound ::= CURRENT ROW", /* 328 */ "frame_exclude_opt ::=", /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", /* 330 */ "frame_exclude ::= NO OTHERS", /* 331 */ "frame_exclude ::= CURRENT ROW", /* 332 */ "frame_exclude ::= GROUP|TIES", /* 333 */ "window_clause ::= WINDOW windowdefn_list", /* 334 */ "filter_over ::= filter_clause over_clause", /* 335 */ "filter_over ::= over_clause", /* 336 */ "filter_over ::= filter_clause", /* 337 */ "over_clause ::= OVER LP window RP", /* 338 */ "over_clause ::= OVER nm", /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", /* 340 */ "input ::= cmdlist", /* 341 */ "cmdlist ::= cmdlist ecmd", /* 342 */ "cmdlist ::= ecmd", /* 343 */ "ecmd ::= SEMI", /* 344 */ "ecmd ::= cmdx SEMI", /* 345 */ "ecmd ::= explain cmdx SEMI", /* 346 */ "trans_opt ::=", /* 347 */ "trans_opt ::= TRANSACTION", /* 348 */ "trans_opt ::= TRANSACTION nm", /* 349 */ "savepoint_opt ::= SAVEPOINT", /* 350 */ "savepoint_opt ::=", /* 351 */ "cmd ::= create_table create_table_args", /* 352 */ "table_option_set ::= table_option", /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", /* 354 */ "columnlist ::= columnname carglist", /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", /* 356 */ "nm ::= STRING", /* 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 */ "indexed_opt ::= indexed_by", /* 377 */ "returning ::=", /* 378 */ "expr ::= term", /* 379 */ "likeop ::= LIKE_KW|MATCH", /* 380 */ "case_operand ::= expr", /* 381 */ "exprlist ::= nexprlist", /* 382 */ "nmnum ::= plus_num", /* 383 */ "nmnum ::= nm", /* 384 */ "nmnum ::= ON", /* 385 */ "nmnum ::= DELETE", /* 386 */ "nmnum ::= DEFAULT", /* 387 */ "plus_num ::= INTEGER|FLOAT", /* 388 */ "foreach_clause ::=", /* 389 */ "foreach_clause ::= FOR EACH ROW", /* 390 */ "trnm ::= nm", /* 391 */ "tridxby ::=", /* 392 */ "database_kw_opt ::= DATABASE", /* 393 */ "database_kw_opt ::=", /* 394 */ "kwcolumn_opt ::=", /* 395 */ "kwcolumn_opt ::= COLUMNKW", /* 396 */ "vtabarglist ::= vtabarg", /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg", /* 398 */ "vtabarg ::= vtabarg vtabargtoken", /* 399 */ "anylist ::=", /* 400 */ "anylist ::= anylist LP anylist RP", /* 401 */ "anylist ::= anylist ANY", /* 402 */ "with ::=", }; #endif /* NDEBUG */ #if YYSTACKDEPTH<=0 /* ** Try to increase the size of the parser stack. Return the number |
| ︙ | ︙ | |||
169383 169384 169385 169386 169387 169388 169389 | 269, /* (171) insert_cmd ::= INSERT orconf */ 269, /* (172) insert_cmd ::= REPLACE */ 270, /* (173) idlist_opt ::= */ 270, /* (174) idlist_opt ::= LP idlist RP */ 263, /* (175) idlist ::= idlist COMMA nm */ 263, /* (176) idlist ::= nm */ 217, /* (177) expr ::= LP expr RP */ | | | | | | | < | | | > | | | < | > | | | | | | | | < | > | | | | | | | | | | | < | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | < < | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | 170619 170620 170621 170622 170623 170624 170625 170626 170627 170628 170629 170630 170631 170632 170633 170634 170635 170636 170637 170638 170639 170640 170641 170642 170643 170644 170645 170646 170647 170648 170649 170650 170651 170652 170653 170654 170655 170656 170657 170658 170659 170660 170661 170662 170663 170664 170665 170666 170667 170668 170669 170670 170671 170672 170673 170674 170675 170676 170677 170678 170679 170680 170681 170682 170683 170684 170685 170686 170687 170688 170689 170690 170691 170692 170693 170694 170695 170696 170697 170698 170699 170700 170701 170702 170703 170704 170705 170706 170707 170708 170709 170710 170711 170712 170713 170714 170715 170716 170717 170718 170719 170720 170721 170722 170723 170724 170725 170726 170727 170728 170729 170730 170731 170732 170733 170734 170735 170736 170737 170738 170739 170740 170741 170742 170743 170744 170745 170746 170747 170748 170749 170750 170751 170752 170753 170754 170755 170756 170757 170758 170759 170760 170761 170762 170763 170764 170765 170766 170767 170768 170769 170770 170771 170772 170773 170774 170775 170776 170777 170778 170779 170780 170781 170782 170783 170784 170785 170786 170787 170788 170789 170790 170791 170792 170793 170794 170795 170796 170797 170798 170799 170800 170801 170802 170803 170804 170805 170806 170807 170808 170809 170810 170811 170812 170813 170814 170815 170816 170817 170818 170819 170820 170821 170822 170823 170824 170825 170826 170827 170828 170829 170830 170831 170832 170833 170834 170835 170836 170837 170838 170839 170840 170841 170842 170843 170844 170845 170846 170847 170848 170849 170850 170851 170852 170853 170854 170855 170856 170857 |
269, /* (171) insert_cmd ::= INSERT orconf */
269, /* (172) insert_cmd ::= REPLACE */
270, /* (173) idlist_opt ::= */
270, /* (174) idlist_opt ::= LP idlist RP */
263, /* (175) idlist ::= idlist COMMA nm */
263, /* (176) idlist ::= nm */
217, /* (177) expr ::= LP expr RP */
217, /* (178) expr ::= ID|INDEXED|JOIN_KW */
217, /* (179) expr ::= nm DOT nm */
217, /* (180) expr ::= nm DOT nm DOT nm */
216, /* (181) term ::= NULL|FLOAT|BLOB */
216, /* (182) term ::= STRING */
216, /* (183) term ::= INTEGER */
217, /* (184) expr ::= VARIABLE */
217, /* (185) expr ::= expr COLLATE ID|STRING */
217, /* (186) expr ::= CAST LP expr AS typetoken RP */
217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
216, /* (191) term ::= CTIME_KW */
217, /* (192) expr ::= LP nexprlist COMMA expr RP */
217, /* (193) expr ::= expr AND expr */
217, /* (194) expr ::= expr OR expr */
217, /* (195) expr ::= expr LT|GT|GE|LE expr */
217, /* (196) expr ::= expr EQ|NE expr */
217, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
217, /* (198) expr ::= expr PLUS|MINUS expr */
217, /* (199) expr ::= expr STAR|SLASH|REM expr */
217, /* (200) expr ::= expr CONCAT expr */
274, /* (201) likeop ::= NOT LIKE_KW|MATCH */
217, /* (202) expr ::= expr likeop expr */
217, /* (203) expr ::= expr likeop expr ESCAPE expr */
217, /* (204) expr ::= expr ISNULL|NOTNULL */
217, /* (205) expr ::= expr NOT NULL */
217, /* (206) expr ::= expr IS expr */
217, /* (207) expr ::= expr IS NOT expr */
217, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
217, /* (209) expr ::= expr IS DISTINCT FROM expr */
217, /* (210) expr ::= NOT expr */
217, /* (211) expr ::= BITNOT expr */
217, /* (212) expr ::= PLUS|MINUS expr */
217, /* (213) expr ::= expr PTR expr */
275, /* (214) between_op ::= BETWEEN */
275, /* (215) between_op ::= NOT BETWEEN */
217, /* (216) expr ::= expr between_op expr AND expr */
276, /* (217) in_op ::= IN */
276, /* (218) in_op ::= NOT IN */
217, /* (219) expr ::= expr in_op LP exprlist RP */
217, /* (220) expr ::= LP select RP */
217, /* (221) expr ::= expr in_op LP select RP */
217, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
217, /* (223) expr ::= EXISTS LP select RP */
217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
279, /* (226) case_exprlist ::= WHEN expr THEN expr */
280, /* (227) case_else ::= ELSE expr */
280, /* (228) case_else ::= */
278, /* (229) case_operand ::= */
261, /* (230) exprlist ::= */
253, /* (231) nexprlist ::= nexprlist COMMA expr */
253, /* (232) nexprlist ::= expr */
277, /* (233) paren_exprlist ::= */
277, /* (234) paren_exprlist ::= LP exprlist RP */
190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
281, /* (236) uniqueflag ::= UNIQUE */
281, /* (237) uniqueflag ::= */
221, /* (238) eidlist_opt ::= */
221, /* (239) eidlist_opt ::= LP eidlist RP */
232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
232, /* (241) eidlist ::= nm collate sortorder */
282, /* (242) collate ::= */
282, /* (243) collate ::= COLLATE ID|STRING */
190, /* (244) cmd ::= DROP INDEX ifexists fullname */
190, /* (245) cmd ::= VACUUM vinto */
190, /* (246) cmd ::= VACUUM nm vinto */
283, /* (247) vinto ::= INTO expr */
283, /* (248) vinto ::= */
190, /* (249) cmd ::= PRAGMA nm dbnm */
190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
287, /* (258) trigger_time ::= BEFORE|AFTER */
287, /* (259) trigger_time ::= INSTEAD OF */
287, /* (260) trigger_time ::= */
288, /* (261) trigger_event ::= DELETE|INSERT */
288, /* (262) trigger_event ::= UPDATE */
288, /* (263) trigger_event ::= UPDATE OF idlist */
290, /* (264) when_clause ::= */
290, /* (265) when_clause ::= WHEN expr */
286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
292, /* (268) trnm ::= nm DOT nm */
293, /* (269) tridxby ::= INDEXED BY nm */
293, /* (270) tridxby ::= NOT INDEXED */
291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
291, /* (274) trigger_cmd ::= scanpt select scanpt */
217, /* (275) expr ::= RAISE LP IGNORE RP */
217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
236, /* (277) raisetype ::= ROLLBACK */
236, /* (278) raisetype ::= ABORT */
236, /* (279) raisetype ::= FAIL */
190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
190, /* (282) cmd ::= DETACH database_kw_opt expr */
295, /* (283) key_opt ::= */
295, /* (284) key_opt ::= KEY expr */
190, /* (285) cmd ::= REINDEX */
190, /* (286) cmd ::= REINDEX nm dbnm */
190, /* (287) cmd ::= ANALYZE */
190, /* (288) cmd ::= ANALYZE nm dbnm */
190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
296, /* (292) add_column_fullname ::= fullname */
190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
190, /* (294) cmd ::= create_vtab */
190, /* (295) cmd ::= create_vtab LP vtabarglist RP */
298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
300, /* (297) vtabarg ::= */
301, /* (298) vtabargtoken ::= ANY */
301, /* (299) vtabargtoken ::= lp anylist RP */
302, /* (300) lp ::= LP */
266, /* (301) with ::= WITH wqlist */
266, /* (302) with ::= WITH RECURSIVE wqlist */
305, /* (303) wqas ::= AS */
305, /* (304) wqas ::= AS MATERIALIZED */
305, /* (305) wqas ::= AS NOT MATERIALIZED */
304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
241, /* (307) wqlist ::= wqitem */
241, /* (308) wqlist ::= wqlist COMMA wqitem */
306, /* (309) windowdefn_list ::= windowdefn */
306, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */
307, /* (311) windowdefn ::= nm AS LP window RP */
308, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
308, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
308, /* (314) window ::= ORDER BY sortlist frame_opt */
308, /* (315) window ::= nm ORDER BY sortlist frame_opt */
308, /* (316) window ::= frame_opt */
308, /* (317) window ::= nm frame_opt */
309, /* (318) frame_opt ::= */
309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
315, /* (322) frame_bound_s ::= frame_bound */
315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
316, /* (324) frame_bound_e ::= frame_bound */
316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
314, /* (327) frame_bound ::= CURRENT ROW */
317, /* (328) frame_exclude_opt ::= */
317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
318, /* (330) frame_exclude ::= NO OTHERS */
318, /* (331) frame_exclude ::= CURRENT ROW */
318, /* (332) frame_exclude ::= GROUP|TIES */
251, /* (333) window_clause ::= WINDOW windowdefn_list */
273, /* (334) filter_over ::= filter_clause over_clause */
273, /* (335) filter_over ::= over_clause */
273, /* (336) filter_over ::= filter_clause */
312, /* (337) over_clause ::= OVER LP window RP */
312, /* (338) over_clause ::= OVER nm */
311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
185, /* (340) input ::= cmdlist */
186, /* (341) cmdlist ::= cmdlist ecmd */
186, /* (342) cmdlist ::= ecmd */
187, /* (343) ecmd ::= SEMI */
187, /* (344) ecmd ::= cmdx SEMI */
187, /* (345) ecmd ::= explain cmdx SEMI */
192, /* (346) trans_opt ::= */
192, /* (347) trans_opt ::= TRANSACTION */
192, /* (348) trans_opt ::= TRANSACTION nm */
194, /* (349) savepoint_opt ::= SAVEPOINT */
194, /* (350) savepoint_opt ::= */
190, /* (351) cmd ::= create_table create_table_args */
203, /* (352) table_option_set ::= table_option */
201, /* (353) columnlist ::= columnlist COMMA columnname carglist */
201, /* (354) columnlist ::= columnname carglist */
193, /* (355) nm ::= ID|INDEXED|JOIN_KW */
193, /* (356) nm ::= STRING */
208, /* (357) typetoken ::= typename */
209, /* (358) typename ::= ID|STRING */
210, /* (359) signed ::= plus_num */
210, /* (360) signed ::= minus_num */
207, /* (361) carglist ::= carglist ccons */
207, /* (362) carglist ::= */
215, /* (363) ccons ::= NULL onconf */
215, /* (364) ccons ::= GENERATED ALWAYS AS generated */
215, /* (365) ccons ::= AS generated */
202, /* (366) conslist_opt ::= COMMA conslist */
228, /* (367) conslist ::= conslist tconscomma tcons */
228, /* (368) conslist ::= tcons */
229, /* (369) tconscomma ::= */
233, /* (370) defer_subclause_opt ::= defer_subclause */
235, /* (371) resolvetype ::= raisetype */
239, /* (372) selectnowith ::= oneselect */
240, /* (373) oneselect ::= values */
254, /* (374) sclp ::= selcollist COMMA */
255, /* (375) as ::= ID|STRING */
264, /* (376) indexed_opt ::= indexed_by */
272, /* (377) returning ::= */
217, /* (378) expr ::= term */
274, /* (379) likeop ::= LIKE_KW|MATCH */
278, /* (380) case_operand ::= expr */
261, /* (381) exprlist ::= nexprlist */
284, /* (382) nmnum ::= plus_num */
284, /* (383) nmnum ::= nm */
284, /* (384) nmnum ::= ON */
284, /* (385) nmnum ::= DELETE */
284, /* (386) nmnum ::= DEFAULT */
211, /* (387) plus_num ::= INTEGER|FLOAT */
289, /* (388) foreach_clause ::= */
289, /* (389) foreach_clause ::= FOR EACH ROW */
292, /* (390) trnm ::= nm */
293, /* (391) tridxby ::= */
294, /* (392) database_kw_opt ::= DATABASE */
294, /* (393) database_kw_opt ::= */
297, /* (394) kwcolumn_opt ::= */
297, /* (395) kwcolumn_opt ::= COLUMNKW */
299, /* (396) vtabarglist ::= vtabarg */
299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
300, /* (398) vtabarg ::= vtabarg vtabargtoken */
303, /* (399) anylist ::= */
303, /* (400) anylist ::= anylist LP anylist RP */
303, /* (401) anylist ::= anylist ANY */
266, /* (402) 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 */
|
| ︙ | ︙ | |||
169793 169794 169795 169796 169797 169798 169799 |
-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 */
| | | | | | | < | | | | | | | | | > | | | | | | | < | | | | > | | < | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > < < | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | 171027 171028 171029 171030 171031 171032 171033 171034 171035 171036 171037 171038 171039 171040 171041 171042 171043 171044 171045 171046 171047 171048 171049 171050 171051 171052 171053 171054 171055 171056 171057 171058 171059 171060 171061 171062 171063 171064 171065 171066 171067 171068 171069 171070 171071 171072 171073 171074 171075 171076 171077 171078 171079 171080 171081 171082 171083 171084 171085 171086 171087 171088 171089 171090 171091 171092 171093 171094 171095 171096 171097 171098 171099 171100 171101 171102 171103 171104 171105 171106 171107 171108 171109 171110 171111 171112 171113 171114 171115 171116 171117 171118 171119 171120 171121 171122 171123 171124 171125 171126 171127 171128 171129 171130 171131 171132 171133 171134 171135 171136 171137 171138 171139 171140 171141 171142 171143 171144 171145 171146 171147 171148 171149 171150 171151 171152 171153 171154 171155 171156 171157 171158 171159 171160 171161 171162 171163 171164 171165 171166 171167 171168 171169 171170 171171 171172 171173 171174 171175 171176 171177 171178 171179 171180 171181 171182 171183 171184 171185 171186 171187 171188 171189 171190 171191 171192 171193 171194 171195 171196 171197 171198 171199 171200 171201 171202 171203 171204 171205 171206 171207 171208 171209 171210 171211 171212 171213 171214 171215 171216 171217 171218 171219 171220 171221 171222 171223 171224 171225 171226 171227 171228 171229 171230 171231 171232 171233 171234 171235 171236 171237 171238 171239 171240 171241 171242 171243 171244 171245 171246 171247 171248 171249 171250 171251 171252 171253 171254 171255 171256 171257 171258 171259 171260 171261 171262 171263 171264 171265 |
-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|JOIN_KW */
-3, /* (179) expr ::= nm DOT nm */
-5, /* (180) expr ::= nm DOT nm DOT nm */
-1, /* (181) term ::= NULL|FLOAT|BLOB */
-1, /* (182) term ::= STRING */
-1, /* (183) term ::= INTEGER */
-1, /* (184) expr ::= VARIABLE */
-3, /* (185) expr ::= expr COLLATE ID|STRING */
-6, /* (186) expr ::= CAST LP expr AS typetoken RP */
-5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
-4, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
-6, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
-5, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
-1, /* (191) term ::= CTIME_KW */
-5, /* (192) expr ::= LP nexprlist COMMA expr RP */
-3, /* (193) expr ::= expr AND expr */
-3, /* (194) expr ::= expr OR expr */
-3, /* (195) expr ::= expr LT|GT|GE|LE expr */
-3, /* (196) expr ::= expr EQ|NE expr */
-3, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
-3, /* (198) expr ::= expr PLUS|MINUS expr */
-3, /* (199) expr ::= expr STAR|SLASH|REM expr */
-3, /* (200) expr ::= expr CONCAT expr */
-2, /* (201) likeop ::= NOT LIKE_KW|MATCH */
-3, /* (202) expr ::= expr likeop expr */
-5, /* (203) expr ::= expr likeop expr ESCAPE expr */
-2, /* (204) expr ::= expr ISNULL|NOTNULL */
-3, /* (205) expr ::= expr NOT NULL */
-3, /* (206) expr ::= expr IS expr */
-4, /* (207) expr ::= expr IS NOT expr */
-6, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
-5, /* (209) expr ::= expr IS DISTINCT FROM expr */
-2, /* (210) expr ::= NOT expr */
-2, /* (211) expr ::= BITNOT expr */
-2, /* (212) expr ::= PLUS|MINUS expr */
-3, /* (213) expr ::= expr PTR expr */
-1, /* (214) between_op ::= BETWEEN */
-2, /* (215) between_op ::= NOT BETWEEN */
-5, /* (216) expr ::= expr between_op expr AND expr */
-1, /* (217) in_op ::= IN */
-2, /* (218) in_op ::= NOT IN */
-5, /* (219) expr ::= expr in_op LP exprlist RP */
-3, /* (220) expr ::= LP select RP */
-5, /* (221) expr ::= expr in_op LP select RP */
-5, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
-4, /* (223) expr ::= EXISTS LP select RP */
-5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
-5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
-4, /* (226) case_exprlist ::= WHEN expr THEN expr */
-2, /* (227) case_else ::= ELSE expr */
0, /* (228) case_else ::= */
0, /* (229) case_operand ::= */
0, /* (230) exprlist ::= */
-3, /* (231) nexprlist ::= nexprlist COMMA expr */
-1, /* (232) nexprlist ::= expr */
0, /* (233) paren_exprlist ::= */
-3, /* (234) paren_exprlist ::= LP exprlist RP */
-12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
-1, /* (236) uniqueflag ::= UNIQUE */
0, /* (237) uniqueflag ::= */
0, /* (238) eidlist_opt ::= */
-3, /* (239) eidlist_opt ::= LP eidlist RP */
-5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
-3, /* (241) eidlist ::= nm collate sortorder */
0, /* (242) collate ::= */
-2, /* (243) collate ::= COLLATE ID|STRING */
-4, /* (244) cmd ::= DROP INDEX ifexists fullname */
-2, /* (245) cmd ::= VACUUM vinto */
-3, /* (246) cmd ::= VACUUM nm vinto */
-2, /* (247) vinto ::= INTO expr */
0, /* (248) vinto ::= */
-3, /* (249) cmd ::= PRAGMA nm dbnm */
-5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
-6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
-5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
-6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
-2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
-2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
-5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
-11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
-1, /* (258) trigger_time ::= BEFORE|AFTER */
-2, /* (259) trigger_time ::= INSTEAD OF */
0, /* (260) trigger_time ::= */
-1, /* (261) trigger_event ::= DELETE|INSERT */
-1, /* (262) trigger_event ::= UPDATE */
-3, /* (263) trigger_event ::= UPDATE OF idlist */
0, /* (264) when_clause ::= */
-2, /* (265) when_clause ::= WHEN expr */
-3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
-2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
-3, /* (268) trnm ::= nm DOT nm */
-3, /* (269) tridxby ::= INDEXED BY nm */
-2, /* (270) tridxby ::= NOT INDEXED */
-9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
-6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-3, /* (274) trigger_cmd ::= scanpt select scanpt */
-4, /* (275) expr ::= RAISE LP IGNORE RP */
-6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
-1, /* (277) raisetype ::= ROLLBACK */
-1, /* (278) raisetype ::= ABORT */
-1, /* (279) raisetype ::= FAIL */
-4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
-6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
-3, /* (282) cmd ::= DETACH database_kw_opt expr */
0, /* (283) key_opt ::= */
-2, /* (284) key_opt ::= KEY expr */
-1, /* (285) cmd ::= REINDEX */
-3, /* (286) cmd ::= REINDEX nm dbnm */
-1, /* (287) cmd ::= ANALYZE */
-3, /* (288) cmd ::= ANALYZE nm dbnm */
-6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
-7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
-6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
-1, /* (292) add_column_fullname ::= fullname */
-8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
-1, /* (294) cmd ::= create_vtab */
-4, /* (295) cmd ::= create_vtab LP vtabarglist RP */
-8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
0, /* (297) vtabarg ::= */
-1, /* (298) vtabargtoken ::= ANY */
-3, /* (299) vtabargtoken ::= lp anylist RP */
-1, /* (300) lp ::= LP */
-2, /* (301) with ::= WITH wqlist */
-3, /* (302) with ::= WITH RECURSIVE wqlist */
-1, /* (303) wqas ::= AS */
-2, /* (304) wqas ::= AS MATERIALIZED */
-3, /* (305) wqas ::= AS NOT MATERIALIZED */
-6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
-1, /* (307) wqlist ::= wqitem */
-3, /* (308) wqlist ::= wqlist COMMA wqitem */
-1, /* (309) windowdefn_list ::= windowdefn */
-3, /* (310) windowdefn_list ::= windowdefn_list COMMA windowdefn */
-5, /* (311) windowdefn ::= nm AS LP window RP */
-5, /* (312) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
-6, /* (313) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
-4, /* (314) window ::= ORDER BY sortlist frame_opt */
-5, /* (315) window ::= nm ORDER BY sortlist frame_opt */
-1, /* (316) window ::= frame_opt */
-2, /* (317) window ::= nm frame_opt */
0, /* (318) frame_opt ::= */
-3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
-6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
-1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */
-1, /* (322) frame_bound_s ::= frame_bound */
-2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */
-1, /* (324) frame_bound_e ::= frame_bound */
-2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */
-2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */
-2, /* (327) frame_bound ::= CURRENT ROW */
0, /* (328) frame_exclude_opt ::= */
-2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */
-2, /* (330) frame_exclude ::= NO OTHERS */
-2, /* (331) frame_exclude ::= CURRENT ROW */
-1, /* (332) frame_exclude ::= GROUP|TIES */
-2, /* (333) window_clause ::= WINDOW windowdefn_list */
-2, /* (334) filter_over ::= filter_clause over_clause */
-1, /* (335) filter_over ::= over_clause */
-1, /* (336) filter_over ::= filter_clause */
-4, /* (337) over_clause ::= OVER LP window RP */
-2, /* (338) over_clause ::= OVER nm */
-5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */
-1, /* (340) input ::= cmdlist */
-2, /* (341) cmdlist ::= cmdlist ecmd */
-1, /* (342) cmdlist ::= ecmd */
-1, /* (343) ecmd ::= SEMI */
-2, /* (344) ecmd ::= cmdx SEMI */
-3, /* (345) ecmd ::= explain cmdx SEMI */
0, /* (346) trans_opt ::= */
-1, /* (347) trans_opt ::= TRANSACTION */
-2, /* (348) trans_opt ::= TRANSACTION nm */
-1, /* (349) savepoint_opt ::= SAVEPOINT */
0, /* (350) savepoint_opt ::= */
-2, /* (351) cmd ::= create_table create_table_args */
-1, /* (352) table_option_set ::= table_option */
-4, /* (353) columnlist ::= columnlist COMMA columnname carglist */
-2, /* (354) columnlist ::= columnname carglist */
-1, /* (355) nm ::= ID|INDEXED|JOIN_KW */
-1, /* (356) nm ::= STRING */
-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 */
-1, /* (376) indexed_opt ::= indexed_by */
0, /* (377) returning ::= */
-1, /* (378) expr ::= term */
-1, /* (379) likeop ::= LIKE_KW|MATCH */
-1, /* (380) case_operand ::= expr */
-1, /* (381) exprlist ::= nexprlist */
-1, /* (382) nmnum ::= plus_num */
-1, /* (383) nmnum ::= nm */
-1, /* (384) nmnum ::= ON */
-1, /* (385) nmnum ::= DELETE */
-1, /* (386) nmnum ::= DEFAULT */
-1, /* (387) plus_num ::= INTEGER|FLOAT */
0, /* (388) foreach_clause ::= */
-3, /* (389) foreach_clause ::= FOR EACH ROW */
-1, /* (390) trnm ::= nm */
0, /* (391) tridxby ::= */
-1, /* (392) database_kw_opt ::= DATABASE */
0, /* (393) database_kw_opt ::= */
0, /* (394) kwcolumn_opt ::= */
-1, /* (395) kwcolumn_opt ::= COLUMNKW */
-1, /* (396) vtabarglist ::= vtabarg */
-3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */
-2, /* (398) vtabarg ::= vtabarg vtabargtoken */
0, /* (399) anylist ::= */
-4, /* (400) anylist ::= anylist LP anylist RP */
-2, /* (401) anylist ::= anylist ANY */
0, /* (402) with ::= */
};
static void yy_accept(yyParser*); /* Forward Declaration */
/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
|
| ︙ | ︙ | |||
170079 170080 170081 170082 170083 170084 170085 |
break;
case 4: /* transtype ::= */
{yymsp[1].minor.yy394 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
| | | 171311 171312 171313 171314 171315 171316 171317 171318 171319 171320 171321 171322 171323 171324 171325 |
break;
case 4: /* transtype ::= */
{yymsp[1].minor.yy394 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321);
{yymsp[0].minor.yy394 = 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 */
|
| ︙ | ︙ | |||
170116 170117 170118 170119 170120 170121 170122 |
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);
| | | 171348 171349 171350 171351 171352 171353 171354 171355 171356 171357 171358 171359 171360 171361 171362 |
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 242: /* collate ::= */ yytestcase(yyruleno==242);
{yymsp[1].minor.yy394 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
{yymsp[-2].minor.yy394 = 1;}
break;
case 17: /* temp ::= TEMP */
{yymsp[0].minor.yy394 = pParse->db->init.busy==0;}
|
| ︙ | ︙ | |||
170300 170301 170302 170303 170304 170305 170306 |
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.yy394 = yymsp[0].minor.yy394;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
| | | | | 171532 171533 171534 171535 171536 171537 171538 171539 171540 171541 171542 171543 171544 171545 171546 171547 171548 |
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.yy394 = yymsp[0].minor.yy394;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215);
case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218);
case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243);
{yymsp[-1].minor.yy394 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
{yymsp[-1].minor.yy394 = 0;}
break;
case 66: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
|
| ︙ | ︙ | |||
170452 170453 170454 170455 170456 170457 170458 |
break;
case 97: /* distinct ::= ALL */
{yymsp[0].minor.yy394 = SF_All;}
break;
case 99: /* sclp ::= */
case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
| | | | | 171684 171685 171686 171687 171688 171689 171690 171691 171692 171693 171694 171695 171696 171697 171698 171699 171700 |
break;
case 97: /* distinct ::= ALL */
{yymsp[0].minor.yy394 = SF_All;}
break;
case 99: /* sclp ::= */
case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
case 230: /* exprlist ::= */ yytestcase(yyruleno==230);
case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233);
case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238);
{yymsp[1].minor.yy322 = 0;}
break;
case 100: /* selcollist ::= sclp scanpt expr scanpt as */
{
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1);
sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522);
|
| ︙ | ︙ | |||
170480 170481 170482 170483 170484 170485 170486 |
Expr *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
}
break;
case 103: /* as ::= AS nm */
case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
| | | | 171712 171713 171714 171715 171716 171717 171718 171719 171720 171721 171722 171723 171724 171725 171726 171727 |
Expr *pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
}
break;
case 103: /* as ::= AS nm */
case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254);
case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 105: /* from ::= */
case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
{yymsp[1].minor.yy131 = 0;}
break;
case 106: /* from ::= FROM seltablist */
|
| ︙ | ︙ | |||
170525 170526 170527 170528 170529 170530 170531 |
yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561);
}
break;
case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131;
| | | 171757 171758 171759 171760 171761 171762 171763 171764 171765 171766 171767 171768 171769 171770 171771 |
yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561);
}
break;
case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131;
}else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){
yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
if( yymsp[-5].minor.yy131 ){
SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1];
SrcItem *pOld = yymsp[-3].minor.yy131->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
|
| ︙ | ︙ | |||
170653 170654 170655 170656 170657 170658 170659 |
case 140: /* nulls ::= NULLS LAST */
{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;}
break;
case 144: /* having_opt ::= */
case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
| | | | | | | 171885 171886 171887 171888 171889 171890 171891 171892 171893 171894 171895 171896 171897 171898 171899 171900 171901 171902 171903 171904 171905 171906 171907 171908 |
case 140: /* nulls ::= NULLS LAST */
{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;}
break;
case 144: /* having_opt ::= */
case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
case 228: /* case_else ::= */ yytestcase(yyruleno==228);
case 229: /* case_operand ::= */ yytestcase(yyruleno==229);
case 248: /* vinto ::= */ yytestcase(yyruleno==248);
{yymsp[1].minor.yy528 = 0;}
break;
case 145: /* having_opt ::= HAVING expr */
case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227);
case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247);
{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
break;
case 147: /* limit_opt ::= LIMIT expr */
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);}
break;
case 148: /* limit_opt ::= LIMIT expr OFFSET expr */
{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
|
| ︙ | ︙ | |||
170774 170775 170776 170777 170778 170779 170780 |
break;
case 176: /* idlist ::= nm */
{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
case 177: /* expr ::= LP expr RP */
{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;}
break;
| | < | | | | | | | 172006 172007 172008 172009 172010 172011 172012 172013 172014 172015 172016 172017 172018 172019 172020 172021 172022 172023 172024 172025 172026 172027 172028 172029 172030 172031 172032 172033 172034 172035 172036 172037 172038 172039 172040 172041 172042 172043 172044 172045 172046 172047 172048 172049 172050 172051 172052 172053 172054 172055 |
break;
case 176: /* idlist ::= nm */
{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
case 177: /* expr ::= LP expr RP */
{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;}
break;
case 178: /* expr ::= ID|INDEXED|JOIN_KW */
{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 179: /* expr ::= nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
yymsp[-2].minor.yy528 = yylhsminor.yy528;
break;
case 180: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
Expr *temp3 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, 0, temp1);
}
yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
case 181: /* term ::= NULL|FLOAT|BLOB */
case 182: /* term ::= STRING */ yytestcase(yyruleno==182);
{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 183: /* term ::= INTEGER */
{
yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
}
yymsp[0].minor.yy528 = yylhsminor.yy528;
break;
case 184: /* 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.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n);
}else{
/* When doing a nested parse, one can include terms in an expression
|
| ︙ | ︙ | |||
170832 170833 170834 170835 170836 170837 170838 |
}else{
yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable);
}
}
}
break;
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | 172063 172064 172065 172066 172067 172068 172069 172070 172071 172072 172073 172074 172075 172076 172077 172078 172079 172080 172081 172082 172083 172084 172085 172086 172087 172088 172089 172090 172091 172092 172093 172094 172095 172096 172097 172098 172099 172100 172101 172102 172103 172104 172105 172106 172107 172108 172109 172110 172111 172112 172113 172114 172115 172116 172117 172118 172119 172120 172121 172122 172123 172124 172125 172126 172127 172128 172129 172130 172131 172132 172133 172134 172135 172136 172137 172138 172139 172140 172141 172142 172143 172144 172145 172146 172147 172148 172149 172150 172151 172152 172153 172154 172155 172156 172157 172158 172159 172160 172161 172162 172163 172164 172165 172166 172167 172168 172169 172170 172171 172172 172173 172174 172175 172176 172177 172178 172179 172180 172181 172182 172183 172184 172185 172186 172187 172188 172189 172190 172191 172192 172193 172194 172195 172196 172197 172198 172199 172200 172201 172202 172203 172204 172205 172206 172207 172208 172209 172210 172211 172212 172213 172214 172215 172216 172217 172218 172219 172220 172221 172222 172223 172224 172225 172226 172227 172228 172229 172230 172231 172232 172233 172234 172235 172236 172237 172238 172239 172240 172241 172242 172243 172244 172245 172246 172247 172248 172249 172250 172251 172252 172253 172254 172255 172256 172257 172258 172259 172260 172261 172262 172263 172264 172265 172266 172267 172268 172269 172270 172271 172272 172273 172274 172275 172276 172277 172278 172279 172280 172281 172282 172283 172284 172285 172286 172287 172288 172289 172290 172291 172292 172293 172294 172295 172296 172297 172298 172299 172300 172301 172302 172303 172304 172305 172306 172307 172308 172309 172310 172311 172312 172313 172314 172315 172316 172317 172318 172319 172320 172321 172322 172323 172324 172325 172326 172327 172328 172329 172330 172331 172332 172333 172334 172335 172336 172337 172338 172339 172340 172341 172342 172343 172344 172345 172346 172347 172348 172349 172350 172351 172352 172353 172354 172355 172356 172357 172358 172359 172360 172361 172362 172363 172364 172365 172366 172367 172368 172369 172370 172371 172372 172373 172374 172375 172376 172377 172378 172379 172380 172381 172382 172383 172384 172385 172386 172387 172388 172389 172390 172391 172392 172393 172394 172395 172396 172397 172398 172399 172400 172401 172402 172403 172404 172405 172406 172407 172408 172409 172410 172411 172412 172413 172414 172415 172416 172417 172418 172419 172420 172421 172422 172423 172424 172425 172426 172427 172428 172429 172430 172431 172432 172433 172434 172435 172436 172437 172438 172439 172440 172441 172442 172443 172444 172445 172446 172447 172448 172449 172450 172451 172452 172453 172454 172455 172456 172457 172458 172459 172460 172461 172462 172463 172464 172465 172466 172467 172468 172469 172470 172471 172472 172473 172474 172475 172476 172477 172478 172479 172480 172481 172482 172483 172484 172485 172486 172487 172488 172489 172490 172491 172492 172493 172494 172495 172496 172497 172498 172499 172500 172501 172502 172503 172504 172505 172506 172507 172508 172509 172510 172511 172512 172513 172514 172515 172516 172517 172518 172519 172520 172521 172522 172523 172524 172525 172526 172527 172528 172529 172530 172531 172532 172533 172534 172535 172536 172537 172538 172539 172540 172541 172542 172543 172544 172545 172546 172547 172548 172549 172550 172551 172552 172553 172554 172555 172556 172557 172558 172559 172560 172561 172562 172563 172564 172565 172566 172567 172568 172569 172570 172571 172572 172573 172574 172575 172576 172577 172578 172579 172580 172581 172582 172583 172584 172585 172586 172587 172588 172589 172590 172591 172592 172593 172594 172595 172596 172597 172598 172599 172600 172601 172602 172603 172604 172605 172606 172607 172608 172609 172610 172611 172612 172613 172614 172615 172616 172617 172618 172619 172620 172621 172622 172623 172624 172625 172626 172627 172628 172629 172630 172631 172632 172633 172634 172635 172636 172637 172638 172639 172640 172641 172642 172643 172644 172645 172646 172647 172648 172649 172650 172651 172652 172653 172654 172655 172656 172657 172658 172659 172660 172661 172662 172663 172664 172665 172666 172667 172668 172669 172670 172671 172672 172673 172674 172675 172676 172677 172678 172679 172680 172681 172682 172683 172684 172685 172686 172687 172688 172689 172690 172691 172692 172693 172694 172695 172696 172697 172698 172699 172700 172701 172702 172703 172704 172705 172706 172707 172708 172709 172710 172711 172712 172713 172714 172715 172716 172717 172718 172719 172720 172721 172722 172723 172724 172725 172726 172727 172728 172729 172730 172731 172732 172733 172734 172735 172736 172737 172738 172739 172740 172741 172742 172743 172744 172745 172746 172747 172748 172749 172750 172751 172752 172753 172754 172755 172756 172757 172758 172759 172760 172761 172762 172763 172764 172765 172766 172767 172768 172769 172770 172771 172772 172773 172774 172775 172776 172777 172778 172779 172780 172781 172782 172783 172784 172785 172786 172787 172788 172789 172790 172791 172792 172793 172794 172795 172796 172797 172798 172799 172800 172801 172802 172803 172804 172805 172806 172807 172808 172809 172810 172811 172812 172813 172814 172815 172816 172817 172818 172819 172820 172821 172822 |
}else{
yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable);
}
}
}
break;
case 185: /* expr ::= expr COLLATE ID|STRING */
{
yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1);
}
break;
case 186: /* expr ::= CAST LP expr AS typetoken RP */
{
yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0);
}
break;
case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394);
}
yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
case 188: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
yymsp[-3].minor.yy528 = yylhsminor.yy528;
break;
case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394);
sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
}
yymsp[-5].minor.yy528 = yylhsminor.yy528;
break;
case 190: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
}
yymsp[-4].minor.yy528 = yylhsminor.yy528;
break;
case 191: /* term ::= CTIME_KW */
{
yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
yymsp[0].minor.yy528 = yylhsminor.yy528;
break;
case 192: /* expr ::= LP nexprlist COMMA expr RP */
{
ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528);
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
if( yymsp[-4].minor.yy528 ){
yymsp[-4].minor.yy528->x.pList = pList;
if( ALWAYS(pList->nExpr) ){
yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate;
}
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
case 193: /* expr ::= expr AND expr */
{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
break;
case 194: /* expr ::= expr OR expr */
case 195: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==195);
case 196: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==196);
case 197: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==197);
case 198: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==198);
case 199: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==199);
case 200: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==200);
{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
break;
case 201: /* 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 202: /* 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.yy528);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528);
yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0);
if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc;
}
break;
case 203: /* 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.yy528);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc;
}
break;
case 204: /* expr ::= expr ISNULL|NOTNULL */
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);}
break;
case 205: /* expr ::= expr NOT NULL */
{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);}
break;
case 206: /* expr ::= expr IS expr */
{
yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL);
}
break;
case 207: /* expr ::= expr IS NOT expr */
{
yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL);
}
break;
case 208: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL);
}
break;
case 209: /* expr ::= expr IS DISTINCT FROM expr */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528);
binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL);
}
break;
case 210: /* expr ::= NOT expr */
case 211: /* expr ::= BITNOT expr */ yytestcase(yyruleno==211);
{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/}
break;
case 212: /* expr ::= PLUS|MINUS expr */
{
yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0);
/*A-overwrites-B*/
}
break;
case 213: /* expr ::= expr PTR expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528);
pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528);
yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
}
yymsp[-2].minor.yy528 = yylhsminor.yy528;
break;
case 214: /* between_op ::= BETWEEN */
case 217: /* in_op ::= IN */ yytestcase(yyruleno==217);
{yymsp[0].minor.yy394 = 0;}
break;
case 216: /* expr ::= expr between_op expr AND expr */
{
ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0);
if( yymsp[-4].minor.yy528 ){
yymsp[-4].minor.yy528->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
case 219: /* expr ::= expr in_op LP exprlist RP */
{
if( yymsp[-1].minor.yy322==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.yy528);
yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false");
if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528);
}else{
Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr;
if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){
yymsp[-1].minor.yy322->a[0].pExpr = 0;
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS);
}else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect);
pRHS->x.pSelect = 0;
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
}else{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
if( yymsp[-4].minor.yy528==0 ){
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
}else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){
int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr;
Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322);
if( pSelectRHS ){
parserDoubleLinkSelect(pParse, pSelectRHS);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS);
}
}else{
yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322;
sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
}
}
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
}
break;
case 220: /* expr ::= LP select RP */
{
yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47);
}
break;
case 221: /* expr ::= expr in_op LP select RP */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47);
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
case 222: /* 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.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322);
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect);
if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
}
break;
case 223: /* expr ::= EXISTS LP select RP */
{
Expr *p;
p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47);
}
break;
case 224: /* expr ::= CASE case_operand case_exprlist case_else END */
{
yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0);
if( yymsp[-4].minor.yy528 ){
yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322;
sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
}else{
sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322);
sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
}
}
break;
case 225: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
}
break;
case 226: /* case_exprlist ::= WHEN expr THEN expr */
{
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
}
break;
case 231: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
break;
case 232: /* nexprlist ::= expr */
{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
break;
case 234: /* paren_exprlist ::= LP exprlist RP */
case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239);
{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
break;
case 235: /* 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.yy322, yymsp[-10].minor.yy394,
&yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
case 236: /* uniqueflag ::= UNIQUE */
case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278);
{yymsp[0].minor.yy394 = OE_Abort;}
break;
case 237: /* uniqueflag ::= */
{yymsp[1].minor.yy394 = OE_None;}
break;
case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
}
break;
case 241: /* eidlist ::= nm collate sortorder */
{
yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
}
break;
case 244: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
break;
case 245: /* cmd ::= VACUUM vinto */
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
break;
case 246: /* cmd ::= VACUUM nm vinto */
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
break;
case 249: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
case 253: /* 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 256: /* 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.yy33, &all);
}
break;
case 257: /* 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.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
case 258: /* trigger_time ::= BEFORE|AFTER */
{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
break;
case 259: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
break;
case 260: /* trigger_time ::= */
{ yymsp[1].minor.yy394 = TK_BEFORE; }
break;
case 261: /* trigger_event ::= DELETE|INSERT */
case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262);
{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
break;
case 263: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
break;
case 264: /* when_clause ::= */
case 283: /* key_opt ::= */ yytestcase(yyruleno==283);
{ yymsp[1].minor.yy528 = 0; }
break;
case 265: /* when_clause ::= WHEN expr */
case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284);
{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
break;
case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
assert( yymsp[-2].minor.yy33!=0 );
yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
assert( yymsp[-1].minor.yy33!=0 );
yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
}
break;
case 268: /* 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 269: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
case 270: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);}
yymsp[-8].minor.yy33 = yylhsminor.yy33;
break;
case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/
}
yymsp[-7].minor.yy33 = yylhsminor.yy33;
break;
case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
yymsp[-5].minor.yy33 = yylhsminor.yy33;
break;
case 274: /* trigger_cmd ::= scanpt select scanpt */
{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
yymsp[-2].minor.yy33 = yylhsminor.yy33;
break;
case 275: /* expr ::= RAISE LP IGNORE RP */
{
yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
if( yymsp[-3].minor.yy528 ){
yymsp[-3].minor.yy528->affExpr = OE_Ignore;
}
}
break;
case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
if( yymsp[-5].minor.yy528 ) {
yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394;
}
}
break;
case 277: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy394 = OE_Rollback;}
break;
case 279: /* raisetype ::= FAIL */
{yymsp[0].minor.yy394 = OE_Fail;}
break;
case 280: /* cmd ::= DROP TRIGGER ifexists fullname */
{
sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
}
break;
case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
}
break;
case 282: /* cmd ::= DETACH database_kw_opt expr */
{
sqlite3Detach(pParse, yymsp[0].minor.yy528);
}
break;
case 285: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
case 286: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
case 287: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
case 288: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
}
break;
case 290: /* 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 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
}
break;
case 292: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
}
break;
case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
case 294: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
case 295: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
case 296: /* 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.yy394);
}
break;
case 297: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
case 298: /* vtabargtoken ::= ANY */
case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299);
case 300: /* lp ::= LP */ yytestcase(yyruleno==300);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
case 301: /* with ::= WITH wqlist */
case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302);
{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
break;
case 303: /* wqas ::= AS */
{yymsp[0].minor.yy516 = M10d_Any;}
break;
case 304: /* wqas ::= AS MATERIALIZED */
{yymsp[-1].minor.yy516 = M10d_Yes;}
break;
case 305: /* wqas ::= AS NOT MATERIALIZED */
{yymsp[-2].minor.yy516 = M10d_No;}
break;
case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */
{
yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/
}
break;
case 307: /* wqlist ::= wqitem */
{
yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
}
break;
case 308: /* wqlist ::= wqlist COMMA wqitem */
{
yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
}
break;
case 309: /* windowdefn_list ::= windowdefn */
{ yylhsminor.yy41 = yymsp[0].minor.yy41; }
yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
case 310: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
assert( yymsp[0].minor.yy41!=0 );
sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41;
yylhsminor.yy41 = yymsp[0].minor.yy41;
}
yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
case 311: /* windowdefn ::= nm AS LP window RP */
{
if( ALWAYS(yymsp[-1].minor.yy41) ){
yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
yylhsminor.yy41 = yymsp[-1].minor.yy41;
}
yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
case 312: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
}
break;
case 313: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
}
yymsp[-5].minor.yy41 = yylhsminor.yy41;
break;
case 314: /* window ::= ORDER BY sortlist frame_opt */
{
yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
}
break;
case 315: /* window ::= nm ORDER BY sortlist frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
}
yymsp[-4].minor.yy41 = yylhsminor.yy41;
break;
case 316: /* window ::= frame_opt */
case 335: /* filter_over ::= over_clause */ yytestcase(yyruleno==335);
{
yylhsminor.yy41 = yymsp[0].minor.yy41;
}
yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
case 317: /* window ::= nm frame_opt */
{
yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
}
yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
case 318: /* frame_opt ::= */
{
yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516);
}
yymsp[-2].minor.yy41 = yylhsminor.yy41;
break;
case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516);
}
yymsp[-5].minor.yy41 = yylhsminor.yy41;
break;
case 322: /* frame_bound_s ::= frame_bound */
case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324);
{yylhsminor.yy595 = yymsp[0].minor.yy595;}
yymsp[0].minor.yy595 = yylhsminor.yy595;
break;
case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */
case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325);
case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327);
{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */
{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
yymsp[-1].minor.yy595 = yylhsminor.yy595;
break;
case 328: /* frame_exclude_opt ::= */
{yymsp[1].minor.yy516 = 0;}
break;
case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
break;
case 330: /* frame_exclude ::= NO OTHERS */
case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331);
{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
break;
case 332: /* frame_exclude ::= GROUP|TIES */
{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 333: /* window_clause ::= WINDOW windowdefn_list */
{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
break;
case 334: /* filter_over ::= filter_clause over_clause */
{
if( yymsp[0].minor.yy41 ){
yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
}else{
sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
}
yylhsminor.yy41 = yymsp[0].minor.yy41;
}
yymsp[-1].minor.yy41 = yylhsminor.yy41;
break;
case 336: /* filter_over ::= filter_clause */
{
yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yylhsminor.yy41 ){
yylhsminor.yy41->eFrmType = TK_FILTER;
yylhsminor.yy41->pFilter = yymsp[0].minor.yy528;
}else{
sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528);
}
}
yymsp[0].minor.yy41 = yylhsminor.yy41;
break;
case 337: /* over_clause ::= OVER LP window RP */
{
yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
assert( yymsp[-3].minor.yy41!=0 );
}
break;
case 338: /* over_clause ::= OVER nm */
{
yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
if( yymsp[-1].minor.yy41 ){
yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
break;
case 339: /* filter_clause ::= FILTER LP WHERE expr RP */
{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
break;
default:
/* (340) input ::= cmdlist */ yytestcase(yyruleno==340);
/* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341);
/* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342);
/* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343);
/* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344);
/* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345);
/* (346) trans_opt ::= */ yytestcase(yyruleno==346);
/* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347);
/* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348);
/* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349);
/* (350) savepoint_opt ::= */ yytestcase(yyruleno==350);
/* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351);
/* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352);
/* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353);
/* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354);
/* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355);
/* (356) nm ::= STRING */ 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) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376);
/* (377) returning ::= */ yytestcase(yyruleno==377);
/* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378);
/* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379);
/* (380) case_operand ::= expr */ yytestcase(yyruleno==380);
/* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381);
/* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382);
/* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383);
/* (384) nmnum ::= ON */ yytestcase(yyruleno==384);
/* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385);
/* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386);
/* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387);
/* (388) foreach_clause ::= */ yytestcase(yyruleno==388);
/* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389);
/* (390) trnm ::= nm */ yytestcase(yyruleno==390);
/* (391) tridxby ::= */ yytestcase(yyruleno==391);
/* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392);
/* (393) database_kw_opt ::= */ yytestcase(yyruleno==393);
/* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394);
/* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395);
/* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396);
/* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397);
/* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398);
/* (399) anylist ::= */ yytestcase(yyruleno==399);
/* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400);
/* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401);
/* (402) with ::= */ yytestcase(yyruleno==402);
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);
|
| ︙ | ︙ | |||
172151 172152 172153 172154 172155 172156 172157 |
50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83,
37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0,
132, 0, 98, 38, 39, 0, 20, 45, 117, 93,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
| | | | | | 173384 173385 173386 173387 173388 173389 173390 173391 173392 173393 173394 173395 173396 173397 173398 173399 173400 173401 173402 173403 173404 173405 173406 173407 173408 173409 173410 173411 173412 173413 173414 173415 173416 173417 173418 173419 173420 173421 173422 173423 173424 173425 173426 173427 173428 173429 173430 173431 173432 173433 173434 173435 173436 173437 173438 173439 173440 173441 173442 173443 173444 |
50, 124, 0, 100, 0, 18, 121, 144, 56, 130, 139, 88, 83,
37, 30, 126, 0, 0, 108, 51, 131, 128, 0, 34, 0, 0,
132, 0, 98, 38, 39, 0, 20, 45, 117, 93,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
static const unsigned char aKWNext[148] = {0,
0, 0, 0, 0, 4, 0, 43, 0, 0, 106, 114, 0, 0,
0, 2, 0, 0, 143, 0, 0, 0, 13, 0, 0, 0, 0,
141, 0, 0, 119, 52, 0, 0, 137, 12, 0, 0, 62, 0,
138, 0, 133, 0, 0, 36, 0, 0, 28, 77, 0, 0, 0,
0, 59, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 69, 0, 0, 0, 0, 0, 146, 3, 0, 58, 0, 1,
75, 0, 0, 0, 31, 0, 0, 0, 0, 0, 127, 0, 104,
0, 64, 66, 63, 0, 0, 0, 0, 0, 46, 0, 16, 8,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 101, 0,
112, 21, 7, 67, 0, 79, 96, 118, 0, 0, 68, 0, 0,
99, 44, 0, 55, 0, 76, 0, 95, 32, 33, 57, 25, 0,
102, 0, 0, 87,
};
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
static const unsigned char aKWLen[148] = {0,
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7,
6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4,
4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6,
2, 3, 7, 5, 9, 6, 6, 4, 5, 5, 10, 6, 5,
7, 4, 5, 7, 6, 7, 7, 6, 5, 7, 3, 7, 4,
7, 6, 12, 9, 4, 6, 5, 4, 7, 6, 12, 8, 8,
2, 6, 6, 7, 6, 4, 5, 9, 5, 5, 6, 3, 4,
9, 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 9,
4, 4, 6, 7, 5, 9, 4, 4, 5, 2, 5, 8, 6,
4, 9, 5, 8, 4, 3, 9, 5, 5, 6, 4, 6, 2,
2, 9, 3, 7,
};
/* aKWOffset[i] is the index into zKWText[] of the start of
** the text for the i-th keyword. */
static const unsigned short int aKWOffset[148] = {0,
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126,
129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184,
184, 187, 189, 195, 198, 206, 211, 216, 219, 222, 226, 236, 239,
244, 244, 248, 252, 259, 265, 271, 277, 277, 283, 284, 288, 295,
299, 306, 312, 324, 333, 335, 341, 346, 348, 355, 359, 370, 377,
378, 385, 391, 397, 402, 408, 412, 415, 424, 429, 433, 439, 441,
444, 453, 455, 457, 466, 470, 476, 482, 490, 495, 495, 495, 511,
520, 523, 527, 532, 539, 544, 553, 557, 560, 565, 567, 571, 579,
585, 588, 597, 602, 610, 610, 614, 623, 628, 633, 639, 642, 645,
648, 650, 655, 659,
};
/* aKWCode[i] is the parser symbol code for the i-th keyword */
static const unsigned char aKWCode[148] = {0,
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR,
TK_ISNULL, TK_NULLS, TK_SAVEPOINT, TK_INTERSECT, TK_TIES,
|
| ︙ | ︙ | |||
172366 172367 172368 172369 172370 172371 172372 |
** parser symbol code for that keyword into *pType. Always
** return the integer n (the length of the token). */
static int keywordCode(const char *z, int n, int *pType){
int i, j;
const char *zKW;
if( n>=2 ){
i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127;
| | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | 173599 173600 173601 173602 173603 173604 173605 173606 173607 173608 173609 173610 173611 173612 173613 173614 173615 173616 173617 173618 173619 173620 173621 173622 173623 173624 173625 173626 173627 173628 173629 173630 173631 173632 173633 173634 173635 173636 173637 173638 173639 173640 173641 173642 173643 173644 173645 173646 173647 173648 173649 173650 173651 173652 173653 173654 173655 173656 173657 173658 173659 173660 173661 173662 173663 173664 173665 173666 173667 173668 173669 173670 173671 173672 173673 173674 173675 173676 173677 173678 173679 173680 173681 173682 173683 173684 173685 173686 173687 173688 173689 173690 173691 173692 173693 173694 173695 173696 173697 173698 173699 173700 173701 173702 173703 173704 173705 173706 173707 173708 173709 173710 173711 173712 173713 173714 173715 173716 173717 173718 173719 173720 173721 173722 173723 173724 173725 173726 173727 173728 173729 173730 173731 173732 173733 173734 173735 173736 173737 173738 173739 173740 173741 173742 173743 173744 173745 173746 173747 173748 173749 173750 173751 173752 173753 173754 173755 173756 173757 173758 173759 173760 173761 173762 173763 173764 173765 173766 173767 173768 173769 173770 173771 173772 173773 173774 173775 173776 173777 173778 173779 173780 173781 173782 173783 173784 173785 173786 173787 173788 173789 173790 |
** parser symbol code for that keyword into *pType. Always
** return the integer n (the length of the token). */
static int keywordCode(const char *z, int n, int *pType){
int i, j;
const char *zKW;
if( n>=2 ){
i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127;
for(i=(int)aKWHash[i]; i>0; i=aKWNext[i]){
if( aKWLen[i]!=n ) continue;
zKW = &zKWText[aKWOffset[i]];
#ifdef SQLITE_ASCII
if( (z[0]&~0x20)!=zKW[0] ) continue;
if( (z[1]&~0x20)!=zKW[1] ) continue;
j = 2;
while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }
#endif
#ifdef SQLITE_EBCDIC
if( toupper(z[0])!=zKW[0] ) continue;
if( toupper(z[1])!=zKW[1] ) continue;
j = 2;
while( j<n && toupper(z[j])==zKW[j] ){ j++; }
#endif
if( j<n ) continue;
testcase( i==1 ); /* REINDEX */
testcase( i==2 ); /* INDEXED */
testcase( i==3 ); /* INDEX */
testcase( i==4 ); /* DESC */
testcase( i==5 ); /* ESCAPE */
testcase( i==6 ); /* EACH */
testcase( i==7 ); /* CHECK */
testcase( i==8 ); /* KEY */
testcase( i==9 ); /* BEFORE */
testcase( i==10 ); /* FOREIGN */
testcase( i==11 ); /* FOR */
testcase( i==12 ); /* IGNORE */
testcase( i==13 ); /* REGEXP */
testcase( i==14 ); /* EXPLAIN */
testcase( i==15 ); /* INSTEAD */
testcase( i==16 ); /* ADD */
testcase( i==17 ); /* DATABASE */
testcase( i==18 ); /* AS */
testcase( i==19 ); /* SELECT */
testcase( i==20 ); /* TABLE */
testcase( i==21 ); /* LEFT */
testcase( i==22 ); /* THEN */
testcase( i==23 ); /* END */
testcase( i==24 ); /* DEFERRABLE */
testcase( i==25 ); /* ELSE */
testcase( i==26 ); /* EXCLUDE */
testcase( i==27 ); /* DELETE */
testcase( i==28 ); /* TEMPORARY */
testcase( i==29 ); /* TEMP */
testcase( i==30 ); /* OR */
testcase( i==31 ); /* ISNULL */
testcase( i==32 ); /* NULLS */
testcase( i==33 ); /* SAVEPOINT */
testcase( i==34 ); /* INTERSECT */
testcase( i==35 ); /* TIES */
testcase( i==36 ); /* NOTNULL */
testcase( i==37 ); /* NOT */
testcase( i==38 ); /* NO */
testcase( i==39 ); /* NULL */
testcase( i==40 ); /* LIKE */
testcase( i==41 ); /* EXCEPT */
testcase( i==42 ); /* TRANSACTION */
testcase( i==43 ); /* ACTION */
testcase( i==44 ); /* ON */
testcase( i==45 ); /* NATURAL */
testcase( i==46 ); /* ALTER */
testcase( i==47 ); /* RAISE */
testcase( i==48 ); /* EXCLUSIVE */
testcase( i==49 ); /* EXISTS */
testcase( i==50 ); /* CONSTRAINT */
testcase( i==51 ); /* INTO */
testcase( i==52 ); /* OFFSET */
testcase( i==53 ); /* OF */
testcase( i==54 ); /* SET */
testcase( i==55 ); /* TRIGGER */
testcase( i==56 ); /* RANGE */
testcase( i==57 ); /* GENERATED */
testcase( i==58 ); /* DETACH */
testcase( i==59 ); /* HAVING */
testcase( i==60 ); /* GLOB */
testcase( i==61 ); /* BEGIN */
testcase( i==62 ); /* INNER */
testcase( i==63 ); /* REFERENCES */
testcase( i==64 ); /* UNIQUE */
testcase( i==65 ); /* QUERY */
testcase( i==66 ); /* WITHOUT */
testcase( i==67 ); /* WITH */
testcase( i==68 ); /* OUTER */
testcase( i==69 ); /* RELEASE */
testcase( i==70 ); /* ATTACH */
testcase( i==71 ); /* BETWEEN */
testcase( i==72 ); /* NOTHING */
testcase( i==73 ); /* GROUPS */
testcase( i==74 ); /* GROUP */
testcase( i==75 ); /* CASCADE */
testcase( i==76 ); /* ASC */
testcase( i==77 ); /* DEFAULT */
testcase( i==78 ); /* CASE */
testcase( i==79 ); /* COLLATE */
testcase( i==80 ); /* CREATE */
testcase( i==81 ); /* CURRENT_DATE */
testcase( i==82 ); /* IMMEDIATE */
testcase( i==83 ); /* JOIN */
testcase( i==84 ); /* INSERT */
testcase( i==85 ); /* MATCH */
testcase( i==86 ); /* PLAN */
testcase( i==87 ); /* ANALYZE */
testcase( i==88 ); /* PRAGMA */
testcase( i==89 ); /* MATERIALIZED */
testcase( i==90 ); /* DEFERRED */
testcase( i==91 ); /* DISTINCT */
testcase( i==92 ); /* IS */
testcase( i==93 ); /* UPDATE */
testcase( i==94 ); /* VALUES */
testcase( i==95 ); /* VIRTUAL */
testcase( i==96 ); /* ALWAYS */
testcase( i==97 ); /* WHEN */
testcase( i==98 ); /* WHERE */
testcase( i==99 ); /* RECURSIVE */
testcase( i==100 ); /* ABORT */
testcase( i==101 ); /* AFTER */
testcase( i==102 ); /* RENAME */
testcase( i==103 ); /* AND */
testcase( i==104 ); /* DROP */
testcase( i==105 ); /* PARTITION */
testcase( i==106 ); /* AUTOINCREMENT */
testcase( i==107 ); /* TO */
testcase( i==108 ); /* IN */
testcase( i==109 ); /* CAST */
testcase( i==110 ); /* COLUMN */
testcase( i==111 ); /* COMMIT */
testcase( i==112 ); /* CONFLICT */
testcase( i==113 ); /* CROSS */
testcase( i==114 ); /* CURRENT_TIMESTAMP */
testcase( i==115 ); /* CURRENT_TIME */
testcase( i==116 ); /* CURRENT */
testcase( i==117 ); /* PRECEDING */
testcase( i==118 ); /* FAIL */
testcase( i==119 ); /* LAST */
testcase( i==120 ); /* FILTER */
testcase( i==121 ); /* REPLACE */
testcase( i==122 ); /* FIRST */
testcase( i==123 ); /* FOLLOWING */
testcase( i==124 ); /* FROM */
testcase( i==125 ); /* FULL */
testcase( i==126 ); /* LIMIT */
testcase( i==127 ); /* IF */
testcase( i==128 ); /* ORDER */
testcase( i==129 ); /* RESTRICT */
testcase( i==130 ); /* OTHERS */
testcase( i==131 ); /* OVER */
testcase( i==132 ); /* RETURNING */
testcase( i==133 ); /* RIGHT */
testcase( i==134 ); /* ROLLBACK */
testcase( i==135 ); /* ROWS */
testcase( i==136 ); /* ROW */
testcase( i==137 ); /* UNBOUNDED */
testcase( i==138 ); /* UNION */
testcase( i==139 ); /* USING */
testcase( i==140 ); /* VACUUM */
testcase( i==141 ); /* VIEW */
testcase( i==142 ); /* WINDOW */
testcase( i==143 ); /* DO */
testcase( i==144 ); /* BY */
testcase( i==145 ); /* INITIALLY */
testcase( i==146 ); /* ALL */
testcase( i==147 ); /* PRIMARY */
*pType = aKWCode[i];
break;
}
}
return n;
}
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
int id = TK_ID;
keywordCode((char*)z, n, &id);
return id;
}
#define SQLITE_N_KEYWORD 147
SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
i++;
*pzName = zKWText + aKWOffset[i];
*pnName = aKWLen[i];
return SQLITE_OK;
}
SQLITE_API int sqlite3_keyword_count(void){ return SQLITE_N_KEYWORD; }
SQLITE_API int sqlite3_keyword_check(const char *zName, int nName){
return TK_ID!=sqlite3KeywordCode((const u8*)zName, nName);
|
| ︙ | ︙ | |||
174081 174082 174083 174084 174085 174086 174087 |
** threadsafe. Failure to heed these warnings can lead to unpredictable
** behavior.
*/
SQLITE_API int sqlite3_config(int op, ...){
va_list ap;
int rc = SQLITE_OK;
| | | > > | > > > > > > > > > > | 175315 175316 175317 175318 175319 175320 175321 175322 175323 175324 175325 175326 175327 175328 175329 175330 175331 175332 175333 175334 175335 175336 175337 175338 175339 175340 175341 175342 175343 |
** threadsafe. Failure to heed these warnings can lead to unpredictable
** behavior.
*/
SQLITE_API int sqlite3_config(int op, ...){
va_list ap;
int rc = SQLITE_OK;
/* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while
** the SQLite library is in use. Except, a few selected opcodes
** are allowed.
*/
if( sqlite3GlobalConfig.isInit ){
static const u64 mAnytimeConfigOption = 0
| MASKBIT64( SQLITE_CONFIG_LOG )
| MASKBIT64( SQLITE_CONFIG_PCACHE_HDRSZ )
;
if( op<0 || op>63 || (MASKBIT64(op) & mAnytimeConfigOption)==0 ){
return SQLITE_MISUSE_BKPT;
}
testcase( op==SQLITE_CONFIG_LOG );
testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ );
}
va_start(ap, op);
switch( op ){
/* Mutex configuration options are only available in a threadsafe
** compile.
*/
|
| ︙ | ︙ | |||
174152 174153 174154 174155 174156 174157 174158 174159 174160 174161 174162 174163 174164 174165 |
** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
** filled with the currently defined memory allocation routines. */
if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
break;
}
case SQLITE_CONFIG_MEMSTATUS: {
/* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
** single argument of type int, interpreted as a boolean, which enables
** or disables the collection of memory allocation statistics. */
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
break;
}
case SQLITE_CONFIG_SMALL_MALLOC: {
| > | 175398 175399 175400 175401 175402 175403 175404 175405 175406 175407 175408 175409 175410 175411 175412 |
** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
** filled with the currently defined memory allocation routines. */
if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
break;
}
case SQLITE_CONFIG_MEMSTATUS: {
assert( !sqlite3GlobalConfig.isInit ); /* Cannot change at runtime */
/* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
** single argument of type int, interpreted as a boolean, which enables
** or disables the collection of memory allocation statistics. */
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
break;
}
case SQLITE_CONFIG_SMALL_MALLOC: {
|
| ︙ | ︙ | |||
174275 174276 174277 174278 174279 174280 174281 |
*/
case SQLITE_CONFIG_LOG: {
/* MSVC is picky about pulling func ptrs from va lists.
** http://support.microsoft.com/kb/47961
** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*));
*/
typedef void(*LOGFUNC_t)(void*,int,const char*);
| | | > > | > | 175522 175523 175524 175525 175526 175527 175528 175529 175530 175531 175532 175533 175534 175535 175536 175537 175538 175539 175540 175541 175542 175543 175544 175545 175546 175547 175548 175549 175550 175551 175552 175553 175554 |
*/
case SQLITE_CONFIG_LOG: {
/* MSVC is picky about pulling func ptrs from va lists.
** http://support.microsoft.com/kb/47961
** sqlite3GlobalConfig.xLog = va_arg(ap, void(*)(void*,int,const char*));
*/
typedef void(*LOGFUNC_t)(void*,int,const char*);
LOGFUNC_t xLog = va_arg(ap, LOGFUNC_t);
void *pLogArg = va_arg(ap, void*);
AtomicStore(&sqlite3GlobalConfig.xLog, xLog);
AtomicStore(&sqlite3GlobalConfig.pLogArg, pLogArg);
break;
}
/* EVIDENCE-OF: R-55548-33817 The compile-time setting for URI filenames
** can be changed at start-time using the
** sqlite3_config(SQLITE_CONFIG_URI,1) or
** sqlite3_config(SQLITE_CONFIG_URI,0) configuration calls.
*/
case SQLITE_CONFIG_URI: {
/* EVIDENCE-OF: R-25451-61125 The SQLITE_CONFIG_URI option takes a single
** argument of type int. If non-zero, then URI handling is globally
** enabled. If the parameter is zero, then URI handling is globally
** disabled. */
int bOpenUri = va_arg(ap, int);
AtomicStore(&sqlite3GlobalConfig.bOpenUri, bOpenUri);
break;
}
case SQLITE_CONFIG_COVERING_INDEX_SCAN: {
/* EVIDENCE-OF: R-36592-02772 The SQLITE_CONFIG_COVERING_INDEX_SCAN
** option takes a single integer argument which is interpreted as a
** boolean in order to enable or disable the use of covering indices for
|
| ︙ | ︙ | |||
174605 174606 174607 174608 174609 174610 174611 174612 174613 174614 174615 174616 174617 174618 |
{ SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema|
SQLITE_NoSchemaError },
{ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_LegacyAlter },
{ SQLITE_DBCONFIG_DQS_DDL, SQLITE_DqsDDL },
{ SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML },
{ SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt },
{ SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
for(i=0; i<ArraySize(aFlagOp); i++){
if( aFlagOp[i].op==op ){
int onoff = va_arg(ap, int);
int *pRes = va_arg(ap, int*);
| > > | 175855 175856 175857 175858 175859 175860 175861 175862 175863 175864 175865 175866 175867 175868 175869 175870 |
{ SQLITE_DBCONFIG_WRITABLE_SCHEMA, SQLITE_WriteSchema|
SQLITE_NoSchemaError },
{ SQLITE_DBCONFIG_LEGACY_ALTER_TABLE, SQLITE_LegacyAlter },
{ SQLITE_DBCONFIG_DQS_DDL, SQLITE_DqsDDL },
{ SQLITE_DBCONFIG_DQS_DML, SQLITE_DqsDML },
{ SQLITE_DBCONFIG_LEGACY_FILE_FORMAT, SQLITE_LegacyFileFmt },
{ SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema },
{ SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus },
{ SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
for(i=0; i<ArraySize(aFlagOp); i++){
if( aFlagOp[i].op==op ){
int onoff = va_arg(ap, int);
int *pRes = va_arg(ap, int*);
|
| ︙ | ︙ | |||
176590 176591 176592 176593 176594 176595 176596 | const char *zVfs = zDefaultVfs; char *zFile; char c; int nUri = sqlite3Strlen30(zUri); assert( *pzErrMsg==0 ); | | | | | 177842 177843 177844 177845 177846 177847 177848 177849 177850 177851 177852 177853 177854 177855 177856 177857 177858 |
const char *zVfs = zDefaultVfs;
char *zFile;
char c;
int nUri = sqlite3Strlen30(zUri);
assert( *pzErrMsg==0 );
if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */
|| AtomicLoad(&sqlite3GlobalConfig.bOpenUri)) /* IMP: R-51689-46548 */
&& nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */
){
char *zOpt;
int eState; /* Parser state when parsing URI */
int iIn; /* Input character index */
int iOut = 0; /* Output character index */
u64 nByte = nUri+8; /* Bytes of space to allocate */
|
| ︙ | ︙ | |||
176998 176999 177000 177001 177002 177003 177004 177005 177006 177007 177008 177009 177010 177011 |
| SQLITE_EnableQPSG
#endif
#if defined(SQLITE_DEFAULT_DEFENSIVE)
| SQLITE_Defensive
#endif
#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
| SQLITE_LegacyAlter
#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3HashInit(&db->aModule);
#endif
| > > > | 178250 178251 178252 178253 178254 178255 178256 178257 178258 178259 178260 178261 178262 178263 178264 178265 178266 |
| SQLITE_EnableQPSG
#endif
#if defined(SQLITE_DEFAULT_DEFENSIVE)
| SQLITE_Defensive
#endif
#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
| SQLITE_LegacyAlter
#endif
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
| SQLITE_StmtScanStatus
#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3HashInit(&db->aModule);
#endif
|
| ︙ | ︙ | |||
177563 177564 177565 177566 177567 177568 177569 | int rc; pVfs = sqlite3_vfs_find(0); if( pVfs==0 ) return 0; /* This function works in milliseconds, but the underlying OsSleep() ** API uses microseconds. Hence the 1000's. */ | | | 178818 178819 178820 178821 178822 178823 178824 178825 178826 178827 178828 178829 178830 178831 178832 |
int rc;
pVfs = sqlite3_vfs_find(0);
if( pVfs==0 ) return 0;
/* This function works in milliseconds, but the underlying OsSleep()
** API uses microseconds. Hence the 1000's.
*/
rc = (sqlite3OsSleep(pVfs, ms<0 ? 0 : 1000*ms)/1000);
return rc;
}
/*
** Enable or disable the extended result codes.
*/
SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
|
| ︙ | ︙ | |||
180119 180120 180121 180122 180123 180124 180125 180126 180127 180128 180129 180130 180131 180132 | /* fts3_unicode2.c (functions generated by parsing unicode text files) */ #ifndef SQLITE_DISABLE_FTS3_UNICODE SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); #endif #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) | > > | 181374 181375 181376 181377 181378 181379 181380 181381 181382 181383 181384 181385 181386 181387 181388 181389 | /* fts3_unicode2.c (functions generated by parsing unicode text files) */ #ifndef SQLITE_DISABLE_FTS3_UNICODE SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int); SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); #endif SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) |
| ︙ | ︙ | |||
185123 185124 185125 185126 185127 185128 185129 |
** really a match, taking into account deferred tokens and NEAR operators.
*/
static void fts3EvalNextRow(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expr. to advance to next matching row */
int *pRc /* IN/OUT: Error code */
){
| | < | 186380 186381 186382 186383 186384 186385 186386 186387 186388 186389 186390 186391 186392 186393 186394 186395 |
** really a match, taking into account deferred tokens and NEAR operators.
*/
static void fts3EvalNextRow(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expr. to advance to next matching row */
int *pRc /* IN/OUT: Error code */
){
if( *pRc==SQLITE_OK && pExpr->bEof==0 ){
int bDescDoclist = pCsr->bDesc; /* Used by DOCID_CMP() macro */
pExpr->bStart = 1;
switch( pExpr->eType ){
case FTSQUERY_NEAR:
case FTSQUERY_AND: {
Fts3Expr *pLeft = pExpr->pLeft;
Fts3Expr *pRight = pExpr->pRight;
|
| ︙ | ︙ | |||
185600 185601 185602 185603 185604 185605 185606 185607 185608 185609 185610 185611 185612 185613 |
}while( iCol<nCol );
}
fts3EvalUpdateCounts(pExpr->pLeft, nCol);
fts3EvalUpdateCounts(pExpr->pRight, nCol);
}
}
/*
** Expression pExpr must be of type FTSQUERY_PHRASE.
**
** If it is not already allocated and populated, this function allocates and
** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part
** of a NEAR expression, then it also allocates and populates the same array
| > > > > > > > > > > > > > > > > | 186856 186857 186858 186859 186860 186861 186862 186863 186864 186865 186866 186867 186868 186869 186870 186871 186872 186873 186874 186875 186876 186877 186878 186879 186880 186881 186882 186883 186884 186885 |
}while( iCol<nCol );
}
fts3EvalUpdateCounts(pExpr->pLeft, nCol);
fts3EvalUpdateCounts(pExpr->pRight, nCol);
}
}
/*
** This is an sqlite3Fts3ExprIterate() callback. If the Fts3Expr.aMI[] array
** has not yet been allocated, allocate and zero it. Otherwise, just zero
** it.
*/
static int fts3AllocateMSI(Fts3Expr *pExpr, int iPhrase, void *pCtx){
Fts3Table *pTab = (Fts3Table*)pCtx;
UNUSED_PARAMETER(iPhrase);
if( pExpr->aMI==0 ){
pExpr->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32));
if( pExpr->aMI==0 ) return SQLITE_NOMEM;
}
memset(pExpr->aMI, 0, pTab->nColumn * 3 * sizeof(u32));
return SQLITE_OK;
}
/*
** Expression pExpr must be of type FTSQUERY_PHRASE.
**
** If it is not already allocated and populated, this function allocates and
** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part
** of a NEAR expression, then it also allocates and populates the same array
|
| ︙ | ︙ | |||
185622 185623 185624 185625 185626 185627 185628 |
){
int rc = SQLITE_OK; /* Return code */
assert( pExpr->eType==FTSQUERY_PHRASE );
if( pExpr->aMI==0 ){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
Fts3Expr *pRoot; /* Root of NEAR expression */
| < | > > < | < < < < < | | 186894 186895 186896 186897 186898 186899 186900 186901 186902 186903 186904 186905 186906 186907 186908 186909 186910 186911 186912 186913 186914 186915 186916 186917 186918 186919 186920 186921 186922 186923 186924 186925 186926 |
){
int rc = SQLITE_OK; /* Return code */
assert( pExpr->eType==FTSQUERY_PHRASE );
if( pExpr->aMI==0 ){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
Fts3Expr *pRoot; /* Root of NEAR expression */
sqlite3_int64 iPrevId = pCsr->iPrevId;
sqlite3_int64 iDocid;
u8 bEof;
/* Find the root of the NEAR expression */
pRoot = pExpr;
while( pRoot->pParent
&& (pRoot->pParent->eType==FTSQUERY_NEAR || pRoot->bDeferred)
){
pRoot = pRoot->pParent;
}
iDocid = pRoot->iDocid;
bEof = pRoot->bEof;
assert( pRoot->bStart );
/* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */
rc = sqlite3Fts3ExprIterate(pRoot, fts3AllocateMSI, (void*)pTab);
if( rc!=SQLITE_OK ) return rc;
fts3EvalRestart(pCsr, pRoot, &rc);
while( pCsr->isEof==0 && rc==SQLITE_OK ){
do {
/* Ensure the %_content statement is reset. */
if( pCsr->isRequireSeek==0 ) sqlite3_reset(pCsr->pStmt);
|
| ︙ | ︙ | |||
185801 185802 185803 185804 185805 185806 185807 185808 185809 185810 185811 185812 185813 185814 185815 185816 185817 185818 185819 185820 185821 185822 185823 185824 185825 185826 |
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int rc = SQLITE_OK;
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
int bOr = 0;
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
int bMatch;
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
** tree that the node is part of has been marked as EOF, but the node
** itself is not EOF, then it may point to an earlier entry. */
pNear = pExpr;
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
if( p->eType==FTSQUERY_NEAR ) pNear = p;
if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
/* This is the descendent of an OR node. In this case we cannot use
** an incremental phrase. Load the entire doclist for the phrase
** into memory in this case. */
if( pPhrase->bIncr ){
| > > > > > > | | | | | | | | | 187068 187069 187070 187071 187072 187073 187074 187075 187076 187077 187078 187079 187080 187081 187082 187083 187084 187085 187086 187087 187088 187089 187090 187091 187092 187093 187094 187095 187096 187097 187098 187099 187100 187101 187102 187103 187104 187105 187106 187107 187108 187109 187110 187111 187112 187113 187114 187115 187116 187117 187118 187119 187120 |
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int rc = SQLITE_OK;
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
int bOr = 0;
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
Fts3Expr *pRun; /* Closest non-deferred ancestor of pNear */
int bMatch;
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
** tree that the node is part of has been marked as EOF, but the node
** itself is not EOF, then it may point to an earlier entry. */
pNear = pExpr;
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
if( p->eType==FTSQUERY_NEAR ) pNear = p;
if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
pRun = pNear;
while( pRun->bDeferred ){
assert( pRun->pParent );
pRun = pRun->pParent;
}
/* This is the descendent of an OR node. In this case we cannot use
** an incremental phrase. Load the entire doclist for the phrase
** into memory in this case. */
if( pPhrase->bIncr ){
int bEofSave = pRun->bEof;
fts3EvalRestart(pCsr, pRun, &rc);
while( rc==SQLITE_OK && !pRun->bEof ){
fts3EvalNextRow(pCsr, pRun, &rc);
if( bEofSave==0 && pRun->iDocid==iDocid ) break;
}
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
if( rc==SQLITE_OK && pRun->bEof!=bEofSave ){
rc = FTS_CORRUPT_VTAB;
}
}
if( bTreeEof ){
while( rc==SQLITE_OK && !pRun->bEof ){
fts3EvalNextRow(pCsr, pRun, &rc);
}
}
if( rc!=SQLITE_OK ) return rc;
bMatch = 1;
for(p=pNear; p; p=p->pLeft){
u8 bEof = 0;
|
| ︙ | ︙ | |||
192749 192750 192751 192752 192753 192754 192755 |
** trying to resize the buffer, return SQLITE_NOMEM.
*/
static int fts3MsrBufferData(
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
char *pList,
i64 nList
){
| | | | > > | 194022 194023 194024 194025 194026 194027 194028 194029 194030 194031 194032 194033 194034 194035 194036 194037 194038 194039 194040 194041 194042 194043 194044 194045 194046 194047 |
** trying to resize the buffer, return SQLITE_NOMEM.
*/
static int fts3MsrBufferData(
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
char *pList,
i64 nList
){
if( (nList+FTS3_NODE_PADDING)>pMsr->nBuffer ){
char *pNew;
int nNew = nList*2 + FTS3_NODE_PADDING;
pNew = (char *)sqlite3_realloc64(pMsr->aBuffer, nNew);
if( !pNew ) return SQLITE_NOMEM;
pMsr->aBuffer = pNew;
pMsr->nBuffer = nNew;
}
assert( nList>0 );
memcpy(pMsr->aBuffer, pList, nList);
memset(&pMsr->aBuffer[nList], 0, FTS3_NODE_PADDING);
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *p, /* Virtual table handle */
Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */
sqlite3_int64 *piDocid, /* OUT: Docid value */
|
| ︙ | ︙ | |||
195940 195941 195942 195943 195944 195945 195946 | /* ** The default value for the second argument to matchinfo(). */ #define FTS3_MATCHINFO_DEFAULT "pcx" /* | | | 197215 197216 197217 197218 197219 197220 197221 197222 197223 197224 197225 197226 197227 197228 197229 |
/*
** The default value for the second argument to matchinfo().
*/
#define FTS3_MATCHINFO_DEFAULT "pcx"
/*
** Used as an sqlite3Fts3ExprIterate() context when loading phrase doclists to
** Fts3Expr.aDoclist[]/nDoclist.
*/
typedef struct LoadDoclistCtx LoadDoclistCtx;
struct LoadDoclistCtx {
Fts3Cursor *pCsr; /* FTS3 Cursor */
int nPhrase; /* Number of phrases seen so far */
int nToken; /* Number of tokens seen so far */
|
| ︙ | ︙ | |||
195984 195985 195986 195987 195988 195989 195990 | int iCol; /* Column snippet is extracted from */ int iPos; /* Index of first token in snippet */ u64 covered; /* Mask of query phrases covered */ u64 hlmask; /* Mask of snippet terms to highlight */ }; /* | | | 197259 197260 197261 197262 197263 197264 197265 197266 197267 197268 197269 197270 197271 197272 197273 |
int iCol; /* Column snippet is extracted from */
int iPos; /* Index of first token in snippet */
u64 covered; /* Mask of query phrases covered */
u64 hlmask; /* Mask of snippet terms to highlight */
};
/*
** This type is used as an sqlite3Fts3ExprIterate() context object while
** accumulating the data returned by the matchinfo() function.
*/
typedef struct MatchInfo MatchInfo;
struct MatchInfo {
Fts3Cursor *pCursor; /* FTS3 Cursor */
int nCol; /* Number of columns in table */
int nPhrase; /* Number of matchable phrases in query */
|
| ︙ | ︙ | |||
196143 196144 196145 196146 196147 196148 196149 |
static void fts3GetDeltaPosition(char **pp, i64 *piPos){
int iVal;
*pp += fts3GetVarint32(*pp, &iVal);
*piPos += (iVal-2);
}
/*
| | | 197418 197419 197420 197421 197422 197423 197424 197425 197426 197427 197428 197429 197430 197431 197432 |
static void fts3GetDeltaPosition(char **pp, i64 *piPos){
int iVal;
*pp += fts3GetVarint32(*pp, &iVal);
*piPos += (iVal-2);
}
/*
** Helper function for sqlite3Fts3ExprIterate() (see below).
*/
static int fts3ExprIterate2(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
int *piPhrase, /* Pointer to phrase counter */
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
){
|
| ︙ | ︙ | |||
196177 196178 196179 196180 196181 196182 196183 | ** For each phrase node found, the supplied callback function is invoked. ** ** If the callback function returns anything other than SQLITE_OK, ** the iteration is abandoned and the error code returned immediately. ** Otherwise, SQLITE_OK is returned after a callback has been made for ** all eligible phrase nodes. */ | | < | | | 197452 197453 197454 197455 197456 197457 197458 197459 197460 197461 197462 197463 197464 197465 197466 197467 197468 197469 197470 197471 197472 197473 197474 197475 197476 197477 |
** For each phrase node found, the supplied callback function is invoked.
**
** If the callback function returns anything other than SQLITE_OK,
** the iteration is abandoned and the error code returned immediately.
** Otherwise, SQLITE_OK is returned after a callback has been made for
** all eligible phrase nodes.
*/
SQLITE_PRIVATE int sqlite3Fts3ExprIterate(
Fts3Expr *pExpr, /* Expression to iterate phrases of */
int (*x)(Fts3Expr*,int,void*), /* Callback function to invoke for phrases */
void *pCtx /* Second argument to pass to callback */
){
int iPhrase = 0; /* Variable used as the phrase counter */
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
/*
** This is an sqlite3Fts3ExprIterate() callback used while loading the
** doclists for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
** fts3ExprLoadDoclists().
*/
static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
int rc = SQLITE_OK;
Fts3Phrase *pPhrase = pExpr->pPhrase;
LoadDoclistCtx *p = (LoadDoclistCtx *)ctx;
|
| ︙ | ︙ | |||
196221 196222 196223 196224 196225 196226 196227 |
*/
static int fts3ExprLoadDoclists(
Fts3Cursor *pCsr, /* Fts3 cursor for current query */
int *pnPhrase, /* OUT: Number of phrases in query */
int *pnToken /* OUT: Number of tokens in query */
){
int rc; /* Return Code */
| | | | | 197495 197496 197497 197498 197499 197500 197501 197502 197503 197504 197505 197506 197507 197508 197509 197510 197511 197512 197513 197514 197515 197516 197517 197518 197519 197520 197521 197522 197523 197524 |
*/
static int fts3ExprLoadDoclists(
Fts3Cursor *pCsr, /* Fts3 cursor for current query */
int *pnPhrase, /* OUT: Number of phrases in query */
int *pnToken /* OUT: Number of tokens in query */
){
int rc; /* Return Code */
LoadDoclistCtx sCtx = {0,0,0}; /* Context for sqlite3Fts3ExprIterate() */
sCtx.pCsr = pCsr;
rc = sqlite3Fts3ExprIterate(pCsr->pExpr,fts3ExprLoadDoclistsCb,(void*)&sCtx);
if( pnPhrase ) *pnPhrase = sCtx.nPhrase;
if( pnToken ) *pnToken = sCtx.nToken;
return rc;
}
static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
(*(int *)ctx)++;
pExpr->iPhrase = iPhrase;
return SQLITE_OK;
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
int nPhrase = 0;
(void)sqlite3Fts3ExprIterate(pExpr, fts3ExprPhraseCountCb, (void *)&nPhrase);
return nPhrase;
}
/*
** Advance the position list iterator specified by the first two
** arguments so that it points to the first element with a value greater
** than or equal to parameter iNext.
|
| ︙ | ︙ | |||
196364 196365 196366 196367 196368 196369 196370 | *piToken = iStart; *piScore = iScore; *pmCover = mCover; *pmHighlight = mHighlight; } /* | | | > | 197638 197639 197640 197641 197642 197643 197644 197645 197646 197647 197648 197649 197650 197651 197652 197653 197654 |
*piToken = iStart;
*piScore = iScore;
*pmCover = mCover;
*pmHighlight = mHighlight;
}
/*
** This function is an sqlite3Fts3ExprIterate() callback used by
** fts3BestSnippet(). Each invocation populates an element of the
** SnippetIter.aPhrase[] array.
*/
static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
char *pCsr;
int rc;
|
| ︙ | ︙ | |||
196455 196456 196457 196458 196459 196460 196461 | ** the set of phrases in the expression to populate the aPhrase[] array. */ sIter.pCsr = pCsr; sIter.iCol = iCol; sIter.nSnippet = nSnippet; sIter.nPhrase = nList; sIter.iCurrent = -1; | > | > | 197730 197731 197732 197733 197734 197735 197736 197737 197738 197739 197740 197741 197742 197743 197744 197745 197746 |
** the set of phrases in the expression to populate the aPhrase[] array.
*/
sIter.pCsr = pCsr;
sIter.iCol = iCol;
sIter.nSnippet = nSnippet;
sIter.nPhrase = nList;
sIter.iCurrent = -1;
rc = sqlite3Fts3ExprIterate(
pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter
);
if( rc==SQLITE_OK ){
/* Set the *pmSeen output variable. */
for(i=0; i<nList; i++){
if( sIter.aPhrase[i].pHead ){
*pmSeen |= (u64)1 << (i%64);
}
|
| ︙ | ︙ | |||
196816 196817 196818 196819 196820 196821 196822 |
rc = fts3ExprLHits(pExpr, p);
}
}
return rc;
}
/*
| | | | | 198093 198094 198095 198096 198097 198098 198099 198100 198101 198102 198103 198104 198105 198106 198107 198108 198109 198110 |
rc = fts3ExprLHits(pExpr, p);
}
}
return rc;
}
/*
** sqlite3Fts3ExprIterate() callback used to collect the "global" matchinfo
** stats for a single query.
**
** sqlite3Fts3ExprIterate() callback to load the 'global' elements of a
** FTS3_MATCHINFO_HITS matchinfo array. The global stats are those elements
** of the matchinfo array that are constant for all rows returned by the
** current query.
**
** Argument pCtx is actually a pointer to a struct of type MatchInfo. This
** function populates Matchinfo.aMatchinfo[] as follows:
**
|
| ︙ | ︙ | |||
196854 196855 196856 196857 196858 196859 196860 |
MatchInfo *p = (MatchInfo *)pCtx;
return sqlite3Fts3EvalPhraseStats(
p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol]
);
}
/*
| | | 198131 198132 198133 198134 198135 198136 198137 198138 198139 198140 198141 198142 198143 198144 198145 |
MatchInfo *p = (MatchInfo *)pCtx;
return sqlite3Fts3EvalPhraseStats(
p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol]
);
}
/*
** sqlite3Fts3ExprIterate() callback used to collect the "local" part of the
** FTS3_MATCHINFO_HITS array. The local stats are those elements of the
** array that are different for each row returned by the query.
*/
static int fts3ExprLocalHitsCb(
Fts3Expr *pExpr, /* Phrase expression node */
int iPhrase, /* Phrase number */
void *pCtx /* Pointer to MatchInfo structure */
|
| ︙ | ︙ | |||
197050 197051 197052 197053 197054 197055 197056 | int rc = SQLITE_OK; /* Allocate and populate the array of LcsIterator objects. The array ** contains one element for each matchable phrase in the query. **/ aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase); if( !aIter ) return SQLITE_NOMEM; | | | 198327 198328 198329 198330 198331 198332 198333 198334 198335 198336 198337 198338 198339 198340 198341 |
int rc = SQLITE_OK;
/* Allocate and populate the array of LcsIterator objects. The array
** contains one element for each matchable phrase in the query.
**/
aIter = sqlite3Fts3MallocZero(sizeof(LcsIterator) * pCsr->nPhrase);
if( !aIter ) return SQLITE_NOMEM;
(void)sqlite3Fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter);
for(i=0; i<pInfo->nPhrase; i++){
LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken;
}
|
| ︙ | ︙ | |||
197227 197228 197229 197230 197231 197232 197233 |
rc = fts3ExprLoadDoclists(pCsr, 0, 0);
if( rc!=SQLITE_OK ) break;
if( bGlobal ){
if( pCsr->pDeferred ){
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
if( rc!=SQLITE_OK ) break;
}
| | | | 198504 198505 198506 198507 198508 198509 198510 198511 198512 198513 198514 198515 198516 198517 198518 198519 198520 198521 198522 |
rc = fts3ExprLoadDoclists(pCsr, 0, 0);
if( rc!=SQLITE_OK ) break;
if( bGlobal ){
if( pCsr->pDeferred ){
rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &pInfo->nDoc,0,0);
if( rc!=SQLITE_OK ) break;
}
rc = sqlite3Fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
sqlite3Fts3EvalTestDeferred(pCsr, &rc);
if( rc!=SQLITE_OK ) break;
}
(void)sqlite3Fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
break;
}
}
pInfo->aMatchinfo += fts3MatchinfoSize(pInfo, zArg[i]);
}
|
| ︙ | ︙ | |||
197454 197455 197456 197457 197458 197459 197460 | int iCol; /* Column of table to populate aTerm for */ int iTerm; sqlite3_int64 iDocid; TermOffset *aTerm; }; /* | | | 198731 198732 198733 198734 198735 198736 198737 198738 198739 198740 198741 198742 198743 198744 198745 |
int iCol; /* Column of table to populate aTerm for */
int iTerm;
sqlite3_int64 iDocid;
TermOffset *aTerm;
};
/*
** This function is an sqlite3Fts3ExprIterate() callback used by sqlite3Fts3Offsets().
*/
static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
TermOffsetCtx *p = (TermOffsetCtx *)ctx;
int nTerm; /* Number of tokens in phrase */
int iTerm; /* For looping through nTerm phrase terms */
char *pList; /* Pointer to position list for phrase */
i64 iPos = 0; /* First position in position-list */
|
| ︙ | ︙ | |||
197536 197537 197538 197539 197540 197541 197542 |
int nDoc;
/* Initialize the contents of sCtx.aTerm[] for column iCol. This
** operation may fail if the database contains corrupt records.
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
| > | > | 198813 198814 198815 198816 198817 198818 198819 198820 198821 198822 198823 198824 198825 198826 198827 198828 198829 |
int nDoc;
/* Initialize the contents of sCtx.aTerm[] for column iCol. This
** operation may fail if the database contains corrupt records.
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
rc = sqlite3Fts3ExprIterate(
pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx
);
if( rc!=SQLITE_OK ) goto offsets_out;
/* Retreive the text stored in column iCol. If an SQL NULL is stored
** in column iCol, jump immediately to the next iteration of the loop.
** If an OOM occurs while retrieving the data (this can happen if SQLite
** needs to transform the data from utf-16 to utf-8), return SQLITE_NOMEM
** to the caller.
|
| ︙ | ︙ | |||
198543 198544 198545 198546 198547 198548 198549 198550 198551 198552 198553 198554 198555 198556 |
#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
#define JNODE_REMOVE 0x04 /* Do not output */
#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
#define JNODE_LABEL 0x40 /* Is a label of an object */
/* A single node of parsed JSON
*/
struct JsonNode {
u8 eType; /* One of the JSON_ type values */
u8 jnFlags; /* JNODE flags */
| > | 199822 199823 199824 199825 199826 199827 199828 199829 199830 199831 199832 199833 199834 199835 199836 |
#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
#define JNODE_REMOVE 0x04 /* Do not output */
#define JNODE_REPLACE 0x08 /* Replace with JsonNode.u.iReplace */
#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
#define JNODE_LABEL 0x40 /* Is a label of an object */
#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */
/* A single node of parsed JSON
*/
struct JsonNode {
u8 eType; /* One of the JSON_ type values */
u8 jnFlags; /* JNODE flags */
|
| ︙ | ︙ | |||
198569 198570 198571 198572 198573 198574 198575 |
*/
struct JsonParse {
u32 nNode; /* Number of slots of aNode[] used */
u32 nAlloc; /* Number of slots of aNode[] allocated */
JsonNode *aNode; /* Array of nodes containing the parse */
const char *zJson; /* Original JSON string */
u32 *aUp; /* Index of parent of each node */
| | | > > | | | | 199849 199850 199851 199852 199853 199854 199855 199856 199857 199858 199859 199860 199861 199862 199863 199864 199865 199866 199867 199868 199869 199870 199871 199872 199873 199874 199875 199876 199877 199878 199879 |
*/
struct JsonParse {
u32 nNode; /* Number of slots of aNode[] used */
u32 nAlloc; /* Number of slots of aNode[] allocated */
JsonNode *aNode; /* Array of nodes containing the parse */
const char *zJson; /* Original JSON string */
u32 *aUp; /* Index of parent of each node */
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
int nJson; /* Length of the zJson string in bytes */
u32 iErr; /* Error location in zJson[] */
u32 iHold; /* Replace cache line with the lowest iHold value */
};
/*
** Maximum nesting depth of JSON for this implementation.
**
** This limit is needed to avoid a stack overflow in the recursive
** descent parser. A depth of 1000 is far deeper than any sane JSON
** should go. Historical note: This limit was 2000 prior to version 3.42.0
*/
#define JSON_MAX_DEPTH 1000
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
/* Set the JsonString object to an empty string
*/
|
| ︙ | ︙ | |||
198732 198733 198734 198735 198736 198737 198738 198739 198740 198741 198742 198743 198744 198745 198746 198747 198748 198749 198750 198751 198752 |
c = "0123456789abcdef"[c&0xf];
}
p->zBuf[p->nUsed++] = c;
}
p->zBuf[p->nUsed++] = '"';
assert( p->nUsed<p->nAlloc );
}
/*
** Append a function parameter value to the JSON string under
** construction.
*/
static void jsonAppendValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
jsonAppendRaw(p, "null", 4);
break;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | 200014 200015 200016 200017 200018 200019 200020 200021 200022 200023 200024 200025 200026 200027 200028 200029 200030 200031 200032 200033 200034 200035 200036 200037 200038 200039 200040 200041 200042 200043 200044 200045 200046 200047 200048 200049 200050 200051 200052 200053 200054 200055 200056 200057 200058 200059 200060 200061 200062 200063 200064 200065 200066 200067 200068 200069 200070 200071 200072 200073 200074 200075 200076 200077 200078 200079 200080 200081 200082 200083 200084 200085 200086 200087 200088 200089 200090 200091 200092 200093 200094 200095 200096 200097 200098 200099 200100 200101 200102 200103 200104 200105 200106 200107 200108 200109 200110 200111 200112 200113 200114 200115 200116 200117 200118 200119 200120 200121 200122 200123 200124 200125 200126 200127 200128 200129 200130 200131 200132 200133 200134 200135 200136 200137 200138 200139 200140 200141 200142 200143 200144 200145 200146 200147 200148 200149 200150 200151 200152 200153 200154 200155 200156 200157 200158 200159 200160 200161 200162 200163 200164 200165 200166 200167 200168 200169 |
c = "0123456789abcdef"[c&0xf];
}
p->zBuf[p->nUsed++] = c;
}
p->zBuf[p->nUsed++] = '"';
assert( p->nUsed<p->nAlloc );
}
/*
** The zIn[0..N] string is a JSON5 string literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
u32 i;
jsonAppendChar(p, '"');
zIn++;
N -= 2;
while( N>0 ){
for(i=0; i<N && zIn[i]!='\\'; i++){}
if( i>0 ){
jsonAppendRaw(p, zIn, i);
zIn += i;
N -= i;
if( N==0 ) break;
}
assert( zIn[0]=='\\' );
switch( (u8)zIn[1] ){
case '\'':
jsonAppendChar(p, '\'');
break;
case 'v':
jsonAppendRaw(p, "\\u0009", 6);
break;
case 'x':
jsonAppendRaw(p, "\\u00", 4);
jsonAppendRaw(p, &zIn[2], 2);
zIn += 2;
N -= 2;
break;
case '0':
jsonAppendRaw(p, "\\u0000", 6);
break;
case '\r':
if( zIn[2]=='\n' ){
zIn++;
N--;
}
break;
case '\n':
break;
case 0xe2:
assert( N>=4 );
assert( 0x80==(u8)zIn[2] );
assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] );
zIn += 2;
N -= 2;
break;
default:
jsonAppendRaw(p, zIn, 2);
break;
}
zIn += 2;
N -= 2;
}
jsonAppendChar(p, '"');
}
/*
** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
if( zIn[0]=='+' ){
zIn++;
N--;
}else if( zIn[0]=='-' ){
jsonAppendChar(p, '-');
zIn++;
N--;
}
if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){
sqlite3_int64 i = 0;
int rc = sqlite3DecOrHexToI64(zIn, &i);
if( rc<=1 ){
jsonPrintf(100,p,"%lld",i);
}else{
assert( rc==2 );
jsonAppendRaw(p, "9.0e999", 7);
}
return;
}
jsonAppendRaw(p, zIn, N);
}
/*
** The zIn[0..N] string is a JSON5 real literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
u32 i;
if( zIn[0]=='+' ){
zIn++;
N--;
}else if( zIn[0]=='-' ){
jsonAppendChar(p, '-');
zIn++;
N--;
}
if( zIn[0]=='.' ){
jsonAppendChar(p, '0');
}
for(i=0; i<N; i++){
if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
i++;
jsonAppendRaw(p, zIn, i);
zIn += i;
N -= i;
jsonAppendChar(p, '0');
break;
}
}
if( N>0 ){
jsonAppendRaw(p, zIn, N);
}
}
/*
** Append a function parameter value to the JSON string under
** construction.
*/
static void jsonAppendValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
switch( sqlite3_value_type(pValue) ){
case SQLITE_NULL: {
jsonAppendRaw(p, "null", 4);
break;
}
case SQLITE_FLOAT: {
jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue));
break;
}
case SQLITE_INTEGER: {
const char *z = (const char*)sqlite3_value_text(pValue);
u32 n = (u32)sqlite3_value_bytes(pValue);
jsonAppendRaw(p, z, n);
break;
}
case SQLITE_TEXT: {
const char *z = (const char*)sqlite3_value_text(pValue);
|
| ︙ | ︙ | |||
198860 198861 198862 198863 198864 198865 198866 198867 |
break;
}
case JSON_FALSE: {
jsonAppendRaw(pOut, "false", 5);
break;
}
case JSON_STRING: {
if( pNode->jnFlags & JNODE_RAW ){
| > | > > > > | > > > | > | | > > > > > > > > > > > | > | 200268 200269 200270 200271 200272 200273 200274 200275 200276 200277 200278 200279 200280 200281 200282 200283 200284 200285 200286 200287 200288 200289 200290 200291 200292 200293 200294 200295 200296 200297 200298 200299 200300 200301 200302 200303 200304 200305 200306 200307 200308 200309 200310 200311 200312 200313 |
break;
}
case JSON_FALSE: {
jsonAppendRaw(pOut, "false", 5);
break;
}
case JSON_STRING: {
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_RAW ){
if( pNode->jnFlags & JNODE_LABEL ){
jsonAppendChar(pOut, '"');
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
jsonAppendChar(pOut, '"');
}else{
jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
}
}else if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
break;
}
case JSON_REAL: {
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
break;
}
case JSON_INT: {
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
break;
}
case JSON_ARRAY: {
u32 j = 1;
jsonAppendChar(pOut, '[');
for(;;){
while( j<=pNode->n ){
|
| ︙ | ︙ | |||
198986 198987 198988 198989 198990 198991 198992 198993 198994 198995 |
}
case JSON_FALSE: {
sqlite3_result_int(pCtx, 0);
break;
}
case JSON_INT: {
sqlite3_int64 i = 0;
const char *z;
assert( pNode->eU==1 );
z = pNode->u.zJContent;
| > > > > | | | < < < < | > | | < | | | < < < < < < < < < > < < < < < < < < < < | > | | < < | 200415 200416 200417 200418 200419 200420 200421 200422 200423 200424 200425 200426 200427 200428 200429 200430 200431 200432 200433 200434 200435 200436 200437 200438 200439 200440 200441 200442 200443 200444 200445 200446 200447 200448 200449 200450 200451 200452 200453 200454 200455 200456 200457 200458 200459 200460 200461 200462 200463 200464 200465 200466 200467 200468 200469 200470 200471 200472 200473 200474 200475 200476 200477 200478 200479 200480 200481 200482 200483 200484 200485 |
}
case JSON_FALSE: {
sqlite3_result_int(pCtx, 0);
break;
}
case JSON_INT: {
sqlite3_int64 i = 0;
int rc;
int bNeg = 0;
const char *z;
assert( pNode->eU==1 );
z = pNode->u.zJContent;
if( z[0]=='-' ){ z++; bNeg = 1; }
else if( z[0]=='+' ){ z++; }
rc = sqlite3DecOrHexToI64(z, &i);
if( rc<=1 ){
sqlite3_result_int64(pCtx, bNeg ? -i : i);
}else if( rc==3 && bNeg ){
sqlite3_result_int64(pCtx, SMALLEST_INT64);
}else{
goto to_double;
}
break;
}
case JSON_REAL: {
double r;
const char *z;
assert( pNode->eU==1 );
to_double:
z = pNode->u.zJContent;
sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
sqlite3_result_double(pCtx, r);
break;
}
case JSON_STRING: {
if( pNode->jnFlags & JNODE_RAW ){
assert( pNode->eU==1 );
sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
SQLITE_TRANSIENT);
}else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
/* JSON formatted without any backslash-escapes */
assert( pNode->eU==1 );
sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
SQLITE_TRANSIENT);
}else{
/* Translate JSON formatted string into raw text */
u32 i;
u32 n = pNode->n;
const char *z;
char *zOut;
u32 j;
u32 nOut = n;
assert( pNode->eU==1 );
z = pNode->u.zJContent;
zOut = sqlite3_malloc( nOut+1 );
if( zOut==0 ){
sqlite3_result_error_nomem(pCtx);
break;
}
for(i=1, j=0; i<n-1; i++){
char c = z[i];
if( c=='\\' ){
c = z[++i];
if( c=='u' ){
u32 v = jsonHexToInt4(z+i+1);
i += 4;
if( v==0 ) break;
if( v<=0x7f ){
zOut[j++] = (char)v;
|
| ︙ | ︙ | |||
199093 199094 199095 199096 199097 199098 199099 |
zOut[j++] = 0x80 | (v&0x3f);
}else{
zOut[j++] = 0xe0 | (v>>12);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}
}
| | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > | > | | < < | 200503 200504 200505 200506 200507 200508 200509 200510 200511 200512 200513 200514 200515 200516 200517 200518 200519 200520 200521 200522 200523 200524 200525 200526 200527 200528 200529 200530 200531 200532 200533 200534 200535 200536 200537 200538 200539 200540 200541 200542 200543 200544 200545 200546 200547 200548 200549 200550 |
zOut[j++] = 0x80 | (v&0x3f);
}else{
zOut[j++] = 0xe0 | (v>>12);
zOut[j++] = 0x80 | ((v>>6)&0x3f);
zOut[j++] = 0x80 | (v&0x3f);
}
}
continue;
}else if( c=='b' ){
c = '\b';
}else if( c=='f' ){
c = '\f';
}else if( c=='n' ){
c = '\n';
}else if( c=='r' ){
c = '\r';
}else if( c=='t' ){
c = '\t';
}else if( c=='v' ){
c = '\v';
}else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
/* pass through unchanged */
}else if( c=='0' ){
c = 0;
}else if( c=='x' ){
c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
i += 2;
}else if( c=='\r' && z[i+1]=='\n' ){
i++;
continue;
}else if( 0xe2==(u8)c ){
assert( 0x80==(u8)z[i+1] );
assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
i += 2;
continue;
}else{
continue;
}
} /* end if( c=='\\' ) */
zOut[j++] = c;
} /* end for() */
zOut[j] = 0;
sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
}
break;
}
case JSON_ARRAY:
case JSON_OBJECT: {
|
| ︙ | ︙ | |||
199176 199177 199178 199179 199180 199181 199182 |
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];
| | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > | | > > | > > > > | | > > > > < | | > | | > > > > > > > > > > > > > > > > > | | > | | > > > > > > | > | | > > > > > > > > > > > > | | > > > > > > | > | | | | | > > > > > > > > > > > > > > > > | > > > > < < < | | > > > > > > > > > > | > | | | | | > > > > > > > > > > > > > > > > > > > > > > | | > > > | | > > > > > > > > > > > | | < > | < | | | > | | > > | | | > > > > | > > > > | | > > > > > > | | | > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | | > > | > > > > > > > > > > > | | | > | > | | > > > > > > > > | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | | < > > | < < < < < < > | > | 200604 200605 200606 200607 200608 200609 200610 200611 200612 200613 200614 200615 200616 200617 200618 200619 200620 200621 200622 200623 200624 200625 200626 200627 200628 200629 200630 200631 200632 200633 200634 200635 200636 200637 200638 200639 200640 200641 200642 200643 200644 200645 200646 200647 200648 200649 200650 200651 200652 200653 200654 200655 200656 200657 200658 200659 200660 200661 200662 200663 200664 200665 200666 200667 200668 200669 200670 200671 200672 200673 200674 200675 200676 200677 200678 200679 200680 200681 200682 200683 200684 200685 200686 200687 200688 200689 200690 200691 200692 200693 200694 200695 200696 200697 200698 200699 200700 200701 200702 200703 200704 200705 200706 200707 200708 200709 200710 200711 200712 200713 200714 200715 200716 200717 200718 200719 200720 200721 200722 200723 200724 200725 200726 200727 200728 200729 200730 200731 200732 200733 200734 200735 200736 200737 200738 200739 200740 200741 200742 200743 200744 200745 200746 200747 200748 200749 200750 200751 200752 200753 200754 200755 200756 200757 200758 200759 200760 200761 200762 200763 200764 200765 200766 200767 200768 200769 200770 200771 200772 200773 200774 200775 200776 200777 200778 200779 200780 200781 200782 200783 200784 200785 200786 200787 200788 200789 200790 200791 200792 200793 200794 200795 200796 200797 200798 200799 200800 200801 200802 200803 200804 200805 200806 200807 200808 200809 200810 200811 200812 200813 200814 200815 200816 200817 200818 200819 200820 200821 200822 200823 200824 200825 200826 200827 200828 200829 200830 200831 200832 200833 200834 200835 200836 200837 200838 200839 200840 200841 200842 200843 200844 200845 200846 200847 200848 200849 200850 200851 200852 200853 200854 200855 200856 200857 200858 200859 200860 200861 200862 200863 200864 200865 200866 200867 200868 200869 200870 200871 200872 200873 200874 200875 200876 200877 200878 200879 200880 200881 200882 200883 200884 200885 200886 200887 200888 200889 200890 200891 200892 200893 200894 200895 200896 200897 200898 200899 200900 200901 200902 200903 200904 200905 200906 200907 200908 200909 200910 200911 200912 200913 200914 200915 200916 200917 200918 200919 200920 200921 200922 200923 200924 200925 200926 200927 200928 200929 200930 200931 200932 200933 200934 200935 200936 200937 200938 200939 200940 200941 200942 200943 200944 200945 200946 200947 200948 200949 200950 200951 200952 200953 200954 200955 200956 200957 200958 200959 200960 200961 200962 200963 200964 200965 200966 200967 200968 200969 200970 200971 200972 200973 200974 200975 200976 200977 200978 200979 200980 200981 200982 200983 200984 200985 200986 200987 200988 200989 200990 200991 200992 200993 200994 200995 200996 200997 200998 200999 201000 201001 201002 201003 201004 201005 201006 201007 201008 201009 201010 201011 201012 201013 201014 201015 201016 201017 201018 201019 201020 201021 201022 201023 201024 201025 201026 201027 201028 201029 201030 201031 201032 201033 201034 201035 201036 201037 201038 201039 201040 201041 201042 201043 201044 201045 201046 201047 201048 201049 201050 201051 201052 201053 201054 201055 201056 201057 201058 201059 201060 201061 201062 201063 201064 201065 201066 201067 201068 201069 201070 201071 201072 201073 201074 201075 201076 201077 201078 201079 201080 201081 201082 201083 201084 201085 201086 201087 201088 201089 201090 201091 201092 201093 201094 201095 201096 201097 201098 201099 201100 201101 201102 201103 201104 201105 201106 201107 201108 201109 201110 201111 201112 201113 201114 201115 201116 201117 201118 201119 201120 201121 201122 201123 201124 201125 201126 201127 201128 201129 201130 201131 201132 201133 201134 201135 201136 201137 201138 201139 201140 201141 201142 201143 201144 201145 201146 201147 201148 201149 201150 201151 201152 201153 201154 201155 201156 201157 201158 201159 201160 201161 201162 201163 201164 201165 201166 201167 201168 201169 201170 201171 201172 201173 201174 201175 201176 201177 201178 201179 201180 201181 201182 201183 201184 201185 201186 201187 201188 201189 201190 201191 201192 201193 201194 201195 201196 201197 201198 201199 201200 201201 201202 201203 201204 201205 201206 201207 201208 201209 201210 201211 201212 201213 201214 201215 201216 201217 201218 201219 201220 201221 201222 201223 201224 201225 201226 201227 201228 201229 |
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 & 0xff);
p->jnFlags = (u8)(eType >> 8);
VVA( p->eU = zContent ? 1 : 0 );
p->n = n;
p->u.zJContent = zContent;
return pParse->nNode++;
}
/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
}
/*
** Return true if z[] begins with 4 (or more) hexadecimal digits
*/
static int jsonIs4Hex(const char *z){
return jsonIs2Hex(z) && jsonIs2Hex(&z[2]);
}
/*
** Return the number of bytes of JSON5 whitespace at the beginning of
** the input string z[].
**
** JSON5 whitespace consists of any of the following characters:
**
** Unicode UTF-8 Name
** U+0009 09 horizontal tab
** U+000a 0a line feed
** U+000b 0b vertical tab
** U+000c 0c form feed
** U+000d 0d carriage return
** U+0020 20 space
** U+00a0 c2 a0 non-breaking space
** U+1680 e1 9a 80 ogham space mark
** U+2000 e2 80 80 en quad
** U+2001 e2 80 81 em quad
** U+2002 e2 80 82 en space
** U+2003 e2 80 83 em space
** U+2004 e2 80 84 three-per-em space
** U+2005 e2 80 85 four-per-em space
** U+2006 e2 80 86 six-per-em space
** U+2007 e2 80 87 figure space
** U+2008 e2 80 88 punctuation space
** U+2009 e2 80 89 thin space
** U+200a e2 80 8a hair space
** U+2028 e2 80 a8 line separator
** U+2029 e2 80 a9 paragraph separator
** U+202f e2 80 af narrow no-break space (NNBSP)
** U+205f e2 81 9f medium mathematical space (MMSP)
** U+3000 e3 80 80 ideographical space
** U+FEFF ef bb bf byte order mark
**
** In addition, comments between '/', '*' and '*', '/' and
** from '/', '/' to end-of-line are also considered to be whitespace.
*/
static int json5Whitespace(const char *zIn){
int n = 0;
const u8 *z = (u8*)zIn;
while( 1 /*exit by "goto whitespace_done"*/ ){
switch( z[n] ){
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x20: {
n++;
break;
}
case '/': {
if( z[n+1]=='*' && z[n+2]!=0 ){
int j;
for(j=n+3; z[j]!='/' || z[j-1]!='*'; j++){
if( z[j]==0 ) goto whitespace_done;
}
n = j+1;
break;
}else if( z[n+1]=='/' ){
int j;
char c;
for(j=n+2; (c = z[j])!=0; j++){
if( c=='\n' || c=='\r' ) break;
if( 0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])
){
j += 2;
break;
}
}
n = j;
if( z[n] ) n++;
break;
}
goto whitespace_done;
}
case 0xc2: {
if( z[n+1]==0xa0 ){
n += 2;
break;
}
goto whitespace_done;
}
case 0xe1: {
if( z[n+1]==0x9a && z[n+2]==0x80 ){
n += 3;
break;
}
goto whitespace_done;
}
case 0xe2: {
if( z[n+1]==0x80 ){
u8 c = z[n+2];
if( c<0x80 ) goto whitespace_done;
if( c<=0x8a || c==0xa8 || c==0xa9 || c==0xaf ){
n += 3;
break;
}
}else if( z[n+1]==0x81 && z[n+2]==0x9f ){
n += 3;
break;
}
goto whitespace_done;
}
case 0xe3: {
if( z[n+1]==0x80 && z[n+2]==0x80 ){
n += 3;
break;
}
goto whitespace_done;
}
case 0xef: {
if( z[n+1]==0xbb && z[n+2]==0xbf ){
n += 3;
break;
}
goto whitespace_done;
}
default: {
goto whitespace_done;
}
}
}
whitespace_done:
return n;
}
/*
** Extra floating-point literals to allow in JSON.
*/
static const struct NanInfName {
char c1;
char c2;
char n;
char eType;
char nRepl;
char *zMatch;
char *zRepl;
} aNanInfName[] = {
{ 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" },
{ 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
{ 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },
{ 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },
{ 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },
};
/*
** Parse a single JSON value which begins at pParse->zJson[i]. Return the
** index of the first character past the end of the value parsed.
**
** Special return values:
**
** 0 End if input
** -1 Syntax error
** -2 '}' seen
** -3 ']' seen
** -4 ',' seen
** -5 ':' seen
*/
static int jsonParseValue(JsonParse *pParse, u32 i){
char c;
u32 j;
int iThis;
int x;
JsonNode *pNode;
const char *z = pParse->zJson;
json_parse_restart:
switch( (u8)z[i] ){
case '{': {
/* Parse object */
iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
if( iThis<0 ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
for(j=i+1;;j++){
u32 nNode = pParse->nNode;
x = jsonParseValue(pParse, j);
if( x<=0 ){
if( x==(-2) ){
j = pParse->iErr;
if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
break;
}
j += json5Whitespace(&z[j]);
if( sqlite3JsonId1(z[j])
|| (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
){
int k = j+1;
while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
|| (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
){
k++;
}
jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]);
pParse->hasNonstd = 1;
x = k;
}else{
if( x!=-1 ) pParse->iErr = j;
return -1;
}
}
if( pParse->oom ) return -1;
pNode = &pParse->aNode[nNode];
if( pNode->eType!=JSON_STRING ){
pParse->iErr = j;
return -1;
}
pNode->jnFlags |= JNODE_LABEL;
j = x;
if( z[j]==':' ){
j++;
}else{
if( fast_isspace(z[j]) ){
do{ j++; }while( fast_isspace(z[j]) );
if( z[j]==':' ){
j++;
goto parse_object_value;
}
}
x = jsonParseValue(pParse, j);
if( x!=(-5) ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = pParse->iErr+1;
}
parse_object_value:
x = jsonParseValue(pParse, j);
if( x<=0 ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = x;
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}else{
if( fast_isspace(z[j]) ){
do{ j++; }while( fast_isspace(z[j]) );
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}
}
x = jsonParseValue(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
}
if( x==(-2) ){
j = pParse->iErr;
break;
}
}
pParse->iErr = j;
return -1;
}
pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
pParse->iDepth--;
return j+1;
}
case '[': {
/* Parse array */
iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
if( iThis<0 ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
for(j=i+1;;j++){
x = jsonParseValue(pParse, j);
if( x<=0 ){
if( x==(-3) ){
j = pParse->iErr;
if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
break;
}
if( x!=(-1) ) pParse->iErr = j;
return -1;
}
j = x;
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}else{
if( fast_isspace(z[j]) ){
do{ j++; }while( fast_isspace(z[j]) );
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}
}
x = jsonParseValue(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
}
if( x==(-3) ){
j = pParse->iErr;
break;
}
}
pParse->iErr = j;
return -1;
}
pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
pParse->iDepth--;
return j+1;
}
case '\'': {
u8 jnFlags;
char cDelim;
pParse->hasNonstd = 1;
jnFlags = JNODE_JSON5;
goto parse_string;
case '"':
/* Parse string */
jnFlags = 0;
parse_string:
cDelim = z[i];
j = i+1;
for(;;){
c = z[j];
if( (c & ~0x1f)==0 ){
/* Control characters are not allowed in strings */
pParse->iErr = j;
return -1;
}
if( c=='\\' ){
c = z[++j];
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
jnFlags |= JNODE_ESCAPE;
}else if( c=='\'' || c=='0' || c=='v' || c=='\n'
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
pParse->hasNonstd = 1;
}else if( c=='\r' ){
if( z[j+1]=='\n' ) j++;
jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
pParse->hasNonstd = 1;
}else{
pParse->iErr = j;
return -1;
}
}else if( c==cDelim ){
break;
}
j++;
}
jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]);
return j+1;
}
case 't': {
if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
return i+4;
}
pParse->iErr = i;
return -1;
}
case 'f': {
if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
return i+5;
}
pParse->iErr = i;
return -1;
}
case '+': {
u8 seenDP, seenE, jnFlags;
pParse->hasNonstd = 1;
jnFlags = JNODE_JSON5;
goto parse_number;
case '.':
if( sqlite3Isdigit(z[i+1]) ){
pParse->hasNonstd = 1;
jnFlags = JNODE_JSON5;
seenE = 0;
seenDP = JSON_REAL;
goto parse_number_2;
}
pParse->iErr = i;
return -1;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/* Parse number */
jnFlags = 0;
parse_number:
seenDP = JSON_INT;
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
assert( '.' < '0' );
c = z[i];
if( c<='0' ){
if( c=='0' ){
if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
assert( seenDP==JSON_INT );
pParse->hasNonstd = 1;
jnFlags |= JNODE_JSON5;
for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}else if( sqlite3Isdigit(z[i+1]) ){
pParse->iErr = i+1;
return -1;
}
}else{
if( !sqlite3Isdigit(z[i+1]) ){
/* JSON5 allows for "+Infinity" and "-Infinity" using exactly
** that case. SQLite also allows these in any case and it allows
** "+inf" and "-inf". */
if( (z[i+1]=='I' || z[i+1]=='i')
&& sqlite3StrNICmp(&z[i+1], "inf",3)==0
){
pParse->hasNonstd = 1;
if( z[i]=='-' ){
jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999");
}else{
jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999");
}
return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
}
if( z[i+1]=='.' ){
pParse->hasNonstd = 1;
jnFlags |= JNODE_JSON5;
goto parse_number_2;
}
pParse->iErr = i;
return -1;
}
if( z[i+1]=='0' ){
if( sqlite3Isdigit(z[i+2]) ){
pParse->iErr = i+1;
return -1;
}else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
pParse->hasNonstd = 1;
jnFlags |= JNODE_JSON5;
for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}
}
}
}
parse_number_2:
for(j=i+1;; j++){
c = z[j];
if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
if( seenDP==JSON_REAL ){
pParse->iErr = j;
return -1;
}
seenDP = JSON_REAL;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
jnFlags |= JNODE_JSON5;
}else{
pParse->iErr = j;
return -1;
}
}
if( seenE ){
pParse->iErr = j;
return -1;
}
seenDP = JSON_REAL;
seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
j++;
c = z[j+1];
}
if( c<'0' || c>'9' ){
pParse->iErr = j;
return -1;
}
continue;
}
break;
}
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
jnFlags |= JNODE_JSON5;
}else{
pParse->iErr = j;
return -1;
}
}
parse_number_finish:
jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]);
return j;
}
case '}': {
pParse->iErr = i;
return -2; /* End of {...} */
}
case ']': {
pParse->iErr = i;
return -3; /* End of [...] */
}
case ',': {
pParse->iErr = i;
return -4; /* List separator */
}
case ':': {
pParse->iErr = i;
return -5; /* Object label/value separator */
}
case 0: {
return 0; /* End of file */
}
case 0x09:
case 0x0a:
case 0x0d:
case 0x20: {
do{
i++;
}while( fast_isspace(z[i]) );
goto json_parse_restart;
}
case 0x0b:
case 0x0c:
case '/':
case 0xc2:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xef: {
j = json5Whitespace(&z[i]);
if( j>0 ){
i += j;
pParse->hasNonstd = 1;
goto json_parse_restart;
}
pParse->iErr = i;
return -1;
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
jsonParseAddNode(pParse, JSON_NULL, 0, 0);
return i+4;
}
/* fall-through into the default case that checks for NaN */
}
default: {
u32 k;
int nn;
c = z[i];
for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
nn = aNanInfName[k].n;
if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
continue;
}
if( sqlite3Isalnum(z[i+nn]) ) continue;
jsonParseAddNode(pParse, aNanInfName[k].eType,
aNanInfName[k].nRepl, aNanInfName[k].zRepl);
pParse->hasNonstd = 1;
return i + nn;
}
pParse->iErr = i;
return -1; /* Syntax error */
}
} /* End switch(z[i]) */
}
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory associated with
** pParse.
**
|
| ︙ | ︙ | |||
199376 199377 199378 199379 199380 199381 199382 |
if( zJson==0 ) return 1;
pParse->zJson = zJson;
i = jsonParseValue(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
assert( pParse->iDepth==0 );
while( fast_isspace(zJson[i]) ) i++;
| | > > > > > > > | 201239 201240 201241 201242 201243 201244 201245 201246 201247 201248 201249 201250 201251 201252 201253 201254 201255 201256 201257 201258 201259 201260 |
if( zJson==0 ) return 1;
pParse->zJson = zJson;
i = jsonParseValue(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
assert( pParse->iDepth==0 );
while( fast_isspace(zJson[i]) ) i++;
if( zJson[i] ){
i += json5Whitespace(&zJson[i]);
if( zJson[i] ){
jsonParseReset(pParse);
return 1;
}
pParse->hasNonstd = 1;
}
}
if( i<=0 ){
if( pCtx!=0 ){
if( pParse->oom ){
sqlite3_result_error_nomem(pCtx);
}else{
sqlite3_result_error(pCtx, "malformed JSON", -1);
|
| ︙ | ︙ | |||
199447 199448 199449 199450 199451 199452 199453 199454 199455 199456 199457 199458 199459 199460 |
/*
** Obtain a complete parse of the JSON found in the first argument
** of the argv array. Use the sqlite3_get_auxdata() cache for this
** parse if it is available. If the cache is not available or if it
** is no longer valid, parse the JSON again and return the new parse,
** and also register the new parse so that it will be available for
** future sqlite3_get_auxdata() calls.
*/
static JsonParse *jsonParseCached(
sqlite3_context *pCtx,
sqlite3_value **argv,
sqlite3_context *pErrCtx
){
const char *zJson = (const char*)sqlite3_value_text(argv[0]);
| > > > > > > > > > | 201317 201318 201319 201320 201321 201322 201323 201324 201325 201326 201327 201328 201329 201330 201331 201332 201333 201334 201335 201336 201337 201338 201339 |
/*
** Obtain a complete parse of the JSON found in the first argument
** of the argv array. Use the sqlite3_get_auxdata() cache for this
** parse if it is available. If the cache is not available or if it
** is no longer valid, parse the JSON again and return the new parse,
** and also register the new parse so that it will be available for
** future sqlite3_get_auxdata() calls.
**
** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
** and return NULL.
**
** If an error occurs and pErrCtx==0 then return the Parse object with
** JsonParse.nErr non-zero. If the caller invokes this routine with
** pErrCtx==0 and it gets back a JsonParse with nErr!=0, then the caller
** is responsible for invoking jsonParseFree() on the returned value.
** But the caller may invoke jsonParseFree() *only* if pParse->nErr!=0.
*/
static JsonParse *jsonParseCached(
sqlite3_context *pCtx,
sqlite3_value **argv,
sqlite3_context *pErrCtx
){
const char *zJson = (const char*)sqlite3_value_text(argv[0]);
|
| ︙ | ︙ | |||
199496 199497 199498 199499 199500 199501 199502 199503 199504 199505 199506 199507 199508 199509 199510 199511 199512 199513 199514 199515 199516 |
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
p->zJson = (char*)&p[1];
memcpy((char*)p->zJson, zJson, nJson+1);
if( jsonParse(p, pErrCtx, p->zJson) ){
sqlite3_free(p);
return 0;
}
p->nJson = nJson;
p->iHold = iMaxHold+1;
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
(void(*)(void*))jsonParseFree);
return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
** a match.
*/
| > > > > | > > > > > > > > > | 201375 201376 201377 201378 201379 201380 201381 201382 201383 201384 201385 201386 201387 201388 201389 201390 201391 201392 201393 201394 201395 201396 201397 201398 201399 201400 201401 201402 201403 201404 201405 201406 201407 201408 201409 201410 201411 201412 201413 201414 201415 201416 201417 201418 201419 201420 201421 201422 201423 201424 |
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, sizeof(*p));
p->zJson = (char*)&p[1];
memcpy((char*)p->zJson, zJson, nJson+1);
if( jsonParse(p, pErrCtx, p->zJson) ){
if( pErrCtx==0 ){
p->nErr = 1;
return p;
}
sqlite3_free(p);
return 0;
}
p->nJson = nJson;
p->iHold = iMaxHold+1;
sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
(void(*)(void*))jsonParseFree);
return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
** Compare the OBJECT label at pNode against zKey,nKey. Return true on
** a match.
*/
static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_RAW ){
if( pNode->n!=nKey ) return 0;
return strncmp(pNode->u.zJContent, zKey, nKey)==0;
}else{
if( pNode->n!=nKey+2 ) return 0;
return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
}
}
static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
if( p1->jnFlags & JNODE_RAW ){
return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
}else if( p2->jnFlags & JNODE_RAW ){
return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
}else{
return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
}
}
/* forward declaration */
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
/*
** Search along zPath to find the node specified. Return a pointer
|
| ︙ | ︙ | |||
199990 199991 199992 199993 199994 199995 199996 |
p = jsonParseCached(ctx, argv, ctx);
if( p==0 ) return;
if( argc==2 ){
/* With a single PATH argument */
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) return;
if( flags & JSON_ABPATH ){
| | | 201882 201883 201884 201885 201886 201887 201888 201889 201890 201891 201892 201893 201894 201895 201896 |
p = jsonParseCached(ctx, argv, ctx);
if( p==0 ) return;
if( argc==2 ){
/* With a single PATH argument */
zPath = (const char*)sqlite3_value_text(argv[1]);
if( zPath==0 ) return;
if( flags & JSON_ABPATH ){
if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
/* The -> and ->> operators accept abbreviated PATH arguments. This
** is mostly for compatibility with PostgreSQL, but also for
** convenience.
**
** NUMBER ==> $[NUMBER] // PG compatible
** LABEL ==> $.LABEL // PG compatible
** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
|
| ︙ | ︙ | |||
200081 200082 200083 200084 200085 200086 200087 |
u32 nKey;
const char *zKey;
assert( pPatch[i].eType==JSON_STRING );
assert( pPatch[i].jnFlags & JNODE_LABEL );
assert( pPatch[i].eU==1 );
nKey = pPatch[i].n;
zKey = pPatch[i].u.zJContent;
| < | < | 201973 201974 201975 201976 201977 201978 201979 201980 201981 201982 201983 201984 201985 201986 201987 201988 201989 201990 |
u32 nKey;
const char *zKey;
assert( pPatch[i].eType==JSON_STRING );
assert( pPatch[i].jnFlags & JNODE_LABEL );
assert( pPatch[i].eU==1 );
nKey = pPatch[i].n;
zKey = pPatch[i].u.zJContent;
for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
assert( pTarget[j].eType==JSON_STRING );
assert( pTarget[j].jnFlags & JNODE_LABEL );
if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){
if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_PATCH) ) break;
if( pPatch[i+1].eType==JSON_NULL ){
pTarget[j+1].jnFlags |= JNODE_REMOVE;
}else{
JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
if( pNew==0 ) return 0;
pTarget = &pParse->aNode[iTarget];
|
| ︙ | ︙ | |||
200373 200374 200375 200376 200377 200378 200379 |
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
}
/*
** json_valid(JSON)
**
| | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > | 202263 202264 202265 202266 202267 202268 202269 202270 202271 202272 202273 202274 202275 202276 202277 202278 202279 202280 202281 202282 202283 202284 202285 202286 202287 202288 202289 202290 202291 202292 202293 202294 202295 202296 202297 202298 202299 202300 202301 202302 202303 202304 202305 202306 202307 202308 202309 202310 202311 202312 202313 202314 202315 202316 202317 202318 202319 202320 202321 202322 202323 202324 202325 202326 202327 202328 202329 202330 202331 202332 202333 202334 202335 202336 202337 202338 202339 202340 202341 202342 202343 202344 202345 202346 202347 202348 202349 |
sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
}
}
/*
** json_valid(JSON)
**
** Return 1 if JSON is a well-formed canonical JSON string according
** to RFC-7159. Return 0 otherwise.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
p = jsonParseCached(ctx, argv, 0);
if( p==0 || p->oom ){
sqlite3_result_error_nomem(ctx);
sqlite3_free(p);
}else{
sqlite3_result_int(ctx, p->nErr==0 && p->hasNonstd==0);
if( p->nErr ) jsonParseFree(p);
}
}
/*
** json_error_position(JSON)
**
** If the argument is not an interpretable JSON string, then return the 1-based
** character position at which the parser first recognized that the input
** was in error. The left-most character is 1. If the string is valid
** JSON, then return 0.
**
** Note that json_valid() is only true for strictly conforming canonical JSON.
** But this routine returns zero if the input contains extension. Thus:
**
** (1) If the input X is strictly conforming canonical JSON:
**
** json_valid(X) returns true
** json_error_position(X) returns 0
**
** (2) If the input X is JSON but it includes extension (such as JSON5) that
** are not part of RFC-8259:
**
** json_valid(X) returns false
** json_error_position(X) return 0
**
** (3) If the input X cannot be interpreted as JSON even taking extensions
** into account:
**
** json_valid(X) return false
** json_error_position(X) returns 1 or more
*/
static void jsonErrorFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
JsonParse *p; /* The parse */
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
p = jsonParseCached(ctx, argv, 0);
if( p==0 || p->oom ){
sqlite3_result_error_nomem(ctx);
sqlite3_free(p);
}else if( p->nErr==0 ){
sqlite3_result_int(ctx, 0);
}else{
int n = 1;
u32 i;
const char *z = p->zJson;
for(i=0; i<p->iErr && ALWAYS(z[i]); i++){
if( (z[i]&0xc0)!=0x80 ) n++;
}
sqlite3_result_int(ctx, n);
jsonParseFree(p);
}
}
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
/*
|
| ︙ | ︙ | |||
200728 200729 200730 200731 200732 200733 200734 | int jj, nn; const char *z; assert( pNode->eType==JSON_STRING ); assert( pNode->jnFlags & JNODE_LABEL ); assert( pNode->eU==1 ); z = pNode->u.zJContent; nn = pNode->n; | > | | | | | | | | > | 202679 202680 202681 202682 202683 202684 202685 202686 202687 202688 202689 202690 202691 202692 202693 202694 202695 202696 202697 202698 202699 202700 202701 202702 |
int jj, nn;
const char *z;
assert( pNode->eType==JSON_STRING );
assert( pNode->jnFlags & JNODE_LABEL );
assert( pNode->eU==1 );
z = pNode->u.zJContent;
nn = pNode->n;
if( (pNode->jnFlags & JNODE_RAW)==0 ){
assert( nn>=2 );
assert( z[0]=='"' || z[0]=='\'' );
assert( z[nn-1]=='"' || z[0]=='\'' );
if( nn>2 && sqlite3Isalpha(z[1]) ){
for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
if( jj==nn-1 ){
z++;
nn -= 2;
}
}
}
jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
}
/* Append the name of the path for element i to pStr
*/
|
| ︙ | ︙ | |||
200912 200913 200914 200915 200916 200917 200918 200919 200920 200921 200922 200923 200924 200925 |
if( pConstraint->usable==0 ){
unusableMask |= iMask;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
aIdx[iCol] = i;
idxMask |= iMask;
}
}
if( (unusableMask & ~idxMask)!=0 ){
/* If there are any unusable constraints on JSON or ROOT, then reject
** this entire plan */
return SQLITE_CONSTRAINT;
}
if( aIdx[0]<0 ){
/* No JSON input. Leave estimatedCost at the huge value that it was
| > > > > > > > | 202865 202866 202867 202868 202869 202870 202871 202872 202873 202874 202875 202876 202877 202878 202879 202880 202881 202882 202883 202884 202885 |
if( pConstraint->usable==0 ){
unusableMask |= iMask;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
aIdx[iCol] = i;
idxMask |= iMask;
}
}
if( pIdxInfo->nOrderBy>0
&& pIdxInfo->aOrderBy[0].iColumn<0
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
if( (unusableMask & ~idxMask)!=0 ){
/* If there are any unusable constraints on JSON or ROOT, then reject
** this entire plan */
return SQLITE_CONSTRAINT;
}
if( aIdx[0]<0 ){
/* No JSON input. Leave estimatedCost at the huge value that it was
|
| ︙ | ︙ | |||
201088 201089 201090 201091 201092 201093 201094 201095 201096 201097 201098 201099 201100 201101 201102 201103 201104 201105 201106 201107 201108 201109 201110 201111 201112 201113 |
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
JFUNCTION(json, 1, 0, jsonRemoveFunc),
JFUNCTION(json_array, -1, 0, jsonArrayFunc),
JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
JFUNCTION(json_insert, -1, 0, jsonSetFunc),
JFUNCTION(json_object, -1, 0, jsonObjectFunc),
JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
JFUNCTION(json_type, 1, 0, jsonTypeFunc),
JFUNCTION(json_type, 2, 0, jsonTypeFunc),
JFUNCTION(json_valid, 1, 0, jsonValidFunc),
#if SQLITE_DEBUG
JFUNCTION(json_parse, 1, 0, jsonParseFunc),
JFUNCTION(json_test1, 1, 0, jsonTest1Func),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
| > | | | 203048 203049 203050 203051 203052 203053 203054 203055 203056 203057 203058 203059 203060 203061 203062 203063 203064 203065 203066 203067 203068 203069 203070 203071 203072 203073 203074 203075 203076 203077 203078 203079 203080 203081 203082 203083 203084 203085 |
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
JFUNCTION(json, 1, 0, jsonRemoveFunc),
JFUNCTION(json_array, -1, 0, jsonArrayFunc),
JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
JFUNCTION(json_insert, -1, 0, jsonSetFunc),
JFUNCTION(json_object, -1, 0, jsonObjectFunc),
JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
JFUNCTION(json_type, 1, 0, jsonTypeFunc),
JFUNCTION(json_type, 2, 0, jsonTypeFunc),
JFUNCTION(json_valid, 1, 0, jsonValidFunc),
#if SQLITE_DEBUG
JFUNCTION(json_parse, 1, 0, jsonParseFunc),
JFUNCTION(json_test1, 1, 0, jsonTest1Func),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
/*
|
| ︙ | ︙ | |||
201612 201613 201614 201615 201616 201617 201618 | ** ** For best performance, an attempt is made to guess at the byte-order ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined ** at run-time. */ #ifndef SQLITE_BYTEORDER | | | | | | | > | | | | | 203573 203574 203575 203576 203577 203578 203579 203580 203581 203582 203583 203584 203585 203586 203587 203588 203589 203590 203591 203592 203593 203594 203595 203596 203597 |
**
** For best performance, an attempt is made to guess at the byte-order
** using C-preprocessor macros. If that is unsuccessful, or if
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
** at run-time.
*/
#ifndef SQLITE_BYTEORDER
# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
# define SQLITE_BYTEORDER 1234
# elif defined(sparc) || defined(__ppc__) || \
defined(__ARMEB__) || defined(__AARCH64EB__)
# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
#endif
/* What version of MSVC is being used. 0 means MSVC is not being used */
#ifndef MSVC_VERSION
#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
# define MSVC_VERSION _MSC_VER
|
| ︙ | ︙ | |||
208577 208578 208579 208580 208581 208582 208583 | ** Then the RBU database should contain: ** ** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control); ** ** The order of the columns in the data_% table does not matter. ** ** Instead of a regular table, the RBU database may also contain virtual | | | | 210539 210540 210541 210542 210543 210544 210545 210546 210547 210548 210549 210550 210551 210552 210553 210554 210555 210556 210557 210558 210559 210560 210561 210562 210563 210564 210565 210566 | ** Then the RBU database should contain: ** ** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control); ** ** The order of the columns in the data_% table does not matter. ** ** Instead of a regular table, the RBU database may also contain virtual ** tables or views named using the data_<target> naming scheme. ** ** Instead of the plain data_<target> naming scheme, RBU database tables ** may also be named data<integer>_<target>, where <integer> is any sequence ** of zero or more numeric characters (0-9). This can be significant because ** tables within the RBU database are always processed in order sorted by ** name. By judicious selection of the <integer> portion of the names ** of the RBU tables the user can therefore control the order in which they ** are processed. This can be useful, for example, to ensure that "external ** content" FTS4 tables are updated before their underlying content tables. ** ** If the target database table is a virtual table or a table that has no ** PRIMARY KEY declaration, the data_% table must also contain a column ** named "rbu_rowid". This column is mapped to the table's implicit primary ** key column - "rowid". Virtual tables for which the "rowid" column does ** not function like a primary key value cannot be updated using RBU. For ** example, if the target db contains either of the following: ** ** CREATE VIRTUAL TABLE x1 USING fts3(a, b); ** CREATE TABLE x1(a, b) ** |
| ︙ | ︙ | |||
212166 212167 212168 212169 212170 212171 212172 212173 212174 212175 212176 212177 212178 212179 212180 |
p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff);
if( p->rc ) return;
iOff = (i64)(pFrame->iDbPage-1) * p->pgsz;
p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
}
/*
** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if
** successful, or an SQLite error code otherwise.
*/
static int rbuLockDatabase(sqlite3 *db){
int rc = SQLITE_OK;
sqlite3_file *fd = 0;
| > > > > > > > > | > > > | > > > > > | | 214128 214129 214130 214131 214132 214133 214134 214135 214136 214137 214138 214139 214140 214141 214142 214143 214144 214145 214146 214147 214148 214149 214150 214151 214152 214153 214154 214155 214156 214157 214158 214159 214160 214161 214162 214163 214164 214165 214166 214167 214168 |
p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff);
if( p->rc ) return;
iOff = (i64)(pFrame->iDbPage-1) * p->pgsz;
p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
}
/*
** This value is copied from the definition of ZIPVFS_CTRL_FILE_POINTER
** in zipvfs.h.
*/
#define RBU_ZIPVFS_CTRL_FILE_POINTER 230439
/*
** Take an EXCLUSIVE lock on the database file. Return SQLITE_OK if
** successful, or an SQLite error code otherwise.
*/
static int rbuLockDatabase(sqlite3 *db){
int rc = SQLITE_OK;
sqlite3_file *fd = 0;
sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
if( fd ){
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
if( rc==SQLITE_OK ){
rc = fd->pMethods->xUnlock(fd, SQLITE_LOCK_NONE);
}
sqlite3_file_control(db, "main", RBU_ZIPVFS_CTRL_FILE_POINTER, &fd);
}else{
sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, &fd);
}
if( rc==SQLITE_OK && fd->pMethods ){
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_SHARED);
if( rc==SQLITE_OK ){
rc = fd->pMethods->xLock(fd, SQLITE_LOCK_EXCLUSIVE);
}
}
return rc;
}
|
| ︙ | ︙ | |||
215413 215414 215415 215416 215417 215418 215419 215420 215421 215422 215423 215424 215425 215426 |
int rc = SQLITE_OK;
(void)pAux;
(void)argc;
(void)argv;
(void)pzErr;
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
if( rc==SQLITE_OK ){
pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
}
| > | 217391 217392 217393 217394 217395 217396 217397 217398 217399 217400 217401 217402 217403 217404 217405 |
int rc = SQLITE_OK;
(void)pAux;
(void)argc;
(void)argv;
(void)pzErr;
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS);
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(pgno INTEGER PRIMARY KEY, data BLOB, schema HIDDEN)");
if( rc==SQLITE_OK ){
pTab = (DbpageTable *)sqlite3_malloc64(sizeof(DbpageTable));
if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
}
|
| ︙ | ︙ | |||
215496 215497 215498 215499 215500 215501 215502 |
if( pIdxInfo->nOrderBy>=1
&& pIdxInfo->aOrderBy[0].iColumn<=0
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
| < | 217475 217476 217477 217478 217479 217480 217481 217482 217483 217484 217485 217486 217487 217488 |
if( pIdxInfo->nOrderBy>=1
&& pIdxInfo->aOrderBy[0].iColumn<=0
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
return SQLITE_OK;
}
/*
** Open a new dbpagevfs cursor.
*/
static int dbpageOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
| ︙ | ︙ | |||
215581 215582 215583 215584 215585 215586 215587 |
zSchema = (const char*)sqlite3_value_text(argv[0]);
pCsr->iDb = sqlite3FindDbName(db, zSchema);
if( pCsr->iDb<0 ) return SQLITE_OK;
}else{
pCsr->iDb = 0;
}
pBt = db->aDb[pCsr->iDb].pBt;
| | | 217559 217560 217561 217562 217563 217564 217565 217566 217567 217568 217569 217570 217571 217572 217573 |
zSchema = (const char*)sqlite3_value_text(argv[0]);
pCsr->iDb = sqlite3FindDbName(db, zSchema);
if( pCsr->iDb<0 ) return SQLITE_OK;
}else{
pCsr->iDb = 0;
}
pBt = db->aDb[pCsr->iDb].pBt;
if( NEVER(pBt==0) ) return SQLITE_OK;
pCsr->pPager = sqlite3BtreePager(pBt);
pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
pCsr->mxPgno = sqlite3BtreeLastPage(pBt);
if( idxNum & 1 ){
assert( argc>(idxNum>>1) );
pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]);
if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){
|
| ︙ | ︙ | |||
215672 215673 215674 215675 215676 215677 215678 |
goto update_fail;
}
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
}
pgno = sqlite3_value_int(argv[0]);
| > | > | | | | 217650 217651 217652 217653 217654 217655 217656 217657 217658 217659 217660 217661 217662 217663 217664 217665 217666 217667 217668 217669 217670 217671 217672 217673 217674 217675 217676 217677 |
goto update_fail;
}
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
}
pgno = sqlite3_value_int(argv[0]);
if( sqlite3_value_type(argv[0])==SQLITE_NULL
|| (Pgno)sqlite3_value_int(argv[1])!=pgno
){
zErr = "cannot insert";
goto update_fail;
}
zSchema = (const char*)sqlite3_value_text(argv[4]);
iDb = ALWAYS(zSchema) ? sqlite3FindDbName(pTab->db, zSchema) : -1;
if( NEVER(iDb<0) ){
zErr = "no such schema";
goto update_fail;
}
pBt = pTab->db->aDb[iDb].pBt;
if( NEVER(pgno<1) || NEVER(pBt==0) || NEVER(pgno>sqlite3BtreeLastPage(pBt)) ){
zErr = "bad page number";
goto update_fail;
}
szPage = sqlite3BtreeGetPageSize(pBt);
if( sqlite3_value_type(argv[3])!=SQLITE_BLOB
|| sqlite3_value_bytes(argv[3])!=szPage
){
|
| ︙ | ︙ | |||
215722 215723 215724 215725 215726 215727 215728 |
** written by the sqlite_dbpage virtual table, start a write transaction
** on them all.
*/
static int dbpageBegin(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
sqlite3 *db = pTab->db;
int i;
| < | | | | 217702 217703 217704 217705 217706 217707 217708 217709 217710 217711 217712 217713 217714 217715 217716 217717 217718 217719 217720 |
** written by the sqlite_dbpage virtual table, start a write transaction
** on them all.
*/
static int dbpageBegin(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
sqlite3 *db = pTab->db;
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
}
return SQLITE_OK;
}
/*
** Invoke this routine to register the "dbpage" virtual table module
*/
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
|
| ︙ | ︙ | |||
215795 215796 215797 215798 215799 215800 215801 215802 215803 215804 215805 215806 215807 215808 |
#ifndef SESSIONS_STRM_CHUNK_SIZE
# ifdef SQLITE_TEST
# define SESSIONS_STRM_CHUNK_SIZE 64
# else
# define SESSIONS_STRM_CHUNK_SIZE 1024
# endif
#endif
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
typedef struct SessionHook SessionHook;
struct SessionHook {
void *pCtx;
int (*xOld)(void*,int,sqlite3_value**);
| > > | 217774 217775 217776 217777 217778 217779 217780 217781 217782 217783 217784 217785 217786 217787 217788 217789 |
#ifndef SESSIONS_STRM_CHUNK_SIZE
# ifdef SQLITE_TEST
# define SESSIONS_STRM_CHUNK_SIZE 64
# else
# define SESSIONS_STRM_CHUNK_SIZE 1024
# endif
#endif
#define SESSIONS_ROWID "_rowid_"
static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE;
typedef struct SessionHook SessionHook;
struct SessionHook {
void *pCtx;
int (*xOld)(void*,int,sqlite3_value**);
|
| ︙ | ︙ | |||
215817 215818 215819 215820 215821 215822 215823 215824 215825 215826 215827 215828 215829 215830 |
struct sqlite3_session {
sqlite3 *db; /* Database handle session is attached to */
char *zDb; /* Name of database session is attached to */
int bEnableSize; /* True if changeset_size() enabled */
int bEnable; /* True if currently recording */
int bIndirect; /* True if all changes are indirect */
int bAutoAttach; /* True to auto-attach tables */
int rc; /* Non-zero if an error has occurred */
void *pFilterCtx; /* First argument to pass to xTableFilter */
int (*xTableFilter)(void *pCtx, const char *zTab);
i64 nMalloc; /* Number of bytes of data allocated */
i64 nMaxChangesetSize;
sqlite3_value *pZeroBlob; /* Value containing X'' */
sqlite3_session *pNext; /* Next session object on same db. */
| > | 217798 217799 217800 217801 217802 217803 217804 217805 217806 217807 217808 217809 217810 217811 217812 |
struct sqlite3_session {
sqlite3 *db; /* Database handle session is attached to */
char *zDb; /* Name of database session is attached to */
int bEnableSize; /* True if changeset_size() enabled */
int bEnable; /* True if currently recording */
int bIndirect; /* True if all changes are indirect */
int bAutoAttach; /* True to auto-attach tables */
int bImplicitPK; /* True to handle tables with implicit PK */
int rc; /* Non-zero if an error has occurred */
void *pFilterCtx; /* First argument to pass to xTableFilter */
int (*xTableFilter)(void *pCtx, const char *zTab);
i64 nMalloc; /* Number of bytes of data allocated */
i64 nMaxChangesetSize;
sqlite3_value *pZeroBlob; /* Value containing X'' */
sqlite3_session *pNext; /* Next session object on same db. */
|
| ︙ | ︙ | |||
215893 215894 215895 215896 215897 215898 215899 215900 215901 215902 215903 215904 215905 215906 |
** start of the session. Or no initial values if the row was inserted.
*/
struct SessionTable {
SessionTable *pNext;
char *zName; /* Local name of table */
int nCol; /* Number of columns in table zName */
int bStat1; /* True if this is sqlite_stat1 */
const char **azCol; /* Column names */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
SessionChange **apChange; /* Hash table buckets */
};
| > | 217875 217876 217877 217878 217879 217880 217881 217882 217883 217884 217885 217886 217887 217888 217889 |
** start of the session. Or no initial values if the row was inserted.
*/
struct SessionTable {
SessionTable *pNext;
char *zName; /* Local name of table */
int nCol; /* Number of columns in table zName */
int bStat1; /* True if this is sqlite_stat1 */
int bRowid; /* True if this table uses rowid for PK */
const char **azCol; /* Column names */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
SessionChange **apChange; /* Hash table buckets */
};
|
| ︙ | ︙ | |||
216285 216286 216287 216288 216289 216290 216291 216292 216293 216294 216295 216296 216297 216298 216299 |
**
** If an error occurs, an SQLite error code is returned and the final values
** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned
** and the output variables are set as described above.
*/
static int sessionPreupdateHash(
sqlite3_session *pSession, /* Session object that owns pTab */
SessionTable *pTab, /* Session table handle */
int bNew, /* True to hash the new.* PK */
int *piHash, /* OUT: Hash value */
int *pbNullPK /* OUT: True if there are NULL values in PK */
){
unsigned int h = 0; /* Hash value to return */
int i; /* Used to iterate through columns */
| > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 218268 218269 218270 218271 218272 218273 218274 218275 218276 218277 218278 218279 218280 218281 218282 218283 218284 218285 218286 218287 218288 218289 218290 218291 218292 218293 218294 218295 218296 218297 218298 218299 218300 218301 218302 218303 218304 218305 218306 218307 218308 218309 218310 218311 218312 218313 218314 218315 218316 218317 218318 218319 218320 218321 218322 218323 218324 218325 218326 218327 218328 218329 218330 218331 218332 218333 218334 218335 218336 218337 |
**
** If an error occurs, an SQLite error code is returned and the final values
** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned
** and the output variables are set as described above.
*/
static int sessionPreupdateHash(
sqlite3_session *pSession, /* Session object that owns pTab */
i64 iRowid,
SessionTable *pTab, /* Session table handle */
int bNew, /* True to hash the new.* PK */
int *piHash, /* OUT: Hash value */
int *pbNullPK /* OUT: True if there are NULL values in PK */
){
unsigned int h = 0; /* Hash value to return */
int i; /* Used to iterate through columns */
if( pTab->bRowid ){
assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) );
h = sessionHashAppendI64(h, iRowid);
}else{
assert( *pbNullPK==0 );
assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
for(i=0; i<pTab->nCol; i++){
if( pTab->abPK[i] ){
int rc;
int eType;
sqlite3_value *pVal;
if( bNew ){
rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
}else{
rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
}
if( rc!=SQLITE_OK ) return rc;
eType = sqlite3_value_type(pVal);
h = sessionHashAppendType(h, eType);
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
i64 iVal;
if( eType==SQLITE_INTEGER ){
iVal = sqlite3_value_int64(pVal);
}else{
double rVal = sqlite3_value_double(pVal);
assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
memcpy(&iVal, &rVal, 8);
}
h = sessionHashAppendI64(h, iVal);
}else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
const u8 *z;
int n;
if( eType==SQLITE_TEXT ){
z = (const u8 *)sqlite3_value_text(pVal);
}else{
z = (const u8 *)sqlite3_value_blob(pVal);
}
n = sqlite3_value_bytes(pVal);
if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
h = sessionHashAppendBlob(h, n, z);
}else{
assert( eType==SQLITE_NULL );
assert( pTab->bStat1==0 || i!=1 );
*pbNullPK = 1;
}
}
}
}
*piHash = (h % pTab->nChange);
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
216617 216618 216619 216620 216621 216622 216623 216624 216625 216626 216627 216628 216629 216630 216631 216632 216633 216634 216635 216636 |
** It determines if the current pre-update-hook change affects the same row
** as the change stored in argument pChange. If so, it returns true. Otherwise
** if the pre-update-hook does not affect the same row as pChange, it returns
** false.
*/
static int sessionPreupdateEqual(
sqlite3_session *pSession, /* Session object that owns SessionTable */
SessionTable *pTab, /* Table associated with change */
SessionChange *pChange, /* Change to compare to */
int op /* Current pre-update operation */
){
int iCol; /* Used to iterate through columns */
u8 *a = pChange->aRecord; /* Cursor used to scan change record */
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
for(iCol=0; iCol<pTab->nCol; iCol++){
if( !pTab->abPK[iCol] ){
a += sessionSerialLen(a);
}else{
sqlite3_value *pVal; /* Value returned by preupdate_new/old */
| > > > > > > | 218606 218607 218608 218609 218610 218611 218612 218613 218614 218615 218616 218617 218618 218619 218620 218621 218622 218623 218624 218625 218626 218627 218628 218629 218630 218631 |
** It determines if the current pre-update-hook change affects the same row
** as the change stored in argument pChange. If so, it returns true. Otherwise
** if the pre-update-hook does not affect the same row as pChange, it returns
** false.
*/
static int sessionPreupdateEqual(
sqlite3_session *pSession, /* Session object that owns SessionTable */
i64 iRowid, /* Rowid value if pTab->bRowid */
SessionTable *pTab, /* Table associated with change */
SessionChange *pChange, /* Change to compare to */
int op /* Current pre-update operation */
){
int iCol; /* Used to iterate through columns */
u8 *a = pChange->aRecord; /* Cursor used to scan change record */
if( pTab->bRowid ){
if( a[0]!=SQLITE_INTEGER ) return 0;
return sessionGetI64(&a[1])==iRowid;
}
assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
for(iCol=0; iCol<pTab->nCol; iCol++){
if( !pTab->abPK[iCol] ){
a += sessionSerialLen(a);
}else{
sqlite3_value *pVal; /* Value returned by preupdate_new/old */
|
| ︙ | ︙ | |||
216768 216769 216770 216771 216772 216773 216774 | sqlite3_session *pSession, /* For memory accounting. May be NULL */ sqlite3 *db, /* Database connection */ const char *zDb, /* Name of attached database (e.g. "main") */ const char *zThis, /* Table name */ int *pnCol, /* OUT: number of columns */ const char **pzTab, /* OUT: Copy of zThis */ const char ***pazCol, /* OUT: Array of column names for table */ | | > > | 218763 218764 218765 218766 218767 218768 218769 218770 218771 218772 218773 218774 218775 218776 218777 218778 218779 218780 218781 218782 218783 218784 218785 218786 218787 218788 218789 218790 |
sqlite3_session *pSession, /* For memory accounting. May be NULL */
sqlite3 *db, /* Database connection */
const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */
int *pnCol, /* OUT: number of columns */
const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
int *pbRowid /* OUT: True if only PK is a rowid */
){
char *zPragma;
sqlite3_stmt *pStmt;
int rc;
sqlite3_int64 nByte;
int nDbCol = 0;
int nThis;
int i;
u8 *pAlloc = 0;
char **azCol = 0;
u8 *abPK = 0;
int bRowid = 0; /* Set to true to use rowid as PK */
assert( pazCol && pabPK );
nThis = sqlite3Strlen30(zThis);
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
216824 216825 216826 216827 216828 216829 216830 216831 216832 216833 216834 216835 216836 216837 216838 216839 216840 216841 |
*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);
if( rc==SQLITE_OK ){
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
| > > > > > | 218821 218822 218823 218824 218825 218826 218827 218828 218829 218830 218831 218832 218833 218834 218835 218836 218837 218838 218839 218840 218841 218842 218843 |
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
return rc;
}
nByte = nThis + 1;
bRowid = (pbRowid!=0);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
nByte += sqlite3_column_bytes(pStmt, 1);
nDbCol++;
if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
}
if( nDbCol==0 ) bRowid = 0;
nDbCol += bRowid;
nByte += strlen(SESSIONS_ROWID);
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ){
nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
|
| ︙ | ︙ | |||
216849 216850 216851 216852 216853 216854 216855 216856 216857 216858 216859 216860 216861 216862 216863 216864 216865 216866 |
if( pzTab ){
memcpy(pAlloc, zThis, nThis+1);
*pzTab = (char *)pAlloc;
pAlloc += nThis+1;
}
i = 0;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
int nName = sqlite3_column_bytes(pStmt, 1);
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
if( zName==0 ) break;
memcpy(pAlloc, zName, nName+1);
azCol[i] = (char *)pAlloc;
pAlloc += nName+1;
abPK[i] = sqlite3_column_int(pStmt, 5);
i++;
}
rc = sqlite3_reset(pStmt);
| > > > > > > > > < > | 218851 218852 218853 218854 218855 218856 218857 218858 218859 218860 218861 218862 218863 218864 218865 218866 218867 218868 218869 218870 218871 218872 218873 218874 218875 218876 218877 218878 218879 218880 218881 218882 218883 218884 218885 218886 218887 218888 218889 218890 218891 218892 218893 218894 218895 218896 218897 218898 218899 218900 |
if( pzTab ){
memcpy(pAlloc, zThis, nThis+1);
*pzTab = (char *)pAlloc;
pAlloc += nThis+1;
}
i = 0;
if( bRowid ){
size_t nName = strlen(SESSIONS_ROWID);
memcpy(pAlloc, SESSIONS_ROWID, nName+1);
azCol[i] = (char*)pAlloc;
pAlloc += nName+1;
abPK[i] = 1;
i++;
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
int nName = sqlite3_column_bytes(pStmt, 1);
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
if( zName==0 ) break;
memcpy(pAlloc, zName, nName+1);
azCol[i] = (char *)pAlloc;
pAlloc += nName+1;
abPK[i] = sqlite3_column_int(pStmt, 5);
i++;
}
rc = sqlite3_reset(pStmt);
}
/* If successful, populate the output variables. Otherwise, zero them and
** free any allocation made. An error code will be returned in this case.
*/
if( rc==SQLITE_OK ){
*pazCol = (const char **)azCol;
*pabPK = abPK;
*pnCol = nDbCol;
}else{
*pazCol = 0;
*pabPK = 0;
*pnCol = 0;
if( pzTab ) *pzTab = 0;
sessionFree(pSession, azCol);
}
if( pbRowid ) *pbRowid = bRowid;
sqlite3_finalize(pStmt);
return rc;
}
/*
** This function is only called from within a pre-update handler for a
** write to table pTab, part of session pSession. If this is the first
|
| ︙ | ︙ | |||
216898 216899 216900 216901 216902 216903 216904 |
** is set to NULL in this case.
*/
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
| | > | 218908 218909 218910 218911 218912 218913 218914 218915 218916 218917 218918 218919 218920 218921 218922 218923 |
** is set to NULL in this case.
*/
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK,
(pSession->bImplicitPK ? &pTab->bRowid : 0)
);
if( pSession->rc==SQLITE_OK ){
int i;
for(i=0; i<pTab->nCol; i++){
if( abPK[i] ){
pTab->abPK = abPK;
break;
|
| ︙ | ︙ | |||
216970 216971 216972 216973 216974 216975 216976 216977 216978 216979 216980 216981 216982 216983 216984 216985 216986 216987 216988 216989 216990 216991 216992 |
int op,
sqlite3_session *pSession, /* Session object pTab is attached to */
SessionTable *pTab, /* Table that change applies to */
SessionChange *pC /* Update pC->nMaxSize */
){
i64 nNew = 2;
if( pC->op==SQLITE_INSERT ){
if( op!=SQLITE_DELETE ){
int ii;
for(ii=0; ii<pTab->nCol; ii++){
sqlite3_value *p = 0;
pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
sessionSerializeValue(0, p, &nNew);
}
}
}else if( op==SQLITE_DELETE ){
nNew += pC->nRecord;
if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){
nNew += pC->nRecord;
}
}else{
int ii;
u8 *pCsr = pC->aRecord;
| > > > > > | | | 218981 218982 218983 218984 218985 218986 218987 218988 218989 218990 218991 218992 218993 218994 218995 218996 218997 218998 218999 219000 219001 219002 219003 219004 219005 219006 219007 219008 219009 219010 219011 219012 219013 219014 219015 219016 219017 219018 219019 219020 219021 |
int op,
sqlite3_session *pSession, /* Session object pTab is attached to */
SessionTable *pTab, /* Table that change applies to */
SessionChange *pC /* Update pC->nMaxSize */
){
i64 nNew = 2;
if( pC->op==SQLITE_INSERT ){
if( pTab->bRowid ) nNew += 9;
if( op!=SQLITE_DELETE ){
int ii;
for(ii=0; ii<pTab->nCol; ii++){
sqlite3_value *p = 0;
pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
sessionSerializeValue(0, p, &nNew);
}
}
}else if( op==SQLITE_DELETE ){
nNew += pC->nRecord;
if( sqlite3_preupdate_blobwrite(pSession->db)>=0 ){
nNew += pC->nRecord;
}
}else{
int ii;
u8 *pCsr = pC->aRecord;
if( pTab->bRowid ){
nNew += 9 + 1;
pCsr += 9;
}
for(ii=pTab->bRowid; ii<pTab->nCol; ii++){
int bChanged = 1;
int nOld = 0;
int eType;
sqlite3_value *p = 0;
pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p);
if( p==0 ){
return SQLITE_NOMEM;
}
eType = *pCsr++;
switch( eType ){
case SQLITE_NULL:
|
| ︙ | ︙ | |||
217070 217071 217072 217073 217074 217075 217076 217077 217078 217079 217080 217081 217082 217083 217084 217085 217086 217087 217088 217089 217090 217091 |
** (UPDATE, INSERT, DELETE) is specified by the first argument.
**
** Unless one is already present or an error occurs, an entry is added
** to the changed-rows hash table associated with table pTab.
*/
static void sessionPreupdateOneChange(
int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
sqlite3_session *pSession, /* Session object pTab is attached to */
SessionTable *pTab /* Table that change applies to */
){
int iHash;
int bNull = 0;
int rc = SQLITE_OK;
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
if( pSession->rc ) return;
/* Load table details if required */
if( sessionInitTable(pSession, pTab) ) return;
/* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
| > | | 219086 219087 219088 219089 219090 219091 219092 219093 219094 219095 219096 219097 219098 219099 219100 219101 219102 219103 219104 219105 219106 219107 219108 219109 219110 219111 219112 219113 219114 219115 219116 |
** (UPDATE, INSERT, DELETE) is specified by the first argument.
**
** Unless one is already present or an error occurs, an entry is added
** to the changed-rows hash table associated with table pTab.
*/
static void sessionPreupdateOneChange(
int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
i64 iRowid,
sqlite3_session *pSession, /* Session object pTab is attached to */
SessionTable *pTab /* Table that change applies to */
){
int iHash;
int bNull = 0;
int rc = SQLITE_OK;
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
if( pSession->rc ) return;
/* Load table details if required */
if( sessionInitTable(pSession, pTab) ) return;
/* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){
pSession->rc = SQLITE_SCHEMA;
return;
}
/* Grow the hash table if required */
if( sessionGrowHash(pSession, 0, pTab) ){
pSession->rc = SQLITE_NOMEM;
|
| ︙ | ︙ | |||
217118 217119 217120 217121 217122 217123 217124 |
pSession->pZeroBlob = p;
}
}
/* Calculate the hash-key for this change. If the primary key of the row
** includes a NULL value, exit early. Such changes are ignored by the
** session module. */
| | > > | | > > > > > > > > | | 219135 219136 219137 219138 219139 219140 219141 219142 219143 219144 219145 219146 219147 219148 219149 219150 219151 219152 219153 219154 219155 219156 219157 219158 219159 219160 219161 219162 219163 219164 219165 219166 219167 219168 219169 219170 219171 219172 219173 219174 219175 219176 219177 219178 219179 219180 219181 219182 219183 219184 219185 219186 219187 219188 219189 219190 219191 219192 219193 219194 219195 219196 219197 219198 219199 219200 219201 219202 219203 219204 219205 219206 219207 219208 219209 219210 219211 219212 |
pSession->pZeroBlob = p;
}
}
/* Calculate the hash-key for this change. If the primary key of the row
** includes a NULL value, exit early. Such changes are ignored by the
** session module. */
rc = sessionPreupdateHash(
pSession, iRowid, pTab, op==SQLITE_INSERT, &iHash, &bNull
);
if( rc!=SQLITE_OK ) goto error_out;
if( bNull==0 ){
/* Search the hash table for an existing record for this row. */
SessionChange *pC;
for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
if( sessionPreupdateEqual(pSession, iRowid, pTab, pC, op) ) break;
}
if( pC==0 ){
/* Create a new change object containing all the old values (if
** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
** values (if this is an INSERT). */
sqlite3_int64 nByte; /* Number of bytes to allocate */
int i; /* Used to iterate through columns */
assert( rc==SQLITE_OK );
pTab->nEntry++;
/* Figure out how large an allocation is required */
nByte = sizeof(SessionChange);
for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
assert( trc==SQLITE_OK );
}else if( pTab->abPK[i] ){
TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
assert( trc==SQLITE_OK );
}
/* This may fail if SQLite value p contains a utf-16 string that must
** be converted to utf-8 and an OOM error occurs while doing so. */
rc = sessionSerializeValue(0, p, &nByte);
if( rc!=SQLITE_OK ) goto error_out;
}
if( pTab->bRowid ){
nByte += 9; /* Size of rowid field - an integer */
}
/* Allocate the change object */
pC = (SessionChange *)sessionMalloc64(pSession, nByte);
if( !pC ){
rc = SQLITE_NOMEM;
goto error_out;
}else{
memset(pC, 0, sizeof(SessionChange));
pC->aRecord = (u8 *)&pC[1];
}
/* Populate the change object. None of the preupdate_old(),
** preupdate_new() or SerializeValue() calls below may fail as all
** required values and encodings have already been cached in memory.
** It is not possible for an OOM to occur in this block. */
nByte = 0;
if( pTab->bRowid ){
pC->aRecord[0] = SQLITE_INTEGER;
sessionPutI64(&pC->aRecord[1], iRowid);
nByte = 9;
}
for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
pSession->hook.xOld(pSession->hook.pCtx, i, &p);
}else if( pTab->abPK[i] ){
pSession->hook.xNew(pSession->hook.pCtx, i, &p);
}
sessionSerializeValue(&pC->aRecord[nByte], p, &nByte);
|
| ︙ | ︙ | |||
217286 217287 217288 217289 217290 217291 217292 |
if( pSession->bEnable==0 ) continue;
if( pSession->rc ) continue;
if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
pSession->rc = sessionFindTable(pSession, zName, &pTab);
if( pTab ){
assert( pSession->rc==SQLITE_OK );
| > | | | 219313 219314 219315 219316 219317 219318 219319 219320 219321 219322 219323 219324 219325 219326 219327 219328 219329 219330 |
if( pSession->bEnable==0 ) continue;
if( pSession->rc ) continue;
if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
pSession->rc = sessionFindTable(pSession, zName, &pTab);
if( pTab ){
assert( pSession->rc==SQLITE_OK );
assert( op==SQLITE_UPDATE || iKey1==iKey2 );
sessionPreupdateOneChange(op, iKey1, pSession, pTab);
if( op==SQLITE_UPDATE ){
sessionPreupdateOneChange(SQLITE_INSERT, iKey2, pSession, pTab);
}
}
}
}
/*
** The pre-update hook implementations.
|
| ︙ | ︙ | |||
217327 217328 217329 217330 217331 217332 217333 217334 217335 217336 217337 217338 217339 217340 217341 |
pSession->hook.xCount = sessionPreupdateCount;
pSession->hook.xDepth = sessionPreupdateDepth;
}
typedef struct SessionDiffCtx SessionDiffCtx;
struct SessionDiffCtx {
sqlite3_stmt *pStmt;
int nOldOff;
};
/*
** The diff hook implementations.
*/
static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
| > | | | | 219355 219356 219357 219358 219359 219360 219361 219362 219363 219364 219365 219366 219367 219368 219369 219370 219371 219372 219373 219374 219375 219376 219377 219378 219379 219380 219381 219382 219383 219384 219385 219386 219387 219388 |
pSession->hook.xCount = sessionPreupdateCount;
pSession->hook.xDepth = sessionPreupdateDepth;
}
typedef struct SessionDiffCtx SessionDiffCtx;
struct SessionDiffCtx {
sqlite3_stmt *pStmt;
int bRowid;
int nOldOff;
};
/*
** The diff hook implementations.
*/
static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
*ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff+p->bRowid);
return SQLITE_OK;
}
static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
*ppVal = sqlite3_column_value(p->pStmt, iVal+p->bRowid);
return SQLITE_OK;
}
static int sessionDiffCount(void *pCtx){
SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
return (p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt)) - p->bRowid;
}
static int sessionDiffDepth(void *pCtx){
(void)pCtx;
return 0;
}
/*
|
| ︙ | ︙ | |||
217424 217425 217426 217427 217428 217429 217430 217431 217432 217433 217434 |
return zRet;
}
static char *sessionSelectFindNew(
const char *zDb1, /* Pick rows in this db only */
const char *zDb2, /* But not in this one */
const char *zTbl, /* Table name */
const char *zExpr
){
char *zRet = sqlite3_mprintf(
| > > | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > | | | > | < > > > | 219453 219454 219455 219456 219457 219458 219459 219460 219461 219462 219463 219464 219465 219466 219467 219468 219469 219470 219471 219472 219473 219474 219475 219476 219477 219478 219479 219480 219481 219482 219483 219484 219485 219486 219487 219488 219489 219490 219491 219492 219493 219494 219495 219496 219497 219498 219499 219500 219501 219502 219503 219504 219505 219506 219507 219508 219509 219510 219511 219512 219513 219514 219515 219516 219517 219518 219519 219520 219521 219522 219523 219524 219525 219526 219527 219528 219529 219530 219531 219532 219533 219534 219535 219536 219537 219538 219539 219540 219541 219542 219543 219544 219545 219546 219547 219548 219549 219550 219551 219552 219553 219554 219555 219556 219557 219558 219559 219560 219561 219562 219563 219564 219565 219566 219567 219568 219569 219570 219571 219572 219573 219574 219575 219576 |
return zRet;
}
static char *sessionSelectFindNew(
const char *zDb1, /* Pick rows in this db only */
const char *zDb2, /* But not in this one */
int bRowid,
const char *zTbl, /* Table name */
const char *zExpr
){
const char *zSel = (bRowid ? SESSIONS_ROWID ", *" : "*");
char *zRet = sqlite3_mprintf(
"SELECT %s FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
" SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
")",
zSel, zDb1, zTbl, zDb2, zTbl, zExpr
);
return zRet;
}
static int sessionDiffFindNew(
int op,
sqlite3_session *pSession,
SessionTable *pTab,
const char *zDb1,
const char *zDb2,
char *zExpr
){
int rc = SQLITE_OK;
char *zStmt = sessionSelectFindNew(
zDb1, zDb2, pTab->bRowid, pTab->zName, zExpr
);
if( zStmt==0 ){
rc = SQLITE_NOMEM;
}else{
sqlite3_stmt *pStmt;
rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
if( rc==SQLITE_OK ){
SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
pDiffCtx->pStmt = pStmt;
pDiffCtx->nOldOff = 0;
pDiffCtx->bRowid = pTab->bRowid;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
sessionPreupdateOneChange(op, iRowid, pSession, pTab);
}
rc = sqlite3_finalize(pStmt);
}
sqlite3_free(zStmt);
}
return rc;
}
/*
** Return a comma-separated list of the fully-qualified (with both database
** and table name) column names from table pTab. e.g.
**
** "main"."t1"."a", "main"."t1"."b", "main"."t1"."c"
*/
static char *sessionAllCols(
const char *zDb,
SessionTable *pTab
){
int ii;
char *zRet = 0;
for(ii=0; ii<pTab->nCol; ii++){
zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"",
zRet, (zRet ? ", " : ""), zDb, pTab->zName, pTab->azCol[ii]
);
if( !zRet ) break;
}
return zRet;
}
static int sessionDiffFindModified(
sqlite3_session *pSession,
SessionTable *pTab,
const char *zFrom,
const char *zExpr
){
int rc = SQLITE_OK;
char *zExpr2 = sessionExprCompareOther(pTab->nCol,
pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK
);
if( zExpr2==0 ){
rc = SQLITE_NOMEM;
}else{
char *z1 = sessionAllCols(pSession->zDb, pTab);
char *z2 = sessionAllCols(zFrom, pTab);
char *zStmt = sqlite3_mprintf(
"SELECT %s,%s FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
z1, z2, pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
);
if( zStmt==0 || z1==0 || z2==0 ){
rc = SQLITE_NOMEM;
}else{
sqlite3_stmt *pStmt;
rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
if( rc==SQLITE_OK ){
SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
pDiffCtx->pStmt = pStmt;
pDiffCtx->nOldOff = pTab->nCol;
while( SQLITE_ROW==sqlite3_step(pStmt) ){
i64 iRowid = (pTab->bRowid ? sqlite3_column_int64(pStmt, 0) : 0);
sessionPreupdateOneChange(SQLITE_UPDATE, iRowid, pSession, pTab);
}
rc = sqlite3_finalize(pStmt);
}
}
sqlite3_free(zStmt);
sqlite3_free(z1);
sqlite3_free(z2);
}
return rc;
}
SQLITE_API int sqlite3session_diff(
sqlite3_session *pSession,
|
| ︙ | ︙ | |||
217540 217541 217542 217543 217544 217545 217546 217547 217548 |
}
/* Check the table schemas match */
if( rc==SQLITE_OK ){
int bHasPk = 0;
int bMismatch = 0;
int nCol; /* Columns in zFrom.zTbl */
u8 *abPK;
const char **azCol = 0;
| > | > > | 219601 219602 219603 219604 219605 219606 219607 219608 219609 219610 219611 219612 219613 219614 219615 219616 219617 219618 219619 219620 |
}
/* Check the table schemas match */
if( rc==SQLITE_OK ){
int bHasPk = 0;
int bMismatch = 0;
int nCol; /* Columns in zFrom.zTbl */
int bRowid = 0;
u8 *abPK;
const char **azCol = 0;
rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK,
pSession->bImplicitPK ? &bRowid : 0
);
if( rc==SQLITE_OK ){
if( pTo->nCol!=nCol ){
bMismatch = 1;
}else{
int i;
for(i=0; i<nCol; i++){
if( pTo->abPK[i]!=abPK[i] ) bMismatch = 1;
|
| ︙ | ︙ | |||
217884 217885 217886 217887 217888 217889 217890 |
*/
static void sessionAppendStr(
SessionBuffer *p,
const char *zStr,
int *pRc
){
int nStr = sqlite3Strlen30(zStr);
| | > > > > > > > > > > > > > > > > > > > > > > | > | 219948 219949 219950 219951 219952 219953 219954 219955 219956 219957 219958 219959 219960 219961 219962 219963 219964 219965 219966 219967 219968 219969 219970 219971 219972 219973 219974 219975 219976 219977 219978 219979 219980 219981 219982 219983 219984 219985 219986 219987 219988 219989 219990 219991 219992 219993 219994 219995 219996 219997 219998 219999 220000 220001 220002 220003 220004 220005 220006 220007 220008 220009 220010 220011 220012 220013 220014 220015 220016 220017 220018 220019 220020 220021 220022 220023 220024 220025 220026 220027 220028 220029 220030 220031 220032 220033 |
*/
static void sessionAppendStr(
SessionBuffer *p,
const char *zStr,
int *pRc
){
int nStr = sqlite3Strlen30(zStr);
if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
memcpy(&p->aBuf[p->nBuf], zStr, nStr);
p->nBuf += nStr;
p->aBuf[p->nBuf] = 0x00;
}
}
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string representation of integer iVal
** to the buffer. No nul-terminator is written.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
** returning.
*/
static void sessionAppendInteger(
SessionBuffer *p, /* Buffer to append to */
int iVal, /* Value to write the string rep. of */
int *pRc /* IN/OUT: Error code */
){
char aBuf[24];
sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
sessionAppendStr(p, aBuf, pRc);
}
static void sessionAppendPrintf(
SessionBuffer *p, /* Buffer to append to */
int *pRc,
const char *zFmt,
...
){
if( *pRc==SQLITE_OK ){
char *zApp = 0;
va_list ap;
va_start(ap, zFmt);
zApp = sqlite3_vmprintf(zFmt, ap);
if( zApp==0 ){
*pRc = SQLITE_NOMEM;
}else{
sessionAppendStr(p, zApp, pRc);
}
va_end(ap);
sqlite3_free(zApp);
}
}
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string zStr enclosed in quotes (") and
** with any embedded quote characters escaped to the buffer. No
** nul-terminator byte is written.
**
** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
** returning.
*/
static void sessionAppendIdent(
SessionBuffer *p, /* Buffer to a append to */
const char *zStr, /* String to quote, escape and append */
int *pRc /* IN/OUT: Error code */
){
int nStr = sqlite3Strlen30(zStr)*2 + 2 + 2;
if( 0==sessionBufferGrow(p, nStr, pRc) ){
char *zOut = (char *)&p->aBuf[p->nBuf];
const char *zIn = zStr;
*zOut++ = '"';
while( *zIn ){
if( *zIn=='"' ) *zOut++ = '"';
*zOut++ = *(zIn++);
}
*zOut++ = '"';
p->nBuf = (int)((u8 *)zOut - p->aBuf);
p->aBuf[p->nBuf] = 0x00;
}
}
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwse, it appends the serialized version of the value stored
** in column iCol of the row that SQL statement pStmt currently points
|
| ︙ | ︙ | |||
218068 218069 218070 218071 218072 218073 218074 |
bChanged = 1;
}
}
/* If at least one field has been modified, this is not a no-op. */
if( bChanged ) bNoop = 0;
| | | 220155 220156 220157 220158 220159 220160 220161 220162 220163 220164 220165 220166 220167 220168 220169 |
bChanged = 1;
}
}
/* If at least one field has been modified, this is not a no-op. */
if( bChanged ) bNoop = 0;
/* Add a field to the old.* record. This is omitted if this module is
** currently generating a patchset. */
if( bPatchset==0 ){
if( bChanged || abPK[i] ){
sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
}else{
sessionAppendByte(pBuf, 0, &rc);
}
|
| ︙ | ︙ | |||
218157 218158 218159 218160 218161 218162 218163 | return rc; } /* ** Formulate and prepare a SELECT statement to retrieve a row from table ** zTab in database zDb based on its primary key. i.e. ** | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > | 220244 220245 220246 220247 220248 220249 220250 220251 220252 220253 220254 220255 220256 220257 220258 220259 220260 220261 220262 220263 220264 220265 220266 220267 220268 220269 220270 220271 220272 220273 220274 220275 220276 220277 220278 220279 220280 220281 220282 220283 220284 220285 220286 220287 220288 220289 220290 220291 220292 220293 220294 220295 220296 220297 220298 220299 220300 220301 220302 220303 220304 220305 220306 220307 220308 220309 220310 220311 220312 220313 220314 220315 220316 220317 220318 220319 220320 220321 220322 220323 220324 220325 220326 220327 220328 220329 220330 220331 220332 220333 220334 220335 220336 220337 220338 220339 220340 220341 220342 220343 220344 220345 220346 220347 220348 220349 220350 220351 220352 220353 220354 220355 220356 220357 220358 |
return rc;
}
/*
** Formulate and prepare a SELECT statement to retrieve a row from table
** zTab in database zDb based on its primary key. i.e.
**
** SELECT *, <noop-test> FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...)
**
** where <noop-test> is:
**
** 1 AND (?A OR ?1 IS <column>) AND ...
**
** for each non-pk <column>.
*/
static int sessionSelectStmt(
sqlite3 *db, /* Database handle */
int bIgnoreNoop,
const char *zDb, /* Database name */
const char *zTab, /* Table name */
int bRowid,
int nCol, /* Number of columns in table */
const char **azCol, /* Names of table columns */
u8 *abPK, /* PRIMARY KEY array */
sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
){
int rc = SQLITE_OK;
char *zSql = 0;
const char *zSep = "";
const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*";
int nSql = -1;
int i;
SessionBuffer nooptest = {0, 0, 0};
SessionBuffer pkfield = {0, 0, 0};
SessionBuffer pkvar = {0, 0, 0};
sessionAppendStr(&nooptest, ", 1", &rc);
if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
sessionAppendStr(&nooptest, " AND (?6 OR ?3 IS stat)", &rc);
sessionAppendStr(&pkfield, "tbl, idx", &rc);
sessionAppendStr(&pkvar,
"?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc
);
zCols = "tbl, ?2, stat";
}else{
for(i=0; i<nCol; i++){
if( abPK[i] ){
sessionAppendStr(&pkfield, zSep, &rc);
sessionAppendStr(&pkvar, zSep, &rc);
zSep = ", ";
sessionAppendIdent(&pkfield, azCol[i], &rc);
sessionAppendPrintf(&pkvar, &rc, "?%d", i+1);
}else{
sessionAppendPrintf(&nooptest, &rc,
" AND (?%d OR ?%d IS %w.%w)", i+1+nCol, i+1, zTab, azCol[i]
);
}
}
}
if( rc==SQLITE_OK ){
zSql = sqlite3_mprintf(
"SELECT %s%s FROM %Q.%Q WHERE (%s) IS (%s)",
zCols, (bIgnoreNoop ? (char*)nooptest.aBuf : ""),
zDb, zTab, (char*)pkfield.aBuf, (char*)pkvar.aBuf
);
if( zSql==0 ) rc = SQLITE_NOMEM;
}
#if 0
if( 0==sqlite3_stricmp("sqlite_stat1", zTab) ){
zSql = sqlite3_mprintf(
"SELECT tbl, ?2, stat FROM %Q.sqlite_stat1 WHERE tbl IS ?1 AND "
"idx IS (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", zDb
);
if( zSql==0 ) rc = SQLITE_NOMEM;
}else{
const char *zSep = "";
SessionBuffer buf = {0, 0, 0};
sessionAppendStr(&buf, "SELECT * FROM ", &rc);
sessionAppendIdent(&buf, zDb, &rc);
sessionAppendStr(&buf, ".", &rc);
sessionAppendIdent(&buf, zTab, &rc);
sessionAppendStr(&buf, " WHERE ", &rc);
for(i=0; i<nCol; i++){
if( abPK[i] ){
sessionAppendStr(&buf, zSep, &rc);
sessionAppendIdent(&buf, azCol[i], &rc);
sessionAppendStr(&buf, " IS ?", &rc);
sessionAppendInteger(&buf, i+1, &rc);
zSep = " AND ";
}
}
zSql = (char*)buf.aBuf;
nSql = buf.nBuf;
}
#endif
if( rc==SQLITE_OK ){
rc = sqlite3_prepare_v2(db, zSql, nSql, ppStmt, 0);
}
sqlite3_free(zSql);
sqlite3_free(nooptest.aBuf);
sqlite3_free(pkfield.aBuf);
sqlite3_free(pkvar.aBuf);
return rc;
}
/*
** Bind the PRIMARY KEY values from the change passed in argument pChange
** to the SELECT statement passed as the first argument. The SELECT statement
** is as prepared by function sessionSelectStmt().
|
| ︙ | ︙ | |||
218351 218352 218353 218354 218355 218356 218357 218358 218359 |
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. */
| > | > > > > > > | > | > | 220491 220492 220493 220494 220495 220496 220497 220498 220499 220500 220501 220502 220503 220504 220505 220506 220507 220508 220509 220510 220511 220512 220513 220514 220515 220516 220517 220518 220519 220520 220521 220522 220523 220524 220525 220526 220527 |
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 */
int bRowid = 0;
/* Check the table schema is still Ok. */
rc = sessionTableInfo(
0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK,
(pSession->bImplicitPK ? &bRowid : 0)
);
if( rc==SQLITE_OK && (
pTab->nCol!=nCol
|| pTab->bRowid!=bRowid
|| memcmp(abPK, pTab->abPK, nCol)
)){
rc = SQLITE_SCHEMA;
}
/* Write a table header */
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
rc = sessionSelectStmt(
db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
);
}
nNoop = buf.nBuf;
for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
SessionChange *p; /* Used to iterate through changes */
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
|
| ︙ | ︙ | |||
218447 218448 218449 218450 218451 218452 218453 |
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;
| | | 220596 220597 220598 220599 220600 220601 220602 220603 220604 220605 220606 220607 220608 220609 220610 |
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;
}
/*
|
| ︙ | ︙ | |||
218564 218565 218566 218567 218568 218569 218570 218571 218572 218573 218574 218575 218576 218577 |
}else{
pSession->bEnableSize = (iArg!=0);
}
}
*(int*)pArg = pSession->bEnableSize;
break;
}
default:
rc = SQLITE_MISUSE;
}
return rc;
}
| > > > > > > > > > > > > > | 220713 220714 220715 220716 220717 220718 220719 220720 220721 220722 220723 220724 220725 220726 220727 220728 220729 220730 220731 220732 220733 220734 220735 220736 220737 220738 220739 |
}else{
pSession->bEnableSize = (iArg!=0);
}
}
*(int*)pArg = pSession->bEnableSize;
break;
}
case SQLITE_SESSION_OBJCONFIG_ROWID: {
int iArg = *(int*)pArg;
if( iArg>=0 ){
if( pSession->pTable ){
rc = SQLITE_MISUSE;
}else{
pSession->bImplicitPK = (iArg!=0);
}
}
*(int*)pArg = pSession->bImplicitPK;
break;
}
default:
rc = SQLITE_MISUSE;
}
return rc;
}
|
| ︙ | ︙ | |||
219553 219554 219555 219556 219557 219558 219559 219560 219561 219562 219563 219564 219565 219566 | int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ int bInvertConstraints; /* Invert when iterating constraints buffer */ SessionBuffer constraints; /* Deferred constraints are stored here */ SessionBuffer rebase; /* Rebase information (if any) here */ u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ }; /* Number of prepared UPDATE statements to cache. */ #define SESSION_UPDATE_CACHE_SZ 12 /* ** Find a prepared UPDATE statement suitable for the UPDATE step currently | > > | 221715 221716 221717 221718 221719 221720 221721 221722 221723 221724 221725 221726 221727 221728 221729 221730 | int bStat1; /* True if table is sqlite_stat1 */ int bDeferConstraints; /* True to defer constraints */ int bInvertConstraints; /* Invert when iterating constraints buffer */ SessionBuffer constraints; /* Deferred constraints are stored here */ SessionBuffer rebase; /* Rebase information (if any) here */ u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ int bRowid; }; /* Number of prepared UPDATE statements to cache. */ #define SESSION_UPDATE_CACHE_SZ 12 /* ** Find a prepared UPDATE statement suitable for the UPDATE step currently |
| ︙ | ︙ | |||
219803 219804 219805 219806 219807 219808 219809 |
** pointing to the prepared version of the SQL statement.
*/
static int sessionSelectRow(
sqlite3 *db, /* Database handle */
const char *zTab, /* Table name */
SessionApplyCtx *p /* Session changeset-apply context */
){
| > | | > | 221967 221968 221969 221970 221971 221972 221973 221974 221975 221976 221977 221978 221979 221980 221981 221982 221983 221984 |
** pointing to the prepared version of the SQL statement.
*/
static int sessionSelectRow(
sqlite3 *db, /* Database handle */
const char *zTab, /* Table name */
SessionApplyCtx *p /* Session changeset-apply context */
){
/* TODO */
return sessionSelectStmt(db, p->bIgnoreNoop,
"main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect
);
}
/*
** Formulate and prepare an INSERT statement to add a record to table zTab.
** For example:
**
** INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
|
| ︙ | ︙ | |||
219963 219964 219965 219966 219967 219968 219969 | ** ** If the iterator currently points to an INSERT record, bind values from the ** new.* record to the SELECT statement. Or, if it points to a DELETE or ** UPDATE, bind values from the old.* record. */ static int sessionSeekToRow( sqlite3_changeset_iter *pIter, /* Changeset iterator */ | < | > > | > > > > > > > > > > > > | 222129 222130 222131 222132 222133 222134 222135 222136 222137 222138 222139 222140 222141 222142 222143 222144 222145 222146 222147 222148 222149 222150 222151 222152 222153 222154 222155 222156 222157 222158 222159 222160 222161 222162 222163 222164 222165 222166 222167 222168 |
**
** If the iterator currently points to an INSERT record, bind values from the
** new.* record to the SELECT statement. Or, if it points to a DELETE or
** UPDATE, bind values from the old.* record.
*/
static int sessionSeekToRow(
sqlite3_changeset_iter *pIter, /* Changeset iterator */
SessionApplyCtx *p
){
sqlite3_stmt *pSelect = p->pSelect;
int rc; /* Return code */
int nCol; /* Number of columns in table */
int op; /* Changset operation (SQLITE_UPDATE etc.) */
const char *zDummy; /* Unused */
sqlite3_clear_bindings(pSelect);
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
rc = sessionBindRow(pIter,
op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
nCol, p->abPK, pSelect
);
if( op!=SQLITE_DELETE && p->bIgnoreNoop ){
int ii;
for(ii=0; rc==SQLITE_OK && ii<nCol; ii++){
if( p->abPK[ii]==0 ){
sqlite3_value *pVal = 0;
sqlite3changeset_new(pIter, ii, &pVal);
sqlite3_bind_int(pSelect, ii+1+nCol, (pVal==0));
if( pVal ) rc = sessionBindValue(pSelect, ii+1, pVal);
}
}
}
if( rc==SQLITE_OK ){
rc = sqlite3_step(pSelect);
if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
}
return rc;
|
| ︙ | ︙ | |||
220091 220092 220093 220094 220095 220096 220097 |
assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
/* Bind the new.* PRIMARY KEY values to the SELECT statement. */
if( pbReplace ){
| | > > > > > | | | > | 222270 222271 222272 222273 222274 222275 222276 222277 222278 222279 222280 222281 222282 222283 222284 222285 222286 222287 222288 222289 222290 222291 222292 222293 222294 222295 222296 222297 222298 222299 |
assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
/* Bind the new.* PRIMARY KEY values to the SELECT statement. */
if( pbReplace ){
rc = sessionSeekToRow(pIter, p);
}else{
rc = SQLITE_OK;
}
if( rc==SQLITE_ROW ){
/* There exists another row with the new.* primary key. */
if( p->bIgnoreNoop
&& sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
){
res = SQLITE_CHANGESET_OMIT;
}else{
pIter->pConflict = p->pSelect;
res = xConflict(pCtx, eType, pIter);
pIter->pConflict = 0;
}
rc = sqlite3_reset(p->pSelect);
}else if( rc==SQLITE_OK ){
if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){
/* Instead of invoking the conflict handler, append the change blob
** to the SessionApplyCtx.constraints buffer. */
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
|
| ︙ | ︙ | |||
220208 220209 220210 220211 220212 220213 220214 |
if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
}
if( rc!=SQLITE_OK ) return rc;
sqlite3_step(p->pDelete);
rc = sqlite3_reset(p->pDelete);
| | | 222393 222394 222395 222396 222397 222398 222399 222400 222401 222402 222403 222404 222405 222406 222407 |
if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
}
if( rc!=SQLITE_OK ) return rc;
sqlite3_step(p->pDelete);
rc = sqlite3_reset(p->pDelete);
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 && p->bIgnoreNoop==0 ){
rc = sessionConflictHandler(
SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
);
}else if( (rc&0xff)==SQLITE_CONSTRAINT ){
rc = sessionConflictHandler(
SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
);
|
| ︙ | ︙ | |||
220265 220266 220267 220268 220269 220270 220271 |
}else{
assert( op==SQLITE_INSERT );
if( p->bStat1 ){
/* Check if there is a conflicting row. For sqlite_stat1, this needs
** to be done using a SELECT, as there is no PRIMARY KEY in the
** database schema to throw an exception if a duplicate is inserted. */
| | | 222450 222451 222452 222453 222454 222455 222456 222457 222458 222459 222460 222461 222462 222463 222464 |
}else{
assert( op==SQLITE_INSERT );
if( p->bStat1 ){
/* Check if there is a conflicting row. For sqlite_stat1, this needs
** to be done using a SELECT, as there is no PRIMARY KEY in the
** database schema to throw an exception if a duplicate is inserted. */
rc = sessionSeekToRow(pIter, p);
if( rc==SQLITE_ROW ){
rc = SQLITE_CONSTRAINT;
sqlite3_reset(p->pSelect);
}
}
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
220442 220443 220444 220445 220446 220447 220448 220449 220450 220451 220452 220453 220454 220455 |
assert( xConflict!=0 );
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sApply.bRebase = (ppRebase && pnRebase);
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
}
| > | 222627 222628 222629 222630 222631 222632 222633 222634 222635 222636 222637 222638 222639 222640 222641 |
assert( xConflict!=0 );
pIter->in.bNoDiscard = 1;
memset(&sApply, 0, sizeof(sApply));
sApply.bRebase = (ppRebase && pnRebase);
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
sqlite3_mutex_enter(sqlite3_db_mutex(db));
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
}
|
| ︙ | ︙ | |||
220479 220480 220481 220482 220483 220484 220485 220486 220487 220488 220489 220490 220491 220492 220493 220494 220495 220496 220497 220498 220499 220500 220501 220502 220503 220504 |
sApply.pSelect = 0;
sApply.nCol = 0;
sApply.azCol = 0;
sApply.abPK = 0;
sApply.bStat1 = 0;
sApply.bDeferConstraints = 1;
sApply.bRebaseStarted = 0;
memset(&sApply.constraints, 0, sizeof(SessionBuffer));
/* If an xFilter() callback was specified, invoke it now. If the
** xFilter callback returns zero, skip this table. If it returns
** non-zero, proceed. */
schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
if( schemaMismatch ){
zTab = sqlite3_mprintf("%s", zNew);
if( zTab==0 ){
rc = SQLITE_NOMEM;
break;
}
nTab = (int)strlen(zTab);
sApply.azCol = (const char **)zTab;
}else{
int nMinCol = 0;
int i;
sqlite3changeset_pk(pIter, &abPK, 0);
| > | | | 222665 222666 222667 222668 222669 222670 222671 222672 222673 222674 222675 222676 222677 222678 222679 222680 222681 222682 222683 222684 222685 222686 222687 222688 222689 222690 222691 222692 222693 222694 222695 222696 222697 222698 222699 222700 |
sApply.pSelect = 0;
sApply.nCol = 0;
sApply.azCol = 0;
sApply.abPK = 0;
sApply.bStat1 = 0;
sApply.bDeferConstraints = 1;
sApply.bRebaseStarted = 0;
sApply.bRowid = 0;
memset(&sApply.constraints, 0, sizeof(SessionBuffer));
/* If an xFilter() callback was specified, invoke it now. If the
** xFilter callback returns zero, skip this table. If it returns
** non-zero, proceed. */
schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
if( schemaMismatch ){
zTab = sqlite3_mprintf("%s", zNew);
if( zTab==0 ){
rc = SQLITE_NOMEM;
break;
}
nTab = (int)strlen(zTab);
sApply.azCol = (const char **)zTab;
}else{
int nMinCol = 0;
int i;
sqlite3changeset_pk(pIter, &abPK, 0);
rc = sessionTableInfo(0, db, "main", zNew,
&sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid
);
if( rc!=SQLITE_OK ) break;
for(i=0; i<sApply.nCol; i++){
if( sApply.abPK[i] ) nMinCol = i+1;
}
if( sApply.nCol==0 ){
|
| ︙ | ︙ | |||
222381 222382 222383 222384 222385 222386 222387 222388 222389 222390 222391 222392 222393 222394 222395 222396 222397 222398 222399 222400 222401 222402 222403 222404 | char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ /* Values loaded from the %_config table */ int iCookie; /* Incremented when %_config is modified */ int pgsz; /* Approximate page size used in %_data */ int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ char **pzErrmsg; #ifdef SQLITE_DEBUG int bPrefixIndex; /* True to use prefix-indexes */ #endif }; | > > | > > | > | 224568 224569 224570 224571 224572 224573 224574 224575 224576 224577 224578 224579 224580 224581 224582 224583 224584 224585 224586 224587 224588 224589 224590 224591 224592 224593 224594 224595 224596 224597 224598 224599 224600 224601 224602 224603 224604 224605 | char *zContentExprlist; Fts5Tokenizer *pTok; fts5_tokenizer *pTokApi; int bLock; /* True when table is preparing statement */ int ePattern; /* FTS_PATTERN_XXX constant */ /* Values loaded from the %_config table */ int iVersion; /* fts5 file format 'version' */ int iCookie; /* Incremented when %_config is modified */ int pgsz; /* Approximate page size used in %_data */ int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ char **pzErrmsg; #ifdef SQLITE_DEBUG int bPrefixIndex; /* True to use prefix-indexes */ #endif }; /* Current expected value of %_config table 'version' field. And ** the expected version if the 'secure-delete' option has ever been ** set on the table. */ #define FTS5_CURRENT_VERSION 4 #define FTS5_CURRENT_VERSION_SECUREDELETE 5 #define FTS5_CONTENT_NORMAL 0 #define FTS5_CONTENT_NONE 1 #define FTS5_CONTENT_EXTERNAL 2 #define FTS5_DETAIL_FULL 0 #define FTS5_DETAIL_NONE 1 |
| ︙ | ︙ | |||
222565 222566 222567 222568 222569 222570 222571 222572 222573 222574 222575 222576 222577 222578 | #define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 /* ** Create/destroy an Fts5Index object. */ static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); static int sqlite3Fts5IndexClose(Fts5Index *p); | > | 224757 224758 224759 224760 224761 224762 224763 224764 224765 224766 224767 224768 224769 224770 224771 | #define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ #define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 #define FTS5INDEX_QUERY_NOOUTPUT 0x0020 #define FTS5INDEX_QUERY_SKIPHASH 0x0040 /* ** Create/destroy an Fts5Index object. */ static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); static int sqlite3Fts5IndexClose(Fts5Index *p); |
| ︙ | ︙ | |||
222719 222720 222721 222722 222723 222724 222725 | ** Interface to code in fts5_varint.c. */ static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v); static int sqlite3Fts5GetVarintLen(u32 iVal); static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*); static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); | | | 224912 224913 224914 224915 224916 224917 224918 224919 224920 224921 224922 224923 224924 224925 224926 |
** Interface to code in fts5_varint.c.
*/
static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v);
static int sqlite3Fts5GetVarintLen(u32 iVal);
static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*);
static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&(b))
#define fts5GetVarint sqlite3Fts5GetVarint
#define fts5FastGetVarint32(a, iOff, nVal) { \
nVal = (a)[iOff++]; \
if( nVal & 0x80 ){ \
iOff--; \
iOff += fts5GetVarint32(&(a)[iOff], nVal); \
|
| ︙ | ︙ | |||
224698 224699 224700 224701 224702 224703 224704 | int iPos; UNUSED_PARAM2(pToken, nToken); if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK; iPos = p->iPos++; | | | | | 226891 226892 226893 226894 226895 226896 226897 226898 226899 226900 226901 226902 226903 226904 226905 226906 226907 226908 226909 226910 226911 226912 226913 226914 226915 226916 226917 226918 226919 226920 226921 226922 226923 226924 226925 226926 226927 226928 |
int iPos;
UNUSED_PARAM2(pToken, nToken);
if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK;
iPos = p->iPos++;
if( p->iRangeEnd>=0 ){
if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK;
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
}
if( iPos==p->iter.iStart ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->iOff = iStartOff;
}
if( iPos==p->iter.iEnd ){
if( p->iRangeEnd>=0 && p->iter.iStart<p->iRangeStart ){
fts5HighlightAppend(&rc, p, p->zOpen, -1);
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zClose, -1);
p->iOff = iEndOff;
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
p->iOff = iEndOff;
if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
fts5HighlightAppend(&rc, p, p->zClose, -1);
}
}
|
| ︙ | ︙ | |||
224756 224757 224758 224759 224760 224761 224762 224763 224764 224765 224766 224767 224768 224769 |
return;
}
iCol = sqlite3_value_int(apVal[0]);
memset(&ctx, 0, sizeof(HighlightContext));
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
| > | 226949 226950 226951 226952 226953 226954 226955 226956 226957 226958 226959 226960 226961 226962 226963 |
return;
}
iCol = sqlite3_value_int(apVal[0]);
memset(&ctx, 0, sizeof(HighlightContext));
ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
ctx.iRangeEnd = -1;
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
|
| ︙ | ︙ | |||
224941 224942 224943 224944 224945 224946 224947 224948 224949 224950 224951 224952 224953 224954 |
}
nCol = pApi->xColumnCount(pFts);
memset(&ctx, 0, sizeof(HighlightContext));
iCol = sqlite3_value_int(apVal[0]);
ctx.zOpen = fts5ValueToText(apVal[1]);
ctx.zClose = fts5ValueToText(apVal[2]);
zEllips = fts5ValueToText(apVal[3]);
nToken = sqlite3_value_int(apVal[4]);
iBestCol = (iCol>=0 ? iCol : 0);
nPhrase = pApi->xPhraseCount(pFts);
aSeen = sqlite3_malloc(nPhrase);
if( aSeen==0 ){
| > | 227135 227136 227137 227138 227139 227140 227141 227142 227143 227144 227145 227146 227147 227148 227149 |
}
nCol = pApi->xColumnCount(pFts);
memset(&ctx, 0, sizeof(HighlightContext));
iCol = sqlite3_value_int(apVal[0]);
ctx.zOpen = fts5ValueToText(apVal[1]);
ctx.zClose = fts5ValueToText(apVal[2]);
ctx.iRangeEnd = -1;
zEllips = fts5ValueToText(apVal[3]);
nToken = sqlite3_value_int(apVal[4]);
iBestCol = (iCol>=0 ? iCol : 0);
nPhrase = pApi->xPhraseCount(pFts);
aSeen = sqlite3_malloc(nPhrase);
if( aSeen==0 ){
|
| ︙ | ︙ | |||
226209 226210 226211 226212 226213 226214 226215 226216 226217 226218 226219 226220 226221 226222 |
pRet->bPrefixIndex = 1;
#endif
if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
*pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
rc = SQLITE_ERROR;
}
for(i=3; rc==SQLITE_OK && i<nArg; i++){
const char *zOrig = azArg[i];
const char *z;
char *zOne = 0;
char *zTwo = 0;
int bOption = 0;
int bMustBeCol = 0;
| > | 228404 228405 228406 228407 228408 228409 228410 228411 228412 228413 228414 228415 228416 228417 228418 |
pRet->bPrefixIndex = 1;
#endif
if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
*pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
rc = SQLITE_ERROR;
}
assert( (pRet->abUnindexed && pRet->azCol) || rc!=SQLITE_OK );
for(i=3; rc==SQLITE_OK && i<nArg; i++){
const char *zOrig = azArg[i];
const char *z;
char *zOne = 0;
char *zTwo = 0;
int bOption = 0;
int bMustBeCol = 0;
|
| ︙ | ︙ | |||
226562 226563 226564 226565 226566 226567 226568 226569 226570 226571 226572 226573 226574 226575 |
sqlite3_free(pConfig->zRankArgs);
pConfig->zRank = zRank;
pConfig->zRankArgs = zRankArgs;
}else if( rc==SQLITE_ERROR ){
rc = SQLITE_OK;
*pbBadkey = 1;
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
| > > > > > > > > > > > > | 228758 228759 228760 228761 228762 228763 228764 228765 228766 228767 228768 228769 228770 228771 228772 228773 228774 228775 228776 228777 228778 228779 228780 228781 228782 228783 |
sqlite3_free(pConfig->zRankArgs);
pConfig->zRank = zRank;
pConfig->zRankArgs = zRankArgs;
}else if( rc==SQLITE_ERROR ){
rc = SQLITE_OK;
*pbBadkey = 1;
}
}
else if( 0==sqlite3_stricmp(zKey, "secure-delete") ){
int bVal = -1;
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
226606 226607 226608 226609 226610 226611 226612 |
int bDummy = 0;
sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
}
}
rc = sqlite3_finalize(p);
}
| > | > > | | | > > | 228814 228815 228816 228817 228818 228819 228820 228821 228822 228823 228824 228825 228826 228827 228828 228829 228830 228831 228832 228833 228834 228835 228836 228837 228838 228839 228840 228841 |
int bDummy = 0;
sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
}
}
rc = sqlite3_finalize(p);
}
if( rc==SQLITE_OK
&& iVersion!=FTS5_CURRENT_VERSION
&& iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE
){
rc = SQLITE_ERROR;
if( pConfig->pzErrmsg ){
assert( 0==*pConfig->pzErrmsg );
*pConfig->pzErrmsg = sqlite3_mprintf("invalid fts5 file format "
"(found %d, expected %d or %d) - run 'rebuild'",
iVersion, FTS5_CURRENT_VERSION, FTS5_CURRENT_VERSION_SECUREDELETE
);
}
}else{
pConfig->iVersion = iVersion;
}
if( rc==SQLITE_OK ){
pConfig->iCookie = iCookie;
}
return rc;
}
|
| ︙ | ︙ | |||
226641 226642 226643 226644 226645 226646 226647 226648 226649 226650 226651 226652 226653 226654 | ** */ /* #include "fts5Int.h" */ /* #include "fts5parse.h" */ /* ** All token types in the generated fts5parse.h file are greater than 0. */ #define FTS5_EOF 0 #define FTS5_LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) | > > > > | 228854 228855 228856 228857 228858 228859 228860 228861 228862 228863 228864 228865 228866 228867 228868 228869 228870 228871 | ** */ /* #include "fts5Int.h" */ /* #include "fts5parse.h" */ #ifndef SQLITE_FTS5_MAX_EXPR_DEPTH # define SQLITE_FTS5_MAX_EXPR_DEPTH 256 #endif /* ** All token types in the generated fts5parse.h file are greater than 0. */ #define FTS5_EOF 0 #define FTS5_LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| ︙ | ︙ | |||
226682 226683 226684 226685 226686 226687 226688 226689 226690 226691 226692 226693 226694 226695 226696 226697 226698 226699 226700 |
** Expression node type. Always one of:
**
** FTS5_AND (nChild, apChild valid)
** FTS5_OR (nChild, apChild valid)
** FTS5_NOT (nChild, apChild valid)
** FTS5_STRING (pNear valid)
** FTS5_TERM (pNear valid)
*/
struct Fts5ExprNode {
int eType; /* Node type */
int bEof; /* True at EOF */
int bNomatch; /* True if entry is not a match */
/* Next method for this node. */
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
i64 iRowid; /* Current rowid */
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
| > > > > > > | 228899 228900 228901 228902 228903 228904 228905 228906 228907 228908 228909 228910 228911 228912 228913 228914 228915 228916 228917 228918 228919 228920 228921 228922 228923 |
** Expression node type. Always one of:
**
** FTS5_AND (nChild, apChild valid)
** FTS5_OR (nChild, apChild valid)
** FTS5_NOT (nChild, apChild valid)
** FTS5_STRING (pNear valid)
** FTS5_TERM (pNear valid)
**
** iHeight:
** Distance from this node to furthest leaf. This is always 0 for nodes
** of type FTS5_STRING and FTS5_TERM. For all other nodes it is one
** greater than the largest child value.
*/
struct Fts5ExprNode {
int eType; /* Node type */
int bEof; /* True at EOF */
int bNomatch; /* True if entry is not a match */
int iHeight; /* Distance to tree leaf nodes */
/* Next method for this node. */
int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
i64 iRowid; /* Current rowid */
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
|
| ︙ | ︙ | |||
226756 226757 226758 226759 226760 226761 226762 226763 226764 226765 226766 226767 226768 226769 |
int rc;
int nPhrase; /* Size of apPhrase array */
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
Fts5ExprNode *pExpr; /* Result of a successful parse */
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
};
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
if( pParse->rc==SQLITE_OK ){
assert( pParse->zErr==0 );
pParse->zErr = sqlite3_vmprintf(zFmt, ap);
pParse->rc = SQLITE_ERROR;
| > > > > > > > > > > > > > > > > > > > > > > > > > | 228979 228980 228981 228982 228983 228984 228985 228986 228987 228988 228989 228990 228991 228992 228993 228994 228995 228996 228997 228998 228999 229000 229001 229002 229003 229004 229005 229006 229007 229008 229009 229010 229011 229012 229013 229014 229015 229016 229017 |
int rc;
int nPhrase; /* Size of apPhrase array */
Fts5ExprPhrase **apPhrase; /* Array of all phrases */
Fts5ExprNode *pExpr; /* Result of a successful parse */
int bPhraseToAnd; /* Convert "a+b" to "a AND b" */
};
/*
** Check that the Fts5ExprNode.iHeight variables are set correctly in
** the expression tree passed as the only argument.
*/
#ifndef NDEBUG
static void assert_expr_depth_ok(int rc, Fts5ExprNode *p){
if( rc==SQLITE_OK ){
if( p->eType==FTS5_TERM || p->eType==FTS5_STRING || p->eType==0 ){
assert( p->iHeight==0 );
}else{
int ii;
int iMaxChild = 0;
for(ii=0; ii<p->nChild; ii++){
Fts5ExprNode *pChild = p->apChild[ii];
iMaxChild = MAX(iMaxChild, pChild->iHeight);
assert_expr_depth_ok(SQLITE_OK, pChild);
}
assert( p->iHeight==iMaxChild+1 );
}
}
}
#else
# define assert_expr_depth_ok(rc, p)
#endif
static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
va_list ap;
va_start(ap, zFmt);
if( pParse->rc==SQLITE_OK ){
assert( pParse->zErr==0 );
pParse->zErr = sqlite3_vmprintf(zFmt, ap);
pParse->rc = SQLITE_ERROR;
|
| ︙ | ︙ | |||
226869 226870 226871 226872 226873 226874 226875 226876 226877 226878 226879 226880 226881 226882 |
sParse.pConfig = pConfig;
do {
t = fts5ExprGetToken(&sParse, &z, &token);
sqlite3Fts5Parser(pEngine, t, token, &sParse);
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
/* If the LHS of the MATCH expression was a user column, apply the
** implicit column-filter. */
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
int n = sizeof(Fts5Colset);
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
if( pColset ){
| > > | 229117 229118 229119 229120 229121 229122 229123 229124 229125 229126 229127 229128 229129 229130 229131 229132 |
sParse.pConfig = pConfig;
do {
t = fts5ExprGetToken(&sParse, &z, &token);
sqlite3Fts5Parser(pEngine, t, token, &sParse);
}while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
/* If the LHS of the MATCH expression was a user column, apply the
** implicit column-filter. */
if( iCol<pConfig->nCol && sParse.pExpr && sParse.rc==SQLITE_OK ){
int n = sizeof(Fts5Colset);
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
if( pColset ){
|
| ︙ | ︙ | |||
226913 226914 226915 226916 226917 226918 226919 226920 226921 226922 226923 226924 226925 226926 |
sqlite3Fts5ParseNodeFree(sParse.pExpr);
}
sqlite3_free(sParse.apPhrase);
*pzErr = sParse.zErr;
return sParse.rc;
}
/*
** This function is only called when using the special 'trigram' tokenizer.
** Argument zText contains the text of a LIKE or GLOB pattern matched
** against column iCol. This function creates and compiles an FTS5 MATCH
** expression that will match a superset of the rows matched by the LIKE or
** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error
| > > > > > > > > > > > > > | 229163 229164 229165 229166 229167 229168 229169 229170 229171 229172 229173 229174 229175 229176 229177 229178 229179 229180 229181 229182 229183 229184 229185 229186 229187 229188 229189 |
sqlite3Fts5ParseNodeFree(sParse.pExpr);
}
sqlite3_free(sParse.apPhrase);
*pzErr = sParse.zErr;
return sParse.rc;
}
/*
** Assuming that buffer z is at least nByte bytes in size and contains a
** valid utf-8 string, return the number of characters in the string.
*/
static int fts5ExprCountChar(const char *z, int nByte){
int nRet = 0;
int ii;
for(ii=0; ii<nByte; ii++){
if( (z[ii] & 0xC0)!=0x80 ) nRet++;
}
return nRet;
}
/*
** This function is only called when using the special 'trigram' tokenizer.
** Argument zText contains the text of a LIKE or GLOB pattern matched
** against column iCol. This function creates and compiles an FTS5 MATCH
** expression that will match a superset of the rows matched by the LIKE or
** GLOB. If successful, SQLITE_OK is returned. Otherwise, an SQLite error
|
| ︙ | ︙ | |||
226951 226952 226953 226954 226955 226956 226957 |
aSpec[2] = '[';
}
while( i<=nText ){
if( i==nText
|| zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
){
| | > | 229214 229215 229216 229217 229218 229219 229220 229221 229222 229223 229224 229225 229226 229227 229228 229229 |
aSpec[2] = '[';
}
while( i<=nText ){
if( i==nText
|| zText[i]==aSpec[0] || zText[i]==aSpec[1] || zText[i]==aSpec[2]
){
if( fts5ExprCountChar(&zText[iFirst], i-iFirst)>=3 ){
int jj;
zExpr[iOut++] = '"';
for(jj=iFirst; jj<i; jj++){
zExpr[iOut++] = zText[jj];
if( zText[jj]=='"' ) zExpr[iOut++] = '"';
}
zExpr[iOut++] = '"';
|
| ︙ | ︙ | |||
227018 227019 227020 227021 227022 227023 227024 |
}
}
static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
Fts5Parse sParse;
memset(&sParse, 0, sizeof(sParse));
| | | 229282 229283 229284 229285 229286 229287 229288 229289 229290 229291 229292 229293 229294 229295 229296 |
}
}
static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
Fts5Parse sParse;
memset(&sParse, 0, sizeof(sParse));
if( *pp1 && p2 ){
Fts5Expr *p1 = *pp1;
int nPhrase = p1->nPhrase + p2->nPhrase;
p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
p2->pRoot = 0;
if( sParse.rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
227043 227044 227045 227046 227047 227048 227049 |
}
p1->nPhrase = nPhrase;
p1->apExprPhrase = ap;
}
}
sqlite3_free(p2->apExprPhrase);
sqlite3_free(p2);
| | | 229307 229308 229309 229310 229311 229312 229313 229314 229315 229316 229317 229318 229319 229320 229321 |
}
p1->nPhrase = nPhrase;
p1->apExprPhrase = ap;
}
}
sqlite3_free(p2->apExprPhrase);
sqlite3_free(p2);
}else if( p2 ){
*pp1 = p2;
}
return sParse.rc;
}
/*
|
| ︙ | ︙ | |||
228817 228818 228819 228820 228821 228822 228823 228824 228825 228826 228827 228828 228829 228830 228831 228832 228833 228834 228835 228836 228837 228838 |
pNode->xNext = fts5ExprNodeNext_NOT;
break;
};
}
}
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
p->nChild += pSub->nChild;
sqlite3_free(pSub);
}else{
p->apChild[p->nChild++] = pSub;
}
}
/*
** This function is used when parsing LIKE or GLOB patterns against
** trigram indexes that specify either detail=column or detail=none.
** It converts a phrase:
**
| > > > > | 231081 231082 231083 231084 231085 231086 231087 231088 231089 231090 231091 231092 231093 231094 231095 231096 231097 231098 231099 231100 231101 231102 231103 231104 231105 231106 |
pNode->xNext = fts5ExprNodeNext_NOT;
break;
};
}
}
static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
int ii = p->nChild;
if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
p->nChild += pSub->nChild;
sqlite3_free(pSub);
}else{
p->apChild[p->nChild++] = pSub;
}
for( ; ii<p->nChild; ii++){
p->iHeight = MAX(p->iHeight, p->apChild[ii]->iHeight + 1);
}
}
/*
** This function is used when parsing LIKE or GLOB patterns against
** trigram indexes that specify either detail=column or detail=none.
** It converts a phrase:
**
|
| ︙ | ︙ | |||
228855 228856 228857 228858 228859 228860 228861 228862 228863 228864 228865 228866 228867 228868 |
assert( pParse->bPhraseToAnd );
nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
pRet->eType = FTS5_AND;
pRet->nChild = nTerm;
fts5ExprAssignXNext(pRet);
pParse->nPhrase--;
for(ii=0; ii<nTerm; ii++){
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
&pParse->rc, sizeof(Fts5ExprPhrase)
);
if( pPhrase ){
| > | 231123 231124 231125 231126 231127 231128 231129 231130 231131 231132 231133 231134 231135 231136 231137 |
assert( pParse->bPhraseToAnd );
nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
pRet->eType = FTS5_AND;
pRet->nChild = nTerm;
pRet->iHeight = 1;
fts5ExprAssignXNext(pRet);
pParse->nPhrase--;
for(ii=0; ii<nTerm; ii++){
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
&pParse->rc, sizeof(Fts5ExprPhrase)
);
if( pPhrase ){
|
| ︙ | ︙ | |||
228960 228961 228962 228963 228964 228965 228966 228967 228968 228969 228970 228971 228972 228973 |
sqlite3_free(pRet);
pRet = 0;
}
}
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
}
}
}
}
if( pRet==0 ){
assert( pParse->rc!=SQLITE_OK );
| > > > > > > > > | 231229 231230 231231 231232 231233 231234 231235 231236 231237 231238 231239 231240 231241 231242 231243 231244 231245 231246 231247 231248 231249 231250 |
sqlite3_free(pRet);
pRet = 0;
}
}
}else{
fts5ExprAddChildren(pRet, pLeft);
fts5ExprAddChildren(pRet, pRight);
if( pRet->iHeight>SQLITE_FTS5_MAX_EXPR_DEPTH ){
sqlite3Fts5ParseError(pParse,
"fts5 expression tree is too large (maximum depth %d)",
SQLITE_FTS5_MAX_EXPR_DEPTH
);
sqlite3_free(pRet);
pRet = 0;
}
}
}
}
}
if( pRet==0 ){
assert( pParse->rc!=SQLITE_OK );
|
| ︙ | ︙ | |||
230312 230313 230314 230315 230316 230317 230318 230319 230320 230321 230322 230323 230324 230325 | #define FTS5_MAIN_PREFIX '0' #if FTS5_MAX_PREFIX_INDEXES > 31 # error "FTS5_MAX_PREFIX_INDEXES is too large" #endif /* ** Details: ** ** The %_data table managed by this module, ** ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); ** | > > | 232589 232590 232591 232592 232593 232594 232595 232596 232597 232598 232599 232600 232601 232602 232603 232604 | #define FTS5_MAIN_PREFIX '0' #if FTS5_MAX_PREFIX_INDEXES > 31 # error "FTS5_MAX_PREFIX_INDEXES is too large" #endif #define FTS5_MAX_LEVEL 64 /* ** Details: ** ** The %_data table managed by this module, ** ** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); ** |
| ︙ | ︙ | |||
230558 230559 230560 230561 230562 230563 230564 230565 230566 230567 230568 230569 230570 230571 |
sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */
sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
sqlite3_stmt *pIdxSelect;
int nRead; /* Total number of blocks read */
sqlite3_stmt *pDataVersion;
i64 iStructVersion; /* data_version when pStruct read */
Fts5Structure *pStruct; /* Current db structure (or NULL) */
};
struct Fts5DoclistIter {
u8 *aEof; /* Pointer to 1 byte past end of doclist */
| > > | 232837 232838 232839 232840 232841 232842 232843 232844 232845 232846 232847 232848 232849 232850 232851 232852 |
sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */
sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
sqlite3_stmt *pIdxSelect;
int nRead; /* Total number of blocks read */
sqlite3_stmt *pDeleteFromIdx;
sqlite3_stmt *pDataVersion;
i64 iStructVersion; /* data_version when pStruct read */
Fts5Structure *pStruct; /* Current db structure (or NULL) */
};
struct Fts5DoclistIter {
u8 *aEof; /* Pointer to 1 byte past end of doclist */
|
| ︙ | ︙ | |||
230650 230651 230652 230653 230654 230655 230656 | ** ** iLeafPgno: ** Current leaf page number within segment. ** ** iLeafOffset: ** Byte offset within the current leaf that is the first byte of the ** position list data (one byte passed the position-list size field). | < < < | 232931 232932 232933 232934 232935 232936 232937 232938 232939 232940 232941 232942 232943 232944 | ** ** iLeafPgno: ** Current leaf page number within segment. ** ** iLeafOffset: ** Byte offset within the current leaf that is the first byte of the ** position list data (one byte passed the position-list size field). ** ** pLeaf: ** Buffer containing current leaf page data. Set to NULL at EOF. ** ** iTermLeafPgno, iTermLeafOffset: ** Leaf page number containing the last term read from the segment. And ** the offset immediately following the term data. |
| ︙ | ︙ | |||
231211 231212 231213 231214 231215 231216 231217 231218 231219 231220 231221 231222 231223 231224 |
pLvl->nSeg = nTotal;
for(iSeg=0; iSeg<nTotal; iSeg++){
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
if( i>=nData ){
rc = FTS5_CORRUPT;
break;
}
i += fts5GetVarint32(&pData[i], pSeg->iSegid);
i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst);
i += fts5GetVarint32(&pData[i], pSeg->pgnoLast);
if( pSeg->pgnoLast<pSeg->pgnoFirst ){
rc = FTS5_CORRUPT;
break;
}
| > | 233489 233490 233491 233492 233493 233494 233495 233496 233497 233498 233499 233500 233501 233502 233503 |
pLvl->nSeg = nTotal;
for(iSeg=0; iSeg<nTotal; iSeg++){
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
if( i>=nData ){
rc = FTS5_CORRUPT;
break;
}
assert( pSeg!=0 );
i += fts5GetVarint32(&pData[i], pSeg->iSegid);
i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst);
i += fts5GetVarint32(&pData[i], pSeg->pgnoLast);
if( pSeg->pgnoLast<pSeg->pgnoFirst ){
rc = FTS5_CORRUPT;
break;
}
|
| ︙ | ︙ | |||
231241 231242 231243 231244 231245 231246 231247 231248 231249 231250 231251 231252 231253 231254 |
/*
** 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 */
);
| > | 233520 233521 233522 233523 233524 233525 233526 233527 233528 233529 233530 233531 233532 233533 233534 |
/*
** Add a level to the Fts5Structure.aLevel[] array of structure object
** (*ppStruct).
*/
static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
fts5StructureMakeWritable(pRc, ppStruct);
assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
if( *pRc==SQLITE_OK ){
Fts5Structure *pStruct = *ppStruct;
int nLevel = pStruct->nLevel;
sqlite3_int64 nByte = (
sizeof(Fts5Structure) + /* Main structure */
sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
);
|
| ︙ | ︙ | |||
231699 231700 231701 231702 231703 231704 231705 |
int iOff = pLvl->iOff;
assert( pLvl->bEof==0 );
if( iOff<=pLvl->iFirstOff ){
pLvl->bEof = 1;
}else{
u8 *a = pLvl->pData->p;
| | | > | | | < < < < < < < < | < < < < | | > | < < < | < < < < < | | < | > | > | 233979 233980 233981 233982 233983 233984 233985 233986 233987 233988 233989 233990 233991 233992 233993 233994 233995 233996 233997 233998 233999 234000 234001 234002 234003 234004 234005 234006 234007 234008 234009 234010 234011 |
int iOff = pLvl->iOff;
assert( pLvl->bEof==0 );
if( iOff<=pLvl->iFirstOff ){
pLvl->bEof = 1;
}else{
u8 *a = pLvl->pData->p;
pLvl->iOff = 0;
fts5DlidxLvlNext(pLvl);
while( 1 ){
int nZero = 0;
int ii = pLvl->iOff;
u64 delta = 0;
while( a[ii]==0 ){
nZero++;
ii++;
}
ii += sqlite3Fts5GetVarint(&a[ii], &delta);
if( ii>=iOff ) break;
pLvl->iLeafPgno += nZero+1;
pLvl->iRowid += delta;
pLvl->iOff = ii;
}
}
return pLvl->bEof;
}
static int fts5DlidxIterPrevR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl];
|
| ︙ | ︙ | |||
231930 231931 231932 231933 231934 231935 231936 |
}
static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
i64 iOff = pIter->iLeafOffset;
ASSERT_SZLEAF_OK(pIter->pLeaf);
| | | 234193 234194 234195 234196 234197 234198 234199 234200 234201 234202 234203 234204 234205 234206 234207 |
}
static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
i64 iOff = pIter->iLeafOffset;
ASSERT_SZLEAF_OK(pIter->pLeaf);
while( iOff>=pIter->pLeaf->szLeaf ){
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf==0 ){
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
return;
}
iOff = 4;
a = pIter->pLeaf->p;
|
| ︙ | ︙ | |||
232029 232030 232031 232032 232033 232034 232035 |
}
if( p->rc==SQLITE_OK ){
memset(pIter, 0, sizeof(*pIter));
fts5SegIterSetNext(p, pIter);
pIter->pSeg = pSeg;
pIter->iLeafPgno = pSeg->pgnoFirst-1;
| > | > | | 234292 234293 234294 234295 234296 234297 234298 234299 234300 234301 234302 234303 234304 234305 234306 234307 234308 234309 234310 234311 |
}
if( p->rc==SQLITE_OK ){
memset(pIter, 0, sizeof(*pIter));
fts5SegIterSetNext(p, pIter);
pIter->pSeg = pSeg;
pIter->iLeafPgno = pSeg->pgnoFirst-1;
do {
fts5SegIterNextPage(p, pIter);
}while( p->rc==SQLITE_OK && pIter->pLeaf && pIter->pLeaf->nn==4 );
}
if( p->rc==SQLITE_OK && pIter->pLeaf ){
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);
|
| ︙ | ︙ | |||
232226 232227 232228 232229 232230 232231 232232 | assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 ); assert( p->pConfig->eDetail==FTS5_DETAIL_NONE ); ASSERT_SZLEAF_OK(pIter->pLeaf); iOff = pIter->iLeafOffset; /* Next entry is on the next page */ | | | 234491 234492 234493 234494 234495 234496 234497 234498 234499 234500 234501 234502 234503 234504 234505 |
assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 );
assert( p->pConfig->eDetail==FTS5_DETAIL_NONE );
ASSERT_SZLEAF_OK(pIter->pLeaf);
iOff = pIter->iLeafOffset;
/* Next entry is on the next page */
while( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
fts5SegIterNextPage(p, pIter);
if( p->rc || pIter->pLeaf==0 ) return;
pIter->iRowid = 0;
iOff = 4;
}
if( iOff<pIter->iEndofDoclist ){
|
| ︙ | ︙ | |||
232419 232420 232421 232422 232423 232424 232425 |
** the doclist.
*/
static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
Fts5DlidxIter *pDlidx = pIter->pDlidx;
Fts5Data *pLast = 0;
int pgnoLast = 0;
| | | 234684 234685 234686 234687 234688 234689 234690 234691 234692 234693 234694 234695 234696 234697 234698 |
** the doclist.
*/
static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
Fts5DlidxIter *pDlidx = pIter->pDlidx;
Fts5Data *pLast = 0;
int pgnoLast = 0;
if( pDlidx && p->pConfig->iVersion==FTS5_CURRENT_VERSION ){
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
|
| ︙ | ︙ | |||
232980 232981 232982 232983 232984 232985 232986 | pRes->iFirst = (u16)iRes; return 0; } /* ** Move the seg-iter so that it points to the first rowid on page iLeafPgno. | | > < < | > > > > | | < < | | | | | | > > | 235245 235246 235247 235248 235249 235250 235251 235252 235253 235254 235255 235256 235257 235258 235259 235260 235261 235262 235263 235264 235265 235266 235267 235268 235269 235270 235271 235272 235273 235274 235275 235276 235277 235278 235279 235280 235281 235282 235283 235284 235285 235286 235287 235288 235289 235290 235291 |
pRes->iFirst = (u16)iRes;
return 0;
}
/*
** Move the seg-iter so that it points to the first rowid on page iLeafPgno.
** It is an error if leaf iLeafPgno does not exist. Unless the db is
** a 'secure-delete' db, if it contains no rowids then this is also an error.
*/
static void fts5SegIterGotoPage(
Fts5Index *p, /* FTS5 backend object */
Fts5SegIter *pIter, /* Iterator to advance */
int iLeafPgno
){
assert( iLeafPgno>pIter->iLeafPgno );
if( iLeafPgno>pIter->pSeg->pgnoLast ){
p->rc = FTS5_CORRUPT;
}else{
fts5DataRelease(pIter->pNextLeaf);
pIter->pNextLeaf = 0;
pIter->iLeafPgno = iLeafPgno-1;
while( p->rc==SQLITE_OK ){
int iOff;
fts5SegIterNextPage(p, pIter);
if( pIter->pLeaf==0 ) break;
iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
if( iOff>0 ){
u8 *a = pIter->pLeaf->p;
int n = pIter->pLeaf->szLeaf;
if( iOff<4 || iOff>=n ){
p->rc = FTS5_CORRUPT;
}else{
iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
pIter->iLeafOffset = iOff;
fts5SegIterLoadNPos(p, pIter);
}
break;
}
}
}
}
/*
** Advance the iterator passed as the second argument until it is at or
|
| ︙ | ︙ | |||
233724 233725 233726 233727 233728 233729 233730 |
assert( (pTerm==0 && nTerm==0) || iLevel<0 );
/* Allocate space for the new multi-seg-iterator. */
if( p->rc==SQLITE_OK ){
if( iLevel<0 ){
assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
nSeg = pStruct->nSegment;
| | | | 235992 235993 235994 235995 235996 235997 235998 235999 236000 236001 236002 236003 236004 236005 236006 236007 236008 236009 236010 236011 236012 236013 236014 236015 236016 236017 236018 236019 236020 236021 236022 236023 236024 236025 236026 236027 |
assert( (pTerm==0 && nTerm==0) || iLevel<0 );
/* Allocate space for the new multi-seg-iterator. */
if( p->rc==SQLITE_OK ){
if( iLevel<0 ){
assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
nSeg = pStruct->nSegment;
nSeg += (p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH));
}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);
}
/* Initialize each of the component segment iterators. */
if( p->rc==SQLITE_OK ){
if( iLevel<0 ){
Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
if( p->pHash && 0==(flags & FTS5INDEX_QUERY_SKIPHASH) ){
/* Add a segment iterator for the current contents of the hash table. */
Fts5SegIter *pIter = &pNew->aSeg[iIter++];
fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
}
for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){
for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
|
| ︙ | ︙ | |||
234500 234501 234502 234503 234504 234505 234506 |
p->rc = FTS5_CORRUPT;
}else{
fts5BufferZero(&buf);
fts5BufferGrow(&p->rc, &buf, pData->nn);
fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
| | | 236768 236769 236770 236771 236772 236773 236774 236775 236776 236777 236778 236779 236780 236781 236782 |
p->rc = FTS5_CORRUPT;
}else{
fts5BufferZero(&buf);
fts5BufferGrow(&p->rc, &buf, pData->nn);
fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
fts5BufferAppendBlob(&p->rc, &buf,pData->szLeaf-iOff,&pData->p[iOff]);
if( p->rc==SQLITE_OK ){
/* Set the szLeaf field */
fts5PutU16(&buf.p[2], (u16)buf.n);
}
/* Set up the new page-index array */
fts5BufferAppendVarint(&p->rc, &buf, 4);
|
| ︙ | ︙ | |||
234778 234779 234780 234781 234782 234783 234784 |
static void fts5IndexCrisismerge(
Fts5Index *p, /* FTS5 backend object */
Fts5Structure **ppStruct /* IN/OUT: Current structure of index */
){
const int nCrisis = p->pConfig->nCrisisMerge;
Fts5Structure *pStruct = *ppStruct;
| > | < < | | | | | | | > | 237046 237047 237048 237049 237050 237051 237052 237053 237054 237055 237056 237057 237058 237059 237060 237061 237062 237063 237064 237065 237066 237067 237068 237069 |
static void fts5IndexCrisismerge(
Fts5Index *p, /* FTS5 backend object */
Fts5Structure **ppStruct /* IN/OUT: Current structure of index */
){
const int nCrisis = p->pConfig->nCrisisMerge;
Fts5Structure *pStruct = *ppStruct;
if( pStruct && pStruct->nLevel>0 ){
int iLvl = 0;
while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
fts5StructurePromote(p, iLvl+1, pStruct);
iLvl++;
}
*ppStruct = pStruct;
}
}
static int fts5IndexReturn(Fts5Index *p){
int rc = p->rc;
p->rc = SQLITE_OK;
return rc;
}
|
| ︙ | ︙ | |||
234820 234821 234822 234823 234824 234825 234826 234827 234828 234829 234830 234831 234832 234833 |
int i = fts5GetVarint32(&aBuf[ret], dummy);
if( (ret + i) > nMax ) break;
ret += i;
}
}
return ret;
}
/*
** Flush the contents of in-memory hash table iHash to a new level-0
** segment on disk. Also update the corresponding structure record.
**
** If an error occurs, set the Fts5Index.rc error code. If an error has
** already occurred, this function is a no-op.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 237088 237089 237090 237091 237092 237093 237094 237095 237096 237097 237098 237099 237100 237101 237102 237103 237104 237105 237106 237107 237108 237109 237110 237111 237112 237113 237114 237115 237116 237117 237118 237119 237120 237121 237122 237123 237124 237125 237126 237127 237128 237129 237130 237131 237132 237133 237134 237135 237136 237137 237138 237139 237140 237141 237142 237143 237144 237145 237146 237147 237148 237149 237150 237151 237152 237153 237154 237155 237156 237157 237158 237159 237160 237161 237162 237163 237164 237165 237166 237167 237168 237169 237170 237171 237172 237173 237174 237175 237176 237177 237178 237179 237180 237181 237182 237183 237184 237185 237186 237187 237188 237189 237190 237191 237192 237193 237194 237195 237196 237197 237198 237199 237200 237201 237202 237203 237204 237205 237206 237207 237208 237209 237210 237211 237212 237213 237214 237215 237216 237217 237218 237219 237220 237221 237222 237223 237224 237225 237226 237227 237228 237229 237230 237231 237232 237233 237234 237235 237236 237237 237238 237239 237240 237241 237242 237243 237244 237245 237246 237247 237248 237249 237250 237251 237252 237253 237254 237255 237256 237257 237258 237259 237260 237261 237262 237263 237264 237265 237266 237267 237268 237269 237270 237271 237272 237273 237274 237275 237276 237277 237278 237279 237280 237281 237282 237283 237284 237285 237286 237287 237288 237289 237290 237291 237292 237293 237294 237295 237296 237297 237298 237299 237300 237301 237302 237303 237304 237305 237306 237307 237308 237309 237310 237311 237312 237313 237314 237315 237316 237317 237318 237319 237320 237321 237322 237323 237324 237325 237326 237327 237328 237329 237330 237331 237332 237333 237334 237335 237336 237337 237338 237339 237340 237341 237342 237343 237344 237345 237346 237347 237348 237349 237350 237351 237352 237353 237354 237355 237356 237357 237358 237359 237360 237361 237362 237363 237364 237365 237366 237367 237368 237369 237370 237371 237372 237373 237374 237375 237376 237377 237378 237379 237380 237381 237382 237383 237384 237385 237386 237387 237388 237389 237390 237391 237392 237393 237394 237395 237396 237397 237398 237399 237400 237401 237402 237403 237404 237405 237406 237407 237408 237409 237410 237411 237412 237413 237414 237415 237416 237417 237418 237419 237420 237421 237422 237423 237424 237425 237426 237427 237428 237429 237430 237431 237432 237433 237434 237435 237436 237437 237438 237439 237440 237441 237442 237443 237444 237445 237446 237447 237448 237449 237450 237451 237452 237453 237454 237455 237456 237457 237458 237459 237460 237461 237462 237463 237464 237465 237466 237467 237468 237469 237470 237471 237472 237473 237474 237475 237476 237477 237478 237479 237480 237481 237482 237483 237484 237485 237486 237487 237488 237489 237490 237491 237492 237493 237494 237495 237496 237497 237498 237499 237500 237501 237502 237503 237504 237505 237506 237507 237508 |
int i = fts5GetVarint32(&aBuf[ret], dummy);
if( (ret + i) > nMax ) break;
ret += i;
}
}
return ret;
}
/*
** Execute the SQL statement:
**
** DELETE FROM %_idx WHERE (segid, (pgno/2)) = ($iSegid, $iPgno);
**
** This is used when a secure-delete operation removes the last term
** from a segment leaf page. In that case the %_idx entry is removed
** too. This is done to ensure that if all instances of a token are
** removed from an fts5 database in secure-delete mode, no trace of
** the token itself remains in the database.
*/
static void fts5SecureDeleteIdxEntry(
Fts5Index *p, /* FTS5 backend object */
int iSegid, /* Id of segment to delete entry for */
int iPgno /* Page number within segment */
){
if( iPgno!=1 ){
assert( p->pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE );
if( p->pDeleteFromIdx==0 ){
fts5IndexPrepareStmt(p, &p->pDeleteFromIdx, sqlite3_mprintf(
"DELETE FROM '%q'.'%q_idx' WHERE (segid, (pgno/2)) = (?1, ?2)",
p->pConfig->zDb, p->pConfig->zName
));
}
if( p->rc==SQLITE_OK ){
sqlite3_bind_int(p->pDeleteFromIdx, 1, iSegid);
sqlite3_bind_int(p->pDeleteFromIdx, 2, iPgno);
sqlite3_step(p->pDeleteFromIdx);
p->rc = sqlite3_reset(p->pDeleteFromIdx);
}
}
}
/*
** This is called when a secure-delete operation removes a position-list
** that overflows onto segment page iPgno of segment pSeg. This function
** rewrites node iPgno, and possibly one or more of its right-hand peers,
** to remove this portion of the position list.
**
** Output variable (*pbLastInDoclist) is set to true if the position-list
** removed is followed by a new term or the end-of-segment, or false if
** it is followed by another rowid/position list.
*/
static void fts5SecureDeleteOverflow(
Fts5Index *p,
Fts5StructureSegment *pSeg,
int iPgno,
int *pbLastInDoclist
){
const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE);
int pgno;
Fts5Data *pLeaf = 0;
assert( iPgno!=1 );
*pbLastInDoclist = 1;
for(pgno=iPgno; p->rc==SQLITE_OK && pgno<=pSeg->pgnoLast; pgno++){
i64 iRowid = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
int iNext = 0;
u8 *aPg = 0;
pLeaf = fts5DataRead(p, iRowid);
if( pLeaf==0 ) break;
aPg = pLeaf->p;
iNext = fts5GetU16(&aPg[0]);
if( iNext!=0 ){
*pbLastInDoclist = 0;
}
if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){
fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext);
}
if( iNext==0 ){
/* The page contains no terms or rowids. Replace it with an empty
** page and move on to the right-hand peer. */
const u8 aEmpty[] = {0x00, 0x00, 0x00, 0x04};
assert_nc( bDetailNone==0 || pLeaf->nn==4 );
if( bDetailNone==0 ) fts5DataWrite(p, iRowid, aEmpty, sizeof(aEmpty));
fts5DataRelease(pLeaf);
pLeaf = 0;
}else if( bDetailNone ){
break;
}else if( iNext>=pLeaf->szLeaf || iNext<4 ){
p->rc = FTS5_CORRUPT;
break;
}else{
int nShift = iNext - 4;
int nPg;
int nIdx = 0;
u8 *aIdx = 0;
/* Unless the current page footer is 0 bytes in size (in which case
** the new page footer will be as well), allocate and populate a
** buffer containing the new page footer. Set stack variables aIdx
** and nIdx accordingly. */
if( pLeaf->nn>pLeaf->szLeaf ){
int iFirst = 0;
int i1 = pLeaf->szLeaf;
int i2 = 0;
aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
if( aIdx==0 ) break;
i1 += fts5GetVarint32(&aPg[i1], iFirst);
i2 = sqlite3Fts5PutVarint(aIdx, iFirst-nShift);
if( i1<pLeaf->nn ){
memcpy(&aIdx[i2], &aPg[i1], pLeaf->nn-i1);
i2 += (pLeaf->nn-i1);
}
nIdx = i2;
}
/* Modify the contents of buffer aPg[]. Set nPg to the new size
** in bytes. The new page is always smaller than the old. */
nPg = pLeaf->szLeaf - nShift;
memmove(&aPg[4], &aPg[4+nShift], nPg-4);
fts5PutU16(&aPg[2], nPg);
if( fts5GetU16(&aPg[0]) ) fts5PutU16(&aPg[0], 4);
if( nIdx>0 ){
memcpy(&aPg[nPg], aIdx, nIdx);
nPg += nIdx;
}
sqlite3_free(aIdx);
/* Write the new page to disk and exit the loop */
assert( nPg>4 || fts5GetU16(aPg)==0 );
fts5DataWrite(p, iRowid, aPg, nPg);
break;
}
}
fts5DataRelease(pLeaf);
}
/*
** Completely remove the entry that pSeg currently points to from
** the database.
*/
static void fts5DoSecureDelete(
Fts5Index *p,
Fts5SegIter *pSeg
){
const int bDetailNone = (p->pConfig->eDetail==FTS5_DETAIL_NONE);
int iSegid = pSeg->pSeg->iSegid;
u8 *aPg = pSeg->pLeaf->p;
int nPg = pSeg->pLeaf->nn;
int iPgIdx = pSeg->pLeaf->szLeaf;
u64 iDelta = 0;
u64 iNextDelta = 0;
int iNextOff = 0;
int iOff = 0;
int nIdx = 0;
u8 *aIdx = 0;
int bLastInDoclist = 0;
int iIdx = 0;
int iStart = 0;
int iKeyOff = 0;
int iPrevKeyOff = 0;
int iDelKeyOff = 0; /* Offset of deleted key, if any */
nIdx = nPg-iPgIdx;
aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16);
if( p->rc ) return;
memcpy(aIdx, &aPg[iPgIdx], nIdx);
/* At this point segment iterator pSeg points to the entry
** this function should remove from the b-tree segment.
**
** In detail=full or detail=column mode, pSeg->iLeafOffset is the
** offset of the first byte in the position-list for the entry to
** remove. Immediately before this comes two varints that will also
** need to be removed:
**
** + the rowid or delta rowid value for the entry, and
** + the size of the position list in bytes.
**
** Or, in detail=none mode, there is a single varint prior to
** pSeg->iLeafOffset - the rowid or delta rowid value.
**
** This block sets the following variables:
**
** iStart:
** iDelta:
*/
{
int iSOP;
if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){
iStart = pSeg->iTermLeafOffset;
}else{
iStart = fts5GetU16(&aPg[0]);
}
iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
assert_nc( iSOP<=pSeg->iLeafOffset );
if( bDetailNone ){
while( iSOP<pSeg->iLeafOffset ){
if( aPg[iSOP]==0x00 ) iSOP++;
if( aPg[iSOP]==0x00 ) iSOP++;
iStart = iSOP;
iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
}
iNextOff = iSOP;
if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++;
if( iNextOff<pSeg->iEndofDoclist && aPg[iNextOff]==0x00 ) iNextOff++;
}else{
int nPos = 0;
iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
while( iSOP<pSeg->iLeafOffset ){
iStart = iSOP + (nPos/2);
iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
iSOP += fts5GetVarint32(&aPg[iSOP], nPos);
}
assert_nc( iSOP==pSeg->iLeafOffset );
iNextOff = pSeg->iLeafOffset + pSeg->nPos;
}
}
iOff = iStart;
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
}else{
/* Set bLastInDoclist to true if the entry being removed is the last
** in its doclist. */
for(iIdx=0, iKeyOff=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff==iNextOff ){
bLastInDoclist = 1;
}
}
}
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist||iNextOff==iPgIdx) ){
fts5PutU16(&aPg[0], 0);
}
if( bLastInDoclist==0 ){
if( iNextOff!=iPgIdx ){
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta);
}
}else if(
iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno
){
/* The entry being removed was the only position list in its
** doclist. Therefore the term needs to be removed as well. */
int iKey = 0;
for(iIdx=0, iKeyOff=0; iIdx<nIdx; iKey++){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
if( (iKeyOff+iVal)>(u32)iStart ) break;
iKeyOff += iVal;
}
iDelKeyOff = iOff = iKeyOff;
if( iNextOff!=iPgIdx ){
int nPrefix = 0;
int nSuffix = 0;
int nPrefix2 = 0;
int nSuffix2 = 0;
iDelKeyOff = iNextOff;
iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2);
iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2);
if( iKey!=1 ){
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix);
}
iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix);
nPrefix = MIN(nPrefix, nPrefix2);
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
p->rc = FTS5_CORRUPT;
}else{
if( iKey!=1 ){
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
}
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
if( nPrefix2>nPrefix ){
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
iOff += (nPrefix2-nPrefix);
}
memmove(&aPg[iOff], &aPg[iNextOff], nSuffix2);
iOff += nSuffix2;
iNextOff += nSuffix2;
}
}
}else if( iStart==4 ){
int iPgno;
assert_nc( pSeg->iLeafPgno>pSeg->iTermLeafPgno );
/* The entry being removed may be the only position list in
** its doclist. */
for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){
Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno));
int bEmpty = (pPg && pPg->nn==4);
fts5DataRelease(pPg);
if( bEmpty==0 ) break;
}
if( iPgno==pSeg->iTermLeafPgno ){
i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno);
Fts5Data *pTerm = fts5DataRead(p, iId);
if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){
u8 *aTermIdx = &pTerm->p[pTerm->szLeaf];
int nTermIdx = pTerm->nn - pTerm->szLeaf;
int iTermIdx = 0;
int iTermOff = 0;
while( 1 ){
u32 iVal = 0;
int nByte = fts5GetVarint32(&aTermIdx[iTermIdx], iVal);
iTermOff += iVal;
if( (iTermIdx+nByte)>=nTermIdx ) break;
iTermIdx += nByte;
}
nTermIdx = iTermIdx;
memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx);
fts5PutU16(&pTerm->p[2], iTermOff);
fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx);
if( nTermIdx==0 ){
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno);
}
}
fts5DataRelease(pTerm);
}
}
if( p->rc==SQLITE_OK ){
const int nMove = nPg - iNextOff;
int nShift = 0;
memmove(&aPg[iOff], &aPg[iNextOff], nMove);
iPgIdx -= (iNextOff - iOff);
nPg = iPgIdx;
fts5PutU16(&aPg[2], iPgIdx);
nShift = iNextOff - iOff;
for(iIdx=0, iKeyOff=0, iPrevKeyOff=0; iIdx<nIdx; /* no-op */){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
iKeyOff += iVal;
if( iKeyOff!=iDelKeyOff ){
if( iKeyOff>iOff ){
iKeyOff -= nShift;
nShift = 0;
}
nPg += sqlite3Fts5PutVarint(&aPg[nPg], iKeyOff - iPrevKeyOff);
iPrevKeyOff = iKeyOff;
}
}
if( iPgIdx==nPg && nIdx>0 && pSeg->iLeafPgno!=1 ){
fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iLeafPgno);
}
assert_nc( nPg>4 || fts5GetU16(aPg)==0 );
fts5DataWrite(p, FTS5_SEGMENT_ROWID(iSegid,pSeg->iLeafPgno), aPg,nPg);
}
sqlite3_free(aIdx);
}
/*
** This is called as part of flushing a delete to disk in 'secure-delete'
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
*/
static void fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
i64 iRowid
){
const int f = FTS5INDEX_QUERY_SKIPHASH;
int nTerm = (int)strlen(zTerm);
Fts5Iter *pIter = 0; /* Used to find term instance */
fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
if( fts5MultiIterEof(p, pIter)==0 ){
i64 iThis = fts5MultiIterRowid(pIter);
if( iThis<iRowid ){
fts5MultiIterNextFrom(p, pIter, iRowid);
}
if( p->rc==SQLITE_OK
&& fts5MultiIterEof(p, pIter)==0
&& iRowid==fts5MultiIterRowid(pIter)
){
Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
fts5DoSecureDelete(p, pSeg);
}
}
fts5MultiIterFree(pIter);
}
/*
** Flush the contents of in-memory hash table iHash to a new level-0
** segment on disk. Also update the corresponding structure record.
**
** If an error occurs, set the Fts5Index.rc error code. If an error has
** already occurred, this function is a no-op.
|
| ︙ | ︙ | |||
234843 234844 234845 234846 234847 234848 234849 234850 234851 234852 234853 234854 234855 234856 |
pStruct = fts5StructureRead(p);
iSegid = fts5AllocateSegid(p, pStruct);
fts5StructureInvalidate(p);
if( iSegid ){
const int pgsz = p->pConfig->pgsz;
int eDetail = p->pConfig->eDetail;
Fts5StructureSegment *pSeg; /* New segment within pStruct */
Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
Fts5SegWriter writer;
fts5WriteInit(p, &writer, iSegid);
| > | 237518 237519 237520 237521 237522 237523 237524 237525 237526 237527 237528 237529 237530 237531 237532 |
pStruct = fts5StructureRead(p);
iSegid = fts5AllocateSegid(p, pStruct);
fts5StructureInvalidate(p);
if( iSegid ){
const int pgsz = p->pConfig->pgsz;
int eDetail = p->pConfig->eDetail;
int bSecureDelete = p->pConfig->bSecureDelete;
Fts5StructureSegment *pSeg; /* New segment within pStruct */
Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
Fts5SegWriter writer;
fts5WriteInit(p, &writer, iSegid);
|
| ︙ | ︙ | |||
234865 234866 234867 234868 234869 234870 234871 234872 234873 234874 |
/* Begin scanning through hash table entries. This loop runs once for each
** term/doclist currently stored within the hash table. */
if( p->rc==SQLITE_OK ){
p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
}
while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
const char *zTerm; /* Buffer containing term */
const u8 *pDoclist; /* Pointer to doclist for this term */
int nDoclist; /* Size of doclist in bytes */
| > | > > | | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > | 237541 237542 237543 237544 237545 237546 237547 237548 237549 237550 237551 237552 237553 237554 237555 237556 237557 237558 237559 237560 237561 237562 237563 237564 237565 237566 237567 237568 237569 237570 237571 237572 237573 237574 237575 237576 237577 237578 237579 237580 237581 237582 237583 237584 237585 237586 237587 237588 237589 237590 237591 237592 237593 237594 237595 237596 237597 237598 237599 237600 237601 237602 237603 237604 237605 237606 237607 237608 237609 237610 237611 237612 237613 237614 237615 237616 237617 237618 237619 237620 237621 237622 237623 237624 237625 |
/* Begin scanning through hash table entries. This loop runs once for each
** term/doclist currently stored within the hash table. */
if( p->rc==SQLITE_OK ){
p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
}
while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
const char *zTerm; /* Buffer containing term */
int nTerm; /* Size of zTerm in bytes */
const u8 *pDoclist; /* Pointer to doclist for this term */
int nDoclist; /* Size of doclist in bytes */
/* Get the term and doclist for this entry. */
sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
nTerm = (int)strlen(zTerm);
if( bSecureDelete==0 ){
fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
if( p->rc!=SQLITE_OK ) break;
assert( writer.bFirstRowidInPage==0 );
}
if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
/* The entire doclist will fit on the current leaf. */
fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
}else{
int bTermWritten = !bSecureDelete;
i64 iRowid = 0;
i64 iPrev = 0;
int iOff = 0;
/* The entire doclist will not fit on this leaf. The following
** loop iterates through the poslists that make up the current
** doclist. */
while( p->rc==SQLITE_OK && iOff<nDoclist ){
u64 iDelta = 0;
iOff += fts5GetVarint(&pDoclist[iOff], &iDelta);
iRowid += iDelta;
/* If in secure delete mode, and if this entry in the poslist is
** in fact a delete, then edit the existing segments directly
** using fts5FlushSecureDelete(). */
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
iOff++;
nDoclist = 0;
}else{
continue;
}
}
}else if( (pDoclist[iOff] & 0x01) ){
fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
iOff++;
continue;
}
}
}
if( p->rc==SQLITE_OK && bTermWritten==0 ){
fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
bTermWritten = 1;
assert( p->rc!=SQLITE_OK || writer.bFirstRowidInPage==0 );
}
if( writer.bFirstRowidInPage ){
fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
writer.bFirstRowidInPage = 0;
fts5WriteDlidxAppend(p, &writer, iRowid);
}else{
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid-iPrev);
}
if( p->rc!=SQLITE_OK ) break;
assert( pBuf->n<=pBuf->nSpace );
iPrev = iRowid;
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0 ){
pBuf->p[pBuf->n++] = 0;
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0 ){
pBuf->p[pBuf->n++] = 0;
|
| ︙ | ︙ | |||
234957 234958 234959 234960 234961 234962 234963 |
/* pBuf->p[pBuf->n++] = '\0'; */
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
| > > | | | | | | | | | | | | | | > | 237670 237671 237672 237673 237674 237675 237676 237677 237678 237679 237680 237681 237682 237683 237684 237685 237686 237687 237688 237689 237690 237691 237692 237693 237694 237695 237696 237697 237698 237699 237700 |
/* pBuf->p[pBuf->n++] = '\0'; */
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
if( pgnoLast>0 ){
/* Update the Fts5Structure. It is written back to the database by the
** fts5StructureRelease() call below. */
if( pStruct->nLevel==0 ){
fts5StructureAddLevel(&p->rc, &pStruct);
}
fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
if( p->rc==SQLITE_OK ){
pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
pSeg->iSegid = iSegid;
pSeg->pgnoFirst = 1;
pSeg->pgnoLast = pgnoLast;
pStruct->nSegment++;
}
fts5StructurePromote(p, 0, pStruct);
}
}
fts5IndexAutomerge(p, &pStruct, pgnoLast);
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
}
|
| ︙ | ︙ | |||
235026 235027 235028 235029 235030 235031 235032 |
nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel);
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
Fts5StructureLevel *pLvl;
nByte = nSeg * sizeof(Fts5StructureSegment);
| | | | 237742 237743 237744 237745 237746 237747 237748 237749 237750 237751 237752 237753 237754 237755 237756 237757 237758 237759 |
nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel);
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
Fts5StructureLevel *pLvl;
nByte = nSeg * sizeof(Fts5StructureSegment);
pNew->nLevel = MIN(pStruct->nLevel+1, FTS5_MAX_LEVEL);
pNew->nRef = 1;
pNew->nWriteCounter = pStruct->nWriteCounter;
pLvl = &pNew->aLevel[pNew->nLevel-1];
pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pLvl->aSeg ){
int iLvl, iSeg;
int iSegOut = 0;
/* Iterate through all segments, from oldest to newest. Add them to
** the new Fts5Level object so that pLvl->aSeg[0] is the oldest
** segment in the data structure. */
|
| ︙ | ︙ | |||
235711 235712 235713 235714 235715 235716 235717 235718 235719 235720 235721 235722 235723 235724 |
fts5StructureInvalidate(p);
sqlite3_finalize(p->pWriter);
sqlite3_finalize(p->pDeleter);
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3Fts5HashFree(p->pHash);
sqlite3_free(p->zDataTbl);
sqlite3_free(p);
}
return rc;
}
| > | 238427 238428 238429 238430 238431 238432 238433 238434 238435 238436 238437 238438 238439 238440 238441 |
fts5StructureInvalidate(p);
sqlite3_finalize(p->pWriter);
sqlite3_finalize(p->pDeleter);
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
sqlite3_free(p->zDataTbl);
sqlite3_free(p);
}
return rc;
}
|
| ︙ | ︙ | |||
236341 236342 236343 236344 236345 236346 236347 236348 236349 236350 236351 236352 236353 236354 |
}
static void fts5IndexIntegrityCheckSegment(
Fts5Index *p, /* FTS5 backend object */
Fts5StructureSegment *pSeg /* Segment to check internal consistency */
){
Fts5Config *pConfig = p->pConfig;
sqlite3_stmt *pStmt = 0;
int rc2;
int iIdxPrevLeaf = pSeg->pgnoFirst-1;
int iDlidxPrevLeaf = pSeg->pgnoLast;
if( pSeg->pgnoFirst==0 ) return;
| > | 239058 239059 239060 239061 239062 239063 239064 239065 239066 239067 239068 239069 239070 239071 239072 |
}
static void fts5IndexIntegrityCheckSegment(
Fts5Index *p, /* FTS5 backend object */
Fts5StructureSegment *pSeg /* Segment to check internal consistency */
){
Fts5Config *pConfig = p->pConfig;
int bSecureDelete = (pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE);
sqlite3_stmt *pStmt = 0;
int rc2;
int iIdxPrevLeaf = pSeg->pgnoFirst-1;
int iDlidxPrevLeaf = pSeg->pgnoLast;
if( pSeg->pgnoFirst==0 ) return;
|
| ︙ | ︙ | |||
236376 236377 236378 236379 236380 236381 236382 |
if( pLeaf==0 ) break;
/* Check that the leaf contains at least one term, and that it is equal
** to or larger than the split-key in zIdxTerm. Also check that if there
** is also a rowid pointer within the leaf page header, it points to a
** location before the term. */
if( pLeaf->nn<=pLeaf->szLeaf ){
| > > > > > > > > > > | > > | 239094 239095 239096 239097 239098 239099 239100 239101 239102 239103 239104 239105 239106 239107 239108 239109 239110 239111 239112 239113 239114 239115 239116 239117 239118 239119 239120 |
if( pLeaf==0 ) break;
/* Check that the leaf contains at least one term, and that it is equal
** to or larger than the split-key in zIdxTerm. Also check that if there
** is also a rowid pointer within the leaf page header, it points to a
** location before the term. */
if( pLeaf->nn<=pLeaf->szLeaf ){
if( nIdxTerm==0
&& pConfig->iVersion==FTS5_CURRENT_VERSION_SECUREDELETE
&& pLeaf->nn==pLeaf->szLeaf
&& pLeaf->nn==4
){
/* special case - the very first page in a segment keeps its %_idx
** entry even if all the terms are removed from it by secure-delete
** operations. */
}else{
p->rc = FTS5_CORRUPT;
}
}else{
int iOff; /* Offset of first term on leaf */
int iRowidOff; /* Offset of first rowid on leaf */
int nTerm; /* Size of term on leaf in bytes */
int res; /* Comparison of term and split-key */
iOff = fts5LeafFirstTermOff(pLeaf);
|
| ︙ | ︙ | |||
236440 236441 236442 236443 236444 236445 236446 |
pLeaf = fts5DataRead(p, iKey);
if( pLeaf ){
i64 iRowid;
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
ASSERT_SZLEAF_OK(pLeaf);
if( iRowidOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
| | > > | > | 239170 239171 239172 239173 239174 239175 239176 239177 239178 239179 239180 239181 239182 239183 239184 239185 239186 239187 239188 239189 |
pLeaf = fts5DataRead(p, iKey);
if( pLeaf ){
i64 iRowid;
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
ASSERT_SZLEAF_OK(pLeaf);
if( iRowidOff>=pLeaf->szLeaf ){
p->rc = FTS5_CORRUPT;
}else if( bSecureDelete==0 || iRowidOff>0 ){
i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){
p->rc = FTS5_CORRUPT;
}
}
fts5DataRelease(pLeaf);
}
}
iDlidxPrevLeaf = iPg;
fts5DlidxIterFree(pDlidx);
|
| ︙ | ︙ | |||
238704 238705 238706 238707 238708 238709 238710 238711 238712 238713 238714 238715 238716 238717 238718 238719 238720 238721 238722 238723 238724 238725 238726 238727 |
sqlite3_value **apVal, /* Array of arguments */
sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
Fts5Config *pConfig = pTab->p.pConfig;
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
/* A transaction must be open when this is called. */
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
assert( pVtab->zErrMsg==0 );
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER
|| sqlite3_value_type(apVal[0])==SQLITE_NULL
);
assert( pTab->p.pConfig->pzErrmsg==0 );
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
/* Put any active cursors into REQUIRE_SEEK state. */
fts5TripCursors(pTab);
eType0 = sqlite3_value_type(apVal[0]);
if( eType0==SQLITE_NULL
| > > > > > > > | 241437 241438 241439 241440 241441 241442 241443 241444 241445 241446 241447 241448 241449 241450 241451 241452 241453 241454 241455 241456 241457 241458 241459 241460 241461 241462 241463 241464 241465 241466 241467 |
sqlite3_value **apVal, /* Array of arguments */
sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
Fts5Config *pConfig = pTab->p.pConfig;
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
int bUpdateOrDelete = 0;
/* A transaction must be open when this is called. */
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
assert( pVtab->zErrMsg==0 );
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER
|| sqlite3_value_type(apVal[0])==SQLITE_NULL
);
assert( pTab->p.pConfig->pzErrmsg==0 );
if( pConfig->pgsz==0 ){
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
if( rc!=SQLITE_OK ) return rc;
}
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
/* Put any active cursors into REQUIRE_SEEK state. */
fts5TripCursors(pTab);
eType0 = sqlite3_value_type(apVal[0]);
if( eType0==SQLITE_NULL
|
| ︙ | ︙ | |||
238766 238767 238768 238769 238770 238771 238772 238773 238774 238775 238776 238777 238778 238779 238780 238781 238782 238783 238784 238785 238786 238787 238788 238789 238790 238791 238792 238793 238794 |
rc = SQLITE_ERROR;
}
/* DELETE */
else if( nArg==1 ){
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
}
/* INSERT or UPDATE */
else{
int eType1 = sqlite3_value_numeric_type(apVal[1]);
if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){
rc = SQLITE_MISMATCH;
}
else if( eType0!=SQLITE_INTEGER ){
/* If this is a REPLACE, first remove the current entry (if any) */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
}
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
/* UPDATE */
else{
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
| > > | 241506 241507 241508 241509 241510 241511 241512 241513 241514 241515 241516 241517 241518 241519 241520 241521 241522 241523 241524 241525 241526 241527 241528 241529 241530 241531 241532 241533 241534 241535 241536 |
rc = SQLITE_ERROR;
}
/* DELETE */
else if( nArg==1 ){
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
bUpdateOrDelete = 1;
}
/* INSERT or UPDATE */
else{
int eType1 = sqlite3_value_numeric_type(apVal[1]);
if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){
rc = SQLITE_MISMATCH;
}
else if( eType0!=SQLITE_INTEGER ){
/* If this is a REPLACE, first remove the current entry (if any) */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
bUpdateOrDelete = 1;
}
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
/* UPDATE */
else{
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
|
| ︙ | ︙ | |||
238809 238810 238811 238812 238813 238814 238815 238816 238817 238818 238819 238820 238821 238822 238823 238824 238825 |
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid);
}
}
}else{
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
}
}
}
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
/*
** Implementation of xSync() method.
| > > > > > > > > > > > > > > | 241551 241552 241553 241554 241555 241556 241557 241558 241559 241560 241561 241562 241563 241564 241565 241566 241567 241568 241569 241570 241571 241572 241573 241574 241575 241576 241577 241578 241579 241580 241581 |
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid);
}
}
}else{
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
bUpdateOrDelete = 1;
}
}
}
if( rc==SQLITE_OK
&& bUpdateOrDelete
&& pConfig->bSecureDelete
&& pConfig->iVersion==FTS5_CURRENT_VERSION
){
rc = sqlite3Fts5StorageConfigValue(
pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE
);
if( rc==SQLITE_OK ){
pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE;
}
}
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
/*
** Implementation of xSync() method.
|
| ︙ | ︙ | |||
239672 239673 239674 239675 239676 239677 239678 239679 239680 239681 239682 239683 239684 239685 |
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
return sqlite3Fts5StorageRollback(pTab->pStorage);
}
/*
** Register a new auxiliary function with global context pGlobal.
*/
static int fts5CreateAux(
| > | 242428 242429 242430 242431 242432 242433 242434 242435 242436 242437 242438 242439 242440 242441 242442 |
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
pTab->p.pConfig->pgsz = 0;
return sqlite3Fts5StorageRollback(pTab->pStorage);
}
/*
** Register a new auxiliary function with global context pGlobal.
*/
static int fts5CreateAux(
|
| ︙ | ︙ | |||
239874 239875 239876 239877 239878 239879 239880 |
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);
| | | 242631 242632 242633 242634 242635 242636 242637 242638 242639 242640 242641 242642 242643 242644 242645 |
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: 2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0", -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){
|
| ︙ | ︙ | |||
239947 239948 239949 239950 239951 239952 239953 |
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
| | > > | 242704 242705 242706 242707 242708 242709 242710 242711 242712 242713 242714 242715 242716 242717 242718 242719 242720 |
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_source_id", 0,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
p, fts5SourceIdFunc, 0, 0
);
}
}
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
** fts5_test_mi.c is compiled and linked into the executable. And call
** its entry point to enable the matchinfo() demo. */
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.42.0" #define SQLITE_VERSION_NUMBER 3042000 #define SQLITE_SOURCE_ID "2023-05-16 12:36:15 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0" /* ** 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 |
| ︙ | ︙ | |||
1172 1173 1174 1175 1176 1177 1178 | ** file to the database file. ** ** <li>[[SQLITE_FCNTL_CKPT_DONE]] ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. | < < > | | | | | 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 | ** file to the database file. ** ** <li>[[SQLITE_FCNTL_CKPT_DONE]] ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** ** <li>[[SQLITE_FCNTL_EXTERNAL_READER]] ** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ** whether or not there is a database client in another process with a wal-mode ** transaction open on the database or not. It is only available on unix.The ** (void*) argument passed with this file-control should be a pointer to a ** value of type (int). The integer value is set to 1 if the database is a wal ** mode database and there exists at least one client in another process that ** currently has an SQL transaction open on the database. It is set to 0 if ** the database is not a wal-mode db, or if there is no such connection in any ** other process. This opcode cannot be used to detect transactions opened ** by clients within the current process, only within other processes. ** ** <li>[[SQLITE_FCNTL_CKSM_FILE]] ** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use interally by the ** [checksum VFS shim] only. ** ** <li>[[SQLITE_FCNTL_RESET_CACHE]] ** If there is currently no transaction open on the database, and the ** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control ** purges the contents of the in-memory page cache. If there is an open ** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** </ul> */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_GET_LOCKPROXYFILE 2 #define SQLITE_FCNTL_SET_LOCKPROXYFILE 3 #define SQLITE_FCNTL_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 |
| ︙ | ︙ | |||
1652 1653 1654 1655 1656 1657 1658 | ** applications and so this routine is usually not necessary. It is ** provided to support rare applications with unusual needs. ** ** <b>The sqlite3_config() interface is not threadsafe. The application ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running.</b> ** | < < < < < < < < > > > > > > > > > > > | 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 | ** applications and so this routine is usually not necessary. It is ** provided to support rare applications with unusual needs. ** ** <b>The sqlite3_config() interface is not threadsafe. The application ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running.</b> ** ** The first argument to sqlite3_config() is an integer ** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [configuration option] ** in the first argument. ** ** For most configuration options, the sqlite3_config() interface ** may only be invoked prior to library initialization using ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. ** The exceptional configuration options that may be invoked at any time ** are called "anytime configuration options". ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ** [sqlite3_shutdown()] with a first argument that is not an anytime ** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. ** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ SQLITE_API int sqlite3_config(int, ...); |
| ︙ | ︙ | |||
1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 |
/*
** CAPI3REF: Configuration Options
** KEYWORDS: {configuration option}
**
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
** the call worked. The [sqlite3_config()] interface will return a
** non-zero [error code] if a discontinued or unsupported configuration option
** is invoked.
| > > > > > > > > > > > > > > > > > | 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 |
/*
** CAPI3REF: Configuration Options
** KEYWORDS: {configuration option}
**
** These constants are the available integer configuration options that
** can be passed as the first argument to the [sqlite3_config()] interface.
**
** Most of the configuration options for sqlite3_config()
** will only work if invoked prior to [sqlite3_initialize()] or after
** [sqlite3_shutdown()]. The few exceptions to this rule are called
** "anytime configuration options".
** ^Calling [sqlite3_config()] with a first argument that is not an
** anytime configuration option in between calls to [sqlite3_initialize()] and
** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE.
**
** The set of anytime configuration options can change (by insertions
** and/or deletions) from one release of SQLite to the next.
** As of SQLite version 3.42.0, the complete set of anytime configuration
** options is:
** <ul>
** <li> SQLITE_CONFIG_LOG
** <li> SQLITE_CONFIG_PCACHE_HDRSZ
** </ul>
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_config()] to make sure that
** the call worked. The [sqlite3_config()] interface will return a
** non-zero [error code] if a discontinued or unsupported configuration option
** is invoked.
|
| ︙ | ︙ | |||
2119 2120 2121 2122 2123 2124 2125 | ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ | | | | | | | | | | | | | | | | | | | | | | | 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 | ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* no-op */ #define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ #define SQLITE_CONFIG_URI 17 /* int */ #define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ |
| ︙ | ︙ | |||
2375 2376 2377 2378 2379 2380 2381 | ** behaves as it did prior to [version 3.24.0] (2018-06-04). See the ** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for ** additional information. This feature can also be turned on and off ** using the [PRAGMA legacy_alter_table] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DML]] | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 | ** behaves as it did prior to [version 3.24.0] (2018-06-04). See the ** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for ** additional information. This feature can also be turned on and off ** using the [PRAGMA legacy_alter_table] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DML]] ** <dt>SQLITE_DBCONFIG_DQS_DML</dt> ** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DML statements ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The ** default value of this setting is determined by the [-DSQLITE_DQS] ** compile-time option. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DDL]] ** <dt>SQLITE_DBCONFIG_DQS_DDL</dt> ** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DDL statements, ** such as CREATE TABLE and CREATE INDEX. The ** default value of this setting is determined by the [-DSQLITE_DQS] ** compile-time option. ** </dd> ** ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] ** <dt>SQLITE_DBCONFIG_TRUSTED_SCHEMA</dt> ** <dd>The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to ** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite ** takes additional defensive steps to protect the application from harm ** including: ** <ul> ** <li> Prohibit the use of SQL functions inside triggers, views, ** CHECK constraints, DEFAULT clauses, expression indexes, ** partial indexes, or generated columns ** unless those functions are tagged with [SQLITE_INNOCUOUS]. ** <li> Prohibit the use of virtual tables inside of triggers or views ** unless those virtual tables are tagged with [SQLITE_VTAB_INNOCUOUS]. ** </ul> ** This setting defaults to "on" for legacy compatibility, however ** all applications are advised to turn it off if possible. This setting ** can also be controlled using the [PRAGMA trusted_schema] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database file to have a schema format version number (the 4-byte ** integer found at offset 44 into the database header) of 1. This in turn ** means that the resulting database file will be readable and writable by ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there ** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little ** practical use, but is provided so that SQLite can continue to claim the ** ability to generate new database files that are compatible with version ** 3.0.0. ** <p>Note that when the SQLITE_DBCONFIG_LEGACY_FILE_FORMAT setting is on, ** the [VACUUM] command will fail with an obscure error when attempting to ** process a table with generated columns and a descending index. This is ** not considered a bug since SQLite versions 3.3.0 and earlier do not support ** either generated columns or decending indexes. ** </dd> ** ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) ** by default. This option takes two arguments: an integer and a pointer to ** an integer.. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after ** processing the first argument is written into the integer that the second ** argument points to. ** </dd> ** ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ** same as setting [PRAGMA reverse_unordered_selects]. This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, ** then 0 or 1 is written into the integer that the second argument points to ** depending on if the reverse scan order flag is set after processing the ** first argument. ** </dd> ** ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ #define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */ #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ #define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
| ︙ | ︙ | |||
6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 | ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is | > > > > > > > | 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 | ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. ** ** If a negative argument is passed to sqlite3_sleep() the results vary by ** VFS and operating system. Some system treat a negative argument as an ** instruction to sleep forever. Others understand it to mean do not sleep ** at all. ^In SQLite version 3.42.0 and later, a negative ** argument passed into sqlite3_sleep() is changed to zero before it is relayed ** down into the xSleep method of the VFS. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is |
| ︙ | ︙ | |||
7825 7826 7827 7828 7829 7830 7831 | ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** | | | | | 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 | ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), ** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, ** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); |
| ︙ | ︙ | |||
9561 9562 9563 9564 9565 9566 9567 | ** prohibits that virtual table from being used from within triggers and ** views. ** </dd> ** ** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt> ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the | | > > > > > > > > > > | 9621 9622 9623 9624 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 9650 9651 9652 9653 9654 9655 9656 |
** prohibits that virtual table from being used from within triggers and
** views.
** </dd>
**
** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
** identify that virtual table as being safe to use from within triggers
** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the
** virtual table can do no serious harm even if it is controlled by a
** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS
** flag unless absolutely necessary.
** </dd>
**
** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]<dt>SQLITE_VTAB_USES_ALL_SCHEMAS</dt>
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the
** the [xConnect] or [xCreate] methods of a [virtual table] implementation
** instruct the query planner to begin at least a read transaction on
** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the
** virtual table is used.
** </dd>
** </dl>
*/
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
#define SQLITE_VTAB_INNOCUOUS 2
#define SQLITE_VTAB_DIRECTONLY 3
#define SQLITE_VTAB_USES_ALL_SCHEMAS 4
/*
** CAPI3REF: Determine The Virtual Table Conflict Policy
**
** This function may only be called from within a call to the [xUpdate] method
** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
|
| ︙ | ︙ | |||
9802 9803 9804 9805 9806 9807 9808 | ** ** These interfaces are only useful from within the ** [xFilter|xFilter() method] of a [virtual table] implementation. ** The result of invoking these interfaces from any other context ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or | | | < | | 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 |
**
** These interfaces are only useful from within the
** [xFilter|xFilter() method] of a [virtual table] implementation.
** The result of invoking these interfaces from any other context
** is undefined and probably harmful.
**
** The X parameter in a call to sqlite3_vtab_in_first(X,P) or
** sqlite3_vtab_in_next(X,P) should be one of the parameters to the
** xFilter method which invokes these routines, and specifically
** a parameter that was previously selected for all-at-once IN constraint
** processing use the [sqlite3_vtab_in()] interface in the
** [xBestIndex|xBestIndex method]. ^(If the X parameter is not
** an xFilter argument that was selected for all-at-once IN constraint
** processing, then these routines return [SQLITE_ERROR].)^
**
** ^(Use these routines to access all values on the right-hand side
** of the IN constraint using code like the following:
**
** <blockquote><pre>
** for(rc=sqlite3_vtab_in_first(pList, &pVal);
** rc==SQLITE_OK && pVal;
** rc=sqlite3_vtab_in_next(pList, &pVal)
** ){
** // do something with pVal
** }
** if( rc!=SQLITE_OK ){
** // an error has occurred
** }
|
| ︙ | ︙ | |||
9950 9951 9952 9953 9954 9955 9956 | ** description for the X-th loop. ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt> ** <dd>^The "int" variable pointed to by the V parameter will be set to the ** id for the X-th query plan element. The id value is unique within the ** statement. The select-id is the same value as is output in the first ** column of an [EXPLAIN QUERY PLAN] query. | < > | 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 | ** description for the X-th loop. ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt> ** <dd>^The "int" variable pointed to by the V parameter will be set to the ** id for the X-th query plan element. The id value is unique within the ** statement. The select-id is the same value as is output in the first ** column of an [EXPLAIN QUERY PLAN] query. ** ** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt> ** <dd>The "int" variable pointed to by the V parameter will be set to the ** the id of the parent of the current query element, if applicable, or ** to zero if the query element has no parent. This is the same value as ** returned in the second column of an [EXPLAIN QUERY PLAN] query. ** ** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt> ** <dd>The sqlite3_int64 output value is set to the number of cycles, ** according to the processor time-stamp counter, that elapsed while the ** query element was being processed. This value is not available for ** all query elements - if it is unavailable the output variable is ** set to -1. ** </dl> */ #define SQLITE_SCANSTAT_NLOOP 0 #define SQLITE_SCANSTAT_NVISIT 1 #define SQLITE_SCANSTAT_EST 2 #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 |
| ︙ | ︙ | |||
10120 10121 10122 10123 10124 10125 10126 | ** or any operation on a WITHOUT ROWID table, the value of the sixth ** parameter is undefined. For an INSERT or UPDATE on a rowid table the ** seventh parameter is the final rowid value of the row being inserted ** or updated. The value of the seventh parameter passed to the callback ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** | | | 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 | ** or any operation on a WITHOUT ROWID table, the value of the sixth ** parameter is undefined. For an INSERT or UPDATE on a rowid table the ** seventh parameter is the final rowid value of the row being inserted ** or updated. The value of the seventh parameter passed to the callback ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** ** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from ** the previous call on the same [database connection] D, or NULL for ** the first call on D. ** ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines ** may only be called from within a preupdate callback. Invoking any of |
| ︙ | ︙ | |||
10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 | /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ /******** Begin file sqlite3rtree.h *********/ | > > > > > > > > > > > > > | 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 | /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 # undef SQLITE_OMIT_WAL # define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ # ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION # endif # ifndef SQLITE_THREADSAFE # define SQLITE_THREADSAFE 0 # endif #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif /* SQLITE3_H */ /******** Begin file sqlite3rtree.h *********/ |
| ︙ | ︙ | |||
10735 10736 10737 10738 10739 10740 10741 | ** Session objects must be deleted before the database handle to which they ** are attached is closed. Refer to the documentation for ** [sqlite3session_create()] for details. */ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* | | | | > > > > | | | | | > | > > > > > > > | > | 10817 10818 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 10840 10841 10842 10843 10844 10845 10846 10847 10848 10849 10850 10851 10852 10853 10854 10855 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 | ** Session objects must be deleted before the database handle to which they ** are attached is closed. Refer to the documentation for ** [sqlite3session_create()] for details. */ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* ** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been ** created. At present the only valid values for the second parameter are ** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** */ SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); /* ** CAPI3REF: Options for sqlite3session_object_config ** ** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** ** <dt>SQLITE_SESSION_OBJCONFIG_SIZE <dd> ** This option is used to set, clear or query the flag that enables ** the [sqlite3session_changeset_size()] API. Because it imposes some ** computational overhead, this API is disabled by default. Argument ** pArg must point to a value of type (int). If the value is initially ** 0, then the sqlite3session_changeset_size() API is disabled. If it ** is greater than 0, then the same API is enabled. Or, if the initial ** value is less than zero, no change is made. In all cases the (int) ** variable is set to 1 if the sqlite3session_changeset_size() API is ** enabled following the current call, or 0 otherwise. ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. ** ** <dt>SQLITE_SESSION_OBJCONFIG_ROWID <dd> ** This option is used to set, clear or query the flag that enables ** collection of data for tables with no explicit PRIMARY KEY. ** ** Normally, tables with no explicit PRIMARY KEY are simply ignored ** by the sessions module. However, if this flag is set, it behaves ** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted ** as their leftmost columns. ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. */ #define SQLITE_SESSION_OBJCONFIG_SIZE 1 #define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object ** METHOD: sqlite3_session ** ** Enable or disable the recording of changes by a session object. When ** enabled, a session object records changes made to the database. When |
| ︙ | ︙ | |||
11898 11899 11900 11901 11902 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 | ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. ** ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 /* ** CAPI3REF: Constants Passed To The Conflict Handler ** ** Values that may be passed as the second argument to a conflict-handler. ** ** <dl> | > > > > > > > > > > > > > > | 11993 11994 11995 11996 11997 11998 11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 12021 12022 12023 | ** caller has an open transaction or savepoint when apply_v2() is called, ** it may revert the partially applied changeset by rolling it back. ** ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. ** ** <dt>SQLITE_CHANGESETAPPLY_IGNORENOOP <dd> ** Do not invoke the conflict handler callback for any changes that ** would not actually modify the database even if they were applied. ** Specifically, this means that the conflict handler is not invoked ** for: ** <ul> ** <li>a delete change if the row being deleted cannot be found, ** <li>an update change if the modified fields are already set to ** their new values in the conflicting row, or ** <li>an insert change if all fields of the conflicting row match ** the row being inserted. ** </ul> */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 /* ** CAPI3REF: Constants Passed To The Conflict Handler ** ** Values that may be passed as the second argument to a conflict-handler. ** ** <dl> |
| ︙ | ︙ |
Changes to skins/README.md.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
called "skins/newskin" below but you should use a new original
name, of course.)
2. Add files skins/newskin/css.txt, skins/newskin/details.txt,
skins/newskin/footer.txt, skins/newskin/header.txt, and
skins/newskin/js.txt. Be sure to "fossil add" these files.
| | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
called "skins/newskin" below but you should use a new original
name, of course.)
2. Add files skins/newskin/css.txt, skins/newskin/details.txt,
skins/newskin/footer.txt, skins/newskin/header.txt, and
skins/newskin/js.txt. Be sure to "fossil add" these files.
3. Go to the tools/ directory and rerun "tclsh makemake.tcl". This
step rebuilds the various makefiles so that they have dependencies
on the skin files you just installed.
4. Edit the BuiltinSkin[] array near the top of the src/skins.c source
file so that it describes and references the "newskin" skin.
5. Type "make" to rebuild.
|
| ︙ | ︙ |
Changes to skins/ardoise/css.txt.
| ︙ | ︙ | |||
305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
display: inline-block;
box-sizing: border-box;
text-decoration: none;
text-align: center;
white-space: nowrap;
cursor: pointer
}
@media (min-width:550px) {
.container {
width: 95%
}
.column,
.columns {
margin-left: 4%
| > > > > > | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
display: inline-block;
box-sizing: border-box;
text-decoration: none;
text-align: center;
white-space: nowrap;
cursor: pointer
}
input[type=submit]:disabled {
color: rgb(70,70,70);
background-color: rgb(153,153,153);
}
@media (min-width:550px) {
.container {
width: 95%
}
.column,
.columns {
margin-left: 4%
|
| ︙ | ︙ | |||
1380 1381 1382 1383 1384 1385 1386 |
.intLink[title=Hyperlink] {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43NTIgMy45NjloLjc5M20tLjc5My0yLjExN2guNzkzTTEuNzUyIDMuOTdjLS4zNDMgMC0uNjU5LS4xMTktLjgzLS40MTUtLjE3MS0uMjk3LS4xNzEtLjk5IDAtMS4yODcuMTcxLS4yOTYuNDg3LS40MTUuODMtLjQxNW0yLjIxNyAyLjExNmgtLjc5NG0uNzk0LTIuMTE3aC0uNzk0bS43OTQgMi4xMTdjLjM0MiAwIC42NTgtLjExOS44My0uNDE1LjE3LS4yOTcuMTctLjk5IDAtMS4yODctLjE3Mi0uMjk2LS40ODgtLjQxNS0uODMtLjQxNU0yLjExNyAyLjkxaDEuNTg3IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==)
}
.intLink[title=Hyperlink]:hover {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=)
}
.statistics-report-graph-line {
| > | > > > > | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 |
.intLink[title=Hyperlink] {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cGF0aCBkPSJNMS43NTIgMy45NjloLjc5M20tLjc5My0yLjExN2guNzkzTTEuNzUyIDMuOTdjLS4zNDMgMC0uNjU5LS4xMTktLjgzLS40MTUtLjE3MS0uMjk3LS4xNzEtLjk5IDAtMS4yODcuMTcxLS4yOTYuNDg3LS40MTUuODMtLjQxNW0yLjIxNyAyLjExNmgtLjc5NG0uNzk0LTIuMTE3aC0uNzk0bS43OTQgMi4xMTdjLjM0MiAwIC42NTgtLjExOS44My0uNDE1LjE3LS4yOTcuMTctLjk5IDAtMS4yODctLjE3Mi0uMjk2LS40ODgtLjQxNS0uODMtLjQxNU0yLjExNyAyLjkxaDEuNTg3IiBmaWxsPSJub25lIiBzdHJva2U9IiNhYWEiIHN0cm9rZS13aWR0aD0iLjUyOSIvPjwvc3ZnPg==)
}
.intLink[title=Hyperlink]:hover {
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDUuODIxIDUuODIxIj48cmVjdCByeT0iLjI2NSIgcng9Ii4yNjUiIHk9Ii0uMDAxIiBoZWlnaHQ9IjUuODIxIiB3aWR0aD0iNS44MjEiIGZpbGw9IiM1NTUiLz48cGF0aCBkPSJNMS43NTIgMy45NjhoLjc5M20tLjc5My0yLjExN2guNzkzbS0uNzkzIDIuMTE3Yy0uMzQzIDAtLjY1OS0uMTE5LS44My0uNDE1LS4xNzEtLjI5Ny0uMTcxLS45OSAwLTEuMjg3LjE3MS0uMjk2LjQ4Ny0uNDE1LjgzLS40MTVtMi4yMTcgMi4xMTdoLS43OTRtLjc5NC0yLjExN2gtLjc5NG0uNzk0IDIuMTE3Yy4zNDIgMCAuNjU4LS4xMTkuODMtLjQxNS4xNy0uMjk3LjE3LS45OSAwLTEuMjg3LS4xNzItLjI5Ni0uNDg4LS40MTUtLjgzLS40MTVNMi4xMTcgMi45MWgxLjU4NyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZGRkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=)
}
.statistics-report-graph-line {
border: 2px solid #ff8000;
background-color: #ff8000;
}
.statistics-report-graph-extra {
border: 2px dashed #446979;
border-left-style: none;
}
mark,
p.noMoreShun,
p.shunned,
span.modpending {
color: #ff8000
}
|
| ︙ | ︙ | |||
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 |
.u-cf {
content: "";
display: table;
clear: both
}
div.forumSel {
background-color: #3a3a3a;
}
.debug {
background-color: #330;
border: 2px solid #aa0;
}
.capsumOff {
| > > > | 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 |
.u-cf {
content: "";
display: table;
clear: both
}
div.forumSel {
background-color: #3a3a3a;
}
body.forum .forumPosts.fileage a:visited {
color: rgb(72, 144, 224);
}
.debug {
background-color: #330;
border: 2px solid #aa0;
}
.capsumOff {
|
| ︙ | ︙ |
Changes to skins/blitz/css.txt.
| ︙ | ︙ | |||
561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
input[type="submit"]:hover,
input[type="submit"]:focus {
color: white !important;
background-color: #648898;
border-color: #648898;
}
/* Forms
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
| > > > > > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
input[type="submit"]:hover,
input[type="submit"]:focus {
color: white !important;
background-color: #648898;
border-color: #648898;
}
input[type="submit"]:disabled {
color: rgb(128,128,128);
background-color: rgb(153,153,153);
}
/* Forms
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
|
| ︙ | ︙ | |||
1112 1113 1114 1115 1116 1117 1118 |
}
span.timelineComment {
padding: 0px 5px;
}
| | | 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 |
}
span.timelineComment {
padding: 0px 5px;
}
/* Login/Logout
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
table.login_out {
}
table.login_out .login_out_label {
font-weight: 700;
text-align: right;
|
| ︙ | ︙ | |||
1264 1265 1266 1267 1268 1269 1270 |
.mainmenu:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both;
}
| > > > > | 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 |
.mainmenu:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both;
}
body.forum .forumPosts.fileage a:visited {
color: #648999;
}
|
Changes to skins/darkmode/css.txt.
| ︙ | ︙ | |||
124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
input[type=button]:hover,
input[type=reset]:hover,
input[type=submit]:hover {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
outline: 0
}
.button:focus,
button:focus,
input[type=button]:focus,
input[type=reset]:focus,
input[type=submit]:focus {
outline: 2px outset #333;
border-color: #888;
| > > > > | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
input[type=button]:hover,
input[type=reset]:hover,
input[type=submit]:hover {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
outline: 0
}
input[type=submit]:disabled {
color: #363636;
background-color: #707070;
}
.button:focus,
button:focus,
input[type=button]:focus,
input[type=reset]:focus,
input[type=submit]:focus {
outline: 2px outset #333;
border-color: #888;
|
| ︙ | ︙ | |||
554 555 556 557 558 559 560 |
}
body.forum .debug {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
}
| | > > > > > > > > > > > > | 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 |
}
body.forum .debug {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
}
body.forum .forumPosts.fileage tr:hover {
background-color: #333;
color: rgba(24,24,24,0.8);
}
body.forum .forumPosts.fileage tr:hover {
background-color: #333;
color: rgba(24,24,24,0.8);
}
body.forum .forumPosts.fileage tr:hover > td:nth-child(1),
body.forum .forumPosts.fileage tr:hover > td:nth-child(3) {
color: #ffffffe0;
}
body.forum .forumPostBody > div blockquote {
border: 1px inset;
padding: 0 0.5em;
}
body.forum .forumPosts.fileage a:visited {
color: rgba(98, 150, 205, 0.9);
}
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/eagle/css.txt.
| ︙ | ︙ | |||
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 |
}
div.forumSel {
background-color: #808080;
}
div.forumObs {
color: white;
}
.fileage td {
font-family: "courier new";
}
div.filetreeline:hover {
background-color: #7EA2D9;
}
table.numbered-lines td.line-numbers span.selected-line {
background-color: #7EA2D9;
}
.statistics-report-graph-line {
background-color: #7EA2D9;
}
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
background-color: #455978;
}
.capsumOff {
| > > > > > > > > | 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 |
}
div.forumSel {
background-color: #808080;
}
div.forumObs {
color: white;
}
body.forum .forumPosts.fileage a:visited {
color: rgba(176,176,176,1.0);
}
.fileage td {
font-family: "courier new";
}
div.filetreeline:hover {
background-color: #7EA2D9;
}
table.numbered-lines td.line-numbers span.selected-line {
background-color: #7EA2D9;
}
.statistics-report-graph-line {
border: 2px solid #7EA2D9;
background-color: #7EA2D9;
}
.statistics-report-graph-extra {
border: 2px solid #7EA2D9;
border-left-style: none;
}
.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
background-color: #455978;
}
.capsumOff {
|
| ︙ | ︙ |
Changes to skins/xekri/css.txt.
| ︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
/**************************************
* Statistics Reports
*/
.statistics-report-graph-line {
background-color: #22e;
}
.statistics-report-table-events th {
padding: 0 1rem;
}
.statistics-report-table-events td {
| > > > > > | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
/**************************************
* Statistics Reports
*/
.statistics-report-graph-line {
border: 2px solid #22e;
background-color: #22e;
}
.statistics-report-graph-extra {
border: 2px dashed #22e;
border-left-style: none;
}
.statistics-report-table-events th {
padding: 0 1rem;
}
.statistics-report-table-events td {
|
| ︙ | ︙ | |||
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 |
}
div.forumPostBody blockquote {
border-width: 1pt;
border-style: solid;
padding: 0 0.5em;
border-radius: 0.25em;
}
.debug {
color: black;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
| > > > > > > > | 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 |
}
div.forumPostBody blockquote {
border-width: 1pt;
border-style: solid;
padding: 0 0.5em;
border-radius: 0.25em;
}
body.forum .forumPosts.fileage a {
color: #60c0ff;
}
body.forum .forumPosts.fileage a:visited {
color: #40a0ff;
}
.debug {
color: black;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
|
| ︙ | ︙ |
Changes to src/alerts.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Logic for email notification, also known as "alerts" or "subscriptions". ** ** Are you looking for the code that reads and writes the internet ** email protocol? That is not here. See the "smtp.c" file instead. ** Yes, the choice of source code filenames is not the greatest, but ** it is not so bad that changing them seems justified. | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** ** Logic for email notification, also known as "alerts" or "subscriptions". ** ** Are you looking for the code that reads and writes the internet ** email protocol? That is not here. See the "smtp.c" file instead. ** Yes, the choice of source code filenames is not the greatest, but ** it is not so bad that changing them seems justified. */ #include "config.h" #include "alerts.h" #include <assert.h> #include <time.h> /* ** Maximum size of the subscriberCode blob, in bytes |
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use @ subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE, -- UUID for subscriber @ semail TEXT UNIQUE COLLATE nocase,-- email address @ suname TEXT, -- corresponding USER entry @ sverified BOOLEAN DEFAULT true, -- email address verified | | | | 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 | @ -- @ CREATE TABLE repository.subscriber( @ subscriberId INTEGER PRIMARY KEY, -- numeric subscriber ID. Internal use @ subscriberCode BLOB DEFAULT (randomblob(32)) UNIQUE, -- UUID for subscriber @ semail TEXT UNIQUE COLLATE nocase,-- email address @ suname TEXT, -- corresponding USER entry @ sverified BOOLEAN DEFAULT true, -- email address verified @ sdonotcall BOOLEAN, -- true for Do Not Call @ sdigest BOOLEAN, -- true for daily digests only @ ssub TEXT, -- baseline subscriptions @ sctime INTDATE, -- When this entry was created. unixtime @ mtime INTDATE, -- Last change. unixtime @ smip TEXT, -- IP address of last change @ lastContact INT -- Last contact. days since 1970 @ ); @ CREATE INDEX repository.subscriberUname @ ON subscriber(suname) WHERE suname IS NOT NULL; @ @ DROP TABLE IF EXISTS repository.pending_alert; @ -- Email notifications that need to be sent. @ -- @ -- The first character of the eventid determines the event type. @ -- Remaining characters determine the specific event. For example, @ -- 'c4413' means check-in with rid=4413. @ -- |
| ︙ | ︙ | |||
184 185 186 187 188 189 190 |
" INSERT INTO pending_alert(eventid)\n"
" SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n"
" ON CONFLICT(eventId) DO NOTHING;\n"
"END;"
);
}
if( db_table_exists("repository","chat")
| | | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
" INSERT INTO pending_alert(eventid)\n"
" SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n"
" ON CONFLICT(eventId) DO NOTHING;\n"
"END;"
);
}
if( db_table_exists("repository","chat")
&& db_get("chat-timeline-user", "")[0]!=0
){
/* Record events that will be relayed to chat, but do not relay
** them immediately, as the chat_msg_from_event() function requires
** that TAGXREF be up-to-date, and that has not happened yet when
** the insert into the EVENT table occurs. Make arrangements to
** invoke alert_process_deferred_triggers() when the transaction
** commits. The TAGXREF table will be ready by then. */
|
| ︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
** Return true if email alerts are active.
*/
int alert_enabled(void){
if( !alert_tables_exist() ) return 0;
if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0;
return 1;
}
/*
** If the subscriber table does not exist, then paint an error message
** web page and return true.
**
** If the subscriber table does exist, return 0 without doing anything.
*/
| > > > > > > > > > > > > > > > > | 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 |
** Return true if email alerts are active.
*/
int alert_enabled(void){
if( !alert_tables_exist() ) return 0;
if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0;
return 1;
}
/*
** If alerts are enabled, removes the pending_alert entry which
** matches (eventType || rid). Note that pending_alert entries are
** added via the manifest crosslinking process, so this has no effect
** if called before crosslinking is performed. Because alerts are sent
** asynchronously, unqueuing needs to be performed as part of the
** transaction in which crosslinking is performed in order to avoid a
** race condition.
*/
void alert_unqueue(char eventType, int rid){
if( alert_enabled() ){
db_multi_exec("DELETE FROM pending_alert WHERE eventid='%c%d'",
eventType, rid);
}
}
/*
** If the subscriber table does not exist, then paint an error message
** web page and return true.
**
** If the subscriber table does exist, return 0 without doing anything.
*/
|
| ︙ | ︙ | |||
299 300 301 302 303 304 305 |
}else{
@ <th>Disabled</th>
}
@ </table>
@ <hr>
@ <h1> Configuration </h1>
@ <form action="%R/setup_notification" method="post"><div>
| | | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
}else{
@ <th>Disabled</th>
}
@ </table>
@ <hr>
@ <h1> Configuration </h1>
@ <form action="%R/setup_notification" method="post"><div>
@ <input type="submit" name="submit" value="Apply Changes"><hr>
login_insert_csrf_secret();
entry_attribute("Canonical Server URL", 40, "email-url",
"eurl", "", 0);
@ <p><b>Required.</b>
@ This is URL used as the basename for hyperlinks included in
@ email alert text. Omit the trailing "/".
|
| ︙ | ︙ | |||
398 399 400 401 402 403 404 | @ transmitted via the SMTP protocol (rfc5321) to a "Mail Submission @ Agent" or "MSA" (rfc4409) at the hostname shown here. Optionally @ append a colon and TCP port number (ex: smtp.example.com:587). @ The default TCP port number is 25. @ (Property: "email-send-relayhost")</p> @ <hr> | | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | @ transmitted via the SMTP protocol (rfc5321) to a "Mail Submission @ Agent" or "MSA" (rfc4409) at the hostname shown here. Optionally @ append a colon and TCP port number (ex: smtp.example.com:587). @ The default TCP port number is 25. @ (Property: "email-send-relayhost")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } #if 0 /* |
| ︙ | ︙ | |||
613 614 615 616 617 618 619 |
blob_init(&p->out, 0, 0);
}else if( fossil_strcmp(p->zDest, "relay")==0 ){
const char *zRelay = 0;
emailerGetSetting(p, &zRelay, "email-send-relayhost");
if( zRelay ){
u32 smtpFlags = SMTP_DIRECT;
if( mFlags & ALERT_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT;
| | > | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
blob_init(&p->out, 0, 0);
}else if( fossil_strcmp(p->zDest, "relay")==0 ){
const char *zRelay = 0;
emailerGetSetting(p, &zRelay, "email-send-relayhost");
if( zRelay ){
u32 smtpFlags = SMTP_DIRECT;
if( mFlags & ALERT_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT;
p->pSmtp = smtp_session_new(domain_of_addr(p->zFrom), zRelay,
smtpFlags);
smtp_client_startup(p->pSmtp);
}
}
return p;
}
/*
|
| ︙ | ︙ | |||
712 713 714 715 716 717 718 | return i; } /* ** Make a copy of the input string up to but not including the ** first cTerm character. ** | | | | > > > | | | | < | < | 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 |
return i;
}
/*
** Make a copy of the input string up to but not including the
** first cTerm character.
**
** Verify that the string to be copied really is a valid
** email address. If it is not, then return NULL.
**
** This routine is more restrictive than necessary. It does not
** allow comments, IP address, quoted strings, or certain uncommon
** characters. The only non-alphanumerics allowed in the local
** part are "_", "+", "-" and "+".
*/
char *email_copy_addr(const char *z, char cTerm ){
int i = email_address_is_valid(z, cTerm);
return i==0 ? 0 : mprintf("%.*s", i, z);
}
/*
** Scan the input string for a valid email address that may be
** enclosed in <...>, or delimited by ',' or ':' or '=' or ' '.
** If the string contains one or more email addresses, extract the first
** one into memory obtained from mprintf() and return a pointer to it.
** If no valid email address can be found, return NULL.
*/
char *alert_find_emailaddr(const char *zIn){
char *zOut = 0;
do{
zOut = email_copy_addr(zIn, zIn[strcspn(zIn, ">,:= ")]);
if( zOut!=0 ) break;
zIn = (const char *)strpbrk(zIn, "<,:= ");
if( zIn==0 ) break;
zIn++;
}while( zIn!=0 );
return zOut;
}
/*
** SQL function: find_emailaddr(X)
**
** Return the first valid email address of the form <...> in input string
|
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 117 118 119 120 | ** option is supported. ** ** rebuild Rebuild on all repositories. The command line options ** supported by the rebuild command itself, if any are ** present, are passed along verbatim. The --force and ** --randomize options are not supported. ** ** sync Run a "sync" on all repositories. Only the --verbose ** and --unversioned and --share-links options are supported. ** ** set Run the "setting" or "set" commands on all ** repositories. These command are particularly useful in ** conjunction with the "max-loadavg" setting which cannot ** otherwise be set globally. | > > > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | ** option is supported. ** ** rebuild Rebuild on all repositories. The command line options ** supported by the rebuild command itself, if any are ** present, are passed along verbatim. The --force and ** --randomize options are not supported. ** ** remote Show remote hosts for all repositories. ** ** repack Look for extra compression in all repositories. ** ** sync Run a "sync" on all repositories. Only the --verbose ** and --unversioned and --share-links options are supported. ** ** set Run the "setting" or "set" commands on all ** repositories. These command are particularly useful in ** conjunction with the "max-loadavg" setting which cannot ** otherwise be set globally. |
| ︙ | ︙ | |||
299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
|| fossil_strcmp(g.argv[3],"list")==0 ){
zCmd = "remote ls -R";
}else if( fossil_strcmp(g.argv[3],"config-data")==0 ){
zCmd = "remote config-data -R";
}else{
usage("remote ?config-data|list|ls?");
}
}else if( fossil_strcmp(zCmd, "setting")==0 ){
zCmd = "setting -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "unset")==0 ){
zCmd = "unset -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
| > > | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
|| fossil_strcmp(g.argv[3],"list")==0 ){
zCmd = "remote ls -R";
}else if( fossil_strcmp(g.argv[3],"config-data")==0 ){
zCmd = "remote config-data -R";
}else{
usage("remote ?config-data|list|ls?");
}
}else if( fossil_strcmp(zCmd, "repack")==0 ){
zCmd = "repack";
}else if( fossil_strcmp(zCmd, "setting")==0 ){
zCmd = "setting -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "unset")==0 ){
zCmd = "unset -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename);
}else{
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
}
@ <li><p>
@ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
moderation_pending_www(attachid);
| | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
zUrlTail = mprintf("technote=%s&file=%t", zTarget, zFilename);
}else{
zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
}
@ <li><p>
@ Attachment %z(href("%R/ainfo/%!S",zUuid))%S(zUuid)</a>
moderation_pending_www(attachid);
@ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a>
@ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
if( zComment && zComment[0] ){
@ %!W(zComment)<br>
}
if( zPage==0 && zTkt==0 && zTechNote==0 ){
if( zSrc==0 || zSrc[0]==0 ){
zSrc = "Deleted from";
}else {
zSrc = "Added to";
}
|
| ︙ | ︙ | |||
395 396 397 398 399 400 401 |
if( !goodCaptcha ){
@ <p class="generalError">Error: Incorrect security code.</p>
}
@ <h2>Add Attachment To %s(zTargetType)</h2>
form_begin("enctype='multipart/form-data'", "%R/attachadd");
@ <div>
@ File to Attach:
| | | | | | | | | | | 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 |
if( !goodCaptcha ){
@ <p class="generalError">Error: Incorrect security code.</p>
}
@ <h2>Add Attachment To %s(zTargetType)</h2>
form_begin("enctype='multipart/form-data'", "%R/attachadd");
@ <div>
@ File to Attach:
@ <input type="file" name="f" size="60"><br>
@ Description:<br>
@ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br>
if( zTkt ){
@ <input type="hidden" name="tkt" value="%h(zTkt)">
}else if( zTechNote ){
@ <input type="hidden" name="technote" value="%h(zTechNote)">
}else{
@ <input type="hidden" name="page" value="%h(zPage)">
}
@ <input type="hidden" name="from" value="%h(zFrom)">
@ <input type="submit" name="ok" value="Add Attachment">
@ <input type="submit" name="cancel" value="Cancel">
@ </div>
captcha_generate(0);
@ </form>
style_finish_page();
fossil_free(zTargetType);
}
|
| ︙ | ︙ | |||
589 590 591 592 593 594 595 |
@ </table>
if( isModerator && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
form_begin(0, "%R/ainfo/%s", zUuid);
@ <label><input type="radio" name="modaction" value="delete">
| | | | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
@ </table>
if( isModerator && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
form_begin(0, "%R/ainfo/%s", zUuid);
@ <label><input type="radio" name="modaction" value="delete">
@ Delete this change</label><br>
@ <label><input type="radio" name="modaction" value="approve">
@ Approve this change</label><br>
@ <input type="submit" value="Submit">
@ </form>
@ </blockquote>
}
@ <div class="section">Content Appended</div>
@ <blockquote>
|
| ︙ | ︙ | |||
614 615 616 617 618 619 620 |
}else{
@ <pre>
@ %h(z)
@ </pre>
}
}else if( strncmp(zMime, "image/", 6)==0 ){
int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
| | | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 |
}else{
@ <pre>
@ %h(z)
@ </pre>
}
}else if( strncmp(zMime, "image/", 6)==0 ){
int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
@ <i>(file is %d(sz) bytes of image data)</i><br>
@ <img src="%R/raw/%s(zSrc)?m=%s(zMime)"></img>
style_submenu_element("Image", "%R/raw/%s?m=%s", zSrc, zMime);
}else{
int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
@ <i>(file is %d(sz) bytes of binary data)</i>
}
@ </blockquote>
|
| ︙ | ︙ |
Changes to src/backlink.c.
| ︙ | ︙ | |||
250 251 252 253 254 255 256 | char *zTarget = blob_buffer(target); int nTarget = blob_size(target); backlink_create(p, zTarget, nTarget); return 1; } | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
char *zTarget = blob_buffer(target);
int nTarget = blob_size(target);
backlink_create(p, zTarget, nTarget);
return 1;
}
/* No-op routines for the rendering callbacks that we do not need */
static void mkdn_noop_prolog(Blob *b, void *v){ return; }
static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog;
static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; }
static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; }
static void (*mkdn_noop_blockquote)(Blob*, Blob*, void*) = mkdn_noop_blockcode;
static void (*mkdn_noop_blockhtml)(Blob*, Blob*, void*) = mkdn_noop_blockcode;
static void mkdn_noop_header(Blob *b1, Blob *b2, int i, void *v){ return; }
static void (*mkdn_noop_hrule)(Blob*, void*) = mkdn_noop_prolog;
static void (*mkdn_noop_list)(Blob*, Blob*, int, void*) = mkdn_noop_header;
static void (*mkdn_noop_listitem)(Blob*, Blob*, int, void*) = mkdn_noop_header;
static void (*mkdn_noop_paragraph)(Blob*, Blob*, void*) = mkdn_noop_blockcode;
static void mkdn_noop_table(Blob *b1, Blob *b2, Blob *b3, void *v){ return; }
static void (*mkdn_noop_table_cell)(Blob*, Blob*, int,
void*) = mkdn_noop_header;
static void (*mkdn_noop_table_row)(Blob*, Blob*, int,
void*) = mkdn_noop_header;
static void mkdn_noop_footnoteitm(Blob *b1, const Blob *b2, int i1, int i2,
void *v){ return; }
static int mkdn_noop_autolink(Blob *b1, Blob *b2, enum mkd_autolink e,
void *v){ return 1; }
static int mkdn_noop_codespan(Blob *b1, Blob *b2, int i, void *v){ return 1; }
static int mkdn_noop_emphasis(Blob *b1, Blob *b2, char c, void *v){ return 1; }
static int (*mkdn_noop_dbl_emphas)(Blob*, Blob*, char,
void*) = mkdn_noop_emphasis;
static int mkdn_noop_image(Blob *b1, Blob *b2, Blob *b3, Blob *b4,
void *v){ return 1; }
static int mkdn_noop_linebreak(Blob *b1, void *v){ return 1; }
static int mkdn_noop_r_html_tag(Blob *b1, Blob *b2, void *v){ return 1; }
static int (*mkdn_noop_tri_emphas)(Blob*, Blob*, char,
void*) = mkdn_noop_emphasis;
static int mkdn_noop_footnoteref(Blob *b1, const Blob *b2, const Blob *b3,
int i1, int i2, void *v){ return 1; }
static int mkdn_noop_tagref(Blob*,Blob*, enum mkd_tagspan,
void*){ return 1; }
/*
** Scan markdown text and add self-hyperlinks to the BACKLINK table.
*/
void markdown_extract_links(
char *zInputText,
Backlink *p
){
struct mkd_renderer html_renderer = {
/* prolog */ mkdn_noop_prolog,
/* epilog */ mkdn_noop_epilog,
/* footnotes */ mkdn_noop_footnotes,
/* blockcode */ mkdn_noop_blockcode,
/* blockquote */ mkdn_noop_blockquote,
/* blockhtml */ mkdn_noop_blockhtml,
/* header */ mkdn_noop_header,
/* hrule */ mkdn_noop_hrule,
/* list */ mkdn_noop_list,
/* listitem */ mkdn_noop_listitem,
/* paragraph */ mkdn_noop_paragraph,
/* table */ mkdn_noop_table,
/* table_cell */ mkdn_noop_table_cell,
/* table_row */ mkdn_noop_table_row,
/* footnoteitm*/ mkdn_noop_footnoteitm,
/* autolink */ mkdn_noop_autolink,
/* codespan */ mkdn_noop_codespan,
/* dbl_emphas */ mkdn_noop_dbl_emphas,
/* emphasis */ mkdn_noop_emphasis,
/* image */ mkdn_noop_image,
/* linebreak */ mkdn_noop_linebreak,
/* link */ backlink_md_link,
/* r_html_tag */ mkdn_noop_r_html_tag,
/* @/#tags */ mkdn_noop_tagref,
/* tri_emphas */ mkdn_noop_tri_emphas,
/* footnoteref*/ mkdn_noop_footnoteref,
0, /* entity */
0, /* normal_text */
"*_", /* emphasis characters */
0 /* client data */
};
Blob out, in;
|
| ︙ | ︙ |
Changes to src/backoffice.c.
| ︙ | ︙ | |||
539 540 541 542 543 544 545 |
if( backofficeSleep(1000*(x.tmCurrent - tmNow + 1)) ){
/* The sleep was interrupted by a signal from another thread. */
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
db_end_transaction(0);
break;
}
}else{
| | | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
if( backofficeSleep(1000*(x.tmCurrent - tmNow + 1)) ){
/* The sleep was interrupted by a signal from another thread. */
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
db_end_transaction(0);
break;
}
}else{
if( (sqlite3_uint64)(lastWarning+warningDelay) < tmNow ){
fossil_warning(
"backoffice process %lld still running after %d seconds",
x.idCurrent, (int)(BKOFCE_LEASE_TIME + tmNow - x.tmCurrent));
lastWarning = tmNow;
warningDelay *= 2;
}
if( backofficeSleep(1000) ){
|
| ︙ | ︙ |
Changes to src/bag.c.
| ︙ | ︙ | |||
99 100 101 102 103 104 105 |
memset(p->a, 0, sizeof(p->a[0])*newSize );
for(i=0; i<old.sz; i++){
int e = old.a[i];
if( e>0 ){
unsigned h = bag_hash(e)%newSize;
while( p->a[h] ){
h++;
| | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
memset(p->a, 0, sizeof(p->a[0])*newSize );
for(i=0; i<old.sz; i++){
int e = old.a[i];
if( e>0 ){
unsigned h = bag_hash(e)%newSize;
while( p->a[h] ){
h++;
if( (int)h==newSize ) h = 0;
}
p->a[h] = e;
nLive++;
}else if( e<0 ){
nDel++;
}
}
|
| ︙ | ︙ | |||
129 130 131 132 133 134 135 |
if( p->used+1 >= p->sz/2 ){
int n = p->sz*2;
bag_resize(p, n + 20 );
}
h = bag_hash(e)%p->sz;
while( p->a[h]>0 && p->a[h]!=e ){
h++;
| | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
if( p->used+1 >= p->sz/2 ){
int n = p->sz*2;
bag_resize(p, n + 20 );
}
h = bag_hash(e)%p->sz;
while( p->a[h]>0 && p->a[h]!=e ){
h++;
if( (int)h>=p->sz ) h = 0;
}
if( p->a[h]<=0 ){
if( p->a[h]==0 ) p->used++;
p->a[h] = e;
p->cnt++;
rc = 1;
}
|
| ︙ | ︙ | |||
152 153 154 155 156 157 158 |
assert( e>0 );
if( p->sz==0 ){
return 0;
}
h = bag_hash(e)%p->sz;
while( p->a[h] && p->a[h]!=e ){
h++;
| | | | 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 |
assert( e>0 );
if( p->sz==0 ){
return 0;
}
h = bag_hash(e)%p->sz;
while( p->a[h] && p->a[h]!=e ){
h++;
if( (int)h>=p->sz ) h = 0;
}
return p->a[h]==e;
}
/*
** Remove element e from the bag if it exists in the bag.
** If e is not in the bag, this is a no-op.
*/
void bag_remove(Bag *p, int e){
unsigned h;
assert( e>0 );
if( p->sz==0 ) return;
h = bag_hash(e)%p->sz;
while( p->a[h] && p->a[h]!=e ){
h++;
if( (int)h>=p->sz ) h = 0;
}
if( p->a[h] ){
int nx = h+1;
if( nx>=p->sz ) nx = 0;
if( p->a[nx]==0 ){
p->a[h] = 0;
p->used--;
|
| ︙ | ︙ | |||
215 216 217 218 219 220 221 |
int bag_next(Bag *p, int e){
unsigned h;
assert( p->sz>0 );
assert( e>0 );
h = bag_hash(e)%p->sz;
while( p->a[h] && p->a[h]!=e ){
h++;
| | | | | 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
int bag_next(Bag *p, int e){
unsigned h;
assert( p->sz>0 );
assert( e>0 );
h = bag_hash(e)%p->sz;
while( p->a[h] && p->a[h]!=e ){
h++;
if( (int)h>=p->sz ) h = 0;
}
assert( p->a[h] );
h++;
while( (int)h<p->sz && p->a[h]<=0 ){
h++;
}
return (int)h<p->sz ? p->a[h] : 0;
}
/*
** Return the number of elements in the bag.
*/
int bag_count(Bag *p){
return p->cnt;
}
|
Changes to src/blob.c.
| ︙ | ︙ | |||
676 677 678 679 680 681 682 |
p->iCursor = 0;
}
/*
** Truncate a blob back to zero length
*/
void blob_truncate(Blob *p, int sz){
| | | 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
p->iCursor = 0;
}
/*
** Truncate a blob back to zero length
*/
void blob_truncate(Blob *p, int sz){
if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz;
}
/*
** Seek the cursor in a blob to the indicated offset.
*/
int blob_seek(Blob *p, int offset, int whence){
if( whence==BLOB_SEEK_SET ){
|
| ︙ | ︙ | |||
849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
i++;
}
if( pTo ){
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
}
pFrom->iCursor = i;
}
/*
** Ensure that the text in pBlob ends with '\n'
*/
void blob_add_final_newline(Blob *pBlob){
if( pBlob->nUsed<=0 ) return;
if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
i++;
}
if( pTo ){
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
}
pFrom->iCursor = i;
}
/*
** Remove comment lines (starting with '#') from a blob pIn.
** Keep lines starting with "\#" but remove the initial backslash.
**
** Store the result in pOut. It is ok for pIn and pOut to be the same blob.
**
** pOut must either be the same as pIn or else uninitialized.
*/
void blob_strip_comment_lines(Blob *pIn, Blob *pOut){
char *z = pIn->aData;
unsigned int i = 0;
unsigned int n = pIn->nUsed;
unsigned int lineStart = 0;
unsigned int copyStart = 0;
int doCopy = 1;
Blob temp;
blob_zero(&temp);
while( i<n ){
if( i==lineStart && z[i]=='#' ){
copyStart = i;
doCopy = 0;
}else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){
/* keep lines starting with an escaped '#' (and unescape it) */
copyStart = i + 1;
}
if( z[i]=='\n' ){
if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1);
lineStart = copyStart = i + 1;
doCopy = 1;
}
i++;
}
/* Last line */
if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart);
if( pOut==pIn ) blob_reset(pOut);
*pOut = temp;
}
/*
** COMMAND: test-strip-comment-lines
**
** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE
**
** Read INPUTFILE and print it without comment lines (starting with '#').
** Keep lines starting with "\\#" but remove the initial backslash.
**
** This is used to test and debug the blob_strip_comment_lines() routine.
**
** Options:
** -y|--side-by-side Show diff of INPUTFILE and output side-by-side
** -W|--width N Width of lines in side-by-side diff
*/
void test_strip_comment_lines_cmd(void){
Blob f, h; /* unitialized */
Blob out;
DiffConfig dCfg;
int sbs = 0;
const char *z;
int w = 0;
memset(&dCfg, 0, sizeof(dCfg));
sbs = find_option("side-by-side","y",0)!=0;
if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){
dCfg.wColumn = w;
}
verify_all_options();
if( g.argc!=3 ) usage("INPUTFILE");
blob_read_from_file(&f, g.argv[2], ExtFILE);
blob_strip_comment_lines(&f, &h);
if ( !sbs ){
blob_write_to_file(&h, "-");
}else{
blob_zero(&out);
dCfg.nContext = -1; /* whole content */
dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR;
diff_begin(&dCfg);
text_diff(&f, &h, &out, &dCfg);
blob_write_to_file(&out, "-");
diff_end(&dCfg, 0);
}
}
/*
** Ensure that the text in pBlob ends with '\n'
*/
void blob_add_final_newline(Blob *pBlob){
if( pBlob->nUsed<=0 ) return;
if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
|
| ︙ | ︙ | |||
1161 1162 1163 1164 1165 1166 1167 |
fossil_fatal_recursive("unable to open file \"%s\" for writing",
zFilename);
return 0;
}
blob_is_init(pBlob);
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
fclose(out);
| | | 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 |
fossil_fatal_recursive("unable to open file \"%s\" for writing",
zFilename);
return 0;
}
blob_is_init(pBlob);
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
fclose(out);
if( nWrote!=(int)blob_size(pBlob) ){
fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote,
blob_size(pBlob), zFilename);
}
}
return nWrote;
}
|
| ︙ | ︙ | |||
1359 1360 1361 1362 1363 1364 1365 |
char *z = p->aData;
int j = p->nUsed;
int i, n;
for(i=n=0; i<j; i++){
if( z[i]=='\n' ) n++;
}
j += n;
| | | 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 |
char *z = p->aData;
int j = p->nUsed;
int i, n;
for(i=n=0; i<j; i++){
if( z[i]=='\n' ) n++;
}
j += n;
if( j>=(int)(p->nAlloc) ){
blob_resize(p, j);
z = p->aData;
}
p->nUsed = j;
z[j] = 0;
while( j>i ){
if( (z[--j] = z[--i]) =='\n' ){
|
| ︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 |
if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){
n++;
}
n++;
}
}
j += n;
| | | 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 |
if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){
n++;
}
n++;
}
}
j += n;
if( j>=(int)(p->nAlloc) ){
blob_resize(p, j);
z = (unsigned char *)p->aData;
}
p->nUsed = j;
z[j] = 0;
while( j>i ){
if( z[--i]>=0x80 ){
|
| ︙ | ︙ |
Changes to src/browse.c.
| ︙ | ︙ | |||
658 659 660 661 662 663 664 |
int rid = 0;
char *zUuid = 0;
Blob dirname;
Manifest *pM = 0;
double rNow = 0;
char *zNow = 0;
int useMtime = atoi(PD("mtime","0"));
| < | 658 659 660 661 662 663 664 665 666 667 668 669 670 671 |
int rid = 0;
char *zUuid = 0;
Blob dirname;
Manifest *pM = 0;
double rNow = 0;
char *zNow = 0;
int useMtime = atoi(PD("mtime","0"));
int linkTrunk = 1; /* include link to "trunk" */
int linkTip = 1; /* include link to "tip" */
const char *zRE; /* the value for the re=REGEXP query parameter */
const char *zObjType; /* "files" by default or "folders" for "nofiles" */
char *zREx = ""; /* Extra parameters for path hyperlinks */
ReCompiled *pRE = 0; /* Compiled regular expression */
FileTreeNode *p; /* One line of the tree */
|
| ︙ | ︙ | |||
800 801 802 803 804 805 806 |
const char *zUuid = db_column_text(&q,1);
double mtime = db_column_double(&q,2);
if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){
continue;
}
if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue;
tree_add_node(&sTree, zFile, zUuid, mtime);
| < | 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
const char *zUuid = db_column_text(&q,1);
double mtime = db_column_double(&q,2);
if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){
continue;
}
if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue;
tree_add_node(&sTree, zFile, zUuid, mtime);
}
db_finalize(&q);
}else{
Stmt q;
db_prepare(&q,
"SELECT\n"
" (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n"
|
| ︙ | ︙ | |||
822 823 824 825 826 827 828 |
const char *zUuid = db_column_text(&q,1);
double mtime = db_column_double(&q,2);
if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){
continue;
}
if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue;
tree_add_node(&sTree, zName, zUuid, mtime);
| < < < < | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 |
const char *zUuid = db_column_text(&q,1);
double mtime = db_column_double(&q,2);
if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){
continue;
}
if( pRE && re_match(pRE, (const u8*)zName, -1)==0 ) continue;
tree_add_node(&sTree, zName, zUuid, mtime);
}
db_finalize(&q);
}
style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
if( showDirOnly ){
zObjType = "Folders";
}else{
zObjType = "Files";
}
if( zCI && strcmp(zCI,"tip")==0 ){
@ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a>
|
| ︙ | ︙ | |||
898 899 900 901 902 903 904 |
p = sortTreeByMtime(sTree.pFirst);
memset(&sTree, 0, sizeof(sTree));
relinkTree(&sTree, p);
}
for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
const char *zLastClass = p->pSibling==0 ? " last" : "";
if( p->pChild ){
| | | | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 |
p = sortTreeByMtime(sTree.pFirst);
memset(&sTree, 0, sizeof(sTree));
relinkTree(&sTree, p);
}
for(p=sTree.pFirst, nDir=0; p; p=p->pNext){
const char *zLastClass = p->pSibling==0 ? " last" : "";
if( p->pChild ){
const char *zSubdirClass = (int)(p->nFullName)==nD-1 ? " subdir" : "";
@ <li class="dir%s(zSubdirClass)%s(zLastClass)"><div class="filetreeline">
@ %z(href("%s",url_render(&sURI,"name",p->zFullName,0,0)))%h(p->zName)</a>
if( p->mtime>0.0 ){
char *zAge = human_readable_age(rNow - p->mtime);
@ <div class="filetreeage">%s(zAge)</div>
}
@ </div>
if( startExpanded || (int)(p->nFullName)<=nD ){
@ <ul id="dir%d(nDir)">
}else{
@ <ul id="dir%d(nDir)" class="collapsed">
}
nDir++;
}else if( !showDirOnly ){
const char *zFileClass = fileext_class(p->zName);
|
| ︙ | ︙ | |||
1168 1169 1170 1171 1172 1173 1174 |
@ <td>
db_bind_int(&q2, ":mid", mid);
while( db_step(&q2)==SQLITE_ROW ){
const char *zFile = db_column_text(&q2,0);
@ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
if( showId ){
int fid = db_column_int(&q2,1);
| | | | 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 |
@ <td>
db_bind_int(&q2, ":mid", mid);
while( db_step(&q2)==SQLITE_ROW ){
const char *zFile = db_column_text(&q2,0);
@ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
if( showId ){
int fid = db_column_int(&q2,1);
@ (%d(fid))<br>
}else{
@ </a><br>
}
}
db_reset(&q2);
@ </td>
@ <td>
@ %W(zComment)
@ (check-in: %z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
|
| ︙ | ︙ |
Changes to src/cache.c.
| ︙ | ︙ | |||
421 422 423 424 425 426 427 |
" ORDER BY (tm + 3600*min(nRef,48)) DESC"
);
if( pStmt ){
@ <ol>
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const unsigned char *zName = sqlite3_column_text(pStmt,0);
char *zHash = cache_hash_of_key((const char*)zName);
| | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
" ORDER BY (tm + 3600*min(nRef,48)) DESC"
);
if( pStmt ){
@ <ol>
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const unsigned char *zName = sqlite3_column_text(pStmt,0);
char *zHash = cache_hash_of_key((const char*)zName);
@ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br>
@ size: %,lld(sqlite3_column_int64(pStmt,1))
@ hit-count: %d(sqlite3_column_int(pStmt,2))
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
if( zHash ){
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
fossil_free(zHash);
}
|
| ︙ | ︙ |
Changes to src/capabilities.c.
| ︙ | ︙ | |||
321 322 323 324 325 326 327 |
static int done = 0;
Stmt q;
if( done ) return;
db_prepare(&q, "SELECT fullcap(cap) FROM user");
while( db_step(&q)==SQLITE_ROW ){
const char *zCap = db_column_text(&q, 0);
if( zCap==0 || zCap[0]==0 ) continue;
| | | | 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 |
static int done = 0;
Stmt q;
if( done ) return;
db_prepare(&q, "SELECT fullcap(cap) FROM user");
while( db_step(&q)==SQLITE_ROW ){
const char *zCap = db_column_text(&q, 0);
if( zCap==0 || zCap[0]==0 ) continue;
for(i=0; i<(int)(sizeof(aCap)/sizeof(aCap[0])); i++){
if( strchr(zCap, aCap[i].cCap) ) aCap[i].nUser++;
}
}
db_finalize(&q);
done = 1;
}
/*
** Generate HTML that lists all of the capability letters together with
** a brief summary of what each letter means.
*/
void capabilities_table(unsigned mClass){
int i;
if( g.perm.Admin ) capabilities_count();
@ <table>
@ <tbody>
for(i=0; i<(int)(sizeof(aCap)/sizeof(aCap[0])); i++){
int n;
if( (aCap[i].eClass & mClass)==0 ) continue;
@ <tr><th valign="top">%c(aCap[i].cCap)</th>
@ <td>%h(aCap[i].zAbbrev)</td><td>%h(aCap[i].zOneLiner)</td>\
n = aCap[i].nUser;
if( n && g.perm.Admin ){
@ <td><a href="%R/setup_ulist?with=%c(aCap[i].cCap)">\
|
| ︙ | ︙ | |||
385 386 387 388 389 390 391 |
" SELECT 'Public Pages', %Q, 100, %d"
" UNION ALL"
" SELECT 'New User Default', %Q, 110, 1"
" UNION ALL"
" SELECT 'Regular User', fullcap(capunion(cap)), 200, count(*) FROM user"
" WHERE cap NOT GLOB '*[as]*' AND login NOT IN (SELECT id FROM t)"
" UNION ALL"
| | | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
" SELECT 'Public Pages', %Q, 100, %d"
" UNION ALL"
" SELECT 'New User Default', %Q, 110, 1"
" UNION ALL"
" SELECT 'Regular User', fullcap(capunion(cap)), 200, count(*) FROM user"
" WHERE cap NOT GLOB '*[as]*' AND login NOT IN (SELECT id FROM t)"
" UNION ALL"
" SELECT 'Administrator', fullcap(capunion(cap)), 300, count(*) FROM user"
" WHERE cap GLOB '*[as]*'"
" ORDER BY 3 ASC",
zSelfCap, hasPubPages, zSelfCap
);
@ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1">
@ <tr><th> <th>Code<th>Forum<th>Tickets<th>Wiki<th>Chat\
@ <th>Unversioned Content</th></tr>
|
| ︙ | ︙ |
Changes to src/captcha.c.
| ︙ | ︙ | |||
542 543 544 545 546 547 548 | uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed); zCaptcha = captcha_render(zDecoded); @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha"> @ %h(zCaptcha) @ </pre> @ Enter security code shown above: | | | | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
uSeed = captcha_seed();
zDecoded = captcha_decode(uSeed);
zCaptcha = captcha_render(zDecoded);
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
@ %h(zCaptcha)
@ </pre>
@ Enter security code shown above:
@ <input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <input type="text" name="captcha" size=8>
if( showButton ){
@ <input type="submit" value="Submit">
}
@ <br/>\
captcha_speakit_button(uSeed, 0);
@ </td></tr></table></div>
}
|
| ︙ | ︙ | |||
683 684 685 686 687 688 689 |
/*
** WEBPAGE: /captcha-audio
**
** Return a WAV file that pronounces the digits of the captcha that
** is determined by the seed given in the name= query parameter.
*/
void captcha_wav_page(void){
| | | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 |
/*
** WEBPAGE: /captcha-audio
**
** Return a WAV file that pronounces the digits of the captcha that
** is determined by the seed given in the name= query parameter.
*/
void captcha_wav_page(void){
const char *zSeed = PD("name","0");
const char *zDecode = captcha_decode((unsigned int)atoi(zSeed));
Blob audio;
captcha_wav(zDecode, &audio);
cgi_set_content_type("audio/wav");
cgi_set_content(&audio);
}
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
92 93 94 95 96 97 98 |
#if INTERFACE
/*
** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie. PD("x","y")
** does the same except "y" is returned in place of NULL if there is not match.
*/
| | | | | | | | > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#if INTERFACE
/*
** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
** or cookie "x", or NULL if there is no such parameter or cookie. PD("x","y")
** does the same except "y" is returned in place of NULL if there is not match.
*/
#define P(x) cgi_parameter((x),0)
#define PD(x,y) cgi_parameter((x),(y))
#define PT(x) cgi_parameter_trimmed((x),0)
#define PDT(x,y) cgi_parameter_trimmed((x),(y))
#define PB(x) cgi_parameter_boolean(x)
#define PCK(x) cgi_parameter_checked(x,1)
#define PIF(x,y) cgi_parameter_checked(x,y)
#define P_NoBot(x) cgi_parameter_nosql((x),0)
#define PD_NoBot(x,y) cgi_parameter_nosql((x),(y))
/*
** Shortcut for the cgi_printf() routine. Instead of using the
**
** @ ...
**
** notation provided by the translate.c utility, you can also
|
| ︙ | ︙ | |||
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 |
CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
return zValue;
}
}
CGIDEBUG(("no-match [%s]\n", zName));
return zDefault;
}
/*
** Return the value of the first defined query parameter or cookie whose
** name appears in the list of arguments. Or if no parameter is found,
** return NULL.
*/
const char *cgi_coalesce(const char *zName, ...){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
return zValue;
}
}
CGIDEBUG(("no-match [%s]\n", zName));
return zDefault;
}
/*
** Renders the "begone, spider" page and exits.
*/
static void cgi_begone_spider(void){
Blob content = empty_blob;
cgi_set_content(&content);
style_set_current_feature("test");
style_header("Malicious Query Detected");
@ <h2>Begone, Fiend!</h2>
@ <p>This page was generated because Fossil believes it has
@ detected an SQL injection attack. If you believe you are seeing
@ this in error, contact the developers on the Fossil-SCM Forum. Type
@ "fossil-scm forum" into any search engine to locate the Fossil-SCM Forum.
style_finish_page();
cgi_set_status(404,"Robot Attack Detected");
cgi_reply();
exit(0);
}
/*
** If looks_like_sql_injection() returns true for the given string, calls
** cgi_begone_spider() and does not return, else this function has no
** side effects. The range of checks performed by this function may
** be extended in the future.
**
** Checks are omitted for any logged-in user.
**
** This is NOT a defense against SQL injection. Fossil should easily be
** proof against SQL injection without this routine. Rather, this is an
** attempt to avoid denial-of-service caused by persistent spiders that hammer
** the server with dozens or hundreds of SQL injection attempts per second
** against pages (such as /vdiff) that are expensive to compute. In other
** words, this is an effort to reduce the CPU load imposed by malicious
** spiders. It is not an effect defense against SQL injection vulnerabilities.
*/
void cgi_value_spider_check(const char *zTxt){
if( g.zLogin==0 && looks_like_sql_injection(zTxt) ){
cgi_begone_spider();
}
}
/*
** A variant of cgi_parameter() with the same semantics except that if
** cgi_parameter(zName,zDefault) returns a value other than zDefault
** then it passes that value to cgi_value_spider_check().
*/
const char *cgi_parameter_nosql(const char *zName, const char *zDefault){
const char *zTxt = cgi_parameter(zName, zDefault);
if( zTxt!=zDefault ){
cgi_value_spider_check(zTxt);
}
return zTxt;
}
/*
** Return the value of the first defined query parameter or cookie whose
** name appears in the list of arguments. Or if no parameter is found,
** return NULL.
*/
const char *cgi_coalesce(const char *zName, ...){
|
| ︙ | ︙ | |||
1687 1688 1689 1690 1691 1692 1693 |
const char *zName = aParamQP[i].zName;
if( !showAll ){
if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
}
switch( eDest ){
case 0: {
| | | 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 |
const char *zName = aParamQP[i].zName;
if( !showAll ){
if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
}
switch( eDest ){
case 0: {
cgi_printf("%h = %h <br>\n", zName, aParamQP[i].zValue);
break;
}
case 1: {
fossil_trace("%s = %s\n", zName, aParamQP[i].zValue);
break;
}
case 2: {
|
| ︙ | ︙ | |||
1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 |
cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
}else if( fossil_strcmp(zFieldName,"referer:")==0 ){
cgi_setenv("HTTP_REFERER", zVal);
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
cgi_setenv("HTTP_USER_AGENT", zVal);
}else if( fossil_strcmp(zFieldName,"authorization:")==0 ){
cgi_setenv("HTTP_AUTHORIZATION", zVal);
}else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){
const char *zIpAddr = cgi_accept_forwarded_for(zVal);
if( zIpAddr!=0 ){
g.zIpAddr = fossil_strdup(zIpAddr);
cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr);
}
}else if( fossil_strcmp(zFieldName,"range:")==0 ){
| > > | 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 |
cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
}else if( fossil_strcmp(zFieldName,"referer:")==0 ){
cgi_setenv("HTTP_REFERER", zVal);
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
cgi_setenv("HTTP_USER_AGENT", zVal);
}else if( fossil_strcmp(zFieldName,"authorization:")==0 ){
cgi_setenv("HTTP_AUTHORIZATION", zVal);
}else if( fossil_strcmp(zFieldName,"accept-language:")==0 ){
cgi_setenv("HTTP_ACCEPT_LANGUAGE", zVal);
}else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){
const char *zIpAddr = cgi_accept_forwarded_for(zVal);
if( zIpAddr!=0 ){
g.zIpAddr = fossil_strdup(zIpAddr);
cgi_replace_parameter("REMOTE_ADDR", g.zIpAddr);
}
}else if( fossil_strcmp(zFieldName,"range:")==0 ){
|
| ︙ | ︙ | |||
2340 2341 2342 2343 2344 2345 2346 |
int iPort = mnPort;
while( iPort<=mxPort ){
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
if( zIpAddr ){
inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
| | | 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 |
int iPort = mnPort;
while( iPort<=mxPort ){
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
if( zIpAddr ){
inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
if( inaddr.sin_addr.s_addr == INADDR_NONE ){
fossil_fatal("not a valid IP address: %s", zIpAddr);
}
}else if( flags & HTTP_SERVER_LOCALHOST ){
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}else{
inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
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 |
int fUnicode; /* return value of could_be_utf16() */
int fBinary; /* does the blob content appear to be binary? */
int lookFlags; /* output flags from looks_like_utf8/utf16() */
int fHasAnyCr; /* the blob contains one or more CR chars */
int fHasLoneCrOnly; /* all detected line endings are CR only */
int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */
int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */
char *zMsg; /* Warning message */
Blob fname; /* Relative pathname of the file */
static int allOk = 0; /* Set to true to disable this routine */
if( allOk ) return 0;
if( sizeOk ){
fUnicode = could_be_utf16(pContent, &bReverse);
if( fUnicode ){
lookFlags = looks_like_utf16(pContent, bReverse, LOOK_NUL);
}else{
lookFlags = looks_like_utf8(pContent, LOOK_NUL);
if( !(lookFlags & LOOK_BINARY) && invalid_utf8(pContent) ){
fHasInvalidUtf8 = 1;
}
}
fHasAnyCr = (lookFlags & LOOK_CR);
fBinary = (lookFlags & LOOK_BINARY);
fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR);
fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF);
}else{
fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0;
| > > > > | < < | 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 |
int fUnicode; /* return value of could_be_utf16() */
int fBinary; /* does the blob content appear to be binary? */
int lookFlags; /* output flags from looks_like_utf8/utf16() */
int fHasAnyCr; /* the blob contains one or more CR chars */
int fHasLoneCrOnly; /* all detected line endings are CR only */
int fHasCrLfOnly; /* all detected line endings are CR/LF pairs */
int fHasInvalidUtf8 = 0;/* contains invalid UTF-8 */
int fHasNul; /* contains NUL chars? */
int fHasLong; /* overly long line? */
char *zMsg; /* Warning message */
Blob fname; /* Relative pathname of the file */
static int allOk = 0; /* Set to true to disable this routine */
if( allOk ) return 0;
if( sizeOk ){
fUnicode = could_be_utf16(pContent, &bReverse);
if( fUnicode ){
lookFlags = looks_like_utf16(pContent, bReverse, LOOK_NUL);
}else{
lookFlags = looks_like_utf8(pContent, LOOK_NUL);
if( !(lookFlags & LOOK_BINARY) && invalid_utf8(pContent) ){
fHasInvalidUtf8 = 1;
}
}
fHasAnyCr = (lookFlags & LOOK_CR);
fBinary = (lookFlags & LOOK_BINARY);
fHasLoneCrOnly = ((lookFlags & LOOK_EOL) == LOOK_LONE_CR);
fHasCrLfOnly = ((lookFlags & LOOK_EOL) == LOOK_CRLF);
fHasNul = (lookFlags & LOOK_NUL);
fHasLong = (lookFlags & LOOK_LONG);
}else{
fUnicode = fHasAnyCr = fBinary = fHasInvalidUtf8 = 0;
fHasLoneCrOnly = fHasCrLfOnly = fHasNul = fHasLong = 0;
}
if( !sizeOk || fUnicode || fHasAnyCr || fBinary || fHasInvalidUtf8 ){
const char *zWarning = 0;
const char *zDisable = 0;
const char *zConvert = "c=convert/";
const char *zIn = "in";
Blob ans;
char cReply;
if( fBinary ){
if( binOk ){
return 0; /* We don't want binary warnings for this file. */
}
if( !fHasNul && fHasLong ){
zWarning = "long lines";
zConvert = ""; /* We cannot convert overlong lines. */
}else{
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
277 278 279 280 281 282 283 284 |
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(1, 0);
if( !noCompress ){
fossil_print("Extra delta compression... "); fflush(stdout);
| > > | > > > > > | > | 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 |
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(1, 0);
if( !noCompress ){
int nDelta = 0;
i64 nByte;
fossil_print("Extra delta compression... "); fflush(stdout);
nByte = extra_deltification(&nDelta);
if( nDelta==1 ){
fossil_print("1 delta saves %,lld bytes\n", nByte);
}else if( nDelta>1 ){
fossil_print("%d deltas save %,lld bytes\n", nDelta, nByte);
}else{
fossil_print("none found\n");
}
}
db_end_transaction(0);
fossil_print("Vacuuming the database... "); fflush(stdout);
if( db_int(0, "PRAGMA page_count")>1000
&& db_int(0, "PRAGMA page_size")<8192 ){
db_multi_exec("PRAGMA page_size=8192;");
}
|
| ︙ | ︙ |
Changes to src/color.c.
| ︙ | ︙ | |||
159 160 161 162 163 164 165 |
@ %h(zBr) - %s(hash_color(zBr)) -
@ Omnes nos quasi oves erravimus unusquisque in viam
@ suam declinavit.</p>
cnt++;
}
}
if( cnt ){
| | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
@ %h(zBr) - %s(hash_color(zBr)) -
@ Omnes nos quasi oves erravimus unusquisque in viam
@ suam declinavit.</p>
cnt++;
}
}
if( cnt ){
@ <hr>
}
@ <form method="POST">
@ <p>Enter candidate branch names below and see them displayed in their
@ default background colors above.</p>
for(i=0; i<10; i++){
sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
zBr = P(zNm);
@ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br>
}
@ <input type="submit" value="Submit">
@ <input type="submit" name="rand" value="Random">
@ </form>
style_finish_page();
}
|
Changes to src/comformat.c.
| ︙ | ︙ | |||
211 212 213 214 215 216 217 | int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */ if( !zLine ) return; if( lineChars<=0 ) return; #if 0 assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */ assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */ #endif | | | | | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */
if( !zLine ) return;
if( lineChars<=0 ) return;
#if 0
assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */
assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */
#endif
if( indent>(int)sizeof(zBuf)-6 ){
/* Limit initial indent to fit output buffer. */
indent = sizeof(zBuf)-6;
}
comment_calc_indent(zLine, indent, trimCrLf, trimSpace, &index);
if( indent>0 ){
for(i=0; i<indent; i++){
zBuf[iBuf++] = ' ';
}
}
if( origIndent>(int)sizeof(zBuf)-6 ){
/* Limit line indent to fit output buffer. */
origIndent = sizeof(zBuf)-6;
}
maxChars = lineChars;
for(;;){
int useChars = 1;
char c = zLine[index];
/* Flush the output buffer if there's no space left for at least one more
** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces,
** a new line, and a terminating NULL. */
if( iBuf>(int)sizeof(zBuf)-origIndent-6 ){
zBuf[iBuf]=0;
iBuf=0;
fossil_print("%s", zBuf);
}
if( c==0 ){
break;
}else{
|
| ︙ | ︙ | |||
346 347 348 349 350 351 352 |
comment_set_maxchars(indent, &maxChars);
}
if( zText==0 ) zText = "(NULL)";
if( maxChars<=0 ){
maxChars = strlen(zText);
}
/* Ensure the buffer can hold the longest-possible UTF-8 sequences. */
| | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
comment_set_maxchars(indent, &maxChars);
}
if( zText==0 ) zText = "(NULL)";
if( maxChars<=0 ){
maxChars = strlen(zText);
}
/* Ensure the buffer can hold the longest-possible UTF-8 sequences. */
if( maxChars >= ((int)sizeof(zBuffer)/4-1) ){
zBuf = fossil_malloc(maxChars*4+1);
}else{
zBuf = zBuffer;
}
for(;;){
while( fossil_isspace(zText[0]) ){ zText++; }
if( zText[0]==0 ){
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
812 813 814 815 816 817 818 | ** converted to undeltaed text before the aSrc[bestSrc]->rid delta is ** created, in order to prevent a delta loop. ** ** If either rid or aSrc[i] contain less than 50 bytes, or if the ** resulting delta does not achieve a compression of at least 25% ** the rid is left untouched. ** | | > | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
** converted to undeltaed text before the aSrc[bestSrc]->rid delta is
** created, in order to prevent a delta loop.
**
** If either rid or aSrc[i] contain less than 50 bytes, or if the
** resulting delta does not achieve a compression of at least 25%
** the rid is left untouched.
**
** Return the number of bytes by which the storage associated with rid
** is reduced. A return of 0 means no new deltification occurs.
*/
int content_deltify(int rid, int *aSrc, int nSrc, int force){
int s;
Blob data; /* Content of rid */
Blob src; /* Content of aSrc[i] */
Blob delta; /* Delta from aSrc[i] to rid */
Blob bestDelta; /* Best delta seen so far */
|
| ︙ | ︙ | |||
901 902 903 904 905 906 907 908 909 910 911 912 913 |
if( bestSrc>0 ){
Stmt s1, s2; /* Statements used to create the delta */
blob_compress(&bestDelta, &bestDelta);
db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid);
db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, bestSrc);
db_bind_blob(&s1, ":data", &bestDelta);
db_begin_transaction();
db_exec(&s1);
db_exec(&s2);
db_end_transaction(0);
db_finalize(&s1);
db_finalize(&s2);
verify_before_commit(rid);
| > | | 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
if( bestSrc>0 ){
Stmt s1, s2; /* Statements used to create the delta */
blob_compress(&bestDelta, &bestDelta);
db_prepare(&s1, "UPDATE blob SET content=:data WHERE rid=%d", rid);
db_prepare(&s2, "REPLACE INTO delta(rid,srcid)VALUES(%d,%d)", rid, bestSrc);
db_bind_blob(&s1, ":data", &bestDelta);
db_begin_transaction();
rc = db_int(0, "SELECT length(content) FROM blob WHERE rid=%d", rid);
db_exec(&s1);
db_exec(&s2);
db_end_transaction(0);
db_finalize(&s1);
db_finalize(&s2);
verify_before_commit(rid);
rc -= blob_size(&bestDelta);
}
blob_reset(&data);
blob_reset(&bestDelta);
return rc;
}
/*
|
| ︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 |
fossil_print(" %d/%d\r", n1, total);
fflush(stdout);
if( size<0 ){
fossil_print("skip phantom %d %s\n", rid, zUuid);
continue; /* Ignore phantoms */
}
content_get(rid, &content);
| | | | 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 |
fossil_print(" %d/%d\r", n1, total);
fflush(stdout);
if( size<0 ){
fossil_print("skip phantom %d %s\n", rid, zUuid);
continue; /* Ignore phantoms */
}
content_get(rid, &content);
if( (int)blob_size(&content)!=size ){
fossil_print("size mismatch on artifact %d: wanted %d but got %d\n",
rid, size, blob_size(&content));
nErr++;
}
if( !hname_verify_hash(&content, zUuid, nUuid) ){
fossil_print("wrong hash on artifact %d\n",rid);
nErr++;
}
if( bParse && looks_like_control_artifact(&content) ){
Blob err;
int i, n;
char *z;
Manifest *p;
char zFirstLine[400];
blob_zero(&err);
z = blob_buffer(&content);
n = blob_size(&content);
for(i=0; i<n && z[i] && z[i]!='\n' && i<(int)sizeof(zFirstLine)-1; i++){}
memcpy(zFirstLine, z, i);
zFirstLine[i] = 0;
p = manifest_parse(&content, 0, &err);
if( p==0 ){
fossil_print("manifest_parse failed for %s:\n%s\n",
zUuid, blob_str(&err));
if( strncmp(blob_str(&err), "line 1:", 7)==0 ){
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
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 |
** and located at the root of the local copy of the source tree.
**
*/
#include "config.h"
#if defined(_WIN32)
# if USE_SEE
# include <windows.h>
# endif
#else
# include <pwd.h>
#endif
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
# define SQLITE_HAS_CODEC
#endif
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include "db.h"
#if INTERFACE
/*
** An single SQL statement is represented as an instance of the following
** structure.
*/
struct Stmt {
Blob sql; /* The SQL for this statement */
sqlite3_stmt *pStmt; /* The results of sqlite3_prepare_v2() */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
** and located at the root of the local copy of the source tree.
**
*/
#include "config.h"
#if defined(_WIN32)
# if USE_SEE
# include <windows.h>
# define GETPID (int)GetCurrentProcessId
# endif
#else
# include <pwd.h>
# if USE_SEE
# define GETPID getpid
# endif
#endif
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
# define SQLITE_HAS_CODEC
#endif
#if USE_SEE && defined(__linux__)
# include <sys/uio.h>
#endif
#include <sqlite3.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
#if USE_SEE
#if defined(_WIN32)
typedef DWORD PID_T;
#else
typedef pid_t PID_T;
#endif
#endif
#include "db.h"
#if INTERFACE
/*
** Type definitions used for handling the saved encryption key for SEE.
*/
#if !defined(_WIN32)
typedef void *LPVOID;
typedef size_t SIZE_T;
#endif
/*
** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
*/
#define SEE_KEY_READ ((int)0)
#define SEE_KEY_WRITE ((int)1)
#define SEE_KEY_ZERO ((int)2)
/*
** An single SQL statement is represented as an instance of the following
** structure.
*/
struct Stmt {
Blob sql; /* The SQL for this statement */
sqlite3_stmt *pStmt; /* The results of sqlite3_prepare_v2() */
|
| ︙ | ︙ | |||
1537 1538 1539 1540 1541 1542 1543 | ** This is a pointer to the saved database encryption key string. */ static char *zSavedKey = 0; /* ** This is the size of the saved database encryption key, in bytes. */ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
** This is a pointer to the saved database encryption key string.
*/
static char *zSavedKey = 0;
/*
** This is the size of the saved database encryption key, in bytes.
*/
static size_t savedKeySize = 0;
/*
** This function returns non-zero if there is a saved database encryption
** key available.
*/
int db_have_saved_encryption_key(){
return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
}
/*
** This function returns non-zero if the specified database encryption key
** is valid.
*/
int db_is_valid_saved_encryption_key(const char *p, size_t n){
if( p==0 ) return 0;
if( n==0 ) return 0;
if( p[0]==0 ) return 0;
return 1;
}
/*
** This function returns the saved database encryption key -OR- zero if
** no database encryption key is saved.
*/
char *db_get_saved_encryption_key(){
return zSavedKey;
}
/*
** This function returns the size of the saved database encryption key
** -OR- zero if no database encryption key is saved.
*/
size_t db_get_saved_encryption_key_size(){
return savedKeySize;
}
/*
** This function arranges for the saved database encryption key buffer
** to be allocated and then sets up the environment variable to allow
** a child process to initialize it with the actual database encryption
** key.
*/
void db_setup_for_saved_encryption_key(){
void *p = NULL;
size_t n = 0;
size_t pageSize = 0;
Blob pidKey;
assert( !db_have_saved_encryption_key() );
db_unsave_encryption_key();
fossil_get_page_size(&pageSize);
assert( pageSize>0 );
p = fossil_secure_alloc_page(&n);
assert( p!=NULL );
assert( n==pageSize );
blob_zero(&pidKey);
blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
zSavedKey = p;
savedKeySize = n;
}
/*
** This function arranges for the database encryption key to be securely
** saved in non-pagable memory (on platforms where this is possible).
*/
static void db_save_encryption_key(
Blob *pKey
){
void *p = NULL;
size_t n = 0;
size_t pageSize = 0;
size_t blobSize = 0;
assert( !db_have_saved_encryption_key() );
blobSize = blob_size(pKey);
if( blobSize==0 ) return;
fossil_get_page_size(&pageSize);
assert( pageSize>0 );
if( blobSize>pageSize ){
fossil_panic("key blob too large: %u versus %u", blobSize, pageSize);
}
|
| ︙ | ︙ | |||
1597 1598 1599 1600 1601 1602 1603 | savedKeySize = 0; } /* ** This function sets the saved database encryption key to the specified ** string value, allocating or freeing the underlying memory if needed. */ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < > > | > | | | | > | < < < < | | | | | > | | | | > | > > > > > > > > > > > > > > > > > > > | | | > > > < > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 |
savedKeySize = 0;
}
/*
** This function sets the saved database encryption key to the specified
** string value, allocating or freeing the underlying memory if needed.
*/
static void db_set_saved_encryption_key(
Blob *pKey
){
if( zSavedKey!=NULL ){
size_t blobSize = blob_size(pKey);
if( blobSize==0 ){
db_unsave_encryption_key();
}else{
if( blobSize>savedKeySize ){
fossil_panic("key blob too large: %u versus %u",
blobSize, savedKeySize);
}
fossil_secure_zero(zSavedKey, savedKeySize);
memcpy(zSavedKey, blob_str(pKey), blobSize);
}
}else{
db_save_encryption_key(pKey);
}
}
/*
** WEBPAGE: setseekey
**
** Sets the sets the saved database encryption key to one that gets passed
** via the "key" query string parameter. If the saved database encryption
** key has already been set, does nothing. This web page does not produce
** any output on success or failure. No permissions are required and none
** are checked (partially due to lack of encrypted database access).
**
** Query parameters:
**
** key The string to set as the saved database encryption
** key.
*/
void db_set_see_key_page(void){
Blob key;
const char *zKey;
if( db_have_saved_encryption_key() ){
fossil_trace("SEE: encryption key was already set\n");
return;
}
zKey = P("key");
blob_init(&key, 0, 0);
if( zKey!=0 ){
PID_T processId;
blob_set(&key, zKey);
db_set_saved_encryption_key(&key);
processId = db_maybe_handle_saved_encryption_key_for_process(
SEE_KEY_WRITE
);
fossil_trace("SEE: set encryption key for process %lu, length %u\n",
(unsigned long)processId, blob_size(&key));
}else{
fossil_trace("SEE: no encryption key specified\n");
}
blob_reset(&key);
}
/*
** WEBPAGE: unsetseekey
**
** Sets the saved database encryption key to zeros in the current and parent
** Fossil processes. This web page does not produce any output on success
** or failure. Setup permission is required.
*/
void db_unset_see_key_page(void){
PID_T processId;
login_check_credentials();
if( !g.perm.Setup ){ login_needed(0); return; }
processId = db_maybe_handle_saved_encryption_key_for_process(
SEE_KEY_ZERO
);
fossil_trace("SEE: unset encryption key for process %lu\n",
(unsigned long)processId);
}
/*
** This function reads the saved database encryption key from the
** specified Fossil parent process. This is only necessary (or
** functional) on Windows or Linux.
*/
static void db_read_saved_encryption_key_from_process(
PID_T processId, /* Identifier for Fossil parent process. */
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
SIZE_T nSize /* Size of saved key buffer in the parent process. */
){
void *p = NULL;
size_t n = 0;
size_t pageSize = 0;
fossil_get_page_size(&pageSize);
assert( pageSize>0 );
if( nSize>pageSize ){
fossil_panic("key too large: %u versus %u", nSize, pageSize);
}
p = fossil_secure_alloc_page(&n);
assert( p!=NULL );
assert( n==pageSize );
assert( n>=nSize );
{
#if defined(_WIN32)
HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
if( hProcess!=NULL ){
SIZE_T nRead = 0;
if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
CloseHandle(hProcess);
if( nRead==nSize ){
db_unsave_encryption_key();
zSavedKey = p;
savedKeySize = n;
}else{
fossil_secure_free_page(p, n);
fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
nRead, nSize, pAddress, processId);
}
}else{
CloseHandle(hProcess);
fossil_secure_free_page(p, n);
fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
pAddress, processId, GetLastError());
}
}else{
fossil_secure_free_page(p, n);
fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
}
#elif defined(__linux__)
ssize_t nRead;
struct iovec liov = {0};
struct iovec riov = {0};
liov.iov_base = p;
liov.iov_len = n;
riov.iov_base = pAddress;
riov.iov_len = nSize;
nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
if( nRead==nSize ){
db_unsave_encryption_key();
zSavedKey = p;
savedKeySize = n;
}else{
fossil_secure_free_page(p, n);
fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
nRead, nSize, pAddress, (unsigned long)processId);
}
#else
fossil_secure_free_page(p, n);
fossil_trace("db_read_saved_encryption_key_from_process unsupported");
#endif
}
}
/*
** This function writes the saved database encryption key into the
** specified Fossil parent process. This is only necessary (or
** functional) on Windows or Linux.
*/
static void db_write_saved_encryption_key_to_process(
PID_T processId, /* Identifier for Fossil parent process. */
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
SIZE_T nSize /* Size of saved key buffer in the parent process. */
){
void *p = db_get_saved_encryption_key();
size_t n = db_get_saved_encryption_key_size();
size_t pageSize = 0;
fossil_get_page_size(&pageSize);
assert( pageSize>0 );
if( nSize>pageSize ){
fossil_panic("key too large: %u versus %u", nSize, pageSize);
}
assert( p!=NULL );
assert( n==pageSize );
assert( n>=nSize );
{
#if defined(_WIN32)
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
FALSE, processId);
if( hProcess!=NULL ){
SIZE_T nWrite = 0;
if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
CloseHandle(hProcess);
if( nWrite!=nSize ){
fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
nWrite, nSize, pAddress, processId);
}
}else{
CloseHandle(hProcess);
fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
pAddress, processId, GetLastError());
}
}else{
fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
}
#elif defined(__linux__)
ssize_t nWrite;
struct iovec liov = {0};
struct iovec riov = {0};
liov.iov_base = p;
liov.iov_len = n;
riov.iov_base = pAddress;
riov.iov_len = nSize;
nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
if( nWrite!=nSize ){
fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
nWrite, nSize, pAddress, (unsigned long)processId);
}
#else
fossil_trace("db_write_saved_encryption_key_to_process unsupported");
#endif
}
}
/*
** This function zeros the saved database encryption key in the specified
** Fossil parent process. This is only necessary (or functional) on
** Windows or Linux.
*/
static void db_zero_saved_encryption_key_in_process(
PID_T processId, /* Identifier for Fossil parent process. */
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
SIZE_T nSize /* Size of saved key buffer in the parent process. */
){
void *p = NULL;
size_t n = 0;
size_t pageSize = 0;
fossil_get_page_size(&pageSize);
assert( pageSize>0 );
if( nSize>pageSize ){
fossil_panic("key too large: %u versus %u", nSize, pageSize);
}
p = fossil_secure_alloc_page(&n);
assert( p!=NULL );
assert( n==pageSize );
assert( n>=nSize );
{
#if defined(_WIN32)
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
FALSE, processId);
if( hProcess!=NULL ){
SIZE_T nWrite = 0;
if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
CloseHandle(hProcess);
fossil_secure_free_page(p, n);
if( nWrite!=nSize ){
fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
nWrite, nSize, pAddress, processId);
}
}else{
CloseHandle(hProcess);
fossil_secure_free_page(p, n);
fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
pAddress, processId, GetLastError());
}
}else{
fossil_secure_free_page(p, n);
fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
}
#elif defined(__linux__)
ssize_t nWrite;
struct iovec liov = {0};
struct iovec riov = {0};
liov.iov_base = p;
liov.iov_len = n;
riov.iov_base = pAddress;
riov.iov_len = nSize;
nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
if( nWrite!=nSize ){
fossil_secure_free_page(p, n);
fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
nWrite, nSize, pAddress, (unsigned long)processId);
}
#else
fossil_secure_free_page(p, n);
fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
#endif
}
}
/*
** This function evaluates the specified TH1 script and attempts to parse
** its result as a colon-delimited triplet containing a process identifier,
** address, and size (in bytes) of the database encryption key. This is
** only necessary (or functional) on Windows or Linux.
*/
static PID_T db_handle_saved_encryption_key_for_process_via_th1(
const char *zConfig, /* The TH1 script to evaluate. */
int eType /* Non-zero to write key to parent process -OR-
* zero to read it from the parent process. */
){
PID_T processId = 0;
int rc;
char *zResult;
char *zPwd = file_getcwd(0, 0);
Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
rc = Th_Eval(g.interp, 0, zConfig, -1);
zResult = (char*)Th_GetResult(g.interp, 0);
if( rc!=TH_OK ){
fossil_fatal("script for pid key failed: %s", zResult);
}
if( zResult ){
LPVOID pAddress = NULL;
SIZE_T nSize = 0;
parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
if( eType==SEE_KEY_READ ){
db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
}else if( eType==SEE_KEY_WRITE ){
db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
}else if( eType==SEE_KEY_ZERO ){
db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
}else{
fossil_panic("unsupported SEE key operation %d", eType);
}
}
file_chdir(zPwd, 0);
fossil_free(zPwd);
return processId;
}
/*
** This function sets the saved database encryption key to one that gets
** read from the specified Fossil parent process, if applicable. This is
** only necessary (or functional) on Windows or Linux.
*/
PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
PID_T processId = 0;
g.zPidKey = find_option("usepidkey",0,1);
if( !g.zPidKey ){
g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
}
if( g.zPidKey ){
LPVOID pAddress = NULL;
SIZE_T nSize = 0;
parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
if( eType==SEE_KEY_READ ){
db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
}else if( eType==SEE_KEY_WRITE ){
db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
}else if( eType==SEE_KEY_ZERO ){
db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
}else{
fossil_panic("unsupported SEE key operation %d", eType);
}
}else{
const char *zSeeDbConfig = find_option("seedbcfg",0,1);
if( !zSeeDbConfig ){
zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
}
if( zSeeDbConfig ){
processId = db_handle_saved_encryption_key_for_process_via_th1(
zSeeDbConfig, eType
);
}
}
return processId;
}
#endif /* USE_SEE */
/*
** If the database file zDbFile has a name that suggests that it is
** encrypted, then prompt for the database encryption key and return it
** in the blob *pKey. Or, if the encryption key has previously been
** requested, just return a copy of the previous result. The blob in
|
| ︙ | ︙ | |||
1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 |
db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
);
if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
db_add_aux_functions(db);
re_add_sql_func(db); /* The REGEXP operator */
foci_register(db); /* The "files_of_checkin" virtual table */
sqlite3_set_authorizer(db, db_top_authorizer, db);
return db;
}
/*
** Detaches the zLabel database.
*/
| > | 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
);
if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
db_add_aux_functions(db);
re_add_sql_func(db); /* The REGEXP operator */
foci_register(db); /* The "files_of_checkin" virtual table */
sqlite3_set_authorizer(db, db_top_authorizer, db);
db_register_fts5(db) /* in search.c */;
return db;
}
/*
** Detaches the zLabel database.
*/
|
| ︙ | ︙ | |||
2202 2203 2204 2205 2206 2207 2208 |
g.zLocalDbName = mprintf("%s", zPwd);
zPwd[n] = 0;
while( n>0 && zPwd[n-1]=='/' ){
n--;
zPwd[n] = 0;
}
g.zLocalRoot = mprintf("%s/", zPwd);
| | | 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 |
g.zLocalDbName = mprintf("%s", zPwd);
zPwd[n] = 0;
while( n>0 && zPwd[n-1]=='/' ){
n--;
zPwd[n] = 0;
}
g.zLocalRoot = mprintf("%s/", zPwd);
g.localOpen = db_lget_int("checkout", -1);
db_open_repository(zDbName);
return 1;
}
}
if( bRootOnly ) break;
n--;
while( n>1 && zPwd[n]!='/' ){ n--; }
|
| ︙ | ︙ | |||
2725 2726 2727 2728 2729 2730 2731 |
db_multi_exec(
"UPDATE user SET cap='s', pw=%Q"
" WHERE login=%Q", fossil_random_password(10), zUser
);
if( !setupUserOnly ){
db_multi_exec(
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
| | | 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 |
db_multi_exec(
"UPDATE user SET cap='s', pw=%Q"
" WHERE login=%Q", fossil_random_password(10), zUser
);
if( !setupUserOnly ){
db_multi_exec(
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('anonymous',hex(randomblob(8)),'hz','Anon');"
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('nobody','','gjorz','Nobody');"
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('developer','','ei','Dev');"
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('reader','','kptw','Reader');"
);
|
| ︙ | ︙ | |||
3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 |
blob_append(&versionedPathname, ".no-warn", -1);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
noWarn = 1;
}
}
blob_reset(&versionedPathname);
if( found ){
blob_trim(&setting); /* Avoid non-obvious problems with line endings
** on boolean properties */
zVersionedSetting = fossil_strdup(blob_str(&setting));
}
blob_reset(&setting);
/* Store result in cache, which can be the value or 0 if not found */
cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
| > | 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 |
blob_append(&versionedPathname, ".no-warn", -1);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
noWarn = 1;
}
}
blob_reset(&versionedPathname);
if( found ){
blob_strip_comment_lines(&setting, &setting);
blob_trim(&setting); /* Avoid non-obvious problems with line endings
** on boolean properties */
zVersionedSetting = fossil_strdup(blob_str(&setting));
}
blob_reset(&setting);
/* Store result in cache, which can be the value or 0 if not found */
cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry));
|
| ︙ | ︙ | |||
4155 4156 4157 4158 4159 4160 4161 | ** If backoffice-logfile is not an empty string and is a valid ** filename, then a one-line message is appended to that file ** every time the backoffice runs. This can be used for debugging, ** to ensure that backoffice is running appropriately. */ /* ** SETTING: binary-glob width=40 versionable block-text | | | | > | 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 | ** If backoffice-logfile is not an empty string and is a valid ** filename, then a one-line message is appended to that file ** every time the backoffice runs. This can be used for debugging, ** to ensure that backoffice is running appropriately. */ /* ** SETTING: binary-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** that should be treated as "binary" for committing and merging ** purposes. Example: *.jpg,*.png The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__) /* ** SETTING: case-sensitive boolean default=off ** If TRUE, the files whose names differ only in case ** are considered distinct. If FALSE files whose names ** differ only in case are the same file. Defaults to |
| ︙ | ︙ | |||
4179 4180 4181 4182 4183 4184 4185 | ** are considered distinct. If FALSE files whose names ** differ only in case are the same file. Defaults to ** TRUE for unix and FALSE for Cygwin, Mac and Windows. */ #endif /* ** SETTING: clean-glob width=40 versionable block-text | | | < | > | 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 | ** are considered distinct. If FALSE files whose names ** differ only in case are the same file. Defaults to ** TRUE for unix and FALSE for Cygwin, Mac and Windows. */ #endif /* ** SETTING: clean-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** that the "clean" command will delete without prompting or allowing ** undo. Example: *.a,*.o,*.so The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits ** with gpg. When disabled, commits will be unsigned. */ /* |
| ︙ | ︙ | |||
4214 4215 4216 4217 4218 4219 4220 | ** printing format (i.e. set to "0", or a combination not including "1"). ** ** Note: The options for timeline comments displayed on the web UI can be ** configured through the /setup_timeline web page. */ /* ** SETTING: crlf-glob width=40 versionable block-text | | | > | | 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 | ** printing format (i.e. set to "0", or a combination not including "1"). ** ** Note: The options for timeline comments displayed on the web UI can be ** configured through the /setup_timeline web page. */ /* ** SETTING: crlf-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** in which it is allowed to have CR, CR+LF or mixed line endings, ** suppressing Fossil's normal warning about this. Set it to "*" to ** disable CR+LF checking entirely. Example: *.md,*.txt ** The crnl-glob setting is a compatibility alias. */ /* ** SETTING: crnl-glob width=40 versionable block-text ** This is an alias for the crlf-glob setting. */ /* |
| ︙ | ︙ | |||
4262 4263 4264 4265 4266 4267 4268 | /* ** SETTING: editor width=32 sensitive ** The value is an external command that will launch the ** text editor command used for check-in comments. */ /* ** SETTING: empty-dirs width=40 versionable block-text | | | | | | < | | | > | | | > | | 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 | /* ** SETTING: editor width=32 sensitive ** The value is an external command that will launch the ** text editor command used for check-in comments. */ /* ** SETTING: empty-dirs width=40 versionable block-text ** The value is a list of pathnames parsed according to the same rules as ** the *-glob settings. On update and checkout commands, if no directory ** exists with that name, an empty directory will be be created, even if ** it must create one or more parent directories. */ /* ** SETTING: encoding-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "commit" command will ignore when issuing warnings about text files ** that may use another encoding than ASCII or UTF-8. Set to "*" to disable ** encoding checking. Example: *.md,*.txt The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS) /* ** SETTING: exec-rel-paths boolean default=on ** When executing certain external commands (e.g. diff and ** gdiff), use relative paths. */ #endif #if !defined(FOSSIL_ENABLE_EXEC_REL_PATHS) /* ** SETTING: exec-rel-paths boolean default=off ** When executing certain external commands (e.g. diff and ** gdiff), use relative paths. */ #endif /* ** SETTING: fileedit-glob width=40 block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** which are allowed to be edited using the /fileedit page. An empty list ** suppresses the feature. Example: *.md,*.txt The parsing rules are ** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will |
| ︙ | ︙ | |||
4337 4338 4339 4340 4341 4342 4343 | /* ** SETTING: https-login boolean default=off ** If true, then the Fossil web server will redirect unencrypted ** login screen requests to HTTPS. */ /* ** SETTING: ignore-glob width=40 versionable block-text | | < | < | > | | | > | 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 | /* ** SETTING: https-login boolean default=off ** If true, then the Fossil web server will redirect unencrypted ** login screen requests to HTTPS. */ /* ** SETTING: ignore-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "add", "addremove", "clean", and "extras" commands will ignore. ** Example: *.log,notes.txt The parsing rules are complex; see ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: keep-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files that ** the "clean" command must not delete. Example: build/precious.exe ** The parsing rules are complex; see ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: localauth boolean default=off ** If enabled, require that HTTP connections from the loopback ** address (127.0.0.1) be authenticated by password. If false, ** some HTTP requests might be granted full "Setup" user ** privileges without having to present login credentials. |
| ︙ | ︙ | |||
4743 4744 4745 4746 4747 4748 4749 |
fossil_fatal("no such setting: %s", zName);
}
if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
fossil_fatal("cannot set 'manifest' globally");
}
if( unsetFlag || g.argc==4 ){
int isManifest = fossil_strcmp(pSetting->name, "manifest")==0;
| | | 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 |
fossil_fatal("no such setting: %s", zName);
}
if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
fossil_fatal("cannot set 'manifest' globally");
}
if( unsetFlag || g.argc==4 ){
int isManifest = fossil_strcmp(pSetting->name, "manifest")==0;
if( n!=(int)strlen(pSetting[0].name) && pSetting[1].name &&
fossil_strncmp(pSetting[1].name, zName, n)==0 ){
Blob x;
int i;
blob_init(&x,0,0);
for(i=0; pSetting[i].name; i++){
if( fossil_strncmp(pSetting[i].name,zName,n)!=0 ) break;
blob_appendf(&x, " %s", pSetting[i].name);
|
| ︙ | ︙ | |||
4914 4915 4916 4917 4918 4919 4920 |
/*
** Make sure the adminlog table exists. Create it if it does not
*/
void create_admin_log_table(void){
static int once = 0;
if( once ) return;
| > | | | | | | | | | | > | 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 |
/*
** Make sure the adminlog table exists. Create it if it does not
*/
void create_admin_log_table(void){
static int once = 0;
if( once ) return;
if( !db_table_exists("repository","admin_log") ){
once = 1;
db_multi_exec(
"CREATE TABLE repository.admin_log(\n"
" id INTEGER PRIMARY KEY,\n"
" time INTEGER, -- Seconds since 1970\n"
" page TEXT, -- path of page\n"
" who TEXT, -- User who made the change\n"
" what TEXT -- What changed\n"
")"
);
}
}
/*
** Write a message into the admin_event table, if admin logging is
** enabled via the admin-log configuration option.
*/
void admin_log(const char *zFormat, ...){
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
color: red;
}
pre.textPlain {
white-space: pre-wrap;
word-wrap: break-word;
}
.statistics-report-graph-line {
background-color: #446979;
}
.statistics-report-table-events th {
padding: 0 1em 0 1em;
}
.statistics-report-table-events td {
padding: 0.1em 1em 0.1em 1em;
}
| > > > > > | 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 |
color: red;
}
pre.textPlain {
white-space: pre-wrap;
word-wrap: break-word;
}
.statistics-report-graph-line {
border: 2px solid #446979;
background-color: #446979;
}
.statistics-report-graph-extra {
border: 2px dashed #446979;
border-left-style: none;
}
.statistics-report-table-events th {
padding: 0 1em 0 1em;
}
.statistics-report-table-events td {
padding: 0.1em 1em 0.1em 1em;
}
|
| ︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 |
display: grid; place-items: center;
and does not require setting display:inline-block on the relevant
child items, but caniuse.com/css-grid suggests that some
still-seemingly-legitimate browsers don't support grid mode. */
}
div.pikchr-wrapper.center > div.pikchr-svg {
}
div.pikchr-wrapper.center:not(.source) > pre.pikchr-src,
div.pikchr-wrapper.center:not(.source) > div.pikchr-svg,
/* ^^^ Centered non-source-view elements */
div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src,
div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg
/* ^^^ Centered inline-source-view elements */{
| > | 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 |
display: grid; place-items: center;
and does not require setting display:inline-block on the relevant
child items, but caniuse.com/css-grid suggests that some
still-seemingly-legitimate browsers don't support grid mode. */
}
div.pikchr-wrapper.center > div.pikchr-svg {
width: 100%/*necessary for Chrome!*/;
}
div.pikchr-wrapper.center:not(.source) > pre.pikchr-src,
div.pikchr-wrapper.center:not(.source) > div.pikchr-svg,
/* ^^^ Centered non-source-view elements */
div.pikchr-wrapper.center.source.source-inline > pre.pikchr-src,
div.pikchr-wrapper.center.source.source-inline > div.pikchr-svg
/* ^^^ Centered inline-source-view elements */{
|
| ︙ | ︙ |
Changes to src/delta.c.
| ︙ | ︙ | |||
202 203 204 205 206 207 208 |
return v;
}
/*
** Return the number digits in the base-64 representation of a positive integer
*/
static int digit_count(int v){
| | > | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
return v;
}
/*
** Return the number digits in the base-64 representation of a positive integer
*/
static int digit_count(int v){
unsigned int i;
int x;
for(i=1, x=64; v>=x; i++, x <<= 6){}
return i;
}
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
#else
|
| ︙ | ︙ | |||
377 378 379 380 381 382 383 | /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; collide = fossil_malloc( nHash*2*sizeof(int) ); memset(collide, -1, nHash*2*sizeof(int)); landmark = &collide[nHash]; | | | | 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 |
/* Compute the hash table used to locate matching sections in the
** source file.
*/
nHash = lenSrc/NHASH;
collide = fossil_malloc( nHash*2*sizeof(int) );
memset(collide, -1, nHash*2*sizeof(int));
landmark = &collide[nHash];
for(i=0; i<(int)lenSrc-NHASH; i+=NHASH){
int hv = hash_once(&zSrc[i]) % nHash;
collide[i/NHASH] = landmark[hv];
landmark[hv] = i/NHASH;
}
/* Begin scanning the target file and generating copy commands and
** literal sections of the delta.
*/
base = 0; /* We have already generated everything before zOut[base] */
while( base+NHASH<(int)lenOut ){
int iSrc, iBlock;
unsigned int bestCnt, bestOfst=0, bestLitsz=0;
hash_init(&h, &zOut[base]);
i = 0; /* Trying to match a landmark against zOut[base+i] */
bestCnt = 0;
while( 1 ){
int hv;
|
| ︙ | ︙ | |||
447 448 449 450 451 452 453 |
cnt = j+k+1;
litsz = i-k; /* Number of bytes of literal text before the copy */
DEBUG2( printf("MATCH %d bytes at %d: [%s] litsz=%d\n",
cnt, ofst, print16(&zSrc[ofst]), litsz); )
/* sz will hold the number of bytes needed to encode the "insert"
** command and the copy command, not counting the "insert" text */
sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
| | | 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
cnt = j+k+1;
litsz = i-k; /* Number of bytes of literal text before the copy */
DEBUG2( printf("MATCH %d bytes at %d: [%s] litsz=%d\n",
cnt, ofst, print16(&zSrc[ofst]), litsz); )
/* sz will hold the number of bytes needed to encode the "insert"
** command and the copy command, not counting the "insert" text */
sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3;
if( cnt>=sz && cnt>(int)bestCnt ){
/* Remember this match only if it is the best so far and it
** does not increase the file size */
bestCnt = cnt;
bestOfst = iSrc-k;
bestLitsz = litsz;
DEBUG2( printf("... BEST SO FAR\n"); )
}
|
| ︙ | ︙ | |||
479 480 481 482 483 484 485 |
}
base += bestCnt;
putInt(bestCnt, &zDelta);
*(zDelta++) = '@';
putInt(bestOfst, &zDelta);
DEBUG2( printf("copy %d bytes from %d\n", bestCnt, bestOfst); )
*(zDelta++) = ',';
| | | | | 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 |
}
base += bestCnt;
putInt(bestCnt, &zDelta);
*(zDelta++) = '@';
putInt(bestOfst, &zDelta);
DEBUG2( printf("copy %d bytes from %d\n", bestCnt, bestOfst); )
*(zDelta++) = ',';
if( (int)(bestOfst + bestCnt -1) > lastRead ){
lastRead = bestOfst + bestCnt - 1;
DEBUG2( printf("lastRead becomes %d\n", lastRead); )
}
bestCnt = 0;
break;
}
/* If we reach this point, it means no match is found so far */
if( base+i+NHASH>=(int)lenOut ){
/* We have reached the end of the file and have not found any
** matches. Do an "insert" for everything that does not match */
putInt(lenOut-base, &zDelta);
*(zDelta++) = ':';
memcpy(zDelta, &zOut[base], lenOut-base);
zDelta += lenOut-base;
base = lenOut;
break;
}
/* Advance the hash by one character. Keep looking for a match */
hash_next(&h, zOut[base+i+NHASH]);
i++;
}
}
/* Output a final "insert" record to get all the text at the end of
** the file that does not match anything in the source file.
*/
if( base<(int)lenOut ){
putInt(lenOut-base, &zDelta);
*(zDelta++) = ':';
memcpy(zDelta, &zOut[base], lenOut-base);
zDelta += lenOut-base;
}
/* Output the final checksum record. */
putInt(checksum(zOut, lenOut), &zDelta);
|
| ︙ | ︙ | |||
597 598 599 600 601 602 603 |
zDelta++; lenDelta--;
DEBUG1( printf("COPY %d from %d\n", cnt, ofst); )
total += cnt;
if( total>limit ){
/* ERROR: copy exceeds output file size */
return -1;
}
| | | | 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 |
zDelta++; lenDelta--;
DEBUG1( printf("COPY %d from %d\n", cnt, ofst); )
total += cnt;
if( total>limit ){
/* ERROR: copy exceeds output file size */
return -1;
}
if( (int)(ofst+cnt) > lenSrc ){
/* ERROR: copy extends past end of input */
return -1;
}
memcpy(zOut, &zSrc[ofst], cnt);
zOut += cnt;
break;
}
case ':': {
zDelta++; lenDelta--;
total += cnt;
if( total>limit ){
/* ERROR: insert command gives an output larger than predicted */
return -1;
}
DEBUG1( printf("INSERT %d\n", cnt); )
if( (int)cnt>lenDelta ){
/* ERROR: insert count exceeds size of delta */
return -1;
}
memcpy(zOut, zDelta, cnt);
zOut += cnt;
zDelta += cnt;
lenDelta -= cnt;
|
| ︙ | ︙ | |||
686 687 688 689 690 691 692 |
zDelta++; lenDelta--;
nCopy += cnt;
break;
}
case ':': {
zDelta++; lenDelta--;
nInsert += cnt;
| | | 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 |
zDelta++; lenDelta--;
nCopy += cnt;
break;
}
case ':': {
zDelta++; lenDelta--;
nInsert += cnt;
if( (int)cnt>lenDelta ){
/* ERROR: insert count exceeds size of delta */
return -1;
}
zDelta += cnt;
lenDelta -= cnt;
break;
}
|
| ︙ | ︙ |
Changes to src/deltacmd.c.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
blob_delta_create(&orig, &target, &delta);
| | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
if( blob_read_from_file(&orig, g.argv[2], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(&target, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
blob_delta_create(&orig, &target, &delta);
if( blob_write_to_file(&delta, g.argv[4])<(int)blob_size(&delta) ){
fossil_fatal("cannot write %s", g.argv[4]);
}
blob_reset(&orig);
blob_reset(&target);
blob_reset(&delta);
}
|
| ︙ | ︙ | |||
158 159 160 161 162 163 164 |
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(&delta, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
blob_init(&target, 0, 0);
blob_delta_apply(&orig, &delta, &target);
| | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(&delta, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
blob_init(&target, 0, 0);
blob_delta_apply(&orig, &delta, &target);
if( blob_write_to_file(&target, g.argv[4])<(int)blob_size(&target) ){
fossil_fatal("cannot write %s", g.argv[4]);
}
blob_reset(&orig);
blob_reset(&target);
blob_reset(&delta);
}
|
| ︙ | ︙ |
Changes to src/descendants.c.
| ︙ | ︙ | |||
625 626 627 628 629 630 631 | ** many descenders to (off-screen) parents. */ tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL; if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; if( fUBg ) tmFlags |= TIMELINE_UCOLOR; www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); db_finalize(&q); | | | 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | ** many descenders to (off-screen) parents. */ tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL; if( fNg==0 ) tmFlags |= TIMELINE_GRAPH; if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR; if( fUBg ) tmFlags |= TIMELINE_UCOLOR; www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0); db_finalize(&q); @ <br> style_finish_page(); } #if INTERFACE /* Flag parameters to compute_uses_file() */ #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */ |
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
407 408 409 410 411 412 413 | 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 */ | < | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | 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 */ 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; |
| ︙ | ︙ | |||
2068 2069 2070 2071 2072 2073 2074 |
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 */
| < | 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 |
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 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;
|
| ︙ | ︙ | |||
2150 2151 2152 2153 2154 2155 2156 |
}
}
/* Compute the lowest-cost path back through the matrix */
i = nRight;
j = nLeft;
k = (nRight+1)*(nLeft+1)-1;
| < < < | 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 |
}
}
/* Compute the lowest-cost path back through the matrix */
i = nRight;
j = nLeft;
k = (nRight+1)*(nLeft+1)-1;
while( i+j>0 ){
unsigned char c = aM[k];
if( c>=3 ){
assert( i>0 && j>0 );
i--;
j--;
aM[k] = 3;
}else if( c==2 ){
assert( i>0 );
i--;
}else{
assert( j>0 );
j--;
|
| ︙ | ︙ | |||
2224 2225 2226 2227 2228 2229 2230 |
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 */
| | | 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 |
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]<(int)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 ){
|
| ︙ | ︙ | |||
2254 2255 2256 2257 2258 2259 2260 |
continue;
}
}
/* Figure out how many lines of A and B are to be displayed
** for this change block.
*/
| | | | | 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 |
continue;
}
}
/* Figure out how many lines of A and B are to be displayed
** for this change block.
*/
if( R[r]>(int)nContext ){
skip = R[r] - nContext;
}else{
skip = 0;
}
/* Show the initial common area */
a += skip;
b += skip;
m = R[r] - skip;
if( r ) skip -= nContext;
if( skip>0 ){
if( skip<(int)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; (int)j<skip; j++){
pBuilder->xCommon(pBuilder, &A[a+j-skip]);
}
}else{
pBuilder->xSkip(pBuilder, skip, 0);
}
}
for(j=0; j<m; j++){
|
| ︙ | ︙ | |||
2302 2303 2304 2305 2306 2307 2308 |
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++){
| | | 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 |
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( (int)j<nAlign );
switch( alignment[j] ){
case 1: {
/* Delete one line from the left */
pBuilder->xDelete(pBuilder, &A[a]);
ma--;
a++;
break;
|
| ︙ | ︙ | |||
2344 2345 2346 2347 2348 2349 2350 |
a++;
mb--;
b++;
break;
}
}
}
| | | | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 |
a++;
mb--;
b++;
break;
}
}
}
assert( nAlign==(int)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]>(int)nContext ){
pBuilder->xSkip(pBuilder, R[r] - nContext, 1);
}
pBuilder->xEnd(pBuilder);
}
/*
|
| ︙ | ︙ | |||
2889 2890 2891 2892 2893 2894 2895 |
** 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;
| | | | 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 |
** 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<0 ? 0x7ffffff : n;
}else{
return dflt;
}
}
/*
** Extract the width of columns for side-by-side diff. Supply an
|
| ︙ | ︙ | |||
3150 3151 3152 3153 3154 3155 3156 |
}
/* 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;
}
| | | 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 |
}
/* 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;
|
| ︙ | ︙ | |||
3639 3640 3641 3642 3643 3644 3645 |
for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
@ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
@ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
@ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
@ </span>
}
@ </ol>
| | | 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 |
for(p=ann.aVers, i=0; i<ann.nVers; i++, p++){
@ <li><span style='background-color:%s(p->zBgColor);'>%s(p->zDate)
@ check-in %z(href("%R/info/%!S",p->zMUuid))%S(p->zMUuid)</a>
@ artifact %z(href("%R/artifact/%!S",p->zFUuid))%S(p->zFUuid)</a>
@ </span>
}
@ </ol>
@ <hr>
@ </div>
if( !ann.bMoreToDo ){
assert( ann.origId==0 ); /* bMoreToDo always set for a point-to-point */
@ <h2>Origin for each line in
@ %z(href("%R/finfo?name=%h&from=%!S", zFilename, zCI))%h(zFilename)</a>
@ from check-in %z(href("%R/info/%!S",zCI))%S(zCI)</a>:</h2>
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
366 367 368 369 370 371 372 |
*/
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 ){
| | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
*/
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
|
| ︙ | ︙ | |||
1074 1075 1076 1077 1078 1079 1080 | ** 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" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" | | > | 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 | ** 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" ** -ci|--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, with ** negative N meaning show all content ** --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 |
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
548 549 550 551 552 553 554 |
/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */
| | | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/
if( useHtml ) fossil_print("<!--\n");
fossil_print("Help text for:\n");
if( mask & CMDFLAG_1ST_TIER ) fossil_print(" * Commands\n");
if( mask & CMDFLAG_2ND_TIER ) fossil_print(" * Auxiliary commands\n");
if( mask & CMDFLAG_ALIAS ) fossil_print(" * Aliases\n");
if( mask & CMDFLAG_TEST ) fossil_print(" * Test commands\n");
if( mask & CMDFLAG_WEBPAGE ) fossil_print(" * Web pages\n");
|
| ︙ | ︙ | |||
703 704 705 706 707 708 709 |
** first 100 characters of the pattern are considered.
*/
static int edit_distance(const char *zA, const char *zB){
int nA = (int)strlen(zA);
int nB = (int)strlen(zB);
int i, j, m;
int p0, p1, c0;
| | | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 |
** first 100 characters of the pattern are considered.
*/
static int edit_distance(const char *zA, const char *zB){
int nA = (int)strlen(zA);
int nB = (int)strlen(zB);
int i, j, m;
int p0, p1, c0;
int a[100] = {0};
static const int incr = 4;
for(j=0; j<nB; j++) a[j] = 1;
for(i=0; i<nA; i++){
p0 = i==0 ? 0 : i*incr-1;
c0 = i*incr;
for(j=0; j<nB; j++){
|
| ︙ | ︙ | |||
857 858 859 860 861 862 863 |
help_to_html(pCmd->zHelp, cgi_output_blob());
@ </div>
}
}
}else{
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */
| | | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 |
help_to_html(pCmd->zHelp, cgi_output_blob());
@ </div>
}
}
}else{
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
style_header("Help");
@ <a name='commands'></a>
@ <h1>Available commands:</h1>
@ <div class="columns" style="column-width: 12ex;">
@ <ul>
/* Fill in help string buckets */
|
| ︙ | ︙ | |||
964 965 966 967 968 969 970 |
** WEBPAGE: test-all-help
**
** Show all help text on a single page. Useful for proof-reading.
*/
void test_all_help_page(void){
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */
| | | 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 |
** WEBPAGE: test-all-help
**
** Show all help text on a single page. Useful for proof-reading.
*/
void test_all_help_page(void){
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help string occurrences */
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help strings->commands*/
Blob buf;
blob_init(&buf,0,0);
style_set_current_feature("test");
style_header("All Help Text");
@ <dl>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
457 458 459 460 461 462 463 |
#endif
z = zName;
for(i=0; zName[i]; i++){
if( zName[i]=='.' ) z = &zName[i+1];
}
len = strlen(z);
| | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
#endif
z = zName;
for(i=0; zName[i]; i++){
if( zName[i]=='.' ) z = &zName[i+1];
}
len = strlen(z);
if( len<(int)sizeof(zSuffix)-1 ){
sqlite3_snprintf(sizeof(zSuffix), zSuffix, "%s", z);
for(i=0; zSuffix[i]; i++) zSuffix[i] = fossil_tolower(zSuffix[i]);
z = mimetype_from_name_custom(zSuffix);
if(z!=0){
return z;
}
first = 0;
|
| ︙ | ︙ |
Changes to src/encode.c.
| ︙ | ︙ | |||
706 707 708 709 710 711 712 |
** Return true if the input string contains only valid base-16 digits.
** If any invalid characters appear in the string, return false.
*/
int validate16(const char *zIn, int nIn){
int i;
if( nIn<0 ) nIn = (int)strlen(zIn);
if( zIn[nIn]==0 ){
| | | 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 |
** Return true if the input string contains only valid base-16 digits.
** If any invalid characters appear in the string, return false.
*/
int validate16(const char *zIn, int nIn){
int i;
if( nIn<0 ) nIn = (int)strlen(zIn);
if( zIn[nIn]==0 ){
return (int)strspn(zIn,"0123456789abcdefABCDEF")==nIn;
}
for(i=0; i<nIn; i++, zIn++){
if( zDecode[zIn[0]&0xff]>63 ){
return zIn[0]==0;
}
}
return 1;
|
| ︙ | ︙ |
Changes to src/event.c.
| ︙ | ︙ | |||
210 211 212 213 214 215 216 |
}else{
@ <div>
}
blob_init(&comment, pTNote->zComment, -1);
wiki_convert(&comment, 0, WIKI_INLINE);
blob_reset(&comment);
@ </div>
| | | | 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 |
}else{
@ <div>
}
blob_init(&comment, pTNote->zComment, -1);
wiki_convert(&comment, 0, WIKI_INLINE);
blob_reset(&comment);
@ </div>
@ </blockquote><hr>
}
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
wiki_convert(&fullbody, 0, 0);
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
cgi_append_content(blob_buffer(&tail), blob_size(&tail));
}else{
@ <pre>
@ %h(blob_str(&fullbody))
@ </pre>
}
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
" FROM tag"
" WHERE tagname GLOB 'event-%q*'",
zId);
attachment_list(zFullId, "<hr><h2>Attachments:</h2><ul>");
document_emit_js();
style_finish_page();
manifest_destroy(pTNote);
}
/*
** Add or update a new tech note to the repository. rid is id of
|
| ︙ | ︙ | |||
511 512 513 514 515 516 517 |
@ </blockquote>
@ <p><b>Page content preview:</b><p>
@ <blockquote>
blob_init(&event, 0, 0);
blob_append(&event, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&event, zMimetype);
| | | | | | | | | | 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 |
@ </blockquote>
@ <p><b>Page content preview:</b><p>
@ <blockquote>
blob_init(&event, 0, 0);
blob_append(&event, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&event, zMimetype);
@ </blockquote><hr>
blob_reset(&event);
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>40 ) n = 40;
@ <form method="post" action="%R/technoteedit"><div>
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zId)">
@ <table border="0" cellspacing="10">
@ <tr><th align="right" valign="top">Timestamp (UTC):</th>
@ <td valign="top">
@ <input type="text" name="t" size="25" value="%h(zETime)">
@ </td></tr>
@ <tr><th align="right" valign="top">Timeline Comment:</th>
@ <td valign="top">
@ <textarea name="c" class="technoteedit" cols="80"
@ rows="3" wrap="virtual">%h(zComment)</textarea>
@ </td></tr>
@ <tr><th align="right" valign="top">Timeline Background Color:</th>
@ <td valign="top">
@ <input type='checkbox' name='newclr'%s(zClrFlag)>
@ Use custom color: \
@ <input type='color' name='clr' value='%s(zClr[0]?zClr:"#c0f0ff")'>
@ </td></tr>
@ <tr><th align="right" valign="top">Tags:</th>
@ <td valign="top">
@ <input type="text" name="g" size="40" value="%h(zTags)">
@ </td></tr>
@ <tr><th align="right" valign="top">\
@ %z(href("%R/markup_help"))Markup Style</a>:</th>
@ <td valign="top">
mimetype_option_menu(zMimetype, "mimetype");
@ </td></tr>
@ <tr><th align="right" valign="top">Page Content:</th>
@ <td valign="top">
@ <textarea name="w" class="technoteedit" cols="80"
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
@ </td></tr>
@ <tr><td colspan="2">
@ <input type="submit" name="cancel" value="Cancel">
@ <input type="submit" name="preview" value="Preview">
if( P("preview") ){
@ <input type="submit" name="submit" value="Submit">
}
@ </td></tr></table>
@ </div></form>
style_finish_page();
}
/*
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
1558 1559 1560 1561 1562 1563 1564 |
db_multi_exec(
"CREATE TEMP TABLE tomirror(objid,mtime,uuid);\n"
"INSERT INTO tomirror "
"SELECT objid, mtime, blob.uuid FROM event, blob\n"
" WHERE type='ci'"
" AND mtime>coalesce((SELECT value FROM mconfig WHERE key='start'),0.0)"
" AND blob.rid=event.objid"
| | > | 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 |
db_multi_exec(
"CREATE TEMP TABLE tomirror(objid,mtime,uuid);\n"
"INSERT INTO tomirror "
"SELECT objid, mtime, blob.uuid FROM event, blob\n"
" WHERE type='ci'"
" AND mtime>coalesce((SELECT value FROM mconfig WHERE key='start'),0.0)"
" AND blob.rid=event.objid"
" AND blob.uuid NOT IN (SELECT uuid FROM mirror.mmark WHERE NOT isfile)"
" AND NOT EXISTS (SELECT 1 FROM private WHERE rid=blob.rid);"
);
nTotal = db_int(0, "SELECT count(*) FROM tomirror");
if( nLimit<nTotal ){
nTotal = nLimit;
}else if( nLimit>nTotal ){
nLimit = nTotal;
}
|
| ︙ | ︙ | |||
1865 1866 1867 1868 1869 1870 1871 |
**
** -q|--quiet No output if there is nothing to report
*/
void gitmirror_command(void){
char *zCmd;
int nCmd;
if( g.argc<3 ){
| | | 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 |
**
** -q|--quiet No output if there is nothing to report
*/
void gitmirror_command(void){
char *zCmd;
int nCmd;
if( g.argc<3 ){
usage("SUBCOMMAND ...");
}
zCmd = g.argv[2];
nCmd = (int)strlen(zCmd);
if( nCmd>2 && strncmp(zCmd,"export",nCmd)==0 ){
gitmirror_export_command();
}else
if( nCmd>2 && strncmp(zCmd,"import",nCmd)==0 ){
|
| ︙ | ︙ |
Changes to src/extcgi.c.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 54 55 56 57 58 59 60 | "FOSSIL_REPOSITORY", "FOSSIL_URI", "FOSSIL_USER", "GATEWAY_INTERFACE", "HTTPS", "HTTP_ACCEPT", /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ "HTTP_COOKIE", "HTTP_HOST", "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_NONE_MATCH", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", | > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | "FOSSIL_REPOSITORY", "FOSSIL_URI", "FOSSIL_USER", "GATEWAY_INTERFACE", "HTTPS", "HTTP_ACCEPT", /* "HTTP_ACCEPT_ENCODING", // omitted from sub-cgi */ "HTTP_ACCEPT_LANGUAGE", "HTTP_COOKIE", "HTTP_HOST", "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_NONE_MATCH", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", |
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
static int isDirWithIndexFile(char **pzPath){
static const char *azIndexNames[] = {
"index.html", "index.wiki", "index.md"
};
int i;
if( file_isdir(*pzPath, ExtFILE)!=1 ) return 0;
if( sqlite3_strglob("*/", *pzPath)!=0 ) return 0;
| | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
static int isDirWithIndexFile(char **pzPath){
static const char *azIndexNames[] = {
"index.html", "index.wiki", "index.md"
};
int i;
if( file_isdir(*pzPath, ExtFILE)!=1 ) return 0;
if( sqlite3_strglob("*/", *pzPath)!=0 ) return 0;
for(i=0; i<(int)(sizeof(azIndexNames)/sizeof(azIndexNames[0])); i++){
char *zNew = mprintf("%s%s", *pzPath, azIndexNames[i]);
if( file_isfile(zNew, ExtFILE) ){
fossil_free(*pzPath);
*pzPath = zNew;
return 1;
}
fossil_free(zNew);
|
| ︙ | ︙ | |||
270 271 272 273 274 275 276 |
char *z = mprintf("fossil version %s", get_version());
if( strncmp(zSrvSw,z,strlen(z)-4)!=0 ){
zSrvSw = mprintf("%z, %s", z, zSrvSw);
}
}
cgi_replace_parameter("SERVER_SOFTWARE", zSrvSw);
cgi_replace_parameter("GATEWAY_INTERFACE","CGI/1.0");
| | | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
char *z = mprintf("fossil version %s", get_version());
if( strncmp(zSrvSw,z,strlen(z)-4)!=0 ){
zSrvSw = mprintf("%z, %s", z, zSrvSw);
}
}
cgi_replace_parameter("SERVER_SOFTWARE", zSrvSw);
cgi_replace_parameter("GATEWAY_INTERFACE","CGI/1.0");
for(i=0; i<(int)(sizeof(azCgiEnv)/sizeof(azCgiEnv[0])); i++){
(void)P(azCgiEnv[i]);
}
fossil_clearenv();
for(i=0; i<(int)(sizeof(azCgiEnv)/sizeof(azCgiEnv[0])); i++){
const char *zVal = P(azCgiEnv[i]);
if( zVal ) fossil_setenv(azCgiEnv[i], zVal);
}
fossil_setenv("HTTP_ACCEPT_ENCODING","");
rc = popen2(zScript, &fdFromChild, &toChild, &pidChild, 1);
if( rc ){
zFailReason = "cannot exec CGI child process";
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ** the target pathname of the symbolic link. ** ** RepoFILE Like SymFILE if allow-symlinks is true, or like ** ExtFILE if allow-symlinks is false. In other words, ** symbolic links are only recognized as something different ** from files or directories if allow-symlinks is true. */ #define ExtFILE 0 /* Always follow symlinks */ #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ #define SymFILE 2 /* Never follow symlinks */ #include <dirent.h> #if defined(_WIN32) # define DIR _WDIR | > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** the target pathname of the symbolic link. ** ** RepoFILE Like SymFILE if allow-symlinks is true, or like ** ExtFILE if allow-symlinks is false. In other words, ** symbolic links are only recognized as something different ** from files or directories if allow-symlinks is true. */ #include <stdlib.h> #define ExtFILE 0 /* Always follow symlinks */ #define RepoFILE 1 /* Follow symlinks if and only if allow-symlinks is OFF */ #define SymFILE 2 /* Never follow symlinks */ #include <dirent.h> #if defined(_WIN32) # define DIR _WDIR |
| ︙ | ︙ | |||
201 202 203 204 205 206 207 |
/*
** Return the mode bits for a file. Return -1 if the file does not
** exist. If zFilename is NULL return the size of the most recently
** stat-ed file.
*/
int file_mode(const char *zFilename, int eFType){
| | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
/*
** Return the mode bits for a file. Return -1 if the file does not
** exist. If zFilename is NULL return the size of the most recently
** stat-ed file.
*/
int file_mode(const char *zFilename, int eFType){
return getStat(zFilename, eFType) ? -1 : (int)(fx.fileStat.st_mode);
}
/*
** Return TRUE if either of the following are true:
**
** (1) zFilename is an ordinary file
**
|
| ︙ | ︙ | |||
240 241 242 243 244 245 246 |
void symlink_create(const char *zTargetFile, const char *zLinkFile){
#if !defined(_WIN32)
if( db_allow_symlinks() ){
int i, nName;
char *zName, zBuf[1000];
nName = strlen(zLinkFile);
| | | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
void symlink_create(const char *zTargetFile, const char *zLinkFile){
#if !defined(_WIN32)
if( db_allow_symlinks() ){
int i, nName;
char *zName, zBuf[1000];
nName = strlen(zLinkFile);
if( nName>=(int)sizeof(zBuf) ){
zName = mprintf("%s", zLinkFile);
}else{
zName = zBuf;
memcpy(zName, zLinkFile, nName+1);
}
nName = file_simplify_name(zName, nName, 0);
for(i=1; i<nName; i++){
|
| ︙ | ︙ | |||
2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 |
*/
void file_test_valid_for_windows(void){
int i;
for(i=2; i<g.argc; i++){
fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
}
}
/*
** Remove surplus "/" characters from the beginning of a full pathname.
** Extra leading "/" characters are benign on unix. But on Windows
** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
** into "C:/fossil/xyz.fossil". Cygwin should behave as Windows here.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 |
*/
void file_test_valid_for_windows(void){
int i;
for(i=2; i<g.argc; i++){
fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
}
}
/*
** Returns non-zero if the specified file extension belongs to a Fossil
** repository file.
*/
int file_is_repository_extension(const char *zPath){
if( fossil_strcmp(zPath, ".fossil")==0 ) return 1;
#if USE_SEE
if( fossil_strcmp(zPath, ".efossil")==0 ) return 1;
#endif
return 0;
}
/*
** Returns non-zero if the specified path appears to match a file extension
** that should belong to a Fossil repository file.
*/
int file_contains_repository_extension(const char *zPath){
if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1;
#if USE_SEE
if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1;
#endif
return 0;
}
/*
** Returns non-zero if the specified path ends with a file extension that
** should belong to a Fossil repository file.
*/
int file_ends_with_repository_extension(const char *zPath, int bQual){
if( bQual ){
if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1;
#if USE_SEE
if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1;
#endif
}else{
if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1;
#if USE_SEE
if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1;
#endif
}
return 0;
}
/*
** Remove surplus "/" characters from the beginning of a full pathname.
** Extra leading "/" characters are benign on unix. But on Windows
** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
** into "C:/fossil/xyz.fossil". Cygwin should behave as Windows here.
*/
|
| ︙ | ︙ |
Changes to src/fileedit.c.
| ︙ | ︙ | |||
615 616 617 618 619 620 621 |
}
}else{
assert(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags);
if(!(LOOK_CRLF & lookNew)){
blob_add_cr(&pCI->fileContent);
}
}
| | | 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 |
}
}else{
assert(CIMINI_CONVERT_EOL_WINDOWS & pCI->flags);
if(!(LOOK_CRLF & lookNew)){
blob_add_cr(&pCI->fileContent);
}
}
if((int)blob_size(&pCI->fileContent)!=oldSize){
rehash = 1;
}
}
if(rehash!=0){
hname_hash(&pCI->fileContent, 0, &pCI->fileHash);
}
}
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
270 271 272 273 274 275 276 | ** Usage: %fossil cat FILENAME ... ?OPTIONS? ** ** Print on standard output the content of one or more files as they exist ** in the repository. The version currently checked out is shown by default. ** Other versions may be specified using the -r option. ** ** Options: | > | | > > > > > > > > > | > | 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 |
** Usage: %fossil cat FILENAME ... ?OPTIONS?
**
** Print on standard output the content of one or more files as they exist
** in the repository. The version currently checked out is shown by default.
** Other versions may be specified using the -r option.
**
** Options:
** -o|--out OUTFILE For exactly one given FILENAME, write to OUTFILE
** -R|--repository REPO Extract artifacts from repository REPO
** -r VERSION The specific check-in containing the file
**
** See also: [[finfo]]
*/
void cat_cmd(void){
int i;
Blob content, fname;
const char *zRev;
const char *zFileName;
db_find_and_open_repository(0, 0);
zRev = find_option("r","r",1);
zFileName = find_option("out","o",1);
/* We should be done with options.. */
verify_all_options();
if ( zFileName && g.argc>3 ){
fossil_fatal("output file can only be given when retrieving a single file");
}
for(i=2; i<g.argc; i++){
file_tree_name(g.argv[i], &fname, 0, 1);
blob_zero(&content);
historical_blob(zRev, blob_str(&fname), &content, 1);
if ( g.argc==3 && zFileName ){
blob_write_to_file(&content, zFileName);
}else{
blob_write_to_file(&content, "-");
}
blob_reset(&fname);
blob_reset(&content);
}
}
/* Values for the debug= query parameter to finfo */
#define FINFO_DEBUG_MLINK 0x01
|
| ︙ | ︙ | |||
679 680 681 682 683 684 685 686 687 688 689 690 691 692 |
fossil_free(zNewName);
}else{
@ <b>Deleted:</b>
}
}
if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
hyperlink_to_version(zUuid);
@ part of check-in \
hyperlink_to_version(zCkin);
}
}
@ %W(zCom)</span>
if( (tmFlags & TIMELINE_COMPACT)!=0 ){
@ <span class='timelineEllipsis' data-id='%d(frid)' \
| > > > > > > > > | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
fossil_free(zNewName);
}else{
@ <b>Deleted:</b>
}
}
if( (tmFlags & TIMELINE_VERBOSE)!=0 && zUuid ){
hyperlink_to_version(zUuid);
if( fShowId ){
int srcId = delta_source_rid(frid);
if( srcId ){
@ (%z(href("%R/deltachain/%d",frid))%d(frid)←%d(srcId)</a>)
}else{
@ (%z(href("%R/deltachain/%d",frid))%d(frid)</a>)
}
}
@ part of check-in \
hyperlink_to_version(zCkin);
}
}
@ %W(zCom)</span>
if( (tmFlags & TIMELINE_COMPACT)!=0 ){
@ <span class='timelineEllipsis' data-id='%d(frid)' \
|
| ︙ | ︙ | |||
706 707 708 709 710 711 712 |
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
@ file: %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
@ [%S(zUuid)]</a>
if( fShowId ){
int srcId = delta_source_rid(frid);
if( srcId>0 ){
| > | | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
@ file: %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
@ [%S(zUuid)]</a>
if( fShowId ){
int srcId = delta_source_rid(frid);
if( srcId>0 ){
@ id: %z(href("%R/deltachain/%d",frid))\
@ %d(frid)←%d(srcId)</a>
}else{
@ id: %z(href("%R/deltachain/%d",frid))%d(frid)</a>
}
}
}
@ check-in: \
hyperlink_to_version(zCkin);
if( fShowId ){
@ (%d(fmid))
|
| ︙ | ︙ | |||
745 746 747 748 749 750 751 |
@ [edit]</a>
}
@ </span></span>
}
if( fDebug & FINFO_DEBUG_MLINK ){
int ii;
char *zAncLink;
| | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
@ [edit]</a>
}
@ </span></span>
}
if( fDebug & FINFO_DEBUG_MLINK ){
int ii;
char *zAncLink;
@ <br>fid=%d(frid) \
@ graph-id=%lld(frid>0?(GraphRowId)frid*(mxfnid+1)+fnid:fpid+1000000000) \
@ pid=%d(fpid) mid=%d(fmid) fnid=%d(fnid) \
@ pfnid=%d(pfnid) mxfnid=%d(mxfnid)
if( nParent>0 ){
@ parents=%lld(aParent[0])
for(ii=1; ii<nParent; ii++){
@ %lld(aParent[ii])
|
| ︙ | ︙ | |||
908 909 910 911 912 913 914 |
/* 7 */ " isaux"
" FROM mlink WHERE mid=%d ORDER BY 1",
mid
);
@ <h1>MLINK table for check-in %h(zCI)</h1>
render_checkin_context(mid, 0, 1, 0);
style_table_sorter();
| | | 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 |
/* 7 */ " isaux"
" FROM mlink WHERE mid=%d ORDER BY 1",
mid
);
@ <h1>MLINK table for check-in %h(zCI)</h1>
render_checkin_context(mid, 0, 1, 0);
style_table_sorter();
@ <hr>
@ <div class='brlist'>
@ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
@ <thead><tr>
@ <th>File</th>
@ <th>Parent<br>Check-in</th>
@ <th>Merge?</th>
@ <th>New</th>
|
| ︙ | ︙ |
Changes to src/foci.c.
| ︙ | ︙ | |||
261 262 263 264 265 266 267 |
fociColumn, /* xColumn - read data */
fociRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
| | | > | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
fociColumn, /* xColumn - read data */
fociRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0 /* xShadowName */
};
sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
return SQLITE_OK;
}
|
Changes to src/forum.c.
| ︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
fossil_print("%d->%d\n", p->fpid, p->pEditTail->fpid);
}else{
fossil_print("%d\n", p->fpid);
}
}
forumthread_delete(pThread);
}
/*
** Render a forum post for display
*/
void forum_render(
const char *zTitle, /* The title. Might be NULL for no title */
const char *zMimetype, /* Mimetype of the message */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
fossil_print("%d->%d\n", p->fpid, p->pEditTail->fpid);
}else{
fossil_print("%d\n", p->fpid);
}
}
forumthread_delete(pThread);
}
/*
** WEBPAGE: forumthreadhashlist
**
** Usage: /forumthreadhashlist/HASH-OF-ROOT
**
** This page (accessibly only to admins) shows a list of all artifacts
** associated with a single forum thread. An admin might copy/paste this
** list into the /shun page in order to shun an entire thread.
*/
void forumthreadhashlist(void){
int fpid;
int froot;
const char *zName = P("name");
ForumThread *pThread;
ForumPost *p;
char *fuuid;
login_check_credentials();
if( !g.perm.Admin ){
return;
}
if( zName==0 ){
webpage_error("Missing \"name=\" query parameter");
}
fpid = symbolic_name_to_rid(zName, "f");
if( fpid<=0 ){
if( fpid==0 ){
webpage_notfound_error("Unknown forum id: \"%s\"", zName);
}else{
ambiguous_page();
}
return;
}
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
if( froot==0 ){
webpage_notfound_error("Not a forum post: \"%s\"", zName);
}
fuuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", froot);
style_set_current_feature("forum");
style_header("Artifacts Of Forum Thread");
@ <h2>
@ Artifacts associated with the forum thread
@ <a href="%R/forumthread/%S(fuuid)">%S(fuuid)</a>:</h2>
@ <pre>
pThread = forumthread_create(froot, 1);
for(p=pThread->pFirst; p; p=p->pNext){
@ %h(p->zUuid)
}
forumthread_delete(pThread);
@ </pre>
style_finish_page();
}
/*
** Render a forum post for display
*/
void forum_render(
const char *zTitle, /* The title. Might be NULL for no title */
const char *zMimetype, /* Mimetype of the message */
|
| ︙ | ︙ | |||
702 703 704 705 706 707 708 |
if( bHist ){
zQuery[i] = i==0 ? '?' : '&'; i++;
zQuery[i++] = 'h';
zQuery[i++] = 'i';
zQuery[i++] = 's';
zQuery[i++] = 't';
}
| | | 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
if( bHist ){
zQuery[i] = i==0 ? '?' : '&'; i++;
zQuery[i++] = 'h';
zQuery[i++] = 'i';
zQuery[i++] = 's';
zQuery[i++] = 't';
}
assert( i<(int)sizeof(zQuery) );
zQuery[i] = 0;
assert( zQuery[0]==0 || zQuery[0]=='?' );
/* Identify which post to display first. If history is shown, start with the
** original, unedited post. Otherwise advance to the post's latest edit. */
if( mode==FD_RAW || mode==FD_SINGLE ){
p = pSelect;
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 919 920 921 922 923 924 925 |
}
if( mode!=FD_HIER ){
style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName,
bUnf ? "&unf" : "", bHist ? "&hist" : "");
}
style_submenu_checkbox("unf", "Unformatted", 0, 0);
style_submenu_checkbox("hist", "History", 0, 0);
/* Display the thread. */
if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist);
/* Emit Forum Javascript. */
builtin_request_js("forum.js");
| > > > | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 |
}
if( mode!=FD_HIER ){
style_submenu_element("Hierarchical", "%R/%s/%s?t=h%s%s", g.zPath, zName,
bUnf ? "&unf" : "", bHist ? "&hist" : "");
}
style_submenu_checkbox("unf", "Unformatted", 0, 0);
style_submenu_checkbox("hist", "History", 0, 0);
if( g.perm.Admin ){
style_submenu_element("Artifacts", "%R/forumthreadhashlist/%t", zName);
}
/* Display the thread. */
if( fossil_strcmp(g.zPath,"forumthread")==0 ) fpid = 0;
forum_display_thread(froot, fpid, mode, autoMode, bUnf, bHist);
/* Emit Forum Javascript. */
builtin_request_js("forum.js");
|
| ︙ | ︙ | |||
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 |
** Return true if the string is white-space only.
*/
static int whitespace_only(const char *z){
if( z==0 ) return 1;
while( z[0] && fossil_isspace(z[0]) ){ z++; }
return z[0]==0;
}
/*
** Add a new Forum Post artifact to the repository.
**
** Return true if a redirect occurs.
*/
static int forum_post(
const char *zTitle, /* Title. NULL for replies */
int iInReplyTo, /* Post replying to. 0 for new threads */
int iEdit, /* Post being edited, or zero for a new post */
const char *zUser, /* Username. NULL means use login name */
const char *zMimetype, /* Mimetype of content. */
| > > > > > > > > > > > > > > > | > | 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 |
** Return true if the string is white-space only.
*/
static int whitespace_only(const char *z){
if( z==0 ) return 1;
while( z[0] && fossil_isspace(z[0]) ){ z++; }
return z[0]==0;
}
/* Flags for use with forum_post() */
#define FPOST_NO_ALERT 1 /* do not send any alerts */
/*
** Return a flags value for use with the final argument to
** forum_post(), extracted from the CGI environment.
*/
static int forum_post_flags(void){
int iPostFlags = 0;
if( g.perm.Debug && P("fpsilent")!=0 ){
iPostFlags |= FPOST_NO_ALERT;
}
return iPostFlags;
}
/*
** Add a new Forum Post artifact to the repository.
**
** Return true if a redirect occurs.
*/
static int forum_post(
const char *zTitle, /* Title. NULL for replies */
int iInReplyTo, /* Post replying to. 0 for new threads */
int iEdit, /* Post being edited, or zero for a new post */
const char *zUser, /* Username. NULL means use login name */
const char *zMimetype, /* Mimetype of content. */
const char *zContent, /* Content */
int iFlags /* FPOST_xyz flag values */
){
char *zDate;
char *zI;
char *zG;
int iBasis;
Blob x, cksum, formatCheck, errMsg;
Manifest *pPost;
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 |
@ <div class='debug'>
@ This is the artifact that would have been generated:
@ <pre>%h(blob_str(&x))</pre>
@ </div>
blob_reset(&x);
return 0;
}else{
| | > | > > > > | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 |
@ <div class='debug'>
@ This is the artifact that would have been generated:
@ <pre>%h(blob_str(&x))</pre>
@ </div>
blob_reset(&x);
return 0;
}else{
int nrid;
db_begin_transaction();
nrid = wiki_put(&x, iEdit>0 ? iEdit : 0, forum_need_moderation());
blob_reset(&x);
if( (iFlags & FPOST_NO_ALERT)!=0 ){
alert_unqueue('f', nrid);
}
db_commit_transaction();
cgi_redirectf("%R/forumpost/%S", rid_to_uuid(nrid));
return 1;
}
}
/*
** Paint the form elements for entering a Forum post
|
| ︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 |
static void forum_from_line(void){
if( login_is_nobody() ){
@ From: anonymous<br>
}else{
@ From: %h(login_name())<br>
}
}
/*
** WEBPAGE: forume1
**
** Start a new forum thread.
*/
void forumnew_page(void){
const char *zTitle = PDT("title","");
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
const char *zContent = PDT("content","");
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
if( P("submit") && cgi_csrf_safe(1) ){
| > > > > > > > > > > > > > > > > > > | > < < < | < < < < < < < < | 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 |
static void forum_from_line(void){
if( login_is_nobody() ){
@ From: anonymous<br>
}else{
@ From: %h(login_name())<br>
}
}
static void forum_render_debug_options(void){
if( g.perm.Debug ){
/* Give extra control over the post to users with the special
* Debug capability, which includes Admin and Setup users */
@ <div class="debug">
@ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
@ Dry run</label>
@ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \
@ Require moderator approval</label>
@ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
@ Show query parameters</label>
@ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \
@ Do not sent notification emails</label>
@ </div>
}
}
/*
** WEBPAGE: forume1
**
** Start a new forum thread.
*/
void forumnew_page(void){
const char *zTitle = PDT("title","");
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
const char *zContent = PDT("content","");
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
if( P("submit") && cgi_csrf_safe(1) ){
if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent,
forum_post_flags()) ) return;
}
if( P("preview") && !whitespace_only(zContent) ){
@ <h1>Preview:</h1>
forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
}
style_set_current_feature("forum");
style_header("New Forum Thread");
@ <form action="%R/forume1" method="POST">
@ <h1>New Thread:</h1>
forum_from_line();
forum_post_widget(zTitle, zMimetype, zContent);
@ <input type="submit" name="preview" value="Preview">
if( P("preview") && !whitespace_only(zContent) ){
@ <input type="submit" name="submit" value="Submit">
}else{
@ <input type="submit" name="submit" value="Submit" disabled>
}
forum_render_debug_options();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** WEBPAGE: forume2
|
| ︙ | ︙ | |||
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 |
const char *zMimetype = 0;
const char *zContent = 0;
const char *zTitle = 0;
char *zDate = 0;
const char *zFpid = PD("fpid","");
int isCsrfSafe;
int isDelete = 0;
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
fpid = symbolic_name_to_rid(zFpid, "f");
if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){
webpage_error("Missing or invalid fpid query parameter");
}
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){
webpage_error("fpid does not appear to be a forum post: \"%d\"", fpid);
}
if( P("cancel") ){
cgi_redirectf("%R/forumpost/%S",P("fpid"));
return;
}
isCsrfSafe = cgi_csrf_safe(1);
| > > > > > > | < | 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 |
const char *zMimetype = 0;
const char *zContent = 0;
const char *zTitle = 0;
char *zDate = 0;
const char *zFpid = PD("fpid","");
int isCsrfSafe;
int isDelete = 0;
int bSameUser; /* True if author is also the reader */
int bPrivate; /* True if post is private (not yet moderated) */
login_check_credentials();
if( !g.perm.WrForum ){
login_needed(g.anon.WrForum);
return;
}
fpid = symbolic_name_to_rid(zFpid, "f");
if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){
webpage_error("Missing or invalid fpid query parameter");
}
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
if( froot==0 || (pRootPost = manifest_get(froot, CFTYPE_FORUM, 0))==0 ){
webpage_error("fpid does not appear to be a forum post: \"%d\"", fpid);
}
if( P("cancel") ){
cgi_redirectf("%R/forumpost/%S",P("fpid"));
return;
}
isCsrfSafe = cgi_csrf_safe(1);
bPrivate = content_is_private(fpid);
bSameUser = login_is_individual()
&& fossil_strcmp(pPost->zUser, g.zLogin)==0;
if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){
if( g.perm.ModForum && P("approve") ){
const char *zUserToTrust;
moderation_approve('f', fpid);
if( g.perm.AdminForum
&& PB("trust")
&& (zUserToTrust = P("trustuser"))!=0
){
db_unprotect(PROTECT_USER);
|
| ︙ | ︙ | |||
1266 1267 1268 1269 1270 1271 1272 |
&& isCsrfSafe
&& (zContent = PDT("content",""))!=0
&& (!whitespace_only(zContent) || isDelete)
){
int done = 1;
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
if( P("reply") ){
| | > | > | 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 |
&& isCsrfSafe
&& (zContent = PDT("content",""))!=0
&& (!whitespace_only(zContent) || isDelete)
){
int done = 1;
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
if( P("reply") ){
done = forum_post(0, fpid, 0, 0, zMimetype, zContent,
forum_post_flags());
}else if( P("edit") || isDelete ){
done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent,
forum_post_flags());
}else{
webpage_error("Missing 'reply' query parameter");
}
if( done ) return;
}
if( isDelete ){
zMimetype = "text/x-fossil-wiki";
|
| ︙ | ︙ | |||
1353 1354 1355 1356 1357 1358 1359 |
if( !isDelete ){
@ <input type="submit" name="preview" value="Preview">
}
@ <input type="submit" name="cancel" value="Cancel">
if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
@ <input type="submit" name="submit" value="Submit">
}
| < < | < < < < < < < < | 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 |
if( !isDelete ){
@ <input type="submit" name="preview" value="Preview">
}
@ <input type="submit" name="cancel" value="Cancel">
if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
@ <input type="submit" name="submit" value="Submit">
}
forum_render_debug_options();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** WEBPAGE: forummain
|
| ︙ | ︙ | |||
1385 1386 1387 1388 1389 1390 1391 |
**
** n=N The number of threads to show on each page
** x=X Skip the first X threads
** s=Y Search for term Y.
*/
void forum_main_page(void){
Stmt q;
| | > > | 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 |
**
** n=N The number of threads to show on each page
** x=X Skip the first X threads
** s=Y Search for term Y.
*/
void forum_main_page(void){
Stmt q;
int iLimit = 0, iOfst, iCnt;
int srchFlags;
const int isSearch = P("s")!=0;
char const *zLimit = 0;
login_check_credentials();
srchFlags = search_restrict(SRCH_FORUM);
if( !g.perm.RdForum ){
login_needed(g.anon.RdForum);
return;
}
style_set_current_feature("forum");
|
| ︙ | ︙ | |||
1416 1417 1418 1419 1420 1421 1422 |
if( (srchFlags & SRCH_FORUM)!=0 ){
if( search_screen(SRCH_FORUM, 0) ){
style_submenu_element("Recent Threads","%R/forum");
style_finish_page();
return;
}
}
| > > > | > > > > > > > > > > | 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 |
if( (srchFlags & SRCH_FORUM)!=0 ){
if( search_screen(SRCH_FORUM, 0) ){
style_submenu_element("Recent Threads","%R/forum");
style_finish_page();
return;
}
}
cookie_read_parameter("n","forum-n");
zLimit = P("n");
if( zLimit!=0 ){
iLimit = atoi(zLimit);
if( iLimit>=0 && P("udc")!=0 ){
cookie_write_parameter("n","forum-n",0);
}
}
if( iLimit<=0 ){
cgi_replace_query_parameter("n", fossil_strdup("25"))
/*for the sake of Max, below*/;
iLimit = 25;
}
style_submenu_entry("n","Max:",4,0);
iOfst = atoi(PD("x","0"));
iCnt = 0;
if( db_table_exists("repository","forumpost") ){
db_prepare(&q,
"WITH thread(age,duration,cnt,root,last) AS ("
" SELECT"
" julianday('now') - max(fmtime),"
|
| ︙ | ︙ |
Changes to src/glob.c.
| ︙ | ︙ | |||
87 88 89 90 91 92 93 |
struct Glob {
int nPattern; /* Number of patterns */
char **azPattern; /* Array of pointers to patterns */
};
#endif /* INTERFACE */
/*
| | | | | > | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
struct Glob {
int nPattern; /* Number of patterns */
char **azPattern; /* Array of pointers to patterns */
};
#endif /* INTERFACE */
/*
** zPatternList is a comma- or whitespace-separated list of glob patterns.
** Parse that list and use it to create a new Glob object.
**
** Elements of the glob list may be optionally enclosed in single- or
** double-quotes. This allows commas and whitespace to be part of a
** glob pattern.
**
** Leading and trailing spaces on glob patterns are ignored unless quoted.
**
** An empty or null pattern list results in a null glob, which will
** match nothing.
*/
Glob *glob_create(const char *zPatternList){
int nList; /* Size of zPatternList in bytes */
int i; /* Loop counters */
|
| ︙ | ︙ | |||
124 125 126 127 128 129 130 |
delimiter = z[0];
z++;
}else{
delimiter = ',';
}
p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) );
p->azPattern[p->nPattern++] = z;
| | | < | > | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
delimiter = z[0];
z++;
}else{
delimiter = ',';
}
p->azPattern = fossil_realloc(p->azPattern, (p->nPattern+1)*sizeof(char*) );
p->azPattern[p->nPattern++] = z;
/* Find the next delimiter (or the end of the string). */
for(i=0; z[i] && z[i]!=delimiter &&
!(delimiter==',' && fossil_isspace(z[i])); i++){
/* keep looking for the end of the glob pattern */
}
if( z[i]==0 ) break;
z[i] = 0;
z += i+1;
}
return p;
}
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
const char *zProjectCode = 0;
if( g.url.flags & URL_USE_PARENT ){
zProjectCode = db_get("parent-project-code", 0);
}else{
zProjectCode = db_get("project-code", 0);
}
zPw = sha1_shared_secret(zPw, zLogin, zProjectCode);
| | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
const char *zProjectCode = 0;
if( g.url.flags & URL_USE_PARENT ){
zProjectCode = db_get("parent-project-code", 0);
}else{
zProjectCode = db_get("project-code", 0);
}
zPw = sha1_shared_secret(zPw, zLogin, zProjectCode);
if( g.url.pwConfig!=0 && (g.url.flags & URL_REMEMBER_PW)!=0 ){
char *x = obscure(zPw);
db_set(g.url.pwConfig/*works-like:"x"*/, x, 0);
fossil_free(x);
}
fossil_free(g.url.passwd);
g.url.passwd = fossil_strdup(zPw);
}
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
539 540 541 542 543 544 545 |
/* MMNNFFPPS */
#if OPENSSL_VERSION_NUMBER >= 0x010000000
x = X509_digest(cert, EVP_sha256(), md, &mdLength);
#else
x = X509_digest(cert, EVP_sha1(), md, &mdLength);
#endif
if( x ){
| | | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
/* MMNNFFPPS */
#if OPENSSL_VERSION_NUMBER >= 0x010000000
x = X509_digest(cert, EVP_sha256(), md, &mdLength);
#else
x = X509_digest(cert, EVP_sha1(), md, &mdLength);
#endif
if( x ){
unsigned j;
for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){
zHash[j*2] = "0123456789abcdef"[md[j]>>4];
zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf];
}
zHash[j*2] = 0;
}
|
| ︙ | ︙ |
Changes to src/http_transport.c.
| ︙ | ︙ | |||
80 81 82 83 84 85 86 |
** Check zFossil to see if it is a reasonable "fossil" command to
** run on the server. Do not allow an attacker to substitute something
** like "/bin/rm".
*/
static int is_safe_fossil_command(const char *zFossil){
static const char *const azSafe[] = { "*/fossil", "*/fossil.exe", "*/echo" };
int i;
| | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
** Check zFossil to see if it is a reasonable "fossil" command to
** run on the server. Do not allow an attacker to substitute something
** like "/bin/rm".
*/
static int is_safe_fossil_command(const char *zFossil){
static const char *const azSafe[] = { "*/fossil", "*/fossil.exe", "*/echo" };
int i;
for(i=0; i<(int)(sizeof(azSafe)/sizeof(azSafe[0])); i++){
if( sqlite3_strglob(azSafe[i], zFossil)==0 ) return 1;
if( strcmp(azSafe[i]+2, zFossil)==0 ) return 1;
}
return 0;
}
/*
|
| ︙ | ︙ |
Changes to src/import.c.
| ︙ | ︙ | |||
595 596 597 598 599 600 601 |
** tag to the new commit. However, if there are multiple instances
** of pattern B with the same TAGNAME, then only put the tag on the
** last commit that holds that tag.
**
** None of the above is explained in the git-fast-export
** documentation. We had to figure it out via trial and error.
*/
| | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
** tag to the new commit. However, if there are multiple instances
** of pattern B with the same TAGNAME, then only put the tag on the
** last commit that holds that tag.
**
** None of the above is explained in the git-fast-export
** documentation. We had to figure it out via trial and error.
*/
for(i=5; i<(int)strlen(zRefName) && zRefName[i]!='/'; i++){}
gg.tagCommit = strncmp(&zRefName[5], "tags", 4)==0; /* pattern B */
if( zRefName[i+1]!=0 ) zRefName += i+1;
if( fossil_strcmp(zRefName, "master")==0 ) zRefName = ggit.zMasterName;
gg.zBranch = fossil_strdup(zRefName);
gg.fromLoaded = 0;
}else
if( strncmp(zLine, "tag ", 4)==0 ){
|
| ︙ | ︙ | |||
765 766 767 768 769 770 771 |
i = 0;
mx = gg.nFile;
nFrom = strlen(zFrom);
while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){
if( pFile->isFrom==0 ) continue;
pNew = import_add_file();
pFile = &gg.aFile[i-1];
| | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
i = 0;
mx = gg.nFile;
nFrom = strlen(zFrom);
while( (pFile = import_find_file(zFrom, &i, mx))!=0 ){
if( pFile->isFrom==0 ) continue;
pNew = import_add_file();
pFile = &gg.aFile[i-1];
if( (int)strlen(pFile->zName)>nFrom ){
pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom);
}else{
pNew->zName = fossil_strdup(zTo);
}
pNew->isExe = pFile->isExe;
pNew->isLink = pFile->isLink;
pNew->zUuid = fossil_strdup(pFile->zUuid);
|
| ︙ | ︙ | |||
788 789 790 791 792 793 794 |
zTo = rest_of_line(&z);
i = 0;
nFrom = strlen(zFrom);
while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){
if( pFile->isFrom==0 ) continue;
pNew = import_add_file();
pFile = &gg.aFile[i-1];
| | | 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 |
zTo = rest_of_line(&z);
i = 0;
nFrom = strlen(zFrom);
while( (pFile = import_find_file(zFrom, &i, gg.nFile))!=0 ){
if( pFile->isFrom==0 ) continue;
pNew = import_add_file();
pFile = &gg.aFile[i-1];
if( (int)strlen(pFile->zName)>nFrom ){
pNew->zName = mprintf("%s%s", zTo, pFile->zName+nFrom);
}else{
pNew->zName = fossil_strdup(zTo);
}
pNew->zPrior = pFile->zName;
pNew->isExe = pFile->isExe;
pNew->isLink = pFile->isLink;
|
| ︙ | ︙ | |||
1011 1012 1013 1014 1015 1016 1017 |
svn_read_props(pIn, rec);
blob_zero(&rec->content);
zLen = svn_find_header(*rec, "Text-content-length");
if( zLen ){
rec->contentFlag = 1;
nLen = atoi(zLen);
blob_read_from_channel(&rec->content, pIn, nLen);
| | | 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 |
svn_read_props(pIn, rec);
blob_zero(&rec->content);
zLen = svn_find_header(*rec, "Text-content-length");
if( zLen ){
rec->contentFlag = 1;
nLen = atoi(zLen);
blob_read_from_channel(&rec->content, pIn, nLen);
if( (int)blob_size(&rec->content)!=nLen ){
fossil_fatal("short read: got %d of %d bytes",
blob_size(&rec->content), nLen
);
}
}else{
rec->contentFlag = 0;
}
|
| ︙ | ︙ | |||
1276 1277 1278 1279 1280 1281 1282 |
char *zBranch = 0;
int branchId = 0;
if( gsvn.azIgnTree ){
const char **pzIgnTree;
unsigned nPath = strlen(zPath);
for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){
const char *zIgn = *pzIgnTree;
| | | 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 |
char *zBranch = 0;
int branchId = 0;
if( gsvn.azIgnTree ){
const char **pzIgnTree;
unsigned nPath = strlen(zPath);
for( pzIgnTree = gsvn.azIgnTree; *pzIgnTree; ++pzIgnTree ){
const char *zIgn = *pzIgnTree;
unsigned nIgn = strlen(zIgn);
if( strncmp(zPath, zIgn, nIgn) == 0
&& ( nPath == nIgn || (nPath > nIgn && zPath[nIgn] == '/')) ){
return 0;
}
}
}
*type = SVN_UNKNOWN;
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
451 452 453 454 455 456 457 |
** 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;
| | | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
** 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_NoBot("w") ) diffFlags |= DIFF_IGNORE_ALLWS;
if( PD_NoBot("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;
|
| ︙ | ︙ | |||
646 647 648 649 650 651 652 |
" datetime(omtime,toLocal()), mtime"
" FROM blob, event"
" WHERE blob.rid=%d"
" AND event.objid=%d",
rid, rid
);
zBrName = branch_of_rid(rid);
| | | 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 |
" datetime(omtime,toLocal()), mtime"
" FROM blob, event"
" WHERE blob.rid=%d"
" AND event.objid=%d",
rid, rid
);
zBrName = branch_of_rid(rid);
diffType = preferred_diff_type();
if( db_step(&q1)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q1, 0);
int nUuid = db_column_bytes(&q1, 0);
char *zEUser, *zEComment;
const char *zUser;
const char *zOrigUser;
|
| ︙ | ︙ | |||
867 868 869 870 871 872 873 |
blob_reset(&wiki_add_links);
}else{
style_header("Check-in Information");
login_anonymous_available();
}
db_finalize(&q1);
@ </div>
| | | | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 |
blob_reset(&wiki_add_links);
}else{
style_header("Check-in Information");
login_anonymous_available();
}
db_finalize(&q1);
@ </div>
builtin_request_js("accordion.js");
if( !PB("nowiki") ){
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid,
"<div class=\"section accordion\">References</div>\n");
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
@ </div><div class="section accordion">Changes</div>
@ <div class="accordion_panel">
@ <div class="sectionmenu">
pCfg = construct_diff_flags(diffType, &DCfg);
|
| ︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 |
@ </table>
if( g.perm.ModWiki && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
@ <form method="POST" action="%R/winfo/%s(zUuid)">
@ <label><input type="radio" name="modaction" value="delete">
| | | | 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
@ </table>
if( g.perm.ModWiki && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
@ <form method="POST" action="%R/winfo/%s(zUuid)">
@ <label><input type="radio" name="modaction" value="delete">
@ Delete this change</label><br>
@ <label><input type="radio" name="modaction" value="approve">
@ Approve this change</label><br>
@ <input type="submit" value="Submit">
@ </form>
@ </blockquote>
}
@ <div class="section">Content</div>
|
| ︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 |
}
}
pTo = vdiff_parse_manifest("to", &ridTo);
if( pTo==0 ) return;
pFrom = vdiff_parse_manifest("from", &ridFrom);
if( pFrom==0 ) return;
zGlob = P("glob");
| | | | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 |
}
}
pTo = vdiff_parse_manifest("to", &ridTo);
if( pTo==0 ) return;
pFrom = vdiff_parse_manifest("from", &ridFrom);
if( pFrom==0 ) return;
zGlob = P("glob");
zFrom = P_NoBot("from");
zTo = P_NoBot("to");
if( bInvert ){
Manifest *pTemp = pTo;
const char *zTemp = zTo;
pTo = pFrom;
pFrom = pTemp;
zTo = zFrom;
zFrom = zTemp;
|
| ︙ | ︙ | |||
1294 1295 1296 1297 1298 1299 1300 |
if( pRe ){
@ <p><b>Only differences that match regular expression "%h(zRe)"
@ are shown.</b></p>
}
if( zGlob ){
@ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
}
| | | 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 |
if( pRe ){
@ <p><b>Only differences that match regular expression "%h(zRe)"
@ are shown.</b></p>
}
if( zGlob ){
@ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
}
@<hr><p>
}
blob_reset(&qp);
manifest_file_rewind(pFrom);
pFileFrom = manifest_file_next(pFrom, 0);
manifest_file_rewind(pTo);
pFileTo = manifest_file_next(pTo, 0);
|
| ︙ | ︙ | |||
1684 1685 1686 1687 1688 1689 1690 |
static char zDflt[2]
/*static b/c cookie_link_parameter() does not copy it!*/;
dflt = db_get_int("preferred-diff-type",-99);
if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
zDflt[0] = dflt + '0';
zDflt[1] = 0;
cookie_link_parameter("diff","diff", zDflt);
| | | 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 |
static char zDflt[2]
/*static b/c cookie_link_parameter() does not copy it!*/;
dflt = db_get_int("preferred-diff-type",-99);
if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
zDflt[0] = dflt + '0';
zDflt[1] = 0;
cookie_link_parameter("diff","diff", zDflt);
return atoi(PD_NoBot("diff",zDflt));
}
/*
** WEBPAGE: fdiff
** URL: fdiff?v1=HASH&v2=HASH
**
|
| ︙ | ︙ | |||
1720 1721 1722 1723 1724 1725 1726 |
int v1, v2;
int isPatch = P("patch")!=0;
int diffType; /* 0: none, 1: unified, 2: side-by-side */
char *zV1;
char *zV2;
const char *zRe;
ReCompiled *pRe = 0;
| < > | 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 |
int v1, v2;
int isPatch = P("patch")!=0;
int diffType; /* 0: none, 1: unified, 2: side-by-side */
char *zV1;
char *zV2;
const char *zRe;
ReCompiled *pRe = 0;
u32 objdescFlags = 0;
int verbose = PB("verbose");
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diff_config_init(&DCfg, 0);
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
}else{
Stmt q;
v1 = name_to_rid_www("v1");
|
| ︙ | ︙ | |||
1773 1774 1775 1776 1777 1778 1779 |
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");
| | < | 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 |
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");
DCfg.diffFlags = DIFF_VERBOSE;
content_get(v1, &c1);
content_get(v2, &c2);
DCfg.pRe = pRe;
text_diff(&c1, &c2, pOut, &DCfg);
blob_reset(&c1);
blob_reset(&c2);
return;
}
|
| ︙ | ︙ | |||
1819 1820 1821 1822 1823 1824 1825 |
object_description(v2, objdescFlags,0, 0);
}
if( pRe ){
@ <b>Only differences that match regular expression "%h(zRe)"
@ are shown.</b>
DCfg.pRe = pRe;
}
| | | 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 |
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
|
| ︙ | ︙ | |||
2127 2128 2129 2130 2131 2132 2133 |
@ :</h2>
}
blob_zero(&downloadName);
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
object_description(rid, objdescFlags, 0, &downloadName);
style_submenu_element("Download", "%R/raw/%s?at=%T",
zUuid, file_tail(blob_str(&downloadName)));
| | | 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 |
@ :</h2>
}
blob_zero(&downloadName);
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
object_description(rid, objdescFlags, 0, &downloadName);
style_submenu_element("Download", "%R/raw/%s?at=%T",
zUuid, file_tail(blob_str(&downloadName)));
@ <hr>
content_get(rid, &content);
if( !g.isHuman ){
/* Prevent robots from running hexdump on megabyte-sized source files
** and there by eating up lots of CPU time and bandwidth. There is
** no good reason for a robot to need a hexdump. */
@ <p>A hex dump of this file is not available.
@ Please download the raw binary file and generate a hex dump yourself.</p>
|
| ︙ | ︙ | |||
2493 2494 2495 2496 2497 2498 2499 |
if( db_step(&q)==SQLITE_ROW ){
rid = db_column_int(&q, 0);
zCI = fossil_strdup(db_column_text(&q, 1));
zCIUuid = fossil_strdup(zCI);
url_add_parameter(&url, "ci", zCI);
}
db_finalize(&q);
| | | 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 |
if( db_step(&q)==SQLITE_ROW ){
rid = db_column_int(&q, 0);
zCI = fossil_strdup(db_column_text(&q, 1));
zCIUuid = fossil_strdup(zCI);
url_add_parameter(&url, "ci", zCI);
}
db_finalize(&q);
if( rid==0 ){
style_header("No such file");
@ File '%h(zName)' does not exist in this repository.
}
}else{
style_header("No such artifact");
@ Artifact '%h(zName)' does not exist in this repository.
}
|
| ︙ | ︙ | |||
2653 2654 2655 2656 2657 2658 2659 |
}
if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
style_submenu_element("Parsed", "%R/info/%s", zUuid);
}
if( descOnly ){
style_submenu_element("Content", "%R/artifact/%s", zUuid);
}else{
| | | 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 |
}
if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
style_submenu_element("Parsed", "%R/info/%s", zUuid);
}
if( descOnly ){
style_submenu_element("Content", "%R/artifact/%s", zUuid);
}else{
@ <hr>
content_get(rid, &content);
if( renderAsWiki ){
safe_html_context(DOCSRC_FILE);
wiki_render_by_mimetype(&content, zMime);
document_emit_js();
}else if( renderAsHtml ){
@ <iframe src="%R/raw/%s(zUuid)"
|
| ︙ | ︙ | |||
2800 2801 2802 2803 2804 2805 2806 |
if( g.perm.Setup ){
@ (%d(rid))
}
modPending = moderation_pending_www(rid);
@ <tr><th>Ticket:</th>
@ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
if( zTktTitle ){
| | | | | 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 |
if( g.perm.Setup ){
@ (%d(rid))
}
modPending = moderation_pending_www(rid);
@ <tr><th>Ticket:</th>
@ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a>
if( zTktTitle ){
@<br>%h(zTktTitle)
}
@</td></tr>
@ <tr><th>User & Date:</th><td>
hyperlink_to_user(pTktChng->zUser, zDate, " on ");
hyperlink_to_date(zDate, "</td></tr>");
@ </table>
free(zDate);
free(zTktTitle);
if( g.perm.ModTkt && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
@ <form method="POST" action="%R/tinfo/%s(zUuid)">
@ <label><input type="radio" name="modaction" value="delete">
@ Delete this change</label><br>
@ <label><input type="radio" name="modaction" value="approve">
@ Approve this change</label><br>
@ <input type="submit" value="Submit">
@ </form>
@ </blockquote>
}
@ <div class="section">Changes</div>
@ <p>
|
| ︙ | ︙ | |||
3267 3268 3269 3270 3271 3272 3273 |
@ %s(blob_str(&suffix))
@ </td></tr></table>
if( zChngTime ){
@ <p>The timestamp on the tag used to make the changes above
@ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
}
@ </blockquote>
| | | | | | | | | | | < | 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 |
@ %s(blob_str(&suffix))
@ </td></tr></table>
if( zChngTime ){
@ <p>The timestamp on the tag used to make the changes above
@ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
}
@ </blockquote>
@ <hr>
blob_reset(&suffix);
}
@ <p>Make changes to attributes of check-in
@ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
form_begin(0, "%R/ci_edit");
login_insert_csrf_secret();
@ <div><input type="hidden" name="r" value="%s(zUuid)">
@ <table border="0" cellspacing="10">
@ <tr><th align="right" valign="top">User:</th>
@ <td valign="top">
@ <input type="text" name="u" size="20" value="%h(zNewUser)">
@ </td></tr>
@ <tr><th align="right" valign="top">Comment:</th>
@ <td valign="top">
@ <textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea>
@ </td></tr>
@ <tr><th align="right" valign="top">Check-in Time:</th>
@ <td valign="top">
@ <input type="text" name="dt" size="20" value="%h(zNewDate)">
@ </td></tr>
if( zChngTime ){
@ <tr><th align="right" valign="top">Timestamp of this change:</th>
@ <td valign="top">
@ <input type="text" name="chngtime" size="20" value="%h(zChngTime)">
@ </td></tr>
}
@ <tr><th align="right" valign="top">Background Color:</th>
@ <td valign="top">
@ <div><label><input type='checkbox' name='newclr'%s(zNewColorFlag)>
@ Change background color: \
@ <input type='color' name='clr'\
@ value='%s(zNewColor[0]?zNewColor:"#808080")'></label></div>
@ <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 size="15" name="tagname" id="tagname" value="%h(zNewTag)">
zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
" AND tagxref.tagid=%d", rid, TAG_BRANCH);
db_prepare(&q,
"SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
|
| ︙ | ︙ | |||
3352 3353 3354 3355 3356 3357 3358 |
}else if( tagid==TAG_HIDDEN ){
fHasHidden = 1;
}else if( !isSpecialTag && zTagName &&
fossil_strcmp(&zTagName[4], zBranchName)==0){
continue;
}
sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
| | | | | | | | | | | | | | 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 |
}else if( tagid==TAG_HIDDEN ){
fHasHidden = 1;
}else if( !isSpecialTag && zTagName &&
fossil_strcmp(&zTagName[4], zBranchName)==0){
continue;
}
sqlite3_snprintf(sizeof(zLabel), zLabel, "c%d", tagid);
@ <br><label>
if( P(zLabel) ){
@ <input type="checkbox" name="c%d(tagid)" checked="checked">
}else{
@ <input type="checkbox" name="c%d(tagid)">
}
if( isSpecialTag ){
@ Cancel special tag <b>%h(zTagName)</b></label>
}else{
@ Cancel tag <b>%h(&zTagName[4])</b></label>
}
}
db_finalize(&q);
@ </td></tr>
if( !zBranchName ){
zBranchName = db_get("main-branch", 0);
}
if( !zNewBranch || !zNewBranch[0]){
zNewBranch = zBranchName;
}
@ <tr><th align="right" valign="top">Branching:</th>
@ <td valign="top">
@ <label><input id="newbr" type="checkbox" name="newbr" \
@ data-branch='%h(zBranchName)'%s(zNewBrFlag)>
@ Starting from this check-in, rename the branch to:</label>
@ <input id="brname" type="text" style="width:15;" name="brname" \
@ value="%h(zNewBranch)"></td></tr>
if( !fHasHidden ){
@ <tr><th align="right" valign="top">Branch Hiding:</th>
@ <td valign="top">
@ <label><input type="checkbox" id="hidebr" name="hide"%s(zHideFlag)>
@ Hide branch
@ <span style="font-weight:bold" id="hbranch">%h(zBranchName)</span>
@ from the timeline starting from this check-in</label>
@ </td></tr>
}
if( !fHasClosed ){
if( is_a_leaf(rid) ){
@ <tr><th align="right" valign="top">Leaf Closure:</th>
@ <td valign="top">
@ <label><input type="checkbox" name="close"%s(zCloseFlag)>
@ Mark this leaf as "closed" so that it no longer appears on the
@ "leaves" page and is no longer labeled as a "<b>Leaf</b>"</label>
@ </td></tr>
}else if( zBranchName ){
@ <tr><th align="right" valign="top">Branch Closure:</th>
@ <td valign="top">
@ <label><input type="checkbox" name="close"%s(zCloseFlag)>
@ Mark branch
@ <span style="font-weight:bold" id="cbranch">%h(zBranchName)</span>
@ as "closed".</label>
@ </td></tr>
}
}
if( zBranchName ) fossil_free(zBranchName);
@ <tr><td colspan="2">
@ <input type="submit" name="cancel" value="Cancel">
@ <input type="submit" name="preview" value="Preview">
if( P("preview") ){
@ <input type="submit" name="apply" value="Apply Changes">
}
@ </td></tr>
@ </table>
@ </div></form>
builtin_request_js("ci_edit.js");
style_finish_page();
}
|
| ︙ | ︙ | |||
3477 3478 3479 3480 3481 3482 3483 | ** -M|--message-file FILE Read the amended comment from FILE ** -e|--edit-comment Launch editor to revise comment ** --date DATETIME Make DATETIME the check-in time ** --bgcolor COLOR Apply COLOR to this check-in ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --tag TAG Add new TAG to this check-in ** --cancel TAG Cancel TAG from this check-in | | | 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 | ** -M|--message-file FILE Read the amended comment from FILE ** -e|--edit-comment Launch editor to revise comment ** --date DATETIME Make DATETIME the check-in time ** --bgcolor COLOR Apply COLOR to this check-in ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --tag TAG Add new TAG to this check-in ** --cancel TAG Cancel TAG from this check-in ** --branch NAME Rename branch of check-in to NAME ** --hide Hide branch starting from this check-in ** --close Mark this "leaf" as closed ** -n|--dry-run Print control artifact, but make no changes ** --date-override DATETIME Set the change time on the control artifact ** --user-override USER Set the user name on the control artifact ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| ︙ | ︙ | |||
3684 3685 3686 3687 3688 3689 3690 |
db_column_text(&q,2),
db_column_text(&q,3));
}
db_finalize(&q);
}
#if INTERFACE
| | | 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 |
db_column_text(&q,2),
db_column_text(&q,3));
}
db_finalize(&q);
}
#if INTERFACE
/*
** Description of a check-in relative to an earlier, tagged check-in.
*/
typedef struct CommitDescr {
char *zRelTagname; /* Tag name on the relative check-in */
int nCommitsSince; /* Number of commits since then */
char *zCommitHash; /* Hash of the described check-in */
int isDirty; /* Working directory has uncommitted changes */
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
292 293 294 295 296 297 298 |
blob_appendf(out,"</table></blockquote>\n");
}else{
blob_appendf(out,"<i>None</i></blockquote>\n");
}
}
/*
| | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
blob_appendf(out,"</table></blockquote>\n");
}else{
blob_appendf(out,"<i>None</i></blockquote>\n");
}
}
/*
** WEBPAGE: intermap
**
** View and modify the interwiki tag map or "intermap".
** This page is visible to administrators only.
*/
void interwiki_page(void){
Stmt q;
int n = 0;
|
| ︙ | ︙ |
Changes to src/json.c.
| ︙ | ︙ | |||
286 287 288 289 290 291 292 | cson_value * v; char * zStr; va_list vargs; va_start(vargs,fmt); zStr = vmprintf(fmt,vargs); va_end(vargs); v = cson_value_new_string(zStr, strlen(zStr)); | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
cson_value * v;
char * zStr;
va_list vargs;
va_start(vargs,fmt);
zStr = vmprintf(fmt,vargs);
va_end(vargs);
v = cson_value_new_string(zStr, strlen(zStr));
fossil_free(zStr);
return v;
}
cson_value * json_new_int( i64 v ){
return cson_value_new_integer((cson_int_t)v);
}
|
| ︙ | ︙ | |||
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
** In CLI mode pResponse is sent to stdout immediately. In HTTP
** mode pResponse replaces any current CGI content but cgi_reply()
** is not called to flush the output.
**
** If g.json.jsonp is not NULL then the content type is set to
** text/javascript and the output is wrapped in a jsonp
** wrapper.
*/
void json_send_response( cson_value const * pResponse ){
assert( NULL != pResponse );
if( g.isHTTP ){
cgi_reset_content();
if( g.json.jsonp ){
cgi_set_content_type("text/javascript");
cgi_printf("%s(",g.json.jsonp);
}
cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
| > > > > > > > | 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
** In CLI mode pResponse is sent to stdout immediately. In HTTP
** mode pResponse replaces any current CGI content but cgi_reply()
** is not called to flush the output.
**
** If g.json.jsonp is not NULL then the content type is set to
** text/javascript and the output is wrapped in a jsonp
** wrapper.
**
** This function works only the first time it is called. It "should
** not" ever be called more than once but certain calling
** constellations might trigger that, in which case the second and
** subsequent calls are no-ops.
*/
void json_send_response( cson_value const * pResponse ){
static int once = 0;
assert( NULL != pResponse );
if( once++ ) return;
if( g.isHTTP ){
cgi_reset_content();
if( g.json.jsonp ){
cgi_set_content_type("text/javascript");
cgi_printf("%s(",g.json.jsonp);
}
cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
|
| ︙ | ︙ | |||
842 843 844 845 846 847 848 |
*/
va_list vargs;
char * msg;
va_start(vargs,fmt);
msg = vmprintf(fmt,vargs);
va_end(vargs);
cson_object_set(obj,"text", cson_value_new_string(msg,strlen(msg)));
| | | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 |
*/
va_list vargs;
char * msg;
va_start(vargs,fmt);
msg = vmprintf(fmt,vargs);
va_end(vargs);
cson_object_set(obj,"text", cson_value_new_string(msg,strlen(msg)));
fossil_free(msg);
}
}
/*
** Splits zStr (which must not be NULL) into tokens separated by the
** given separator character. If doDeHttp is true then each element
** will be passed through dehttpize(), otherwise they are used
|
| ︙ | ︙ | |||
1621 1622 1623 1624 1625 1626 1627 |
** Sets g.json.resultCode and g.zErrMsg, but does not report the error
** via json_err(). Returns the code passed to it.
**
** code must be in the inclusive range 1000..9999.
*/
int json_set_err( int code, char const * fmt, ... ){
assert( (code>=1000) && (code<=9999) );
| | | 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 |
** Sets g.json.resultCode and g.zErrMsg, but does not report the error
** via json_err(). Returns the code passed to it.
**
** code must be in the inclusive range 1000..9999.
*/
int json_set_err( int code, char const * fmt, ... ){
assert( (code>=1000) && (code<=9999) );
fossil_free(g.zErrMsg);
g.json.resultCode = code;
if(!fmt || !*fmt){
g.zErrMsg = mprintf("%s", json_err_cstr(code));
}else{
va_list vargs;
char * msg;
va_start(vargs,fmt);
|
| ︙ | ︙ | |||
1769 1770 1771 1772 1773 1774 1775 |
cson_value * json_tags_for_checkin_rid(int rid, int propagatingOnly){
cson_value * v = NULL;
char * tags = info_tags_of_checkin(rid, propagatingOnly);
if(tags){
if(*tags){
v = json_string_split2(tags,',',0);
}
| | | 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 |
cson_value * json_tags_for_checkin_rid(int rid, int propagatingOnly){
cson_value * v = NULL;
char * tags = info_tags_of_checkin(rid, propagatingOnly);
if(tags){
if(*tags){
v = json_string_split2(tags,',',0);
}
fossil_free(tags);
}
return v;
}
/*
** Returns a "new" value representing the boolean value of zVal
** (false if zVal is NULL). Note that cson does not really allocate
|
| ︙ | ︙ | |||
1791 1792 1793 1794 1795 1796 1797 |
: cson_value_false();
}
/*
** Impl of /json/resultCodes
**
*/
| | | 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 |
: cson_value_false();
}
/*
** Impl of /json/resultCodes
**
*/
cson_value * json_page_resultCodes(void){
cson_array * list = cson_new_array();
cson_object * obj = NULL;
cson_string * kRC;
cson_string * kSymbol;
cson_string * kNumber;
cson_string * kDesc;
cson_array_reserve( list, 35 );
|
| ︙ | ︙ | |||
1858 1859 1860 1861 1862 1863 1864 | /* ** /json/version implementation. ** ** Returns the payload object (owned by the caller). */ | | | 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 |
/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(void){
cson_value * jval = NULL;
cson_object * jobj = NULL;
jval = cson_value_new_object();
jobj = cson_value_get_object(jval);
#define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X)))
FSET(MANIFEST_UUID,"manifestUuid");
FSET(MANIFEST_VERSION,"manifestVersion");
|
| ︙ | ︙ | |||
1912 1913 1914 1915 1916 1917 1918 | ** ** Returned object contains details about the "capabilities" of the ** current user (what he may/may not do). ** ** This is primarily intended for debuggering, but may have ** a use in client code. (?) */ | | | 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 |
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**
** This is primarily intended for debuggering, but may have
** a use in client code. (?)
*/
cson_value * json_page_cap(void){
cson_value * payload = cson_value_new_object();
cson_value * sub = cson_value_new_object();
Stmt q;
cson_object * obj = cson_value_get_object(payload);
db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
if( db_step(&q)==SQLITE_ROW ){
/* reminder: we don't use g.zLogin because it's 0 for the guest
|
| ︙ | ︙ | |||
1979 1980 1981 1982 1983 1984 1985 | return payload; } /* ** Implementation of the /json/stat page/command. ** */ | | | 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 |
return payload;
}
/*
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(void){
i64 t, fsize;
int n, m;
int full;
enum { BufLen = 1000 };
char zBuf[BufLen];
cson_value * jv = NULL;
cson_object * jo = NULL;
|
| ︙ | ︙ | |||
2004 2005 2006 2007 2008 2009 2010 |
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
jv = cson_value_new_object();
jo = cson_value_get_object(jv);
zTmp = db_get("project-name",NULL);
cson_object_set(jo, "projectName", json_new_string(zTmp));
| | | | 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 |
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
jv = cson_value_new_object();
jo = cson_value_get_object(jv);
zTmp = db_get("project-name",NULL);
cson_object_set(jo, "projectName", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = db_get("project-description",NULL);
cson_object_set(jo, "projectDescription", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = NULL;
fsize = file_size(g.zRepositoryName, ExtFILE);
cson_object_set(jo, "repositorySize",
cson_value_new_integer((cson_int_t)fsize));
if(full){
n = db_int(0, "SELECT count(*) FROM blob");
|
| ︙ | ︙ | |||
2159 2160 2161 2162 2163 2164 2165 | } } /* ** Impl of /json/rebuild. Requires admin privileges. */ | | | 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 |
}
}
/*
** Impl of /json/rebuild. Requires admin privileges.
*/
static cson_value * json_page_rebuild(void){
if( !g.perm.Admin ){
json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges.");
return NULL;
}else{
/* Reminder: the db_xxx() ops "should" fail via the fossil core
error handlers, which will cause a JSON error and exit(). i.e. we
don't handle the errors here. TODO: confirm that all these db
|
| ︙ | ︙ | |||
2185 2186 2187 2188 2189 2190 2191 |
return NULL;
}
}
/*
** Impl of /json/g. Requires admin/setup rights.
*/
| | | | | | | | | | | | | | | | | 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 |
return NULL;
}
}
/*
** Impl of /json/g. Requires admin/setup rights.
*/
static cson_value * json_page_g(void){
if(!g.perm.Admin || !g.perm.Setup){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'a' or 's' privileges.");
return NULL;
}
return json_g_to_json();
}
/* Impl in json_login.c. */
cson_value * json_page_anon_password(void);
/* Impl in json_artifact.c. */
cson_value * json_page_artifact(void);
/* Impl in json_branch.c. */
cson_value * json_page_branch(void);
/* Impl in json_diff.c. */
cson_value * json_page_diff(void);
/* Impl in json_dir.c. */
cson_value * json_page_dir(void);
/* Impl in json_login.c. */
cson_value * json_page_login(void);
/* Impl in json_login.c. */
cson_value * json_page_logout(void);
/* Impl in json_query.c. */
cson_value * json_page_query(void);
/* Impl in json_report.c. */
cson_value * json_page_report(void);
/* Impl in json_tag.c. */
cson_value * json_page_tag(void);
/* Impl in json_user.c. */
cson_value * json_page_user(void);
/* Impl in json_config.c. */
cson_value * json_page_config(void);
/* Impl in json_finfo.c. */
cson_value * json_page_finfo(void);
/* Impl in json_status.c. */
cson_value * json_page_status(void);
/*
** Mapping of names to JSON pages/commands. Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
|
| ︙ | ︙ | |||
2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 |
{"HAI",json_page_version,0},
{"login",json_page_login,0},
{"logout",json_page_logout,0},
{"query",json_page_query,0},
{"rebuild",json_page_rebuild,0},
{"report", json_page_report, 0},
{"resultCodes", json_page_resultCodes,0},
{"stat",json_page_stat,0},
{"status", json_page_status, 0},
{"tag", json_page_tag,0},
/*{"ticket", json_page_nyi,0},*/
{"timeline", json_page_timeline,0},
{"user",json_page_user,0},
{"version",json_page_version,0},
| > | 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 |
{"HAI",json_page_version,0},
{"login",json_page_login,0},
{"logout",json_page_logout,0},
{"query",json_page_query,0},
{"rebuild",json_page_rebuild,0},
{"report", json_page_report, 0},
{"resultCodes", json_page_resultCodes,0},
{"settings",json_page_settings,0},
{"stat",json_page_stat,0},
{"status", json_page_status, 0},
{"tag", json_page_tag,0},
/*{"ticket", json_page_nyi,0},*/
{"timeline", json_page_timeline,0},
{"user",json_page_user,0},
{"version",json_page_version,0},
|
| ︙ | ︙ |
Changes to src/json_artifact.c.
| ︙ | ︙ | |||
244 245 246 247 248 249 250 | ** Internal helper which returns: ** ** If the "format" (CLI: -f) flag is set function returns the same as ** json_wiki_get_content_format_flag(), else it returns true (non-0) ** if either the includeContent (HTTP) or -content|-c boolean flags ** (CLI) are set. */ | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
** Internal helper which returns:
**
** If the "format" (CLI: -f) flag is set function returns the same as
** json_wiki_get_content_format_flag(), else it returns true (non-0)
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
enum { MagicValue = -9 };
int contentFormat = json_wiki_get_content_format_flag(MagicValue);
if(MagicValue == contentFormat){
contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0;
}
return contentFormat;
}
|
| ︙ | ︙ | |||
396 397 398 399 400 401 402 | return cson_object_value(pay); } /* ** Impl of /json/artifact. This basically just determines the type of ** an artifact and forwards the real work to another function. */ | | | 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
return cson_object_value(pay);
}
/*
** Impl of /json/artifact. This basically just determines the type of
** an artifact and forwards the real work to another function.
*/
cson_value * json_page_artifact(void){
cson_object * pay = NULL;
char const * zName = NULL;
char const * zType = NULL;
char const * zUuid = NULL;
cson_value * entry = NULL;
Blob uuid = empty_blob;
int rc;
|
| ︙ | ︙ |
Changes to src/json_branch.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | #include "json_branch.h" #if INTERFACE #include "json_detail.h" #endif | | | | | | 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 |
#include "json_branch.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_branch_list(void);
static cson_value * json_branch_create(void);
/*
** Mapping of /json/branch/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Branch[] = {
{"create", json_branch_create, 0},
{"list", json_branch_list, 0},
{"new", json_branch_create, -1/* for compat with non-JSON branch command.*/},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implements the /json/branch family of pages/commands. Far from
** complete.
**
*/
cson_value * json_page_branch(void){
return json_page_dispatch_helper(&JsonPageDefs_Branch[0]);
}
/*
** Impl for /json/branch/list
**
**
** CLI mode options:
**
** -r|--range X, where X is one of (open,closed,all)
** (only the first letter is significant, default=open)
** -a (same as --range a)
** -c (same as --range c)
**
** HTTP mode options:
**
** "range" GET/POST.payload parameter. FIXME: currently we also use
** POST, but really want to restrict this to POST.payload.
*/
static cson_value * json_branch_list(void){
cson_value * payV;
cson_object * pay;
cson_value * listV;
cson_array * list;
char const * range = NULL;
int branchListFlags = BRL_OPEN_ONLY;
char * sawConversionError = NULL;
|
| ︙ | ︙ | |||
311 312 313 314 315 316 317 | return 0; } /* ** Impl of /json/branch/create. */ | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
return 0;
}
/*
** Impl of /json/branch/create.
*/
static cson_value * json_branch_create(void){
cson_value * payV = NULL;
cson_object * pay = NULL;
int rc = 0;
BranchCreateOptions opt;
char * zUuid = NULL;
int rid = 0;
if( !g.perm.Write ){
|
| ︙ | ︙ |
Changes to src/json_config.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "config.h" #include "json_config.h" #if INTERFACE #include "json_detail.h" #endif | | | > > > > > > > > > > > > | > > > > > > > > | 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 |
#include "config.h"
#include "json_config.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_config_get(void);
static cson_value * json_config_save(void);
/*
** Mapping of /json/config/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Config[] = {
{"get", json_config_get, 0},
{"save", json_config_save, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
static cson_value * json_settings_get(void);
static cson_value * json_settings_set(void);
/*
** Mapping of /json/settings/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Settings[] = {
{"get", json_settings_get, 0},
{"set", json_settings_set, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implements the /json/config family of pages/commands.
**
*/
cson_value * json_page_config(void){
return json_page_dispatch_helper(&JsonPageDefs_Config[0]);
}
/*
** Implements the /json/settings family of pages/commands.
**
*/
cson_value * json_page_settings(void){
return json_page_dispatch_helper(&JsonPageDefs_Settings[0]);
}
/*
** JSON-internal mapping of config options to config groups. This is
** mostly a copy of the config options in configure.c, but that data
** is private and cannot be re-used directly here.
*/
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 | }; /* ** Impl of /json/config/get. Requires setup rights. ** */ | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
};
/*
** Impl of /json/config/get. Requires setup rights.
**
*/
static cson_value * json_config_get(void){
cson_object * pay = NULL;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
char const * zName = NULL;
int confMask = 0;
char optSkinBackups = 0;
unsigned int i;
|
| ︙ | ︙ | |||
179 180 181 182 183 184 185 | } /* ** Impl of /json/config/save. ** ** TODOs: */ | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
}
/*
** Impl of /json/config/save.
**
** TODOs:
*/
static cson_value * json_config_save(void){
json_set_err(FSL_JSON_E_NYI, NULL);
return NULL;
}
/*
** Impl of /json/settings/get.
*/
static cson_value * json_settings_get(void){
cson_object * pay = cson_new_object(); /* output payload */
int nSetting, i; /* setting count and loop var */
const Setting *aSetting = setting_info(&nSetting);
const char * zRevision = 0; /* revision to look for
versioned settings in */
char * zUuid = 0; /* Resolved UUID of zRevision */
Stmt q = empty_Stmt; /* Config-search query */
Stmt qFoci = empty_Stmt; /* foci query */
if( !g.perm.Read ){
json_set_err( FSL_JSON_E_DENIED, "Fetching settings requires 'o' access." );
return NULL;
}
zRevision = json_find_option_cstr("version",NULL,NULL);
if( 0!=zRevision ){
int rid = name_to_uuid2(zRevision, "ci", &zUuid);
if(rid<=0){
json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
"Cannot find the given version.");
return NULL;
}
db_multi_exec("CREATE VIRTUAL TABLE IF NOT EXISTS "
"temp.foci USING files_of_checkin;");
db_prepare(&qFoci,
"SELECT uuid FROM temp.foci WHERE "
"checkinID=%d AND filename='.fossil-settings/' || :name",
rid);
}
zRevision = 0;
if( g.localOpen ){
db_prepare(&q,
"SELECT 'checkout', value FROM vvar WHERE name=:name"
" UNION ALL "
"SELECT 'repo', value FROM config WHERE name=:name"
);
}else{
db_prepare(&q,
"SELECT 'repo', value FROM config WHERE name=:name"
);
}
for(i=0; i<nSetting; ++i){
const Setting *pSet = &aSetting[i];
cson_object * jSet;
cson_value * pVal = 0, * pSrc = 0;
jSet = cson_new_object();
cson_object_set(pay, pSet->name, cson_object_value(jSet));
cson_object_set(jSet, "versionable", cson_value_new_bool(pSet->versionable));
cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive));
cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0])
? json_new_string(pSet->def)
: cson_value_null());
if( 0==pSet->sensitive || 0!=g.perm.Setup ){
if( pSet->versionable ){
/* Check to see if this is overridden by a versionable
** settings file */
if( 0!=zUuid ){
/* Attempt to find a versioned setting stored in the given
** check-in version. */
db_bind_text(&qFoci, ":name", pSet->name);
if( SQLITE_ROW==db_step(&qFoci) ){
int frid = fast_uuid_to_rid(db_column_text(&qFoci, 0));
Blob content;
blob_zero(&content);
if( 0!=content_get(frid, &content) ){
pSrc = json_new_string("versioned");
pVal = json_new_string(blob_str(&content));
}
blob_reset(&content);
}
db_reset(&qFoci);
}
if( 0==pSrc && g.localOpen ){
/* Pull value from a checkout-local .fossil-settings/X file,
** if one exists. */
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSet->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
Blob content;
blob_zero(&content);
blob_read_from_file(&content, blob_str(&versionedPathname), ExtFILE);
pSrc = json_new_string("versioned");
pVal = json_new_string(blob_str(&content));
blob_reset(&content);
}
blob_reset(&versionedPathname);
}
}
if( 0==pSrc ){
/* Setting is not versionable or we had no versioned value, so
** use the value from localdb.vvar or repository.config (in
** that order). */
db_bind_text(&q, ":name", pSet->name);
if( SQLITE_ROW==db_step(&q) ){
pSrc = json_new_string(db_column_text(&q, 0));
pVal = json_new_string(db_column_text(&q, 1));
}
db_reset(&q);
}
}
cson_object_set(jSet, "valueSource", pSrc ? pSrc : cson_value_null());
cson_object_set(jSet, "value", pVal ? pVal : cson_value_null());
}/*aSetting loop*/
db_finalize(&q);
db_finalize(&qFoci);
fossil_free(zUuid);
return cson_object_value(pay);
}
/*
** Impl of /json/settings/set.
**
** Input payload is an object mapping setting names to values. All
** values are set in the repository.config table. It has no response
** payload.
*/
static cson_value * json_settings_set(void){
Stmt q = empty_Stmt; /* Config-set query */
cson_object_iterator objIter = cson_object_iterator_empty;
cson_kvp * pKvp;
int nErr = 0, nProp = 0;
if( 0==g.perm.Setup ){
json_set_err( FSL_JSON_E_DENIED, "Setting settings requires 's' access." );
return NULL;
}
else if( 0==g.json.reqPayload.o ){
json_set_err(FSL_JSON_E_MISSING_ARGS,
"Missing payload of setting-to-value mappings.");
return NULL;
}
db_unprotect(PROTECT_CONFIG);
db_prepare(&q,
"INSERT OR REPLACE INTO config (name, value, mtime) "
"VALUES(:name, :value, CAST(strftime('%%s') AS INT))"
);
db_begin_transaction();
cson_object_iter_init( g.json.reqPayload.o, &objIter );
while( (pKvp = cson_object_iter_next(&objIter)) ){
char const * zKey = cson_string_cstr( cson_kvp_key(pKvp) );
cson_value * pVal;
const Setting *pSetting = db_find_setting( zKey, 0 );
if( 0==pSetting ){
nErr = json_set_err(FSL_JSON_E_INVALID_ARGS,
"Unknown setting: %s", zKey);
break;
}
pVal = cson_kvp_value(pKvp);
switch( cson_value_type_id(pVal) ){
case CSON_TYPE_NULL:
db_multi_exec("DELETE FROM config WHERE name=%Q", pSetting->name);
continue;
case CSON_TYPE_BOOL:
db_bind_int(&q, ":value", cson_value_get_bool(pVal) ? 1 : 0);
break;
case CSON_TYPE_INTEGER:
db_bind_int64(&q, ":value", cson_value_get_integer(pVal));
break;
case CSON_TYPE_DOUBLE:
db_bind_double(&q, ":value", cson_value_get_double(pVal));
break;
case CSON_TYPE_STRING:
db_bind_text(&q, ":value", cson_value_get_cstr(pVal));
break;
default:
nErr = json_set_err(FSL_JSON_E_USAGE,
"Invalid value type for setting '%s'.",
pSetting->name);
break;
}
if( 0!=nErr ) break;
db_bind_text(&q, ":name", zKey);
db_step(&q);
db_reset(&q);
++nProp;
}
db_finalize(&q);
if( 0==nErr && 0==nProp ){
nErr = json_set_err(FSL_JSON_E_INVALID_ARGS,
"Payload contains no settings to set.");
}
db_end_transaction(nErr);
db_protect_pop();
return NULL;
}
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_detail.h.
| ︙ | ︙ | |||
147 148 149 150 151 152 153 | ** It is imperative that NO callback functions EVER output ANYTHING to ** stdout, as that will effectively corrupt any JSON output, and ** almost certainly will corrupt any HTTP response headers. Output ** sent to stderr ends up in my apache log, so that might be useful ** for debugging in some cases, but no such code should be left ** enabled for non-debugging builds. */ | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | ** It is imperative that NO callback functions EVER output ANYTHING to ** stdout, as that will effectively corrupt any JSON output, and ** almost certainly will corrupt any HTTP response headers. Output ** sent to stderr ends up in my apache log, so that might be useful ** for debugging in some cases, but no such code should be left ** enabled for non-debugging builds. */ typedef cson_value * (*fossil_json_f)(void); /* ** Holds name-to-function mappings for JSON page/command dispatching. ** ** Internally we model page dispatching lists as arrays of these ** objects, where the final entry in the array has a NULL name value ** to act as the end-of-list sentinel. |
| ︙ | ︙ | |||
258 259 260 261 262 263 264 | ** b) We are running in JSON CLI mode, but no POST data has been fed ** in. ** ** Whether or not we need to take args from CLI or POST data makes a ** difference in argument/parameter handling in many JSON routines, ** and thus this distinction. */ | | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
** b) We are running in JSON CLI mode, but no POST data has been fed
** in.
**
** Whether or not we need to take args from CLI or POST data makes a
** difference in argument/parameter handling in many JSON routines,
** and thus this distinction.
*/
int fossil_has_json(void);
enum json_get_changed_files_flags {
json_get_changed_files_ELIDE_PARENT = 1 << 0
};
#endif /* !defined(_RC_COMPILE_) */
#endif/*FOSSIL_JSON_DETAIL_H_INCLUDED*/
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_diff.c.
| ︙ | ︙ | |||
81 82 83 84 85 86 87 | ** v1=1st version to diff ** v2=2nd version to diff ** ** Can come from GET, POST.payload, CLI -v1/-v2 or as positional ** parameters following the command name (in HTTP and CLI modes). ** */ | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
** v1=1st version to diff
** v2=2nd version to diff
**
** Can come from GET, POST.payload, CLI -v1/-v2 or as positional
** parameters following the command name (in HTTP and CLI modes).
**
*/
cson_value * json_page_diff(void){
cson_object * pay = NULL;
cson_value * v = NULL;
char const * zFrom;
char const * zTo;
int nContext = 0;
char doSBS;
char doHtml;
|
| ︙ | ︙ |
Changes to src/json_dir.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "config.h" #include "json_dir.h" #if INTERFACE #include "json_detail.h" #endif | | | | | 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 |
#include "config.h"
#include "json_dir.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_page_dir_list(void);
/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
#if 0 /* TODO: Not used? */
static const JsonPageDef JsonPageDefs_Dir[] = {
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
#endif
#if 0 /* TODO: Not used? */
static char const * json_dir_path_extra(void){
static char const * zP = NULL;
if( !zP ){
zP = g.zExtra;
while(zP && *zP && ('/'==*zP)){
++zP;
}
}
return zP;
}
#endif
/*
** Impl of /json/dir. 98% of it was taken directly
** from browse.c::page_dir()
*/
static cson_value * json_page_dir_list(void){
cson_object * zPayload = NULL; /* return value */
cson_array * zEntries = NULL; /* accumulated list of entries. */
cson_object * zEntry = NULL; /* a single dir/file entry. */
cson_array * keyStore = NULL; /* garbage collector for shared strings. */
cson_string * zKeyName = NULL;
cson_string * zKeySize = NULL;
cson_string * zKeyIsDir = NULL;
|
| ︙ | ︙ | |||
279 280 281 282 283 284 285 | return cson_object_value(zPayload); } /* ** Implements the /json/dir family of pages/commands. ** */ | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
return cson_object_value(zPayload);
}
/*
** Implements the /json/dir family of pages/commands.
**
*/
cson_value * json_page_dir(void){
#if 1
return json_page_dir_list();
#else
return json_page_dispatch_helper(&JsonPageDefs_Dir[0]);
#endif
}
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_finfo.c.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 | #include "json_detail.h" #endif /* ** Implements the /json/finfo page/command. ** */ | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include "json_detail.h"
#endif
/*
** Implements the /json/finfo page/command.
**
*/
cson_value * json_page_finfo(void){
cson_object * pay = NULL;
cson_array * checkins = NULL;
char const * zFilename = NULL;
Blob sql = empty_blob;
Stmt q = empty_Stmt;
char const * zAfter = NULL;
char const * zBefore = NULL;
|
| ︙ | ︙ |
Changes to src/json_login.c.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | #endif /* ** Implementation of the /json/login page. ** */ | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#endif
/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(void){
char preciseErrors = /* if true, "complete" JSON error codes are used,
else they are "dumbed down" to a generic login
error code.
*/
#if 1
g.json.errorDetailParanoia ? 0 : 1
#else
|
| ︙ | ︙ | |||
179 180 181 182 183 184 185 | } } /* ** Impl of /json/logout. ** */ | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
}
}
/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(void){
cson_value const *token = g.json.authToken;
/* Remember that json_bootstrap_late() replaces the login cookie
with the JSON auth token if the request contains it. If the
request is missing the auth token then this will fetch fossil's
original cookie. Either way, it's what we want :).
We require the auth token to avoid someone maliciously
|
| ︙ | ︙ | |||
205 206 207 208 209 210 211 | } return json_page_whoami(); } /* ** Implementation of the /json/anonymousPassword page. */ | | | | 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 |
}
return json_page_whoami();
}
/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(void){
cson_value * v = cson_value_new_object();
cson_object * o = cson_value_get_object(v);
unsigned const int seed = captcha_seed();
char const * zCaptcha = captcha_decode(seed);
cson_object_set(o, "seed",
cson_value_new_integer( (cson_int_t)seed )
);
cson_object_set(o, "password",
cson_value_new_string( zCaptcha, strlen(zCaptcha) )
);
return v;
}
/*
** Implements the /json/whoami page/command.
*/
cson_value * json_page_whoami(void){
cson_value * payload = NULL;
cson_object * obj = NULL;
Stmt q;
if(!g.json.authToken && g.userUid==0){
/* assume we just logged out. */
db_prepare(&q, "SELECT login, cap FROM user WHERE login='nobody'");
}
|
| ︙ | ︙ |
Changes to src/json_query.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | ** ** format=string 'a' means each row is an Array of values, 'o' ** (default) creates each row as an Object. ** ** TODO: in CLI mode (only) use -S FILENAME to read the sql ** from a file. */ | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
**
** format=string 'a' means each row is an Array of values, 'o'
** (default) creates each row as an Object.
**
** TODO: in CLI mode (only) use -S FILENAME to read the sql
** from a file.
*/
cson_value * json_page_query(void){
char const * zSql = NULL;
cson_value * payV;
char const * zFmt;
Stmt q = empty_Stmt;
int check;
if(!g.perm.Admin && !g.perm.Setup){
json_set_err(FSL_JSON_E_DENIED,
|
| ︙ | ︙ |
Changes to src/json_report.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | #include "json_report.h" #if INTERFACE #include "json_detail.h" #endif | | | | | | | | 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 |
#include "json_report.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_report_create(void);
static cson_value * json_report_get(void);
static cson_value * json_report_list(void);
static cson_value * json_report_run(void);
static cson_value * json_report_save(void);
/*
** Mapping of /json/report/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Report[] = {
{"create", json_report_create, 0},
{"get", json_report_get, 0},
{"list", json_report_list, 0},
{"run", json_report_run, 0},
{"save", json_report_save, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implementation of the /json/report page.
**
**
*/
cson_value * json_page_report(void){
if(!g.perm.RdTkt && !g.perm.NewTkt ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'r' or 'n' permissions.");
return NULL;
}
return json_page_dispatch_helper(JsonPageDefs_Report);
}
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
if(arg && fossil_isdigit(*arg)) {
nReport = atoi(arg);
}
}
return nReport;
}
| | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
if(arg && fossil_isdigit(*arg)) {
nReport = atoi(arg);
}
}
return nReport;
}
static cson_value * json_report_create(void){
json_set_err(FSL_JSON_E_NYI, NULL);
return NULL;
}
static cson_value * json_report_get(void){
int nReport;
Stmt q = empty_Stmt;
cson_value * pay = NULL;
if(!g.perm.TktFmt){
json_set_err(FSL_JSON_E_DENIED,
"Requires 't' privileges.");
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 | db_finalize(&q); return pay; } /* ** Impl of /json/report/list. */ | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
db_finalize(&q);
return pay;
}
/*
** Impl of /json/report/list.
*/
static cson_value * json_report_list(void){
Blob sql = empty_blob;
cson_value * pay = NULL;
if(!g.perm.RdTkt){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'r' privileges.");
return NULL;
}
|
| ︙ | ︙ | |||
156 157 158 159 160 161 162 | ** report=int (CLI: -report # or -r #) is the report number to run. ** ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands. ** ** format=a|o Specifies result format: a=each row is an arry, o=each ** row is an object. Default=o. */ | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
** report=int (CLI: -report # or -r #) is the report number to run.
**
** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands.
**
** format=a|o Specifies result format: a=each row is an arry, o=each
** row is an object. Default=o.
*/
static cson_value * json_report_run(void){
int nReport;
Stmt q = empty_Stmt;
cson_object * pay = NULL;
cson_array * tktList = NULL;
char const * zFmt;
char * zTitle = NULL;
Blob sql = empty_blob;
|
| ︙ | ︙ | |||
253 254 255 256 257 258 259 | pay = NULL; end: return pay ? cson_object_value(pay) : NULL; } | | | 253 254 255 256 257 258 259 260 261 262 263 |
pay = NULL;
end:
return pay ? cson_object_value(pay) : NULL;
}
static cson_value * json_report_save(void){
return NULL;
}
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_tag.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | #include "json_tag.h" #if INTERFACE #include "json_detail.h" #endif | | | | | | | | 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 |
#include "json_tag.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_tag_add(void);
static cson_value * json_tag_cancel(void);
static cson_value * json_tag_find(void);
static cson_value * json_tag_list(void);
/*
** Mapping of /json/tag/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Tag[] = {
{"add", json_tag_add, 0},
{"cancel", json_tag_cancel, 0},
{"find", json_tag_find, 0},
{"list", json_tag_list, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implements the /json/tag family of pages/commands.
**
*/
cson_value * json_page_tag(void){
return json_page_dispatch_helper(&JsonPageDefs_Tag[0]);
}
/*
** Impl of /json/tag/add.
*/
static cson_value * json_tag_add(void){
cson_value * payV = NULL;
cson_object * pay = NULL;
char const * zName = NULL;
char const * zCheckin = NULL;
char fRaw = 0;
char fPropagate = 0;
char const * zValue = NULL;
|
| ︙ | ︙ | |||
136 137 138 139 140 141 142 | return payV; } /* ** Impl of /json/tag/cancel. */ | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
return payV;
}
/*
** Impl of /json/tag/cancel.
*/
static cson_value * json_tag_cancel(void){
char const * zName = NULL;
char const * zCheckin = NULL;
char fRaw = 0;
const char *zPrefix = NULL;
if( !g.perm.Write ){
json_set_err(FSL_JSON_E_DENIED,
|
| ︙ | ︙ | |||
186 187 188 189 190 191 192 | return NULL; } /* ** Impl of /json/tag/find. */ | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
return NULL;
}
/*
** Impl of /json/tag/find.
*/
static cson_value * json_tag_find(void){
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
char const * zName = NULL;
char const * zType = NULL;
char const * zType2 = NULL;
|
| ︙ | ︙ | |||
321 322 323 324 325 326 327 | /* ** Impl for /json/tag/list ** ** TODOs: ** ** Add -type TYPE (ci, w, e, t) */ | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
/*
** Impl for /json/tag/list
**
** TODOs:
**
** Add -type TYPE (ci, w, e, t)
*/
static cson_value * json_tag_list(void){
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_value const * tagsVal = NULL;
char const * zCheckin = NULL;
char fRaw = 0;
char fTicket = 0;
Stmt q = empty_Stmt;
|
| ︙ | ︙ |
Changes to src/json_timeline.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | #include "config.h" #include "json_timeline.h" #if INTERFACE #include "json_detail.h" #endif | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include "config.h"
#include "json_timeline.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_timeline_branch(void);
static cson_value * json_timeline_ci(void);
static cson_value * json_timeline_ticket(void);
/*
** Mapping of /json/timeline/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Timeline[] = {
/* the short forms are only enabled in CLI mode, to avoid
that we end up with HTTP clients using 3 different names
for the same requests.
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 | /* ** Implements the /json/timeline family of pages/commands. Far from ** complete. ** */ | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
/*
** Implements the /json/timeline family of pages/commands. Far from
** complete.
**
*/
cson_value * json_page_timeline(void){
#if 0
/* The original timeline code does not require 'h' access,
but it arguably should. For JSON mode i think one could argue
that History permissions are required.
*/
if(! g.perm.Hyperlink && !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED, "Timeline requires 'h' or 'o' access.");
|
| ︙ | ︙ | |||
362 363 364 365 366 367 368 |
cson_object_set(row, "downloadPath", json_new_string(zDownload));
free(zDownload);
}
db_finalize(&q);
return rowsV;
}
| | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
cson_object_set(row, "downloadPath", json_new_string(zDownload));
free(zDownload);
}
db_finalize(&q);
return rowsV;
}
static cson_value * json_timeline_branch(void){
cson_value * pay = NULL;
Blob sql = empty_blob;
Stmt q = empty_Stmt;
int limit = 0;
if(!g.perm.Read){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' permissions.");
|
| ︙ | ︙ | |||
443 444 445 446 447 448 449 | /* ** Implementation of /json/timeline/ci. ** ** Still a few TODOs (like figuring out how to structure ** inheritance info). */ | | | 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
/*
** Implementation of /json/timeline/ci.
**
** Still a few TODOs (like figuring out how to structure
** inheritance info).
*/
static cson_value * json_timeline_ci(void){
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
char verboseFlag;
|
| ︙ | ︙ | |||
524 525 526 527 528 529 530 | return payV; } /* ** Implementation of /json/timeline/event. ** */ | | | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
return payV;
}
/*
** Implementation of /json/timeline/event.
**
*/
cson_value * json_timeline_event(void){
/* This code is 95% the same as json_timeline_ci(), by the way. */
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
|
| ︙ | ︙ | |||
580 581 582 583 584 585 586 | return payV; } /* ** Implementation of /json/timeline/wiki. ** */ | | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
return payV;
}
/*
** Implementation of /json/timeline/wiki.
**
*/
cson_value * json_timeline_wiki(void){
/* This code is 95% the same as json_timeline_ci(), by the way. */
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
|
| ︙ | ︙ | |||
641 642 643 644 645 646 647 | return payV; } /* ** Implementation of /json/timeline/ticket. ** */ | | | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
return payV;
}
/*
** Implementation of /json/timeline/ticket.
**
*/
static cson_value * json_timeline_ticket(void){
/* This code is 95% the same as json_timeline_ci(), by the way. */
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
|
| ︙ | ︙ |
Changes to src/json_user.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "config.h" #include "json_user.h" #if INTERFACE #include "json_detail.h" #endif | | | | | | | 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 |
#include "config.h"
#include "json_user.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_user_get(void);
static cson_value * json_user_list(void);
static cson_value * json_user_save(void);
/*
** Mapping of /json/user/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_User[] = {
{"save", json_user_save, 0},
{"get", json_user_get, 0},
{"list", json_user_list, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implements the /json/user family of pages/commands.
**
*/
cson_value * json_page_user(void){
return json_page_dispatch_helper(&JsonPageDefs_User[0]);
}
/*
** Impl of /json/user/list. Requires admin/setup rights.
*/
static cson_value * json_user_list(void){
cson_value * payV = NULL;
Stmt q;
if(!g.perm.Admin && !g.perm.Setup){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'a' or 's' privileges.");
return NULL;
}
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 | return u; } /* ** Impl of /json/user/get. Requires admin or setup rights. */ | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
return u;
}
/*
** Impl of /json/user/get. Requires admin or setup rights.
*/
static cson_value * json_user_get(void){
cson_value * payV = NULL;
char const * pUser = NULL;
if(!g.perm.Admin && !g.perm.Setup){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'a' or 's' privileges.");
return NULL;
}
|
| ︙ | ︙ | |||
391 392 393 394 395 396 397 | return g.json.resultCode; } /* ** Impl of /json/user/save. */ | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
return g.json.resultCode;
}
/*
** Impl of /json/user/save.
*/
static cson_value * json_user_save(void){
/* try to get user info from GET/CLI args and construct
a JSON form of it... */
cson_object * u = cson_new_object();
char const * str = NULL;
int b = -1;
int i = -1;
int uid = -1;
|
| ︙ | ︙ |
Changes to src/json_wiki.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "config.h" #include "json_wiki.h" #if INTERFACE #include "json_detail.h" #endif | | | | | | | | | 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 |
#include "config.h"
#include "json_wiki.h"
#if INTERFACE
#include "json_detail.h"
#endif
static cson_value * json_wiki_create(void);
static cson_value * json_wiki_get(void);
static cson_value * json_wiki_list(void);
static cson_value * json_wiki_preview(void);
static cson_value * json_wiki_save(void);
static cson_value * json_wiki_diff(void);
/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"create", json_wiki_create, 0},
{"diff", json_wiki_diff, 0},
{"get", json_wiki_get, 0},
{"list", json_wiki_list, 0},
{"preview", json_wiki_preview, 0},
{"save", json_wiki_save, 0},
{"timeline", json_timeline_wiki,0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};
/*
** Implements the /json/wiki family of pages/commands.
**
*/
cson_value * json_page_wiki(void){
return json_page_dispatch_helper(JsonPageDefs_Wiki);
}
/*
** Returns the UUID for the given wiki blob RID, or NULL if not
** found. The returned string is allocated via db_text() and must be
** free()d by the caller.
|
| ︙ | ︙ | |||
242 243 244 245 246 247 248 | } } /* ** Implementation of /json/wiki/get. ** */ | | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
}
}
/*
** Implementation of /json/wiki/get.
**
*/
static cson_value * json_wiki_get(void){
char const * zPageName;
char const * zSymName = NULL;
int contentFormat = -1;
if( !g.perm.RdWiki && !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' or 'j' access.");
return NULL;
|
| ︙ | ︙ | |||
272 273 274 275 276 277 278 | contentFormat = json_wiki_get_content_format_flag(contentFormat); return json_wiki_get_by_name_or_symname( zPageName, zSymName, contentFormat ); } /* ** Implementation of /json/wiki/preview. */ | | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
contentFormat = json_wiki_get_content_format_flag(contentFormat);
return json_wiki_get_by_name_or_symname( zPageName, zSymName, contentFormat );
}
/*
** Implementation of /json/wiki/preview.
*/
static cson_value * json_wiki_preview(void){
char const * zContent = NULL;
char const * zMime = NULL;
cson_string * sContent = NULL;
cson_value * pay = NULL;
Blob contentOrig = empty_blob;
Blob contentHtml = empty_blob;
if( !g.perm.WrWiki ){
|
| ︙ | ︙ | |||
447 448 449 450 451 452 453 | return payV; } /* ** Implementation of /json/wiki/create. */ | | | | | 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 |
return payV;
}
/*
** Implementation of /json/wiki/create.
*/
static cson_value * json_wiki_create(void){
return json_wiki_create_or_save(1,0);
}
/*
** Implementation of /json/wiki/save.
*/
static cson_value * json_wiki_save(void){
char const createIfNotExists = json_getenv_bool("createIfNotExists",0);
return json_wiki_create_or_save(0,createIfNotExists);
}
/*
** Implementation of /json/wiki/list.
*/
static cson_value * json_wiki_list(void){
cson_value * listV = NULL;
cson_array * list = NULL;
char const * zGlob = NULL;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
char const verbose = json_find_option_bool("verbose",NULL,"v",0);
char fInvert = json_find_option_bool("invert",NULL,"i",0);;
|
| ︙ | ︙ | |||
529 530 531 532 533 534 535 | cson_value_free(listV); listV = NULL; end: db_finalize(&q); return listV; } | | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
cson_value_free(listV);
listV = NULL;
end:
db_finalize(&q);
return listV;
}
static cson_value * json_wiki_diff(void){
char const * zV1 = NULL;
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;
|
| ︙ | ︙ |
Changes to src/loadctrl.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 |
}
#endif
style_set_current_feature("test");
style_header("Server Overload");
@ <h2>The server load is currently too high.
@ Please try again later.</h2>
| | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
}
#endif
style_set_current_feature("test");
style_header("Server Overload");
@ <h2>The server load is currently too high.
@ Please try again later.</h2>
@ <p>Current load average: %f(load_average()).<br>
@ Load average limit: %f(mxLoad)</p>
style_finish_page();
cgi_set_status(503,"Server Overload");
cgi_reply();
exit(0);
}
|
Changes to src/login.c.
| ︙ | ︙ | |||
735 736 737 738 739 740 741 |
&& db_get_boolean("https-login",0)
){
form_begin(0, "https:%s/login", g.zBaseURL+5);
}else{
form_begin(0, "%R/login");
}
if( zGoto ){
| | | | 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
&& db_get_boolean("https-login",0)
){
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>
@ </form>
}else{
unsigned int uSeed = captcha_seed();
|
| ︙ | ︙ | |||
773 774 775 776 777 778 779 |
@ <td class="form_label" id="userlabel1">User ID:</td>
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
@ size="30" value="%s(anonFlag?"anonymous":"")"></td>
@ </tr>
@ <tr>
@ <td class="form_label" id="pswdlabel">Password:</td>
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
| | | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
@ <td class="form_label" id="userlabel1">User ID:</td>
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
@ size="30" value="%s(anonFlag?"anonymous":"")"></td>
@ </tr>
@ <tr>
@ <td class="form_label" id="pswdlabel">Password:</td>
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
@ name="p" value="" size="30">\
if( zAnonPw && !noAnon ){
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
}
@ </td>
@ </tr>
@ <tr>
@ <td></td>
|
| ︙ | ︙ | |||
807 808 809 810 811 812 813 |
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
| | | | 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 |
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
@ Visitors may enter <b>anonymous</b> as the user-ID with
@ the 8-character hexadecimal password shown below:</p>
@ <div class="captcha"><table class="captcha"><tr><td>\
@ <pre class="captcha">
@ %h(zCaptcha)
@ </pre></td></tr></table>
if( bAutoCaptcha ) {
@ <input type="button" value="Fill out captcha" id='autofillButton' \
@ data-af='%s(zDecoded)'>
builtin_request_js("login.js");
}
@ </div>
free(zCaptcha);
}
@ </form>
}
|
| ︙ | ︙ | |||
846 847 848 849 850 851 852 |
form_begin(0, "%R/login");
@ <table>
@ <tr><td class="form_label" id="oldpw">Old Password:</td>
@ <td><input aria-labelledby="oldpw" type="password" name="p" \
@ size="30"/></td></tr>
@ <tr><td class="form_label" id="newpw">New Password:</td>
@ <td><input aria-labelledby="newpw" type="password" name="n1" \
| | | | | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 |
form_begin(0, "%R/login");
@ <table>
@ <tr><td class="form_label" id="oldpw">Old Password:</td>
@ <td><input aria-labelledby="oldpw" type="password" name="p" \
@ size="30"/></td></tr>
@ <tr><td class="form_label" id="newpw">New Password:</td>
@ <td><input aria-labelledby="newpw" type="password" name="n1" \
@ size="30"> Suggestion: %z(zRPW)</td></tr>
@ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
@ <td><input aria-labledby="reppw" type="password" name="n2" \
@ size="30"></td></tr>
@ <tr><td></td>
@ <td><input type="submit" value="Change Password"></td></tr>
@ </table>
@ </form>
}
}
style_finish_page();
}
|
| ︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 | zRPW = fossil_random_password(12); @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/resetpw"); @ <input type='hidden' name='name' value='%h(zName)'> @ <table> @ <tr><td class="form_label" id="newpw">New Password:</td> @ <td><input aria-labelledby="newpw" type="password" name="n1" \ | | | | | 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 | zRPW = fossil_random_password(12); @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/resetpw"); @ <input type='hidden' name='name' value='%h(zName)'> @ <table> @ <tr><td class="form_label" id="newpw">New Password:</td> @ <td><input aria-labelledby="newpw" type="password" name="n1" \ @ size="30"> Suggestion: %z(zRPW)</td></tr> @ <tr><td class="form_label" id="reppw">Repeat New Password:</td> @ <td><input aria-labledby="reppw" type="password" name="n2" \ @ size="30"></td></tr> @ <tr><td></td> @ <td><input type="submit" value="Change Password"></td></tr> @ </table> @ </form> style_finish_page(); } /* ** Attempt to find login credentials for user zLogin on a peer repository |
| ︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 |
** 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", "");
| | | | 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 |
** 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>
}
}
/*
** While rendering a form, call this routine to add the Anti-CSRF token
** as a hidden element of the form.
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Before using the results of a form, first call this routine to verify
** that this Anti-CSRF token is present and is valid. If the Anti-CSRF token
** is missing or is incorrect, that indicates a cross-site scripting attack.
** If the event of an attack is detected, an error message is generated and
|
| ︙ | ︙ | |||
2121 2122 2123 2124 2125 2126 2127 |
zCaptcha = captcha_render(zDecoded);
style_header("Register");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/register");
if( P("g") ){
| | | | 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 |
zCaptcha = captcha_render(zDecoded);
style_header("Register");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/register");
if( P("g") ){
@ <input type="hidden" name="g" value="%h(P("g"))">
}
@ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <table class="login_out">
@ <tr>
@ <td class="form_label" align="right" id="uid">User ID:</td>
@ <td><input aria-labelledby="uid" type="text" name="u" \
@ value="%h(zUserID)" size="30"></td>
@
if( iErrLine==1 ){
|
| ︙ | ︙ | |||
2150 2151 2152 2153 2154 2155 2156 |
@ <td class="form_label" align="right" id="emaddr">Email Address:</td>
@ <td><input aria-labelledby="emaddr" type="text" name="ea" \
@ value="%h(zEAddr)" size="30"></td>
@ </tr>
if( iErrLine==3 ){
@ <tr><td><td><span class='loginError'>↑ %h(zErr)</span>
if( uid>0 && login_self_password_reset_available() ){
| | | 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 |
@ <td class="form_label" align="right" id="emaddr">Email Address:</td>
@ <td><input aria-labelledby="emaddr" type="text" name="ea" \
@ value="%h(zEAddr)" size="30"></td>
@ </tr>
if( iErrLine==3 ){
@ <tr><td><td><span class='loginError'>↑ %h(zErr)</span>
if( uid>0 && login_self_password_reset_available() ){
@ <br>
@ <input type="submit" name="pwreset" \
@ value="Request Password Reset For %h(zEAddr)">
}
@ </td></tr>
}
if( canDoAlerts ){
int a = atoi(PD("alerts","1"));
|
| ︙ | ︙ | |||
2198 2199 2200 2201 2202 2203 2204 |
captcha_speakit_button(uSeed, "Speak the captcha text");
@ </td>
@ </tr>
if( iErrLine==6 ){
@ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr>
}
@ <tr><td></td>
| | | 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 |
captcha_speakit_button(uSeed, "Speak the captcha text");
@ </td>
@ </tr>
if( iErrLine==6 ){
@ <tr><td><td><span class='loginError'>↑ %h(zErr)</span></td></tr>
}
@ <tr><td></td>
@ <td><input type="submit" name="new" value="Register"></td></tr>
@ </table>
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
@ %h(zCaptcha)
@ </pre>
@ Enter this 8-letter code in the "Captcha" box above.
@ </td></tr></table></div>
@ </form>
|
| ︙ | ︙ | |||
2326 2327 2328 2329 2330 2331 2332 |
zDecoded = captcha_decode(uSeed);
zCaptcha = captcha_render(zDecoded);
style_header("Request Password Reset");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/reqpwreset");
| | | | 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 |
zDecoded = captcha_decode(uSeed);
zCaptcha = captcha_render(zDecoded);
style_header("Request Password Reset");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/reqpwreset");
@ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <p><input type="hidden" name="reqpwreset" value="1">
@ <table class="login_out">
@ <tr>
@ <td class="form_label" align="right" id="emaddr">Email Address:</td>
@ <td><input aria-labelledby="emaddr" type="text" name="ea" \
@ value="%h(zEAddr)" size="30"></td>
@ </tr>
if( iErrLine==1 ){
|
| ︙ | ︙ |
Changes to src/lookslike.c.
| ︙ | ︙ | |||
341 342 343 344 345 346 347 |
*/
int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
const char *z = blob_buffer(pContent);
int bomSize = 0;
const unsigned char *bom = get_utf8_bom(&bomSize);
if( pnByte ) *pnByte = bomSize;
| | | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
*/
int starts_with_utf8_bom(const Blob *pContent, int *pnByte){
const char *z = blob_buffer(pContent);
int bomSize = 0;
const unsigned char *bom = get_utf8_bom(&bomSize);
if( pnByte ) *pnByte = bomSize;
if( (int)blob_size(pContent)<bomSize ) return 0;
return memcmp(z, bom, bomSize)==0;
}
/*
** This function returns non-zero if the blob starts with a UTF-16
** byte-order-mark (BOM), either in the endianness of the machine
** or in reversed byte order. The UTF-32 BOM is ruled out by checking
|
| ︙ | ︙ | |||
458 459 460 461 462 463 464 |
fossil_print("Has flag LOOK_LONG: %s\n",(lookFlags&LOOK_LONG)?"yes":"no");
fossil_print("Has flag LOOK_INVALID: %s\n",
(lookFlags&LOOK_INVALID)?"yes":"no");
fossil_print("Has flag LOOK_ODD: %s\n",(lookFlags&LOOK_ODD)?"yes":"no");
fossil_print("Has flag LOOK_SHORT: %s\n",(lookFlags&LOOK_SHORT)?"yes":"no");
blob_reset(&blob);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
fossil_print("Has flag LOOK_LONG: %s\n",(lookFlags&LOOK_LONG)?"yes":"no");
fossil_print("Has flag LOOK_INVALID: %s\n",
(lookFlags&LOOK_INVALID)?"yes":"no");
fossil_print("Has flag LOOK_ODD: %s\n",(lookFlags&LOOK_ODD)?"yes":"no");
fossil_print("Has flag LOOK_SHORT: %s\n",(lookFlags&LOOK_SHORT)?"yes":"no");
blob_reset(&blob);
}
/*
** Return true if z[i] is the whole word given by zWord
*/
static int isWholeWord(const char *z, unsigned int i, const char *zWord, int n){
if( i>0 && fossil_isalnum(z[i-1]) ) return 0;
if( sqlite3_strnicmp(z+i, zWord, n)!=0 ) return 0;
if( fossil_isalnum(z[i+n]) ) return 0;
return 1;
}
/*
** Returns true if the given text contains certain keywords or
** punctuation which indicate that it might be an SQL injection attempt
** or some other kind of mischief.
**
** This is not a defense against vulnerabilities in the Fossil code.
** Rather, this is part of an effort to do early detection of malicious
** spiders to avoid them using up too many CPU cycles.
*/
int looks_like_sql_injection(const char *zTxt){
unsigned int i;
if( zTxt==0 ) return 0;
for(i=0; zTxt[i]; i++){
switch( zTxt[i] ){
case ';':
case '\'':
return 1;
case '/': /* 0123456789 123456789 */
if( strncmp(zTxt+i+1, "/wp-content/plugins/", 20)==0 ) return 1;
if( strncmp(zTxt+i+1, "/wp-admin/admin-ajax", 20)==0 ) return 1;
break;
case 'a':
case 'A':
if( isWholeWord(zTxt, i, "and", 3) ) return 1;
break;
case 'n':
case 'N':
if( isWholeWord(zTxt, i, "null", 4) ) return 1;
break;
case 'o':
case 'O':
if( isWholeWord(zTxt, i, "order", 5) ) return 1;
if( isWholeWord(zTxt, i, "or", 2) ) return 1;
break;
case 's':
case 'S':
if( isWholeWord(zTxt, i, "select", 6) ) return 1;
break;
case 'w':
case 'W':
if( isWholeWord(zTxt, i, "waitfor", 7) ) return 1;
break;
}
}
return 0;
}
/*
** This is a utility routine associated with the test-looks-like-sql-injection
** command.
**
** Read input from zInFile and print only those lines that look like they
** might be SQL injection.
**
** Or if bInvert is true, then show the opposite - those lines that do NOT
** look like SQL injection.
*/
static void show_sql_injection_lines(
const char *zInFile, /* Name of input file */
int bInvert, /* Invert the sense of the output (-v) */
int bDeHttpize /* De-httpize the inputs. (-d) */
){
FILE *in;
char zLine[10000];
if( zInFile==0 || strcmp(zInFile,"-")==0 ){
in = stdin;
}else{
in = fopen(zInFile, "rb");
if( in==0 ){
fossil_fatal("cannot open \"%s\" for reading\n", zInFile);
}
}
while( fgets(zLine, sizeof(zLine), in) ){
dehttpize(zLine);
if( (looks_like_sql_injection(zLine)!=0) ^ bInvert ){
fossil_print("%s", zLine);
}
}
if( in!=stdin ) fclose(in);
}
/*
** COMMAND: test-looks-like-sql-injection
**
** Read lines of input from files named as arguments (or from standard
** input if no arguments are provided) and print those that look like they
** might be part of an SQL injection attack.
**
** Used to test the looks_lide_sql_injection() utility subroutine, possibly
** by piping in actual server log data.
*/
void test_looks_like_sql_injection(void){
int i;
int bInvert = find_option("invert","v",0)!=0;
int bDeHttpize = find_option("dehttpize","d",0)!=0;
verify_all_options();
if( g.argc==2 ){
show_sql_injection_lines(0, bInvert, bDeHttpize);
}
for(i=2; i<g.argc; i++){
show_sql_injection_lines(g.argv[i], bInvert, bDeHttpize);
}
}
|
Changes to src/main.c.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> # define isatty(h) _isatty(h) # define GETPID (int)GetCurrentProcessId #endif #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ | > > > > > > > > > > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> # define isatty(h) _isatty(h) # define GETPID (int)GetCurrentProcessId #endif /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ #if USE_SEE #if defined(_WIN32) typedef DWORD PID_T; #else typedef pid_t PID_T; #endif #endif #include "main.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ |
| ︙ | ︙ | |||
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
void *xPostEval; /* Optional, called after Tcl_Eval*(). */
void *pPostContext; /* Optional, provided to xPostEval(). */
};
#endif
struct Global {
int argc; char **argv; /* Command-line arguments to the program */
char *nameOfExe; /* Full path of executable. */
const char *zErrlog; /* Log errors to this file, if not NULL */
const char *zPhase; /* Phase of operation, for use by the error log
** and for deriving $canonical_page TH1 variable */
int isConst; /* True if the output is unchanging & cacheable */
const char *zVfsName; /* The VFS to use for database connections */
sqlite3 *db; /* The connection to the databases */
| > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
void *xPostEval; /* Optional, called after Tcl_Eval*(). */
void *pPostContext; /* Optional, provided to xPostEval(). */
};
#endif
struct Global {
int argc; char **argv; /* Command-line arguments to the program */
char **argvOrig; /* Original g.argv prior to removing options */
char *nameOfExe; /* Full path of executable. */
const char *zErrlog; /* Log errors to this file, if not NULL */
const char *zPhase; /* Phase of operation, for use by the error log
** and for deriving $canonical_page TH1 variable */
int isConst; /* True if the output is unchanging & cacheable */
const char *zVfsName; /* The VFS to use for database connections */
sqlite3 *db; /* The connection to the databases */
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 |
Blob httpHeader; /* Complete text of the HTTP request header */
UrlData url; /* Information about current URL */
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
| | | | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
Blob httpHeader; /* Complete text of the HTTP request header */
UrlData url; /* Information about current URL */
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
|
| ︙ | ︙ | |||
425 426 427 428 429 430 431 | const char *zFileName; /* input file name */ FILE *inFile; /* input FILE */ g.argc = argc; g.argv = argv; sqlite3_initialize(); #if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE) | | | | | > > > > | 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 |
const char *zFileName; /* input file name */
FILE *inFile; /* input FILE */
g.argc = argc;
g.argv = argv;
sqlite3_initialize();
#if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE)
for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]);
#else
for(i=0; (int)i<g.argc; i++) g.argv[i] = fossil_path_to_utf8(g.argv[i]);
#endif
g.nameOfExe = file_fullexename(g.argv[0]);
for(i=1; (int)i<g.argc-1; i++){
z = g.argv[i];
if( z[0]!='-' ) continue;
z++;
if( z[0]=='-' ) z++;
/* Maintenance reminder: we do not stop at a "--" flag here,
** instead delegating that to find_option(). Doing it here
** introduces some weird corner cases, as covered in forum thread
** 4382bbc66757c39f. e.g. (fossil -U -- --args ...) is handled
** differently when we stop at "--" here. */
if( fossil_strcmp(z, "args")==0 ) break;
}
if( (int)i>=g.argc-1 ){
g.argvOrig = fossil_malloc( sizeof(char*)*(g.argc+1) );
memcpy(g.argvOrig, g.argv, sizeof(g.argv[0])*(g.argc+1));
return;
}
zFileName = g.argv[i+1];
if( strcmp(zFileName,"-")==0 ){
inFile = stdin;
}else if( !file_isfile(zFileName, ExtFILE) ){
fossil_fatal("Not an ordinary file: \"%s\"", zFileName);
}else{
|
| ︙ | ︙ | |||
465 466 467 468 469 470 471 |
}
inFile = NULL;
blob_to_utf8_no_bom(&file, 1);
z = blob_str(&file);
for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
if( nLine>100000000 ) fossil_fatal("too many command-line arguments");
nArg = g.argc + nLine*2;
| | | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
}
inFile = NULL;
blob_to_utf8_no_bom(&file, 1);
z = blob_str(&file);
for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
if( nLine>100000000 ) fossil_fatal("too many command-line arguments");
nArg = g.argc + nLine*2;
newArgv = fossil_malloc( sizeof(char*)*nArg*2 + 2);
for(j=0; j<i; j++) newArgv[j] = g.argv[j];
blob_rewind(&file);
while( nLine-->0 && (n = blob_line(&file, &line))>0 ){
/* Reminder: ^^^ nLine check avoids that embedded NUL bytes in the
** --args file causes nLine to be less than blob_line() will end
** up reporting, as such a miscount leads to an illegal memory
|
| ︙ | ︙ | |||
504 505 506 507 508 509 510 |
z[k] = 0;
k++;
if( z[k] ) newArgv[j++] = &z[k];
}
}
}
i += 2;
| | > > | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 |
z[k] = 0;
k++;
if( z[k] ) newArgv[j++] = &z[k];
}
}
}
i += 2;
while( (int)i<g.argc ) newArgv[j++] = g.argv[i++];
newArgv[j] = 0;
g.argc = j;
g.argv = newArgv;
g.argvOrig = &g.argv[j+1];
memcpy(g.argvOrig, g.argv, sizeof(g.argv[0])*(j+1));
}
#ifdef FOSSIL_ENABLE_TCL
/*
** Make a deep copy of the provided argument array and return it.
*/
static char **copy_args(int argc, char **argv){
|
| ︙ | ︙ | |||
791 792 793 794 795 796 797 |
g.zErrlog = find_option("errorlog", 0, 1);
fossil_init_flags_from_options();
if( find_option("utc",0,0) ) g.fTimeFormat = 1;
if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
if( zChdir && file_chdir(zChdir, 0) ){
fossil_fatal("unable to change directories to %s", zChdir);
}
| | < < < < < < < < < < < < < < | < < < | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 |
g.zErrlog = find_option("errorlog", 0, 1);
fossil_init_flags_from_options();
if( find_option("utc",0,0) ) g.fTimeFormat = 1;
if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
if( zChdir && file_chdir(zChdir, 0) ){
fossil_fatal("unable to change directories to %s", zChdir);
}
#if USE_SEE
db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ);
#endif
if( find_option("help",0,0)!=0 ){
/* If --help is found anywhere on the command line, translate the command
* to "fossil help cmdname" where "cmdname" is the first argument that
* does not begin with a "-" character. If all arguments start with "-",
* translate to "fossil help argv[1] argv[2]...". */
int i, nNewArgc;
|
| ︙ | ︙ | |||
1261 1262 1263 1264 1265 1266 1267 | #if defined(HAVE_PLEDGE) blob_append(pOut, "HAVE_PLEDGE\n", -1); #endif #if defined(USE_MMAN_H) blob_append(pOut, "USE_MMAN_H\n", -1); #endif #if defined(USE_SEE) | | > | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
#if defined(HAVE_PLEDGE)
blob_append(pOut, "HAVE_PLEDGE\n", -1);
#endif
#if defined(USE_MMAN_H)
blob_append(pOut, "USE_MMAN_H\n", -1);
#endif
#if defined(USE_SEE)
blob_appendf(pOut, "USE_SEE (%s)\n",
db_have_saved_encryption_key() ? "SET" : "UNSET");
#endif
#if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
#endif
if( g.db==0 ) sqlite3_open(":memory:", &g.db);
db_prepare(&q,
|
| ︙ | ︙ | |||
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 |
json_bootstrap_early();
}
#endif
/* If the repository has not been opened already, then find the
** repository based on the first element of PATH_INFO and open it.
*/
if( !g.repositoryOpen ){
char *zRepo; /* Candidate repository name */
char *zToFree = 0; /* Malloced memory that needs to be freed */
const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
char *zNewScript; /* Revised SCRIPT_NAME after processing */
int j, k; /* Loop variables */
i64 szFile; /* File size of the candidate repository */
| > > | 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 |
json_bootstrap_early();
}
#endif
/* If the repository has not been opened already, then find the
** repository based on the first element of PATH_INFO and open it.
*/
if( !g.repositoryOpen ){
char zBuf[24];
const char *zRepoExt = ".fossil";
char *zRepo; /* Candidate repository name */
char *zToFree = 0; /* Malloced memory that needs to be freed */
const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
char *zNewScript; /* Revised SCRIPT_NAME after processing */
int j, k; /* Loop variables */
i64 szFile; /* File size of the candidate repository */
|
| ︙ | ︙ | |||
1728 1729 1730 1731 1732 1733 1734 |
}
while( 1 ){
size_t nBase = strlen(zBase);
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
/* The candidate repository name is some prefix of the PATH_INFO
** with ".fossil" appended */
| | | 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 |
}
while( 1 ){
size_t nBase = strlen(zBase);
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
/* The candidate repository name is some prefix of the PATH_INFO
** with ".fossil" appended */
zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
if( g.fHttpTrace ){
@ <!-- Looking for repository named "%h(zRepo)" -->
fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
}
/* For safety -- to prevent an attacker from accessing arbitrary disk
|
| ︙ | ︙ | |||
1759 1760 1761 1762 1763 1764 1765 |
#endif
if( c=='/' ) continue;
if( c=='_' ) continue;
if( c=='-' && zRepo[j-1]!='/' ) continue;
if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
continue;
}
| | | 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 |
#endif
if( c=='/' ) continue;
if( c=='_' ) continue;
if( c=='-' && zRepo[j-1]!='/' ) continue;
if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
continue;
}
if( c=='.' && g.fAllowACME && j==(int)nBase+1
&& strncmp(&zRepo[j-1],"/.well-known/",12)==0
){
/* We allow .well-known as the top-level directory for ACME */
continue;
}
/* If we reach this point, it means that the request URI contains
** an illegal character or character combination. Provoke a
|
| ︙ | ︙ | |||
1786 1787 1788 1789 1790 1791 1792 |
** Special case: Assume any file with a basename of ".fossil" does
** not exist.
*/
zCleanRepo = file_cleanup_fullpath(zRepo);
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
szFile = file_size(zCleanRepo, ExtFILE);
if( g.fHttpTrace ){
| < | | 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 |
** Special case: Assume any file with a basename of ".fossil" does
** not exist.
*/
zCleanRepo = file_cleanup_fullpath(zRepo);
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
szFile = file_size(zCleanRepo, ExtFILE);
if( g.fHttpTrace ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
}
}
/* If no file named by zRepo exists, remove the added ".fossil" suffix
** and check to see if there is a file or directory with the same
** name as the raw PATH_INFO text.
*/
if( szFile<0 && i>0 ){
const char *zMimetype;
assert( file_is_repository_extension(&zRepo[j]) );
zRepo[j] = 0; /* Remove the ".fossil" suffix */
/* The PATH_INFO prefix seen so far is a valid directory.
** Continue the loop with the next element of the PATH_INFO */
if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
fossil_free(zToFree);
i++;
|
| ︙ | ︙ | |||
1824 1825 1826 1827 1828 1829 1830 |
** general-purpose web server. The "--file GLOB" mechanism is
** designed to allow the delivery of a few static images or HTML
** pages.
*/
if( pFileGlob!=0
&& file_isfile(zCleanRepo, ExtFILE)
&& glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
| | | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 |
** general-purpose web server. The "--file GLOB" mechanism is
** designed to allow the delivery of a few static images or HTML
** pages.
*/
if( pFileGlob!=0
&& file_isfile(zCleanRepo, ExtFILE)
&& glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
&& !file_contains_repository_extension(zRepo)
&& (zMimetype = mimetype_from_name(zRepo))!=0
&& strcmp(zMimetype, "application/x-fossil-artifact")!=0
){
Blob content;
blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
cgi_set_content_type(zMimetype);
cgi_set_content(&content);
|
| ︙ | ︙ | |||
1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 |
/* If we reach this point, it means that the search of the PATH_INFO
** string is finished. Either zRepo contains the name of the
** repository to be used, or else no repository could be found and
** some kind of error response is required.
*/
if( szFile<1024 ){
set_base_url(0);
if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
&& allowRepoList
&& repo_list_page() ){
/* Will return a list of repositories */
}else if( zNotFound ){
cgi_redirect(zNotFound);
| > > > > > > > | 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 |
/* If we reach this point, it means that the search of the PATH_INFO
** string is finished. Either zRepo contains the name of the
** repository to be used, or else no repository could be found and
** some kind of error response is required.
*/
if( szFile<1024 ){
#if USE_SEE
if( strcmp(zRepoExt,".fossil")==0 ){
fossil_free(zToFree);
zRepoExt = ".efossil";
continue;
}
#endif
set_base_url(0);
if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
&& allowRepoList
&& repo_list_page() ){
/* Will return a list of repositories */
}else if( zNotFound ){
cgi_redirect(zNotFound);
|
| ︙ | ︙ | |||
1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 |
*/
zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
zPathInfo += i;
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
db_open_repository(file_cleanup_fullpath(zRepo));
if( g.fHttpTrace ){
@ <!-- repository: "%h(zRepo)" -->
@ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
@ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
fprintf(stderr,
"# repository: [%s]\n"
| > > > > > > > > > > > > > > > > | 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 |
*/
zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
zPathInfo += i;
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
#if USE_SEE
if( zPathInfo ){
if( g.fHttpTrace ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i);
@ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) -->
fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo);
}
if( strcmp(zPathInfo,"/setseekey")==0
&& strcmp(zRepoExt,".efossil")==0
&& !db_have_saved_encryption_key() ){
db_set_see_key_page();
cgi_reply();
fossil_exit(0);
}
}
#endif
db_open_repository(file_cleanup_fullpath(zRepo));
if( g.fHttpTrace ){
@ <!-- repository: "%h(zRepo)" -->
@ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
@ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
fprintf(stderr,
"# repository: [%s]\n"
|
| ︙ | ︙ | |||
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 | ** ** repolist When in "directory:" mode, display a page ** showing a list of available repositories if ** the URL is "/". ** ** localauth Grant administrator privileges to connections ** from 127.0.0.1 or ::1. ** ** skin: LABEL Use the built-in skin called LABEL rather than ** the default. If there are no skins called LABEL ** then this line is a no-op. ** ** files: GLOBLIST GLOBLIST is a comma-separated list of GLOB ** patterns that specify files that can be | > > > > | 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 | ** ** repolist When in "directory:" mode, display a page ** showing a list of available repositories if ** the URL is "/". ** ** localauth Grant administrator privileges to connections ** from 127.0.0.1 or ::1. ** ** nossl Signal that no SSL connections are available. ** ** nocompress Do not compress HTTP replies. ** ** skin: LABEL Use the built-in skin called LABEL rather than ** the default. If there are no skins called LABEL ** then this line is a no-op. ** ** files: GLOBLIST GLOBLIST is a comma-separated list of GLOB ** patterns that specify files that can be |
| ︙ | ︙ | |||
2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 |
/* localauth
**
** Grant "administrator" privileges to users connecting with HTTP
** from IP address 127.0.0.1. Do not bother checking credentials.
*/
g.useLocalauth = 1;
continue;
}
if( blob_eq(&key, "repolist") ){
/* repolist
**
** If using "directory:" and the URL is "/" then generate a page
** showing a list of available repositories.
*/
| > > > > > > > > > > > > > > > > | 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 |
/* localauth
**
** Grant "administrator" privileges to users connecting with HTTP
** from IP address 127.0.0.1. Do not bother checking credentials.
*/
g.useLocalauth = 1;
continue;
}
if( blob_eq(&key, "nossl") ){
/* nossl
**
** Signal that no SSL connections are available.
*/
g.sslNotAvailable = 1;
continue;
}
if( blob_eq(&key, "nocompress") ){
/* nocompress
**
** Do not compress HTTP replies.
*/
g.fNoHttpCompress = 1;
continue;
}
if( blob_eq(&key, "repolist") ){
/* repolist
**
** If using "directory:" and the URL is "/" then generate a page
** showing a list of available repositories.
*/
|
| ︙ | ︙ | |||
2586 2587 2588 2589 2590 2591 2592 |
}else{
db_open_repository(zRepo);
}
}
}
}
| | | | | | > | > | > | | | 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 |
}else{
db_open_repository(zRepo);
}
}
}
}
#if USE_SEE
/*
** This function attempts to parse a string value in the following
** format:
**
** "%lu:%p:%u"
**
** There are three parts, which must be delimited by colons. The
** first part is an unsigned long integer in base-10 (decimal) format.
** The second part is a numerical representation of a native pointer,
** in the appropriate implementation defined format. The third part
** is an unsigned integer in base-10 (decimal) format.
**
** If the specified value cannot be parsed, for any reason, a fatal
** error will be raised and the process will be terminated.
*/
void parse_pid_key_value(
const char *zPidKey, /* The value to be parsed. */
PID_T *pProcessId, /* The extracted process identifier. */
LPVOID *ppAddress, /* The extracted pointer value. */
SIZE_T *pnSize /* The extracted size value. */
){
unsigned long processId = 0;
unsigned int nSize = 0;
if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){
*pProcessId = (PID_T)processId;
*pnSize = (SIZE_T)nSize;
}else{
fossil_fatal("failed to parse pid key");
}
}
#endif
/*
** WEBPAGE: test-pid
**
** Return the process identifier of the running Fossil server instance.
**
** Query parameters:
**
** usepidkey When present and available, also return the
** address and size, within this server process,
** of the saved database encryption key. This
** is only supported when using SEE on Windows
** or Linux.
*/
void test_pid_page(void){
login_check_credentials();
if( !g.perm.Setup ){ login_needed(0); return; }
#if USE_SEE
if( P("usepidkey")!=0 ){
if( g.zPidKey ){
@ %s(g.zPidKey)
return;
}else{
const char *zSavedKey = db_get_saved_encryption_key();
size_t savedKeySize = db_get_saved_encryption_key_size();
if( zSavedKey!=0 && savedKeySize>0 ){
@ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize)
return;
}
}
}
#endif
@ %d(GETPID())
}
|
| ︙ | ︙ | |||
2741 2742 2743 2744 2745 2746 2747 |
** --pkey FILE Read the private key used for TLS from FILE
** --repolist If REPOSITORY is directory, URL "/" lists all repos
** --scgi Interpret input as SCGI rather than HTTP
** --skin LABEL Use override skin LABEL. Use an empty string ("")
** to force use of the current local skin config.
** --th-trace Trace TH1 execution (for debugging purposes)
** --usepidkey Use saved encryption key from parent process. This is
| | | 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 |
** --pkey FILE Read the private key used for TLS from FILE
** --repolist If REPOSITORY is directory, URL "/" lists all repos
** --scgi Interpret input as SCGI rather than HTTP
** --skin LABEL Use override skin LABEL. Use an empty string ("")
** to force use of the current local skin config.
** --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 or Linux.
**
** See also: [[cgi]], [[server]], [[winsrv]]
*/
void cmd_http(void){
const char *zIpAddr = 0;
const char *zNotFound;
const char *zHost;
|
| ︙ | ︙ | |||
3082 3083 3084 3085 3086 3087 3088 | ** --pkey FILE Read the private key used for TLS from FILE ** -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 | | | 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 |
** --pkey FILE Read the private key used for TLS from FILE
** -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 or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
int iPort, mxPort; /* Range of TCP ports allowed */
const char *zPort; /* Value of the --port option */
const char *zBrowser; /* Name of web browser program */
|
| ︙ | ︙ | |||
3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 |
int fNoBrowser = 0; /* Do not auto-launch web-browser */
const char *zInitPage = 0; /* Start on this page. --page option */
int findServerArg = 2; /* argv index for find_server_repository() */
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
const char *zJsMode; /* The --jsmode parameter */
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
#if defined(_WIN32)
const char *zStopperFile; /* Name of file used to terminate server */
zStopperFile = find_option("stopper", 0, 1);
#endif
if( g.zErrlog==0 ){
| > > > > | 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 |
int fNoBrowser = 0; /* Do not auto-launch web-browser */
const char *zInitPage = 0; /* Start on this page. --page option */
int findServerArg = 2; /* argv index for find_server_repository() */
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
const char *zJsMode; /* The --jsmode parameter */
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
#if USE_SEE
db_setup_for_saved_encryption_key();
#endif
#if defined(_WIN32)
const char *zStopperFile; /* Name of file used to terminate server */
zStopperFile = find_option("stopper", 0, 1);
#endif
if( g.zErrlog==0 ){
|
| ︙ | ︙ | |||
3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 |
** allow the container to shut down quickly.
**
** This has to happen ahead of the other signal() calls below.
** They apply after the HTTP hit is handled, but this one needs
** to be registered while we're waiting for that to occur.
**/
signal(SIGTERM, fossil_exit);
}
#endif /* !WIN32 */
/* Start up an HTTP server
*/
fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
" " MANIFEST_VERSION " " MANIFEST_DATE);
| > | 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 |
** allow the container to shut down quickly.
**
** This has to happen ahead of the other signal() calls below.
** They apply after the HTTP hit is handled, but this one needs
** to be registered while we're waiting for that to occur.
**/
signal(SIGTERM, fossil_exit);
signal(SIGINT, fossil_exit);
}
#endif /* !WIN32 */
/* Start up an HTTP server
*/
fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
" " MANIFEST_VERSION " " MANIFEST_DATE);
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
2113 2114 2115 2116 2117 2118 2119 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ | | | 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 |
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
-sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore,stackAlloc \
-sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \
-sENVIRONMENT=web \
-sMODULARIZE \
-sEXPORT_NAME=initPikchrModule \
--minify 0
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
120 121 122 123 124 125 126 | #define MKD_CELL_ALIGN_LEFT 1 #define MKD_CELL_ALIGN_RIGHT 2 #define MKD_CELL_ALIGN_CENTER 3 /* LEFT | RIGHT */ #define MKD_CELL_ALIGN_MASK 3 #define MKD_CELL_HEAD 4 | < < < < < < < < < < < < < < | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #define MKD_CELL_ALIGN_LEFT 1 #define MKD_CELL_ALIGN_RIGHT 2 #define MKD_CELL_ALIGN_CENTER 3 /* LEFT | RIGHT */ #define MKD_CELL_ALIGN_MASK 3 #define MKD_CELL_HEAD 4 #endif /* INTERFACE */ #define BLOB_COUNT(pBlob,el_type) (blob_size(pBlob)/sizeof(el_type)) #define COUNT_FOOTNOTES(pBlob) BLOB_COUNT(pBlob,struct footnote) #define CAST_AS_FOOTNOTES(pBlob) ((struct footnote*)blob_buffer(pBlob)) /*************** |
| ︙ | ︙ | |||
403 404 405 406 407 408 409 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
| | | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
if( rndr->nBlobCache < (int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){
rndr->aBlobCache[rndr->nBlobCache++] = buf;
}else{
fossil_free(buf);
}
}
|
| ︙ | ︙ | |||
1768 1769 1770 1771 1772 1773 1774 |
/* Return the number of characters in the delimiter of a fenced code
** block. */
static size_t prefix_fencedcode(char *data, size_t size){
char c = data[0];
int nb;
if( c!='`' && c!='~' ) return 0;
| | | | 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 |
/* Return the number of characters in the delimiter of a fenced code
** block. */
static size_t prefix_fencedcode(char *data, size_t size){
char c = data[0];
int nb;
if( c!='`' && c!='~' ) return 0;
for(nb=1; nb<(int)size-3 && data[nb]==c; nb++){}
if( nb<3 ) return 0;
if( nb>=(int)size-nb ) return 0;
return nb;
}
/* prefix_oli -- returns ordered list item prefix */
static size_t prefix_oli(char *data, size_t size){
size_t i = 0;
if( i<size && data[i]==' ') i++;
|
| ︙ | ︙ | |||
1885 1886 1887 1888 1889 1890 1891 |
size_t i = 0, end = 0;
int level = 0;
char *work_data = data;
size_t work_size = 0;
while( i<size ){
char *zEnd = memchr(data+i, '\n', size-i-1);
| | | 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 |
size_t i = 0, end = 0;
int level = 0;
char *work_data = data;
size_t work_size = 0;
while( i<size ){
char *zEnd = memchr(data+i, '\n', size-i-1);
end = zEnd==0 ? size : (size_t)(zEnd - (data-1));
/* The above is the same as:
** for(end=i+1; end<size && data[end-1]!='\n'; end++);
** "end" is left with a value such that data[end] is one byte
** past the first '\n' or one byte past the end of the string */
if( is_empty(data+i, size-i)
|| (level = is_headerline(data+i, size-i))!= 0
){
|
| ︙ | ︙ | |||
1958 1959 1960 1961 1962 1963 1964 |
){
size_t beg, end, pre;
struct Blob *work = new_work_buffer(rndr);
beg = 0;
while( beg<size ){
char *zEnd = memchr(data+beg, '\n', size-beg-1);
| | | 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 |
){
size_t beg, end, pre;
struct Blob *work = new_work_buffer(rndr);
beg = 0;
while( beg<size ){
char *zEnd = memchr(data+beg, '\n', size-beg-1);
end = zEnd==0 ? size : (size_t)(zEnd - (data-1));
/* The above is the same as:
** for(end=beg+1; end<size && data[end-1]!='\n'; end++);
** "end" is left with a value such that data[end] is one byte
** past the first \n or past then end of the string. */
pre = prefix_code(data+beg, end-beg);
if( pre ){
beg += pre; /* skipping prefix */
|
| ︙ | ︙ | |||
2168 2169 2170 2171 2172 2173 2174 |
size_t size
){
int level = 0;
size_t i, end, skip, span_beg, span_size;
if( !size || data[0]!='#' ) return 0;
| | | | 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 |
size_t size
){
int level = 0;
size_t i, end, skip, span_beg, span_size;
if( !size || data[0]!='#' ) return 0;
while( level<(int)size && level<6 && data[level]=='#' ){ level++; }
for(i=level; i<size && (data[i]==' ' || data[i]=='\t'); i++);
if ( (int)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--; }
|
| ︙ | ︙ | |||
2203 2204 2205 2206 2207 2208 2209 |
size_t size
){
size_t i, w;
/* assuming data[0]=='<' && data[1]=='/' already tested */
/* checking tag is a match */
| | | 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 |
size_t size
){
size_t i, w;
/* assuming data[0]=='<' && data[1]=='/' already tested */
/* checking tag is a match */
if( (tag->size+3)>(int)size
|| fossil_strnicmp(data+2, tag->text, tag->size)
|| data[tag->size+2]!='>'
){
return 0;
}
/* checking white lines */
|
| ︙ | ︙ | |||
2833 2834 2835 2836 2837 2838 2839 |
void markdown(
struct Blob *ob, /* output blob for rendered text */
const struct Blob *ib, /* input blob in markdown */
const struct mkd_renderer *rndrer /* renderer descriptor (callbacks) */
){
struct link_ref *lr;
struct footnote *fn;
| > | | 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 |
void markdown(
struct Blob *ob, /* output blob for rendered text */
const struct Blob *ib, /* input blob in markdown */
const struct mkd_renderer *rndrer /* renderer descriptor (callbacks) */
){
struct link_ref *lr;
struct footnote *fn;
int i;
size_t beg, end = 0;
struct render rndr;
size_t size;
Blob text = BLOB_INITIALIZER; /* input after the first pass */
Blob * const allNotes = &rndr.notes.all;
/* filling the render structure */
if( !rndrer ) return;
|
| ︙ | ︙ | |||
2919 2920 2921 2922 2923 2924 2925 |
int nDups = 0;
fn = CAST_AS_FOOTNOTES( allNotes );
qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);
/* concatenate footnotes with equal labels */
for(i=0; i<rndr.notes.nLbled ;){
struct footnote *x = fn + i;
| > | | | | | 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 2960 2961 2962 2963 2964 |
int nDups = 0;
fn = CAST_AS_FOOTNOTES( allNotes );
qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);
/* concatenate footnotes with equal labels */
for(i=0; i<rndr.notes.nLbled ;){
struct footnote *x = fn + i;
int j = i+1;
size_t k = blob_size(&x->text) + 64 + blob_size(&x->upc);
while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
k += blob_size(&fn[j].text) + 10 + blob_size(&fn[j].upc);
j++;
nDups++;
}
if( i+1<j ){
Blob list = empty_blob;
blob_reserve(&list, k);
/* must match _joined_footnote_indicator in html_footnote_item() */
blob_append_literal(&list, "<ul class='fn-joined'>\n");
for(k=i; (int)k<j; k++){
struct footnote *y = fn + k;
blob_append_literal(&list, "<li>");
if( blob_size(&y->upc) ){
blob_appendb(&list, &y->upc);
blob_reset(&y->upc);
}
blob_appendb(&list, &y->text);
blob_append_literal(&list, "</li>\n");
/* free memory buffer */
blob_reset(&y->text);
if( (int)k!=i ) blob_reset(&y->id);
}
blob_append_literal(&list, "</ul>\n");
x->text = list;
g.ftntsIssues[2]++;
}
i = j;
}
if( nDups ){ /* clean rndr.notes.all from invalidated footnotes */
const int n = rndr.notes.nLbled - nDups;
struct Blob filtered = empty_blob;
blob_reserve(&filtered, n*sizeof(struct footnote));
for(i=0; i<rndr.notes.nLbled; i++){
if( blob_size(&fn[i].id) ){
blob_append(&filtered, (char*)(fn+i), sizeof(struct footnote));
}
}
blob_reset( allNotes );
rndr.notes.all = filtered;
rndr.notes.nLbled = n;
assert( (int)(COUNT_FOOTNOTES(allNotes)) == rndr.notes.nLbled );
}
}
fn = CAST_AS_FOOTNOTES( allNotes );
for(i=0; i<rndr.notes.nLbled; i++){
fn[i].index = i;
}
assert( rndr.notes.nMarks==0 );
|
| ︙ | ︙ | |||
3030 3031 3032 3033 3034 3035 3036 |
/* Assert that the in-memory layout of id, text and upc within
** footnote struct matches the expectations of html_footnote_item()
** If it doesn't then a compiler has done something very weird.
*/
assert( &(rndr.notes.misref.id) == &(rndr.notes.misref.text) - 1 );
assert( &(rndr.notes.misref.upc) == &(rndr.notes.misref.text) + 1 );
| | | | | | 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 |
/* Assert that the in-memory layout of id, text and upc within
** footnote struct matches the expectations of html_footnote_item()
** If it doesn't then a compiler has done something very weird.
*/
assert( &(rndr.notes.misref.id) == &(rndr.notes.misref.text) - 1 );
assert( &(rndr.notes.misref.upc) == &(rndr.notes.misref.text) + 1 );
for(i=0; i<(int)(COUNT_FOOTNOTES(notes)); i++){
const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
const int xUsed = x->bRndred ? x->nUsed : 0;
if( !x->iMark ) break;
assert( x->nUsed );
rndr.make.footnote_item(all_items, &x->text, x->iMark,
xUsed, rndr.make.opaque);
if( !xUsed ) g.ftntsIssues[3]++; /* an overnested footnote */
j = i;
}
if( rndr.notes.misref.nUsed ){
rndr.make.footnote_item(all_items, 0, -1,
rndr.notes.misref.nUsed, rndr.make.opaque);
g.ftntsIssues[0] += rndr.notes.misref.nUsed;
}
while( ++j < (int)(COUNT_FOOTNOTES(notes)) ){
const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
assert( !x->iMark );
assert( !x->nUsed );
assert( !x->bRndred );
rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
g.ftntsIssues[1]++;
}
rndr.make.footnotes(ob, all_items, rndr.make.opaque);
release_work_buffer(&rndr, all_items);
}
release_work_buffer(&rndr, notes);
}
if( rndr.make.epilog ) rndr.make.epilog(ob, rndr.make.opaque);
/* clean-up */
assert( rndr.iDepth==0 );
blob_reset(&text);
lr = (struct link_ref *)blob_buffer(&rndr.refs);
end = blob_size(&rndr.refs)/sizeof(struct link_ref);
for(i=0; i<(int)end; i++){
blob_reset(&lr[i].id);
blob_reset(&lr[i].link);
blob_reset(&lr[i].title);
}
blob_reset(&rndr.refs);
fn = CAST_AS_FOOTNOTES( allNotes );
end = COUNT_FOOTNOTES( allNotes );
for(i=0; i<(int)end; i++){
if(blob_size(&fn[i].id)) blob_reset(&fn[i].id);
if(blob_size(&fn[i].upc)) blob_reset(&fn[i].upc);
blob_reset(&fn[i].text);
}
blob_reset(&rndr.notes.all);
for(i=0; i<rndr.nBlobCache; i++){
fossil_free(rndr.aBlobCache[i]);
}
}
|
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
228 229 230 231 232 233 234 |
blob_appendf(ob, "<h%d>", level);
blob_appendb(ob, text);
blob_appendf(ob, "</h%d>", level);
}
static void html_hrule(struct Blob *ob, void *opaque){
INTER_BLOCK(ob);
| | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
blob_appendf(ob, "<h%d>", level);
blob_appendb(ob, text);
blob_appendf(ob, "</h%d>", level);
}
static void html_hrule(struct Blob *ob, void *opaque){
INTER_BLOCK(ob);
blob_append_literal(ob, "<hr>\n");
}
static void html_list(
struct Blob *ob,
struct Blob *text,
int flags,
|
| ︙ | ︙ | |||
756 757 758 759 760 761 762 |
html_quote(ob, blob_buffer(link), blob_size(link));
blob_append_literal(ob, "\" alt=\"");
html_quote(ob, blob_buffer(alt), blob_size(alt));
if( title && blob_size(title)>0 ){
blob_append_literal(ob, "\" title=\"");
html_quote(ob, blob_buffer(title), blob_size(title));
}
| | | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 |
html_quote(ob, blob_buffer(link), blob_size(link));
blob_append_literal(ob, "\" alt=\"");
html_quote(ob, blob_buffer(alt), blob_size(alt));
if( title && blob_size(title)>0 ){
blob_append_literal(ob, "\" title=\"");
html_quote(ob, blob_buffer(title), blob_size(title));
}
blob_append_literal(ob, "\">");
return 1;
}
static int html_linebreak(struct Blob *ob, void *opaque){
blob_append_literal(ob, "<br>\n");
return 1;
}
static int html_link(
struct Blob *ob,
struct Blob *link,
struct Blob *title,
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
760 761 762 763 764 765 766 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
| < < < < < < < < < < < < < < < < < | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
** First, find files that have changed from P->M but not P->V.
** Copy the M content over into V.
*/
db_prepare(&q,
"SELECT idv, ridm, fn, islinkm FROM fv"
" WHERE idp>0 AND idv>0 AND idm>0"
" AND ridm!=ridp AND ridv=ridp AND NOT chnged"
);
|
| ︙ | ︙ | |||
807 808 809 810 811 812 813 814 815 816 |
vfile_to_disk(0, idv, 0, 0);
}
}
db_finalize(&q);
/*
** Do a three-way merge on files that have changes on both P->M and P->V.
*/
db_prepare(&q,
"SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
| > > > > | | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 |
vfile_to_disk(0, idv, 0, 0);
}
}
db_finalize(&q);
/*
** Do a three-way merge on files that have changes on both P->M and P->V.
**
** Proceed even if the file doesn't exist on P, just like the common ancestor
** of M and V is an empty file. In this case, merge conflict marks will be
** added to the file and user will be forced to take a decision.
*/
db_prepare(&q,
"SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm FROM fv"
" WHERE idv>0 AND idm>0"
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
glob_expr("fv.fn", zBinGlob)
);
while( db_step(&q)==SQLITE_ROW ){
int ridm = db_column_int(&q, 0);
int idv = db_column_int(&q, 1);
int ridp = db_column_int(&q, 2);
|
| ︙ | ︙ |
Changes to src/merge3.c.
| ︙ | ︙ | |||
454 455 456 457 458 459 460 |
if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[4]);
}
nConflict = blob_merge(&pivot, &v1, &v2, &merged);
| | | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[4]);
}
nConflict = blob_merge(&pivot, &v1, &v2, &merged);
if( blob_write_to_file(&merged, g.argv[5])<(int)blob_size(&merged) ){
fossil_fatal("cannot write %s", g.argv[4]);
}
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&merged);
if( nConflict>0 ) fossil_warning("WARNING: %d merge conflicts", nConflict);
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
**
** This file contains code used to resolved user-supplied object names.
*/
#include "config.h"
#include "name.h"
#include <assert.h>
/*
** Return TRUE if the string begins with something that looks roughly
** like an ISO date/time string. The SQLite date/time functions will
** have the final say-so about whether or not the date/time string is
** well-formed.
*/
int fossil_isdate(const char *z){
| > > > > > > > > > > > > > | 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 |
**
** This file contains code used to resolved user-supplied object names.
*/
#include "config.h"
#include "name.h"
#include <assert.h>
#if INTERFACE
/*
** An upper boundary on RIDs, provided in order to be able to
** distinguish real RID values from RID_CKOUT and any future
** RID_... values.
*/
#define RID_MAX 0x7ffffff0
/*
** A "magic" RID representing the current checkout in some contexts.
*/
#define RID_CKOUT (RID_MAX+1)
#endif
/*
** Return TRUE if the string begins with something that looks roughly
** like an ISO date/time string. The SQLite date/time functions will
** have the final say-so about whether or not the date/time string is
** well-formed.
*/
int fossil_isdate(const char *z){
|
| ︙ | ︙ | |||
163 164 165 166 167 168 169 |
" par(pid, ex, cnt) as ("
" SELECT pid, EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0"
" AND value=%Q AND rid=plink.pid), 1"
" FROM plink WHERE cid=%d AND isprim"
" UNION ALL "
" SELECT plink.pid, EXISTS(SELECT 1 FROM tagxref "
| | | | | 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 |
" par(pid, ex, cnt) as ("
" SELECT pid, EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0"
" AND value=%Q AND rid=plink.pid), 1"
" FROM plink WHERE cid=%d AND isprim"
" UNION ALL "
" SELECT plink.pid, EXISTS(SELECT 1 FROM tagxref "
" WHERE tagid=%d AND tagtype>0"
" AND value=%Q AND rid=plink.pid),"
" 1+par.cnt"
" FROM plink, par"
" WHERE cid=par.pid AND isprim AND par.ex "
" LIMIT 100000 "
" )"
" SELECT pid FROM par WHERE ex>=%d ORDER BY cnt DESC LIMIT 1",
TAG_BRANCH, zBr, ans, TAG_BRANCH, zBr, eType%2
);
fossil_free(zBr);
rc = db_step(&q);
if( rc==SQLITE_ROW ){
ans = db_column_int(&q, 0);
}
db_finalize(&q);
if( eType==2 && ans>0 ){
zBr = branch_of_rid(ans);
ans = compute_youngest_ancestor_in_branch(rid, zBr);
fossil_free(zBr);
}
return ans;
}
/*
** Find the RID of the most recent object with symbolic tag zTag
** and having a type that matches zType.
**
** Return 0 if there are no matches.
**
** This is a tricky query to do efficiently.
** If the tag is very common (ex: "trunk") then
** we want to use the query identified below as Q1 - which searching
** the most recent EVENT table entries for the most recent with the tag.
** But if the tag is relatively scarce (anything other than "trunk", basically)
** then we want to do the indexed search show below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
|
| ︙ | ︙ | |||
228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
" AND event.objid=tagxref.rid"
" 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==',';
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
" AND event.objid=tagxref.rid"
" AND event.type GLOB '%q'"
" ORDER BY event.mtime DESC LIMIT 1"
") LIMIT 1;",
zType, zTag, zTag, zType
);
}
/*
** Find the RID for a check-in that is the most recent check-in with
** tag zTag that occurs on or prior to rDate.
**
** See also the performance note on most_recent_event_with_tag() which
** applies to this routine too.
*/
int last_checkin_with_tag_before_date(const char *zTag, double rLimit){
Stmt s;
int rid = 0;
if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
db_prepare(&s,
"SELECT objid FROM ("
/* Q1: Begin by looking for the tag in the 30 most recent events */
"SELECT objid"
" FROM (SELECT * FROM event WHERE mtime<=:datelimit"
" ORDER BY mtime DESC LIMIT 30) AS ex"
" WHERE type='ci'"
" AND EXISTS(SELECT 1 FROM tagxref, tag"
" WHERE tag.tagname='sym-%q'"
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tagxref.rid=ex.objid)"
" ORDER BY mtime DESC LIMIT 1"
") UNION ALL SELECT * FROM ("
/* Q2: If the tag is not found in the 30 most recent events, then using
** the tagxref table to index for the tag */
"SELECT event.objid"
" FROM tag, tagxref, event"
" WHERE tag.tagname='sym-%q'"
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND event.objid=tagxref.rid"
" AND event.type='ci'"
" AND event.mtime<=:datelimit"
" ORDER BY event.mtime DESC LIMIT 1"
") LIMIT 1;",
zTag, zTag
);
db_bind_double(&s, ":datelimit", rLimit);
if( db_step(&s)==SQLITE_ROW ){
rid = db_column_int(&s,0);
}
db_finalize(&s);
return rid;
}
/*
** Find the RID of the first check-in (chronologically) after rStart that
** has tag zTag.
**
** See also the performance note on most_recent_event_with_tag() which
** applies to this routine too.
*/
int first_checkin_with_tag_after_date(const char *zTag, double rStart){
Stmt s;
int rid = 0;
if( strncmp(zTag, "tag:", 4)==0 ) zTag += 4;
db_prepare(&s,
"SELECT objid FROM ("
/* Q1: Begin by looking for the tag in the 30 most recent events */
"SELECT objid"
" FROM (SELECT * FROM event WHERE mtime>=:startdate"
" ORDER BY mtime LIMIT 30) AS ex"
" WHERE type='ci'"
" AND EXISTS(SELECT 1 FROM tagxref, tag"
" WHERE tag.tagname='sym-%q'"
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tagxref.rid=ex.objid)"
" ORDER BY mtime LIMIT 1"
") UNION ALL SELECT * FROM ("
/* Q2: If the tag is not found in the 30 most recent events, then using
** the tagxref table to index for the tag */
"SELECT event.objid"
" FROM tag, tagxref, event"
" WHERE tag.tagname='sym-%q'"
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND event.objid=tagxref.rid"
" AND event.type='ci'"
" AND event.mtime>=:startdate"
" ORDER BY event.mtime LIMIT 1"
") LIMIT 1;",
zTag, zTag
);
db_bind_double(&s, ":startdate", rStart);
if( db_step(&s)==SQLITE_ROW ){
rid = db_column_int(&s,0);
}
db_finalize(&s);
return rid;
}
/*
** 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==',';
|
| ︙ | ︙ | |||
276 277 278 279 280 281 282 | ** ** The zType parameter specifies the type of artifact: ci, t, w, e, g, f. ** If zType is NULL or "" or "*" then any type of artifact will serve. ** If zType is "br" then find the first check-in of the named branch ** rather than the last. ** ** zType is "ci" in most use cases since we are usually searching for | | > > > > > < > > > > > > > | > > | | > > | > > | 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 |
**
** The zType parameter specifies the type of artifact: ci, t, w, e, g, f.
** If zType is NULL or "" or "*" then any type of artifact will serve.
** If zType is "br" then find the first check-in of the named branch
** rather than the last.
**
** zType is "ci" in most use cases since we are usually searching for
** a check-in. A value of "ci+" works like "ci" but adds these
** semantics: if zTag is "ckout" and a checkout is open, "ci+" causes
** RID_CKOUT to be returned, in which case g.localOpen will hold the
** RID of the checkout. Conversely, passing in the hash, or another
** symbolic name of the local checkout version, will always result in
** its RID being returned.
**
** Note that the input zTag for types "t" and "e" is the artifact hash of
** the ticket-change or technote-change artifact, not the randomly generated
** hexadecimal identifier assigned to tickets and events. Those identifiers
** live in a separate namespace.
*/
int symbolic_name_to_rid(const char *zTag, const char *zType){
int rid = 0;
int nTag;
int i;
int startOfBranch = 0;
const char *zXTag; /* zTag with optional [...] removed */
int nXTag; /* Size of zXTag */
const char *zDate; /* Expanded date-time string */
int isCheckin = 0; /* zType==ci = 1, zType==ci+ = 2 */
if( zType==0 || zType[0]==0 ){
zType = "*";
}else if( zType[0]=='b' ){
zType = "ci";
startOfBranch = 1;
}
if( zTag==0 || zTag[0]==0 ) return 0;
else if( 'c'==zType[0] ){
if( fossil_strcmp(zType,"ci")==0 ){
isCheckin = 1;
}else if( fossil_strcmp(zType,"ci+")==0 ){
isCheckin = 2;
zType = "ci";
}
}
/* special keyword: "tip" */
if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || isCheckin!=0) ){
rid = db_int(0,
"SELECT objid"
" FROM event"
" WHERE type='ci'"
" ORDER BY event.mtime DESC"
);
if( rid ) return rid;
}
/* special keywords: "prev", "previous", "current", "ckout", and
** "next" */
if( g.localOpen>0 && (zType[0]=='*' || isCheckin!=0) ){
const int vid = g.localOpen;
if( fossil_strcmp(zTag, "current")==0 ){
rid = vid;
}else if( fossil_strcmp(zTag, "prev")==0
|| fossil_strcmp(zTag, "previous")==0 ){
rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid);
}else if( fossil_strcmp(zTag, "next")==0 ){
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
" ORDER BY isprim DESC, mtime DESC", vid);
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
rid = RID_CKOUT;
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
zDate = fossil_expand_datetime(&zTag[5],0);
|
| ︙ | ︙ | |||
521 522 523 524 525 526 527 |
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);
| | | 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 |
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);
|
| ︙ | ︙ | |||
586 587 588 589 590 591 592 |
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to a newly-allocated string,
** the full hash, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
int rid = symbolic_name_to_rid(zName, zType);
if((rid>0) && pUuid){
| > | > | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 |
** negative value is returned. On success the rid is returned and
** pUuid (if it is not NULL) is set to a newly-allocated string,
** the full hash, which must eventually be free()d by the caller.
*/
int name_to_uuid2(const char *zName, const char *zType, char **pUuid){
int rid = symbolic_name_to_rid(zName, zType);
if((rid>0) && pUuid){
*pUuid = (rid<RID_MAX)
? db_text(NULL, "SELECT uuid FROM blob WHERE rid=%d", rid)
: NULL;
}
return rid;
}
/*
** name_collisions searches through events, blobs, and tickets for
|
| ︙ | ︙ | |||
621 622 623 624 625 626 627 | } return c; } /* ** COMMAND: test-name-to-id ** | | > > > > > | | 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 |
}
return c;
}
/*
** COMMAND: test-name-to-id
**
** Usage: %fossil test-name-to-id [--count N] [--type ARTIFACT_TYPE] NAME
**
** Convert a NAME to a full artifact ID. Repeat the conversion N
** times (for timing purposes) if the --count option is given.
*/
void test_name_to_id(void){
int i;
int n = 0;
Blob name;
const char *zType;
db_must_be_within_tree();
if( (zType = find_option("type","t",1))==0 ){
zType = "*";
}
for(i=2; i<g.argc; i++){
if( strcmp(g.argv[i],"--count")==0 && i+1<g.argc ){
i++;
n = atoi(g.argv[i]);
continue;
}
do{
blob_init(&name, g.argv[i], -1);
fossil_print("%s -> ", g.argv[i]);
if( name_to_uuid(&name, 1, zType) ){
fossil_print("ERROR: %s\n", g.zErrMsg);
fossil_error_reset();
}else{
fossil_print("%s\n", blob_buffer(&name));
}
blob_reset(&name);
}while( n-- > 0 );
|
| ︙ | ︙ | |||
1699 1700 1701 1702 1703 1704 1705 | /* ** WEBPAGE: bigbloblist ** ** Return a page showing the largest artifacts in the repository in order ** of decreasing size. ** | | | 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 |
/*
** WEBPAGE: bigbloblist
**
** Return a page showing the largest artifacts in the repository in order
** of decreasing size.
**
** n=N Show the top N artifacts (default: 250)
*/
void bigbloblist_page(void){
Stmt q;
int n = atoi(PD("n","250"));
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
|
| ︙ | ︙ | |||
1734 1735 1736 1737 1738 1739 1740 |
" FROM description, blob LEFT JOIN delta ON delta.rid=blob.rid"
" WHERE description.rid=blob.rid"
" ORDER BY length(content) DESC"
);
@ <table cellpadding="2" cellspacing="0" border="1" \
@ class='sortable' data-column-types='NnnttT' data-init-sort='0'>
@ <thead><tr><th align="right">Size<th align="right">RID
| | | 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 |
" FROM description, blob LEFT JOIN delta ON delta.rid=blob.rid"
" WHERE description.rid=blob.rid"
" ORDER BY length(content) DESC"
);
@ <table cellpadding="2" cellspacing="0" border="1" \
@ class='sortable' data-column-types='NnnttT' data-init-sort='0'>
@ <thead><tr><th align="right">Size<th align="right">RID
@ <th align="right">From<th>Hash<th>Description<th>Date</tr></thead>
@ <tbody>
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int sz = db_column_int(&q,3);
const char *zSrcId = db_column_text(&q,4);
|
| ︙ | ︙ | |||
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 |
@ </tr>
}
@ </tbody></table>
db_finalize(&q);
style_table_sorter();
style_finish_page();
}
/*
** COMMAND: test-unsent
**
** Usage: %fossil test-unsent
**
** Show all artifacts in the unsent table
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 |
@ </tr>
}
@ </tbody></table>
db_finalize(&q);
style_table_sorter();
style_finish_page();
}
/*
** WEBPAGE: deltachain
**
** Usage: /deltachain/RID
**
** The RID query parameter is required. Generate a page with a table
** showing storage characteristics of RID and other artifacts that are
** derived from RID via delta.
*/
void deltachain_page(void){
Stmt q;
int id = atoi(PD("name","0"));
int top;
i64 nStored = 0;
i64 nExpanded = 0;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
top = db_int(id,
"WITH RECURSIVE chain(aa,bb) AS (\n"
" SELECT rid, srcid FROM delta WHERE rid=%d\n"
" UNION ALL\n"
" SELECT bb, delta.srcid"
" FROM chain LEFT JOIN delta ON delta.rid=bb"
" WHERE bb IS NOT NULL\n"
")\n"
"SELECT aa FROM chain WHERE bb IS NULL",
id
);
style_header("Delta Chain Containing Artifact %d", id);
db_multi_exec(
"CREATE TEMP TABLE toshow(rid INT, gen INT);\n"
"WITH RECURSIVE tx(id,px) AS (\n"
" VALUES(%d,0)\n"
" UNION ALL\n"
" SELECT delta.rid, px+1 FROM tx, delta where delta.srcid=tx.id\n"
" ORDER BY 2\n"
") "
"INSERT INTO toshow(rid,gen) SELECT id,px FROM tx;",
top
);
db_multi_exec("CREATE INDEX toshow_rid ON toshow(rid);");
describe_artifacts("IN (SELECT rid FROM toshow)");
db_prepare(&q,
"SELECT description.rid, description.uuid, description.summary,"
" length(blob.content), coalesce(delta.srcid,''),"
" datetime(description.ctime), toshow.gen, blob.size"
" FROM description, toshow, blob LEFT JOIN delta ON delta.rid=blob.rid"
" WHERE description.rid=blob.rid"
" AND toshow.rid=description.rid"
" ORDER BY toshow.gen, description.ctime"
);
@ <table cellpadding="2" cellspacing="0" border="1" \
@ class='sortable' data-column-types='nNnnttT' data-init-sort='0'>
@ <thead><tr><th align="right">Level</th>
@ <th align="right">Size<th align="right">RID
@ <th align="right">From<th>Hash<th>Description<th>Date</tr></thead>
@ <tbody>
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int sz = db_column_int(&q,3);
const char *zSrcId = db_column_text(&q,4);
const char *zDate = db_column_text(&q,5);
int gen = db_column_int(&q,6);
nExpanded += db_column_int(&q,7);
nStored += sz;
@ <tr><td align="right">%d(gen)</td>
@ <td align="right">%d(sz)</td>
if( rid==id ){
@ <td align="right"><b>%d(rid)</b></td>
}else{
@ <td align="right">%d(rid)</td>
}
@ <td align="right">%s(zSrcId)</td>
@ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td>
@ <td align="left">%h(zDesc)</td>
@ <td align="left">%z(href("%R/timeline?c=%T",zDate))%s(zDate)</a></td>
@ </tr>
}
@ </tbody></table>
db_finalize(&q);
style_table_sorter();
@ <p>
@ <table border="0" cellspacing="0" cellpadding="0">
@ <tr><td>Bytes of content</td><td> </td>
@ <td align="right">%,lld(nExpanded)</td></tr>
@ <tr><td>Bytes stored in repository</td><td></td>
@ <td align="right">%,lld(nStored)</td>
@ </table>
@ </p>
style_finish_page();
}
/*
** COMMAND: test-unsent
**
** Usage: %fossil test-unsent
**
** Show all artifacts in the unsent table
|
| ︙ | ︙ |
Changes to src/patch.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
** is running.
*/
char *fossil_hostname(void){
FILE *in;
char zBuf[200];
in = popen("hostname","r");
if( in ){
| | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
** is running.
*/
char *fossil_hostname(void){
FILE *in;
char zBuf[200];
in = popen("hostname","r");
if( in ){
int n = fread(zBuf, 1, sizeof(zBuf)-1, in);
while( n>0 && fossil_isspace(zBuf[n-1]) ){ n--; }
if( n<0 ) n = 0;
zBuf[n] = 0;
pclose(in);
return fossil_strdup(zBuf);
}
return 0;
|
| ︙ | ︙ | |||
859 860 861 862 863 864 865 866 867 | ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved changes ** are reverted and permanently lost. ** -n|--dry-run 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 | > | > | 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 | ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved changes ** are reverted and permanently lost. ** -n|--dry-run Do nothing, but print what would have happened ** -v|--verbose Extra output explaining what happens ** ** > fossil patch diff [DIRECTORY] FILENAME ** > fossil patch gdiff [DIRECTORY] FILENAME ** ** Show a human-readable diff for the patch. All the usual ** diff flags described at "fossil help diff" apply. With gdiff, ** gdiff-command is used instead of internal diff logic. In addition: ** ** -f|--force Continue trying to perform the diff even if ** baseline information is missing from the current ** repository ** ** > fossil patch push REMOTE-CHECKOUT ** |
| ︙ | ︙ | |||
906 907 908 909 910 911 912 |
**
*/
void patch_cmd(void){
const char *zCmd;
size_t n;
if( g.argc<3 ){
patch_usage:
| | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 |
**
*/
void patch_cmd(void){
const char *zCmd;
size_t n;
if( g.argc<3 ){
patch_usage:
usage("apply|create|diff|gdiff|pull|push|view");
}
zCmd = g.argv[2];
n = strlen(zCmd);
if( strncmp(zCmd, "apply", n)==0 ){
char *zIn;
unsigned flags = 0;
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
|
| ︙ | ︙ | |||
932 933 934 935 936 937 938 |
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
zOut = patch_find_patch_filename("create");
verify_all_options();
db_must_be_within_tree();
patch_create(flags, zOut, stdout);
fossil_free(zOut);
}else
| | < > | 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 |
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
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) || (strncmp(zCmd, "gdiff", 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;
}
db_find_and_open_repository(0, 0);
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
diff_options(&DCfg, zCmd[0]=='g', 0);
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 ){
|
| ︙ | ︙ |
Changes to src/piechart.c.
| ︙ | ︙ | |||
310 311 312 313 314 315 316 |
fossil_free(zLabel);
}
db_finalize(&ins);
if( n>1 ){
@ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;">
piechart_render(width,height, PIE_OTHER|PIE_PERCENT);
@ </svg>
| | | | | | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
fossil_free(zLabel);
}
db_finalize(&ins);
if( n>1 ){
@ <svg width=%d(width) height=%d(height) style="border:1px solid #d3d3d3;">
piechart_render(width,height, PIE_OTHER|PIE_PERCENT);
@ </svg>
@ <hr>
}
@ <form method="POST" action='%R/test-piechart'>
@ <p>Comma-separated list of slice widths:<br>
@ <input type='text' name='data' size='80' value='%h(zData)'/><br>
@ Width: <input type='text' size='8' name='width' value='%d(width)'/>
@ Height: <input type='text' size='8' name='height' value='%d(height)'/><br>
@ <input type='submit' value='Draw The Pie Chart'/>
@ </form>
@ <p>Interesting test cases:
@ <ul>
@ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a>
@ <li> <a href='test-piechart?data=2,2,2,2,2,44,44,2,2,2,2,2'>Case 2</a>
@ <li> <a href='test-piechart?data=20,2,2,2,2,2,2,2,2,2,2,80'>Case 3</a>
|
| ︙ | ︙ |
Changes to src/popen.c.
| ︙ | ︙ | |||
181 182 183 184 185 186 187 |
close(pout[1]);
*pChildPid = 0;
return 1;
}
signal(SIGPIPE,SIG_IGN);
if( *pChildPid==0 ){
int fd;
| < | | | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
close(pout[1]);
*pChildPid = 0;
return 1;
}
signal(SIGPIPE,SIG_IGN);
if( *pChildPid==0 ){
int fd;
/* This is the child process */
close(0);
fd = dup(pout[0]);
if( fd!=0 ) fossil_panic("popen() failed to open file descriptor 0");
close(pout[0]);
close(pout[1]);
close(1);
fd = dup(pin[1]);
if( fd!=1 ) fossil_panic("popen() failed to open file descriptor 1");
close(pin[0]);
close(pin[1]);
if( bDirect ){
execl(zCmd, zCmd, (char*)0);
}else{
execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0);
}
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
862 863 864 865 866 867 868 |
** the output.
*/
if( !flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
count += nspace;
| | | | 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 |
** the output.
*/
if( !flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
count += nspace;
while( nspace>=(int)etSPACESIZE ){
blob_append(pBlob,spaces,etSPACESIZE);
nspace -= etSPACESIZE;
}
if( nspace>0 ) blob_append(pBlob,spaces,nspace);
}
}
if( length>0 ){
blob_append(pBlob,bufpt,length);
count += length;
}
if( flag_leftjustify ){
register int nspace;
nspace = width-length;
if( nspace>0 ){
count += nspace;
while( nspace>=(int)etSPACESIZE ){
blob_append(pBlob,spaces,etSPACESIZE);
nspace -= etSPACESIZE;
}
if( nspace>0 ) blob_append(pBlob,spaces,nspace);
}
}
if( zExtra ){
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
254 255 256 257 258 259 260 |
Blob copy;
Blob *pUse;
int nChild, i, cid;
while( rid>0 ){
/* Fix up the "blob.size" field if needed. */
| | | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
Blob copy;
Blob *pUse;
int nChild, i, cid;
while( rid>0 ){
/* Fix up the "blob.size" field if needed. */
if( size!=(int)blob_size(pBase) ){
db_multi_exec(
"UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
);
}
/* Find all children of artifact rid */
db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
|
| ︙ | ︙ | |||
486 487 488 489 490 491 492 | /* ** Number of neighbors to search */ #define N_NEIGHBOR 5 /* ** Attempt to convert more full-text blobs into delta-blobs for | | > | > > > | > > > > | 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 |
/*
** Number of neighbors to search
*/
#define N_NEIGHBOR 5
/*
** Attempt to convert more full-text blobs into delta-blobs for
** storage efficiency. Return the number of bytes of storage space
** saved.
*/
i64 extra_deltification(int *pnDelta){
Stmt q;
int aPrev[N_NEIGHBOR];
int nPrev;
int rid;
int prevfnid, fnid;
int nDelta = 0;
i64 nByte = 0;
int nSaved;
db_begin_transaction();
/* Look for manifests that have not been deltaed and try to make them
** children of one of the 5 chronologically subsequent check-ins
*/
db_prepare(&q,
"SELECT rid FROM event, blob"
" WHERE blob.rid=event.objid"
" AND event.type='ci'"
" AND NOT EXISTS(SELECT 1 FROM delta WHERE rid=blob.rid)"
" ORDER BY event.mtime DESC"
);
nPrev = 0;
while( db_step(&q)==SQLITE_ROW ){
rid = db_column_int(&q, 0);
if( nPrev>0 ){
nSaved = content_deltify(rid, aPrev, nPrev, 0);
if( nSaved>0 ){
nDelta++;
nByte += nSaved;
}
}
if( nPrev<N_NEIGHBOR ){
aPrev[nPrev++] = rid;
}else{
int i;
for(i=0; i<N_NEIGHBOR-1; i++) aPrev[i] = aPrev[i+1];
aPrev[N_NEIGHBOR-1] = rid;
|
| ︙ | ︙ | |||
541 542 543 544 545 546 547 |
prevfnid = 0;
while( db_step(&q)==SQLITE_ROW ){
rid = db_column_int(&q, 0);
fnid = db_column_int(&q, 1);
if( fnid!=prevfnid ) nPrev = 0;
prevfnid = fnid;
if( nPrev>0 ){
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
prevfnid = 0;
while( db_step(&q)==SQLITE_ROW ){
rid = db_column_int(&q, 0);
fnid = db_column_int(&q, 1);
if( fnid!=prevfnid ) nPrev = 0;
prevfnid = fnid;
if( nPrev>0 ){
nSaved = content_deltify(rid, aPrev, nPrev, 0);
if( nSaved>0 ){
nDelta++;
nByte += nSaved;
}
}
if( nPrev<N_NEIGHBOR ){
aPrev[nPrev++] = rid;
}else{
int i;
for(i=0; i<N_NEIGHBOR-1; i++) aPrev[i] = aPrev[i+1];
aPrev[N_NEIGHBOR-1] = rid;
}
}
db_finalize(&q);
db_end_transaction(0);
if( pnDelta!=0 ) *pnDelta = nDelta;
return nByte;
}
/* Reconstruct the private table. The private table contains the rid
** of every manifest that is tagged with "private" and every file that
** is not used by a manifest that is not private.
*/
static void reconstruct_private_table(void){
db_multi_exec(
"CREATE TEMP TABLE private_ckin(rid INTEGER PRIMARY KEY);"
"INSERT INTO private_ckin "
" SELECT rid FROM tagxref WHERE tagid=%d AND tagtype>0;"
"INSERT OR IGNORE INTO private"
" SELECT fid FROM mlink"
" EXCEPT SELECT fid FROM mlink WHERE mid NOT IN private_ckin;"
"INSERT OR IGNORE INTO private SELECT rid FROM private_ckin;"
"DROP TABLE private_ckin;", TAG_PRIVATE
);
fix_private_blob_dependencies(0);
}
/*
** COMMAND: repack
**
** Usage: %fossil repack ?REPOSITORY?
**
** Perform extra delta-compression to try to minimize the size of the
** repository. This command is simply a short-hand for:
**
** fossil rebuild --compress-only
**
** The name for this command is stolen from the "git repack" command that
** does approximately the same thing in Git.
*/
void repack_command(void){
i64 nByte = 0;
int nDelta = 0;
int runVacuum = 0;
verify_all_options();
if( g.argc==3 ){
db_open_repository(g.argv[2]);
}else if( g.argc==2 ){
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
if( g.argc!=2 ){
usage("?REPOSITORY-FILENAME?");
}
db_close(1);
db_open_repository(g.zRepositoryName);
}else{
usage("?REPOSITORY-FILENAME?");
}
db_unprotect(PROTECT_ALL);
nByte = extra_deltification(&nDelta);
if( nDelta>0 ){
if( nDelta==1 ){
fossil_print("1 new delta saves %,lld bytes\n", nByte);
}else{
fossil_print("%d new deltas save %,lld bytes\n", nDelta, nByte);
}
runVacuum = 1;
}else{
fossil_print("no new compression opportunities found\n");
}
if( runVacuum ){
fossil_print("Vacuuming the database... "); fflush(stdout);
db_multi_exec("VACUUM");
fossil_print("done\n");
}
}
/*
** COMMAND: rebuild
**
** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS?
**
|
| ︙ | ︙ | |||
634 635 636 637 638 639 640 |
runCompress = find_option("compress",0,0)!=0;
zPagesize = find_option("pagesize",0,1);
showStats = find_option("stats",0,0)!=0;
optIndex = find_option("index",0,0)!=0;
optNoIndex = find_option("noindex",0,0)!=0;
optIfNeeded = find_option("ifneeded",0,0)!=0;
compressOnlyFlag = find_option("compress-only",0,0)!=0;
| | | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
runCompress = find_option("compress",0,0)!=0;
zPagesize = find_option("pagesize",0,1);
showStats = find_option("stats",0,0)!=0;
optIndex = find_option("index",0,0)!=0;
optNoIndex = find_option("noindex",0,0)!=0;
optIfNeeded = find_option("ifneeded",0,0)!=0;
compressOnlyFlag = find_option("compress-only",0,0)!=0;
if( compressOnlyFlag ) runCompress = 1;
if( zPagesize ){
newPagesize = atoi(zPagesize);
if( newPagesize<512 || newPagesize>65536
|| (newPagesize&(newPagesize-1))!=0
){
fossil_fatal("page size must be a power of two between 512 and 65536");
}
|
| ︙ | ︙ | |||
686 687 688 689 690 691 692 693 |
fossil_print(
"%d errors. Rolling back changes. Use --force to force a commit.\n",
errCnt
);
db_end_transaction(1);
}else{
if( runCompress ){
fossil_print("Extra delta compression... "); fflush(stdout);
| > > | > > > > > > | > > > > | | 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 |
fossil_print(
"%d errors. Rolling back changes. Use --force to force a commit.\n",
errCnt
);
db_end_transaction(1);
}else{
if( runCompress ){
i64 nByte = 0;
int nDelta = 0;
fossil_print("Extra delta compression... "); fflush(stdout);
nByte = extra_deltification(&nDelta);
if( nDelta>0 ){
if( nDelta==1 ){
fossil_print("1 new delta saves %,lld bytes", nByte);
}else{
fossil_print("%d new deltas save %,lld bytes", nDelta, nByte);
}
runVacuum = 1;
}else{
fossil_print("none found");
}
fflush(stdout);
}
if( omitVerify ) verify_cancel();
db_end_transaction(0);
if( runCompress ) fossil_print("\n");
db_close(0);
db_open_repository(g.zRepositoryName);
if( newPagesize ){
db_multi_exec("PRAGMA page_size=%d", newPagesize);
runVacuum = 1;
}
if( runDeanalyze ){
|
| ︙ | ︙ |
Changes to src/repolist.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
** directory.
*/
blob_init(&base, g.zRepositoryName, -1);
sqlite3_open(":memory:", &g.db);
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
db_multi_exec("CREATE TABLE vfile(pathname);");
vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE);
| | > > > > | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
** directory.
*/
blob_init(&base, g.zRepositoryName, -1);
sqlite3_open(":memory:", &g.db);
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
db_multi_exec("CREATE TABLE vfile(pathname);");
vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE);
db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"
#if USE_SEE
" AND pathname NOT GLOB '*[^/].efossil'"
#endif
);
allRepo = 0;
}
n = db_int(0, "SELECT count(*) FROM sfile");
if( n==0 ){
sqlite3_close(g.db);
g.db = 0;
g.repositoryOpen = 0;
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 |
"</thead><tbody>\n");
db_prepare(&q, "SELECT pathname"
" FROM sfile ORDER BY pathname COLLATE nocase;");
rNow = db_double(0, "SELECT julianday('now')");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
int nName = (int)strlen(zName);
char *zUrl;
char *zAge;
char *zFull;
RepoInfo x;
sqlite3_int64 iAge;
| > > > > > | | | > > > > | | | > > > | > > > > | 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 |
"</thead><tbody>\n");
db_prepare(&q, "SELECT pathname"
" FROM sfile ORDER BY pathname COLLATE nocase;");
rNow = db_double(0, "SELECT julianday('now')");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
int nName = (int)strlen(zName);
int nSuffix = 7; /* ".fossil" */
char *zUrl;
char *zAge;
char *zFull;
RepoInfo x;
sqlite3_int64 iAge;
#if USE_SEE
int bEncrypted = sqlite3_strglob("*.efossil", zName)==0;
if( bEncrypted ) nSuffix = 8; /* ".efossil" */
#endif
if( nName<nSuffix ) continue;
zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName);
if( zName[0]=='/'
#ifdef _WIN32
|| sqlite3_strglob("[a-zA-Z]:/*", zName)==0
#endif
){
zFull = mprintf("%s", zName);
}else if ( allRepo ){
zFull = mprintf("/%s", zName);
}else{
zFull = mprintf("%s/%s", g.zRepositoryName, zName);
}
x.zRepoName = zFull;
remote_repo_info(&x);
if( x.isRepolistSkin ){
if( zSkinRepo==0 ){
zSkinRepo = mprintf("%s", x.zRepoName);
zSkinUrl = mprintf("%s", zUrl);
}
}
fossil_free(zFull);
if( !x.isValid
#if USE_SEE
&& !bEncrypted
#endif
){
continue;
}
if( x.isRepolistSkin==2 && !allRepo ){
/* Repositories with repolist-skin==2 are omitted from directory
** scan lists, but included in "fossil all ui" lists */
continue;
}
if( rNow <= x.rMTime ){
x.rMTime = rNow;
}else if( x.rMTime<0.0 ){
x.rMTime = rNow;
}
iAge = (sqlite3_int64)((rNow - x.rMTime)*86400);
zAge = human_readable_age(rNow - x.rMTime);
if( x.rMTime==0.0 ){
/* This repository has no entry in the "event" table.
** Its age will still be maximum, so data-sortkey will work. */
zAge = mprintf("unknown");
}
blob_append_sql(&html, "<tr><td valign='top'>");
if( !file_ends_with_repository_extension(zName,0) ){
/* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
** do not work for repositories whose names do not end in ".fossil".
** So do not hyperlink those cases. */
blob_append_sql(&html,"%h",zName);
} else if( sqlite3_strglob("*/.*", zName)==0 ){
/* Do not show hyperlinks for hidden repos */
blob_append_sql(&html, "%h (hidden)", zName);
} else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
blob_append_sql(&html,
"<a href='%R/%T/home' target='_blank'>/%h</a>\n",
zUrl, zName);
}else if( file_ends_with_repository_extension(zName,1) ){
/* As described in
** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
** foo.fossil and foo/bar.fossil both exist and we create a
** link to foo/bar/... then the URI dispatcher will instead
** see that as a link to foo.fossil. In such cases, do not
** emit a link to foo/bar.fossil. */
char * zDirPart = file_dirname(zName);
if( db_exists("SELECT 1 FROM sfile "
"WHERE pathname=(%Q || '.fossil') COLLATE nocase"
#if USE_SEE
" OR pathname=(%Q || '.efossil') COLLATE nocase"
#endif
, zDirPart
#if USE_SEE
, zDirPart
#endif
) ){
blob_append_sql(&html,
"<s>%h</s> (directory/repo name collision)\n",
zName);
}else{
blob_append_sql(&html,
"<a href='%R/%T/home' target='_blank'>%h</a>\n",
zUrl, zName);
|
| ︙ | ︙ | |||
298 299 300 301 302 303 304 |
style_table_sorter();
style_finish_page();
}else{
/* If no repositories were found that had the "repolist_skin"
** property set, then use a default skin */
@ <html>
@ <head>
| | | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
style_table_sorter();
style_finish_page();
}else{
/* If no repositories were found that had the "repolist_skin"
** property set, then use a default skin */
@ <html>
@ <head>
@ <base href="%s(g.zBaseURL)/">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>Repository List</title>
@ </head>
@ <body>
@ <h1 align="center">Fossil Repositories</h1>
@ %s(blob_str(&html))
@ <script>%s(builtin_text("sorttable.js"))</script>
|
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
** Main menu for Tickets.
*/
void view_list(void){
const char *zScript;
Blob ril; /* Report Item List */
Stmt q;
int rn = 0;
| < | | < | 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 |
** Main menu for Tickets.
*/
void view_list(void){
const char *zScript;
Blob ril; /* Report Item List */
Stmt q;
int rn = 0;
char *defaultReport = db_get("ticket-default-report", 0);
login_check_credentials();
if( !g.perm.RdTkt && !g.perm.NewTkt ){
login_needed(g.anon.RdTkt || g.anon.NewTkt);
return;
}
style_header("Ticket Main Menu");
ticket_standard_submenu(T_ALL_BUT(T_REPLIST));
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST<br>\n", -1);
zScript = ticket_reportlist_code();
if( g.thTrace ) Th_Trace("BEGIN_REPORTLIST_SCRIPT<br>\n", -1);
blob_zero(&ril);
ticket_init();
db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title");
while( db_step(&q)==SQLITE_ROW ){
const char *zTitle = db_column_text(&q, 1);
const char *zOwner = db_column_text(&q, 2);
if( zTitle[0] =='_' && !g.perm.TktFmt ){
continue;
}
rn = db_column_int(&q, 0);
blob_appendf(&ril, "<li>");
if( zTitle[0] == '_' ){
blob_appendf(&ril, "%s", zTitle);
} else {
blob_appendf(&ril, "%z%h</a>", href("%R/rptview/%d", rn), zTitle);
}
blob_appendf(&ril, " ");
|
| ︙ | ︙ | |||
105 106 107 108 109 110 111 |
db_finalize(&q);
Th_Store("report_items", blob_str(&ril));
Th_Render(zScript);
blob_reset(&ril);
| | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
db_finalize(&q);
Th_Store("report_items", blob_str(&ril));
Th_Render(zScript);
blob_reset(&ril);
if( g.thTrace ) Th_Trace("END_REPORTLIST<br>\n", -1);
style_finish_page();
}
/*
** Remove whitespace from both ends of a string.
*/
|
| ︙ | ︙ | |||
593 594 595 596 597 598 599 |
style_submenu_element("Delete", "%R/rptedit/%d?del1=1", rn);
}
style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
if( zErr ){
@ <blockquote class="reportError">%h(zErr)</blockquote>
}
@ <form action="rptedit" method="post"><div>
| | | | | | | | | | | | | | | 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 |
style_submenu_element("Delete", "%R/rptedit/%d?del1=1", rn);
}
style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
if( zErr ){
@ <blockquote class="reportError">%h(zErr)</blockquote>
}
@ <form action="rptedit" method="post"><div>
@ <input type="hidden" name="rn" value="%d(rn)">
@ <p>Report Title:<br>
@ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
@ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
@ </p>
login_insert_csrf_secret();
if( g.perm.Admin ){
@ <p>Report owner:
@ <input type="text" name="w" size="20" value="%h(zOwner)">
@ </p>
@ <p>Tag:
@ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")">
@ </p>
} else {
@ <input type="hidden" name="w" value="%h(zOwner)">
if( zTag && zTag[0] ){
@ <input type="hidden" name="x" value="%h(zTag)">
}
}
@ <p>Enter an optional color key in the following box. (If blank, no
@ color key is displayed.) Each line contains the text for a single
@ entry in the key. The first token of each line is the background
@ color for that line.<br>
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
@ </p>
@ <p>Optional human-readable description for this report<br>
@ %z(href("%R/markup_help"))Markup style</a>:
mimetype_option_menu(zMimetype, "m");
@ <br><textarea aria-label="Description:" name="d" class="wikiedit" \
@ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea>
@ </p>
@ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
@ Make this the default report</label></p>
if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
@ <p>This report format is owned by %h(zOwner). You are not allowed
@ to change it.</p>
@ </form>
report_format_hints();
style_finish_page();
return;
}
@ <input type="submit" value="Apply Changes">
if( rn>0 ){
@ <input type="submit" value="Delete This Report" name="del1">
}
@ </div></form>
report_format_hints();
style_finish_page();
}
/*
** Output a bunch of text that provides information about report
** formats
*/
static void report_format_hints(void){
char *zSchema;
zSchema = db_text(0,"SELECT sql FROM sqlite_schema WHERE name='ticket'");
if( zSchema==0 ){
zSchema = db_text(0,"SELECT sql FROM repository.sqlite_schema"
" WHERE name='ticket'");
}
@ <hr><h3>TICKET Schema</h3>
@ <blockquote><pre>
@ <code class="language-sql">%h(zSchema)</code>
@ </pre></blockquote>
@ <h3>Notes</h3>
@ <ul>
@ <li><p>The SQL must consist of a single SELECT statement</p></li>
@
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
389 390 391 392 393 394 395 | @ @ -- Assignments of tags to artifacts. Note that we allow tags to @ -- have values assigned to them. So we are not really dealing with @ -- tags here. These are really properties. But we are going to @ -- keep calling them tags because in many cases the value is ignored. @ -- @ CREATE TABLE tagxref( | | > | > | > | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 | @ @ -- Assignments of tags to artifacts. Note that we allow tags to @ -- have values assigned to them. So we are not really dealing with @ -- tags here. These are really properties. But we are going to @ -- keep calling them tags because in many cases the value is ignored. @ -- @ CREATE TABLE tagxref( @ tagid INTEGER REFERENCES tag, -- The tag being added, removed, @ -- or propagated @ tagtype INTEGER, -- 0:-,cancel 1:+,single 2:*,propagate @ srcid INTEGER REFERENCES blob, -- Artifact tag originates from, or @ -- 0 for propagated tags @ origid INTEGER REFERENCES blob, -- Artifact holding propagated tag @ -- (any artifact type with a P-card) @ value TEXT, -- Value of the tag. Might be NULL. @ mtime TIMESTAMP, -- Time of addition or removal. Julian day @ rid INTEGER REFERENCE blob, -- Artifact tag is applied to @ UNIQUE(rid, tagid) @ ); @ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime); @ |
| ︙ | ︙ |
Changes to src/search.c.
| ︙ | ︙ | |||
919 920 921 922 923 924 925 926 927 928 929 |
static void search_indexed(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
Blob sql;
char *zPat = mprintf("%s",zPattern);
int i;
if( srchFlags==0 ) return;
sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
search_rank_sqlfunc, 0, 0);
for(i=0; zPat[i]; i++){
| > | > > > > > > > > | | | | 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 |
static void search_indexed(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
Blob sql;
char *zPat = mprintf("%s",zPattern);
int i;
static const char *zSnippetCall;
if( srchFlags==0 ) return;
sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
search_rank_sqlfunc, 0, 0);
for(i=0; zPat[i]; i++){
if( (zPat[i]&0x80)!=0 || !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
}
blob_init(&sql, 0, 0);
if( search_index_type(0)==4 ){
/* If this repo is still using the legacy FTS4 search index, then
** the snippet() function is slightly different */
zSnippetCall = "snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)";
}else{
/* This is the common case - Using newer FTS5 search index */
zSnippetCall = "snippet(ftsidx,-1,'<mark>','</mark>',' ... ',35)";
}
blob_appendf(&sql,
"INSERT INTO x(label,url,score,id,date,snip) "
" SELECT ftsdocs.label,"
" ftsdocs.url,"
" rank(matchinfo(ftsidx,'pcsx')),"
" ftsdocs.type || ftsdocs.rid,"
" datetime(ftsdocs.mtime),"
" %s"
" FROM ftsidx CROSS JOIN ftsdocs"
" WHERE ftsidx MATCH %Q"
" AND ftsdocs.rowid=ftsidx.rowid",
zSnippetCall /*safe-for-%s*/, zPat
);
fossil_free(zPat);
if( srchFlags!=SRCH_ALL ){
const char *zSep = " AND (";
static const struct { unsigned m; char c; } aMask[] = {
{ SRCH_CKIN, 'c' },
{ SRCH_DOC, 'd' },
|
| ︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 |
@ <ol>
}
nRow++;
@ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
if( fDebug ){
@ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
}
| | | 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
@ <ol>
}
nRow++;
@ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
if( fDebug ){
@ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
}
@ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \
if( zDate && zDate[0] && strstr(zLabel,zDate)==0 ){
@ <small>(%h(zDate))</small>
}
@ </span></li>
if( nLimit && nRow>=nLimit ) break;
}
db_finalize(&q);
|
| ︙ | ︙ | |||
1501 1502 1503 1504 1505 1506 1507 |
blob_init(&out, 0, 0);
get_stext_by_mimetype(&in, g.argv[3], &out);
fossil_print("%s\n",blob_str(&out));
blob_reset(&in);
blob_reset(&out);
}
| > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | 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 |
blob_init(&out, 0, 0);
get_stext_by_mimetype(&in, g.argv[3], &out);
fossil_print("%s\n",blob_str(&out));
blob_reset(&in);
blob_reset(&out);
}
/*
** The schema for the full-text index. The %s part must be an empty
** string or a comma followed by additional flags for the FTS virtual
** table.
*/
static const char zFtsSchema[] =
@ -- One entry for each possible search result
@ CREATE TABLE IF NOT EXISTS repository.ftsdocs(
@ rowid INTEGER PRIMARY KEY, -- Maps to the ftsidx.rowid
@ type CHAR(1), -- Type of document
@ rid INTEGER, -- BLOB.RID or TAG.TAGID for the document
@ name TEXT, -- Additional document description
@ idxed BOOLEAN, -- True if currently in the index
@ label TEXT, -- Label to print on search results
@ url TEXT, -- URL to access this document
@ mtime DATE, -- Date when document created
@ bx TEXT, -- Temporary "body" content cache
@ UNIQUE(type,rid)
@ );
@ CREATE INDEX repository.ftsdocIdxed ON ftsdocs(type,rid,name) WHERE idxed==0;
@ CREATE INDEX repository.ftsdocName ON ftsdocs(name) WHERE type='w';
@ CREATE VIEW IF NOT EXISTS repository.ftscontent AS
@ SELECT rowid, type, rid, name, idxed, label, url, mtime,
@ title(type,rid,name) AS 'title', body(type,rid,name) AS 'body'
@ FROM ftsdocs;
@ CREATE VIRTUAL TABLE IF NOT EXISTS repository.ftsidx
@ USING fts5(content="ftscontent", title, body%s);
;
static const char zFtsDrop[] =
@ DROP TABLE IF EXISTS repository.ftsidx;
@ DROP VIEW IF EXISTS repository.ftscontent;
@ DROP TABLE IF EXISTS repository.ftsdocs;
;
#if INTERFACE
/*
** Values for the search-tokenizer config option.
*/
#define FTS5TOK_NONE 0 /* no FTS stemmer */
#define FTS5TOK_PORTER 1 /* porter stemmer */
#define FTS5TOK_TRIGRAM 3 /* trigram stemmer */
#endif
/*
** Cached FTS5TOK_xyz value for search_tokenizer_type() and
** friends.
*/
static int iFtsTokenizer = -1;
/*
** Returns one of the FTS5TOK_xyz values, depending on the value of
** the search-tokenizer config entry, defaulting to FTS5TOK_NONE. The
** result of the first call is cached for subsequent calls unless
** bRecheck is true.
*/
int search_tokenizer_type(int bRecheck){
char *z;
if( iFtsTokenizer>=0 && bRecheck==0 ){
return iFtsTokenizer;
}
z = db_get("search-tokenizer",0);
if( 0==z ){
iFtsTokenizer = FTS5TOK_NONE;
}else if(0==fossil_strcmp(z,"porter")){
iFtsTokenizer = FTS5TOK_PORTER;
}else if(0==fossil_strcmp(z,"trigram")){
iFtsTokenizer = FTS5TOK_TRIGRAM;
}else{
iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE;
}
fossil_free(z);
return iFtsTokenizer;
}
/*
** Returns a string value suitable for use as the search-tokenizer
** setting's value, depending on the value of z. If z is 0 then the
** current search-tokenizer value is used as the basis for formulating
** the result (which may differ from the current value but will have
** the same meaning). Any unknown/unsupported value is interpreted as
** "off".
*/
const char *search_tokenizer_for_string(const char *z){
char * zTmp = 0;
const char *zRc = 0;
if( 0==z ){
z = zTmp = db_get("search-tokenizer",0);
}
if( 0==z ){
zRc = "off";
}else if( 0==fossil_strcmp(z,"porter") ){
zRc = "porter";
}else if( 0==fossil_strcmp(z,"trigram") ){
zRc = "trigram";
}else{
zRc = is_truth(z) ? "porter" : "off";
}
fossil_free(zTmp);
return zRc;
}
/*
** Sets the search-tokenizer config setting to the value of
** search_tokenizer_for_string(zName).
*/
void search_set_tokenizer(const char *zName){
db_set("search-tokenizer", search_tokenizer_for_string( zName ), 0);
iFtsTokenizer = -1;
}
/*
** Create or drop the tables associated with a full-text index.
*/
static int searchIdxExists = -1;
void search_create_index(void){
const int useTokenizer = search_tokenizer_type(0);
const char *zExtra;
switch(useTokenizer){
case FTS5TOK_PORTER: zExtra = ",tokenize=porter"; break;
case FTS5TOK_TRIGRAM: zExtra = ",tokenize=trigram"; break;
default: zExtra = ""; break;
}
search_sql_setup(g.db);
db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/);
searchIdxExists = 1;
}
void search_drop_index(void){
db_multi_exec(zFtsDrop/*works-like:""*/);
searchIdxExists = 0;
}
/*
** Return true if the full-text search index exists. See also the
** search_index_type() function.
*/
int search_index_exists(void){
if( searchIdxExists<0 ){
searchIdxExists = db_table_exists("repository","ftsdocs");
}
return searchIdxExists;
}
/*
** Determine which full-text search index is currently being used to
** add searching. Return values:
**
** 0 No search index is available
** 4 FTS3/4
** 5 FTS5
**
** Results are cached. Make the argument 1 to reset the cache. See
** also the search_index_exists() routine.
*/
int search_index_type(int bReset){
static int idxType = -1;
if( idxType<0 || bReset ){
idxType = db_int(0,
"SELECT CASE WHEN sql GLOB '*fts4*' THEN 4 ELSE 5 END"
" FROM repository.sqlite_schema WHERE name='ftsidx'"
);
}
return idxType;
}
/*
** Fill the FTSDOCS table with unindexed entries for everything
** in the repository. This uses INSERT OR IGNORE so entries already
** in FTSDOCS are unchanged.
*/
void search_fill_index(void){
|
| ︙ | ︙ | |||
1604 1605 1606 1607 1608 1609 1610 |
void search_doc_touch(char cType, int rid, const char *zName){
if( search_index_exists() && !content_is_private(rid) ){
char zType[2];
zType[0] = cType;
zType[1] = 0;
search_sql_setup(g.db);
db_multi_exec(
| | | | 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 |
void search_doc_touch(char cType, int rid, const char *zName){
if( search_index_exists() && !content_is_private(rid) ){
char zType[2];
zType[0] = cType;
zType[1] = 0;
search_sql_setup(g.db);
db_multi_exec(
"DELETE FROM ftsidx WHERE rowid IN"
" (SELECT rowid FROM ftsdocs WHERE type=%Q AND rid=%d AND idxed)",
zType, rid
);
db_multi_exec(
"REPLACE INTO ftsdocs(type,rid,name,idxed)"
" VALUES(%Q,%d,%Q,0)",
zType, rid, zName
);
if( cType=='w' || cType=='e' ){
db_multi_exec(
"DELETE FROM ftsidx WHERE rowid IN"
" (SELECT rowid FROM ftsdocs WHERE type='%c' AND name=%Q AND idxed)",
cType, zName
);
db_multi_exec(
"DELETE FROM ftsdocs WHERE type='%c' AND name=%Q AND rid!=%d",
cType, zName, rid
);
|
| ︙ | ︙ | |||
1655 1656 1657 1658 1659 1660 1661 |
"INSERT OR IGNORE INTO current_docs(rid, name)"
" SELECT blob.rid, foci.filename FROM foci, blob"
" WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
" AND %z",
ckid, glob_expr("foci.filename", db_get("doc-glob",""))
);
db_multi_exec(
| | | | | 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 |
"INSERT OR IGNORE INTO current_docs(rid, name)"
" SELECT blob.rid, foci.filename FROM foci, blob"
" WHERE foci.checkinID=%d AND blob.uuid=foci.uuid"
" AND %z",
ckid, glob_expr("foci.filename", db_get("doc-glob",""))
);
db_multi_exec(
"DELETE FROM ftsidx WHERE rowid IN"
" (SELECT rowid FROM ftsdocs WHERE type='d'"
" AND rid NOT IN (SELECT rid FROM current_docs))"
);
db_multi_exec(
"DELETE FROM ftsdocs WHERE type='d'"
" AND rid NOT IN (SELECT rid FROM current_docs)"
);
db_multi_exec(
"INSERT OR IGNORE INTO ftsdocs(type,rid,name,idxed,label,bx,url,mtime)"
" SELECT 'd', rid, name, 0,"
" title('d',rid,name),"
" body('d',rid,name),"
" printf('/doc/%T/%%s',urlencode(name)),"
" %.17g"
" FROM current_docs",
zDocBr, rTime
);
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, label, bx FROM ftsdocs WHERE type='d' AND NOT idxed"
);
db_multi_exec(
"UPDATE ftsdocs SET"
" idxed=1,"
" bx=NULL,"
" label='Document: '||label"
" WHERE type='d' AND NOT idxed"
);
}
/*
** Deal with all of the unindexed 'c' terms in FTSDOCS
*/
static void search_update_checkin_index(void){
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, '', body('c',rid,NULL) FROM ftsdocs"
" WHERE type='c' AND NOT idxed;"
);
db_multi_exec(
"UPDATE ftsdocs SET idxed=1, name=NULL,"
" (label,url,mtime) = "
" (SELECT printf('Check-in [%%.16s] on %%s',blob.uuid,"
|
| ︙ | ︙ | |||
1714 1715 1716 1717 1718 1719 1720 |
}
/*
** Deal with all of the unindexed 't' terms in FTSDOCS
*/
static void search_update_ticket_index(void){
db_multi_exec(
| | | 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 |
}
/*
** Deal with all of the unindexed 't' terms in FTSDOCS
*/
static void search_update_ticket_index(void){
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, title('t',rid,NULL), body('t',rid,NULL) FROM ftsdocs"
" WHERE type='t' AND NOT idxed;"
);
if( db_changes()==0 ) return;
db_multi_exec(
"UPDATE ftsdocs SET idxed=1, name=NULL,"
" (label,url,mtime) ="
|
| ︙ | ︙ | |||
1737 1738 1739 1740 1741 1742 1743 |
}
/*
** Deal with all of the unindexed 'w' terms in FTSDOCS
*/
static void search_update_wiki_index(void){
db_multi_exec(
| | | 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 |
}
/*
** Deal with all of the unindexed 'w' terms in FTSDOCS
*/
static void search_update_wiki_index(void){
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, title('w',rid,NULL),body('w',rid,NULL) FROM ftsdocs"
" WHERE type='w' AND NOT idxed;"
);
if( db_changes()==0 ) return;
db_multi_exec(
"UPDATE ftsdocs SET idxed=1,"
" (name,label,url,mtime) = "
|
| ︙ | ︙ | |||
1759 1760 1761 1762 1763 1764 1765 |
}
/*
** Deal with all of the unindexed 'f' terms in FTSDOCS
*/
static void search_update_forum_index(void){
db_multi_exec(
| | | 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 |
}
/*
** Deal with all of the unindexed 'f' terms in FTSDOCS
*/
static void search_update_forum_index(void){
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, title('f',rid,NULL),body('f',rid,NULL) FROM ftsdocs"
" WHERE type='f' AND NOT idxed;"
);
if( db_changes()==0 ) return;
db_multi_exec(
"UPDATE ftsdocs SET idxed=1, name=NULL,"
" (label,url,mtime) = "
|
| ︙ | ︙ | |||
1782 1783 1784 1785 1786 1787 1788 |
}
/*
** Deal with all of the unindexed 'e' terms in FTSDOCS
*/
static void search_update_technote_index(void){
db_multi_exec(
| | | 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 |
}
/*
** Deal with all of the unindexed 'e' terms in FTSDOCS
*/
static void search_update_technote_index(void){
db_multi_exec(
"INSERT INTO ftsidx(rowid,title,body)"
" SELECT rowid, title('e',rid,NULL),body('e',rid,NULL) FROM ftsdocs"
" WHERE type='e' AND NOT idxed;"
);
if( db_changes()==0 ) return;
db_multi_exec(
"UPDATE ftsdocs SET idxed=1,"
" (name,label,url,mtime) = "
|
| ︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 | ** index (on|off) Turn the search index on or off ** ** enable cdtwe Enable various kinds of search. c=Check-ins, ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes. ** ** disable cdtwe Disable various kinds of search ** | | > > | | | | 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 |
** index (on|off) Turn the search index on or off
**
** enable cdtwe Enable various kinds of search. c=Check-ins,
** d=Documents, t=Tickets, w=Wiki, e=Tech Notes.
**
** disable cdtwe Disable various kinds of search
**
** tokenizer VALUE Select a tokenizer for indexed search. VALUE
** may be one of (porter, on, off, trigram), and
** "on" is equivalent to "porter". Unindexed
** search never uses tokenization or stemming.
**
** The current search settings are displayed after any changes are applied.
** Run this command with no arguments to simply see the settings.
*/
void fts_config_cmd(void){
static const struct {
int iCmd;
const char *z;
} aCmd[] = {
{ 1, "reindex" },
{ 2, "index" },
{ 3, "disable" },
{ 4, "enable" },
{ 5, "tokenizer"},
};
static const struct {
const char *zSetting;
const char *zName;
const char *zSw;
} aSetng[] = {
{ "search-ci", "check-in search:", "c" },
|
| ︙ | ︙ | |||
1932 1933 1934 1935 1936 1937 1938 |
if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd));
zCtrl = g.argv[3];
for(j=0; j<count(aSetng); j++){
if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
}
}
| < | > | > > | > > > > > | | | | | | 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 |
if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd));
zCtrl = g.argv[3];
for(j=0; j<count(aSetng); j++){
if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
}
}
}else if( iCmd==5 ){
int iOldTokenizer, iNewTokenizer;
if( g.argc<4 ) usage("tokenizer porter|on|off|trigram");
iOldTokenizer = search_tokenizer_type(0);
db_set("search-tokenizer",
search_tokenizer_for_string(g.argv[3]), 0);
iNewTokenizer = search_tokenizer_type(1);
if( iOldTokenizer!=iNewTokenizer ){
/* Drop or rebuild index if tokenizer changes. */
iAction = 1 + ((iOldTokenizer && iNewTokenizer)
? 1 : (iNewTokenizer ? 1 : 0));
}
}
/* destroy or rebuild the index, if requested */
if( iAction>=1 ){
search_drop_index();
}
if( iAction>=2 ){
search_rebuild_index();
}
/* Always show the status before ending */
for(i=0; i<count(aSetng); i++){
fossil_print("%-17s %s\n", aSetng[i].zName,
db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
}
fossil_print("%-17s %s\n", "tokenizer:",
search_tokenizer_for_string(0));
if( search_index_exists() ){
fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1));
fossil_print("%-17s %d\n", "documents:",
db_int(0, "SELECT count(*) FROM ftsdocs"));
}else{
fossil_print("%-17s disabled\n", "full-text index:");
}
db_end_transaction(0);
}
|
| ︙ | ︙ | |||
2000 2001 2002 2003 2004 2005 2006 |
);
if( db_step(&q)==SQLITE_ROW ){
const char *zUrl = db_column_text(&q,4);
const char *zDocId = db_column_text(&q,0);
char *zName;
char *z;
@ <table border=0>
| | | | | 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 |
);
if( db_step(&q)==SQLITE_ROW ){
const char *zUrl = db_column_text(&q,4);
const char *zDocId = db_column_text(&q,0);
char *zName;
char *z;
@ <table border=0>
@ <tr><td align='right'>rowid:<td> <td>%d(id)
@ <tr><td align='right'>id:<td><td>%s(zDocId)
@ <tr><td align='right'>name:<td><td>%h(db_column_text(&q,1))
@ <tr><td align='right'>idxed:<td><td>%d(db_column_int(&q,2))
@ <tr><td align='right'>label:<td><td>%h(db_column_text(&q,3))
@ <tr><td align='right'>url:<td><td>
@ <a href='%R%s(zUrl)'>%h(zUrl)</a>
@ <tr><td align='right'>mtime:<td><td>%s(db_column_text(&q,5))
z = db_text(0, "SELECT title FROM ftsidx WHERE rowid=%d",id);
if( z && z[0] ){
@ <tr><td align="right">title:<td><td>%h(z)
fossil_free(z);
}
z = db_text(0, "SELECT body FROM ftsidx WHERE rowid=%d",id);
if( z && z[0] ){
@ <tr><td align="right" valign="top">body:<td><td>%h(z)
fossil_free(z);
}
@ </table>
zName = mprintf("Indexed '%c' docs",zDocId[0]);
style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=1",zDocId[0]);
|
| ︙ | ︙ | |||
2101 2102 2103 2104 2105 2106 2107 | @ </tbody><tfooter> @ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2) @ <th align="right">%d(cnt3) @ </tfooter> @ </table> style_finish_page(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 |
@ </tbody><tfooter>
@ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2)
@ <th align="right">%d(cnt3)
@ </tfooter>
@ </table>
style_finish_page();
}
/*
** The Fts5MatchinfoCtx bits were all taken verbatim from:
**
** https://sqlite.org/src/finfo?name=ext/fts5/fts5_test_mi.c
*/
typedef struct Fts5MatchinfoCtx Fts5MatchinfoCtx;
#if INTERFACE
#ifndef SQLITE_AMALGAMATION
typedef unsigned int u32;
#endif
#endif
struct Fts5MatchinfoCtx {
int nCol; /* Number of cols in FTS5 table */
int nPhrase; /* Number of phrases in FTS5 query */
char *zArg; /* nul-term'd copy of 2nd arg */
int nRet; /* Number of elements in aRet[] */
u32 *aRet; /* Array of 32-bit unsigned ints to return */
};
/*
** Return a pointer to the fts5_api pointer for database connection db.
** If an error occurs, return NULL and leave an error in the database
** handle (accessible using sqlite3_errcode()/errmsg()).
*/
static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){
sqlite3_stmt *pStmt = 0;
int rc;
*ppApi = 0;
rc = sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_pointer(pStmt, 1, (void*)ppApi, "fts5_api_ptr", 0);
(void)sqlite3_step(pStmt);
rc = sqlite3_finalize(pStmt);
}
return rc;
}
/*
** Argument f should be a flag accepted by matchinfo() (a valid character
** in the string passed as the second argument). If it is not, -1 is
** returned. Otherwise, if f is a valid matchinfo flag, the value returned
** is the number of 32-bit integers added to the output array if the
** table has nCol columns and the query nPhrase phrases.
*/
static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
int ret = -1;
switch( f ){
case 'p': ret = 1; break;
case 'c': ret = 1; break;
case 'x': ret = 3 * nCol * nPhrase; break;
case 'y': ret = nCol * nPhrase; break;
case 'b': ret = ((nCol + 31) / 32) * nPhrase; break;
case 'n': ret = 1; break;
case 'a': ret = nCol; break;
case 'l': ret = nCol; break;
case 's': ret = nCol; break;
}
return ret;
}
static int fts5MatchinfoIter(
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
Fts5Context *pFts, /* First arg to pass to pApi functions */
Fts5MatchinfoCtx *p,
int(*x)(const Fts5ExtensionApi*,Fts5Context*,Fts5MatchinfoCtx*,char,u32*)
){
int i;
int n = 0;
int rc = SQLITE_OK;
char f;
for(i=0; (f = p->zArg[i]); i++){
rc = x(pApi, pFts, p, f, &p->aRet[n]);
if( rc!=SQLITE_OK ) break;
n += fts5MatchinfoFlagsize(p->nCol, p->nPhrase, f);
}
return rc;
}
static int fts5MatchinfoXCb(
const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
void *pUserData
){
Fts5PhraseIter iter;
int iCol, iOff;
u32 *aOut = (u32*)pUserData;
int iPrev = -1;
for(pApi->xPhraseFirst(pFts, 0, &iter, &iCol, &iOff);
iCol>=0;
pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
){
aOut[iCol*3+1]++;
if( iCol!=iPrev ) aOut[iCol*3 + 2]++;
iPrev = iCol;
}
return SQLITE_OK;
}
static int fts5MatchinfoGlobalCb(
const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
Fts5MatchinfoCtx *p,
char f,
u32 *aOut
){
int rc = SQLITE_OK;
switch( f ){
case 'p':
aOut[0] = p->nPhrase;
break;
case 'c':
aOut[0] = p->nCol;
break;
case 'x': {
int i;
for(i=0; i<p->nPhrase && rc==SQLITE_OK; i++){
void *pPtr = (void*)&aOut[i * p->nCol * 3];
rc = pApi->xQueryPhrase(pFts, i, pPtr, fts5MatchinfoXCb);
}
break;
}
case 'n': {
sqlite3_int64 nRow;
rc = pApi->xRowCount(pFts, &nRow);
aOut[0] = (u32)nRow;
break;
}
case 'a': {
sqlite3_int64 nRow = 0;
rc = pApi->xRowCount(pFts, &nRow);
if( nRow==0 ){
memset(aOut, 0, sizeof(u32) * p->nCol);
}else{
int i;
for(i=0; rc==SQLITE_OK && i<p->nCol; i++){
sqlite3_int64 nToken;
rc = pApi->xColumnTotalSize(pFts, i, &nToken);
if( rc==SQLITE_OK){
aOut[i] = (u32)((2*nToken + nRow) / (2*nRow));
}
}
}
break;
}
}
return rc;
}
static int fts5MatchinfoLocalCb(
const Fts5ExtensionApi *pApi,
Fts5Context *pFts,
Fts5MatchinfoCtx *p,
char f,
u32 *aOut
){
int i;
int rc = SQLITE_OK;
switch( f ){
case 'b': {
int iPhrase;
int nInt = ((p->nCol + 31) / 32) * p->nPhrase;
for(i=0; i<nInt; i++) aOut[i] = 0;
for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
Fts5PhraseIter iter;
int iCol;
for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
iCol>=0;
pApi->xPhraseNextColumn(pFts, &iter, &iCol)
){
aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32);
}
}
break;
}
case 'x':
case 'y': {
int nMul = (f=='x' ? 3 : 1);
int iPhrase;
for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0;
for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
Fts5PhraseIter iter;
int iOff, iCol;
for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
iOff>=0;
pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
){
aOut[nMul * (iCol + iPhrase * p->nCol)]++;
}
}
break;
}
case 'l': {
for(i=0; rc==SQLITE_OK && i<p->nCol; i++){
int nToken;
rc = pApi->xColumnSize(pFts, i, &nToken);
aOut[i] = (u32)nToken;
}
break;
}
case 's': {
int nInst;
memset(aOut, 0, sizeof(u32) * p->nCol);
rc = pApi->xInstCount(pFts, &nInst);
for(i=0; rc==SQLITE_OK && i<nInst; i++){
int iPhrase, iOff, iCol = 0;
int iNextPhrase;
int iNextOff;
u32 nSeq = 1;
int j;
rc = pApi->xInst(pFts, i, &iPhrase, &iCol, &iOff);
iNextPhrase = iPhrase+1;
iNextOff = iOff+pApi->xPhraseSize(pFts, 0);
for(j=i+1; rc==SQLITE_OK && j<nInst; j++){
int ip, ic, io;
rc = pApi->xInst(pFts, j, &ip, &ic, &io);
if( ic!=iCol || io>iNextOff ) break;
if( ip==iNextPhrase && io==iNextOff ){
nSeq++;
iNextPhrase = ip+1;
iNextOff = io + pApi->xPhraseSize(pFts, ip);
}
}
if( nSeq>aOut[iCol] ) aOut[iCol] = nSeq;
}
break;
}
}
return rc;
}
static Fts5MatchinfoCtx *fts5MatchinfoNew(
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
Fts5Context *pFts, /* First arg to pass to pApi functions */
sqlite3_context *pCtx, /* Context for returning error message */
const char *zArg /* Matchinfo flag string */
){
Fts5MatchinfoCtx *p;
int nCol;
int nPhrase;
int i;
int nInt;
sqlite3_int64 nByte;
int rc;
nCol = pApi->xColumnCount(pFts);
nPhrase = pApi->xPhraseCount(pFts);
nInt = 0;
for(i=0; zArg[i]; i++){
int n = fts5MatchinfoFlagsize(nCol, nPhrase, zArg[i]);
if( n<0 ){
char *zErr = sqlite3_mprintf("unrecognized matchinfo flag: %c", zArg[i]);
sqlite3_result_error(pCtx, zErr, -1);
sqlite3_free(zErr);
return 0;
}
nInt += n;
}
nByte = sizeof(Fts5MatchinfoCtx) /* The struct itself */
+ sizeof(u32) * nInt /* The p->aRet[] array */
+ (i+1); /* The p->zArg string */
p = (Fts5MatchinfoCtx*)sqlite3_malloc64(nByte);
if( p==0 ){
sqlite3_result_error_nomem(pCtx);
return 0;
}
memset(p, 0, nByte);
p->nCol = nCol;
p->nPhrase = nPhrase;
p->aRet = (u32*)&p[1];
p->nRet = nInt;
p->zArg = (char*)&p->aRet[nInt];
memcpy(p->zArg, zArg, i);
rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoGlobalCb);
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(pCtx, rc);
sqlite3_free(p);
p = 0;
}
return p;
}
static void fts5MatchinfoFunc(
const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
Fts5Context *pFts, /* First arg to pass to pApi functions */
sqlite3_context *pCtx, /* Context for returning result/error */
int nVal, /* Number of values in apVal[] array */
sqlite3_value **apVal /* Array of trailing arguments */
){
const char *zArg;
Fts5MatchinfoCtx *p;
int rc = SQLITE_OK;
if( nVal>0 ){
zArg = (const char*)sqlite3_value_text(apVal[0]);
}else{
zArg = "pcx";
}
p = (Fts5MatchinfoCtx*)pApi->xGetAuxdata(pFts, 0);
if( p==0 || sqlite3_stricmp(zArg, p->zArg) ){
p = fts5MatchinfoNew(pApi, pFts, pCtx, zArg);
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
rc = pApi->xSetAuxdata(pFts, p, sqlite3_free);
}
}
if( rc==SQLITE_OK ){
rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb);
}
if( rc!=SQLITE_OK ){
sqlite3_result_error_code(pCtx, rc);
}else{
/* No errors has occured, so return a copy of the array of integers. */
int nByte = p->nRet * sizeof(u32);
sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT);
}
}
int db_register_fts5(sqlite3 *db){
int rc; /* Return code */
fts5_api *pApi; /* FTS5 API functions */
/* Extract the FTS5 API pointer from the database handle. The
** fts5_api_from_db() function above is copied verbatim from the
** FTS5 documentation. Refer there for details. */
rc = fts5_api_from_db(db, &pApi);
if( rc!=SQLITE_OK ) return rc;
/* If fts5_api_from_db() returns NULL, then either FTS5 is not registered
** with this database handle, or an error (OOM perhaps?) has occurred.
**
** Also check that the fts5_api object is version 2 or newer.
*/
if( pApi==0 || pApi->iVersion<2 ){
return SQLITE_ERROR;
}
/* Register the implementation of matchinfo() */
rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0);
return rc;
}
|
Changes to src/security_audit.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 105 | const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */ const char *zDevCap; /* Capabilities of user group "developer" */ const char *zReadCap; /* Capabilities of user group "reader" */ const char *zPubPages; /* GLOB pattern for public pages */ const char *zSelfCap; /* Capabilities of self-registered users */ int hasSelfReg = 0; /* True if able to self-register */ const char *zPublicUrl; /* Canonical access URL */ char *z; | > | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */
const char *zDevCap; /* Capabilities of user group "developer" */
const char *zReadCap; /* Capabilities of user group "reader" */
const char *zPubPages; /* GLOB pattern for public pages */
const char *zSelfCap; /* Capabilities of self-registered users */
int hasSelfReg = 0; /* True if able to self-register */
const char *zPublicUrl; /* Canonical access URL */
Blob cmd;
char *z;
int n, i;
CapabilityString *pCap;
char **azCSP; /* Parsed content security policy */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
|
| ︙ | ︙ | |||
689 690 691 692 693 694 695 696 697 698 699 700 701 702 |
@ <blockquote><pre>
@ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL;
@ </pre></blockquote>
@ </p>
table_of_public_phantoms();
@ </li>
}
@ </ol>
style_finish_page();
}
/*
** WEBPAGE: takeitprivate
| > > > > > > > > > > > | 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 |
@ <blockquote><pre>
@ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL;
@ </pre></blockquote>
@ </p>
table_of_public_phantoms();
@ </li>
}
blob_init(&cmd, 0, 0);
for(i=0; g.argvOrig[i]!=0; i++){
blob_append_escaped_arg(&cmd, g.argvOrig[i], 0);
}
@ <li><p>
@ The command that generated this page:
@ <blockquote>
@ <tt>%h(blob_str(&cmd))</tt>
@ </blockquote></li>
blob_zero(&cmd);
@ </ol>
style_finish_page();
}
/*
** WEBPAGE: takeitprivate
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
"Configure the wiki for this repository");
setup_menu_entry("Chat", "setup_chat",
"Configure the chatroom");
}
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
| > > | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
"Configure the wiki for this repository");
setup_menu_entry("Interwiki Map", "intermap",
"Mapping keywords for interwiki links");
setup_menu_entry("Chat", "setup_chat",
"Configure the chatroom");
}
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
|
| ︙ | ︙ | |||
215 216 217 218 219 220 221 |
@ aria-label="%h(zLabel[0]?zLabel:zQParm)" \
if( iVal ){
@ checked="checked" \
}
if( disabled ){
@ disabled="disabled" \
}
| | | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
@ aria-label="%h(zLabel[0]?zLabel:zQParm)" \
if( iVal ){
@ checked="checked" \
}
if( disabled ){
@ disabled="disabled" \
}
@ > <b>%s(zLabel)</b></label>
}
/*
** Generate an entry box for an attribute.
*/
void entry_attribute(
const char *zLabel, /* The text label on the entry box */
|
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
zVal = zQ;
}
@ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \
@ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
if( disabled ){
@ disabled="disabled" \
}
| | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
zVal = zQ;
}
@ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \
@ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
if( disabled ){
@ disabled="disabled" \
}
@ > <b>%s(zLabel)</b>
}
/*
** Generate a text box for an attribute.
*/
const char *textarea_attribute(
const char *zLabel, /* The text label on the textarea */
|
| ︙ | ︙ | |||
367 368 369 370 371 372 373 |
@ this means that hyperlink visited/unvisited colors will not operate
@ on those platforms when "UserAgent and Javascript" is selected.</p>
@
@ <p>Additional parameters that control the behavior of Javascript:</p>
@ <blockquote>
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
"auto-hyperlink-delay", "ah-delay", "50", 0);
| | | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
@ this means that hyperlink visited/unvisited colors will not operate
@ on those platforms when "UserAgent and Javascript" is selected.</p>
@
@ <p>Additional parameters that control the behavior of Javascript:</p>
@ <blockquote>
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
"auto-hyperlink-delay", "ah-delay", "50", 0);
@ <br>
onoff_attribute("Also require a mouse event before enabling hyperlinks",
"auto-hyperlink-mouseover", "ahmo", 0, 0);
@ </blockquote>
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
@ and "require a mouse event" should be turned on. These values only come
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
@ Javascript").</p>
|
| ︙ | ︙ | |||
408 409 410 411 412 413 414 | @ website can present a crippling CPU and bandwidth load. @ @ <p>The settings on this page are intended to help site administrators @ defend the site against robots. @ @ <form action="%R/setup_robot" method="post"><div> login_insert_csrf_secret(); | | | | | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | @ website can present a crippling CPU and bandwidth load. @ @ <p>The settings on this page are intended to help site administrators @ defend the site against robots. @ @ <form action="%R/setup_robot" method="post"><div> login_insert_csrf_secret(); @ <input type="submit" name="submit" value="Apply Changes"></p> @ <hr> addAutoHyperlinkSettings(); @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } /* ** WEBPAGE: setup_access |
| ︙ | ︙ | |||
440 441 442 443 444 445 446 |
}
style_set_current_feature("setup");
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%R/setup_access" method="post"><div>
login_insert_csrf_secret();
| | | | | 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 |
}
style_set_current_feature("setup");
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%R/setup_access" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
multiple_choice_attribute("Redirect to HTTPS",
"redirect-to-https", "redirhttps", "0",
count(azRedirectOpts)/2, azRedirectOpts);
@ <p>Force the use of HTTPS by redirecting to HTTPS when an
@ unencrypted request is received. This feature can be enabled
@ for the Login page only, or for all pages.
@ <p>Further details: When enabled, this option causes the $secureurl TH1
@ variable is set to an "https:" variant of $baseurl. Otherwise,
@ $secureurl is just an alias for $baseurl.
@ (Property: "redirect-to-https". "0" for off, "1" for Login page only,
@ "2" otherwise.)
@ <hr>
onoff_attribute("Require password for local access",
"localauth", "localauth", 0, 0);
@ <p>When enabled, the password sign-in is always required for
@ web access. When disabled, unrestricted web access from 127.0.0.1
@ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
@ from the <a href="%R/help/server">fossil server</a>,
@ <a href="%R/help/http">fossil http</a> commands when the
|
| ︙ | ︙ | |||
479 480 481 482 483 484 485 | @ <a href="%R/help/server">fossil http</a> commands @ without the "--localauth" option. @ <li> The server is started from CGI without the "localauth" keyword @ in the CGI script. @ </ol> @ (Property: "localauth") @ | | | | | | | | | | | | | | | | | | | | | | 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 |
@ <a href="%R/help/server">fossil http</a> commands
@ without the "--localauth" option.
@ <li> The server is started from CGI without the "localauth" keyword
@ in the CGI script.
@ </ol>
@ (Property: "localauth")
@
@ <hr>
onoff_attribute("Enable /test_env",
"test_env_enable", "test_env_enable", 0, 0);
@ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
@ users. When disabled (the default) only users Admin and Setup can visit
@ the /test_env page.
@ (Property: "test_env_enable")
@ </p>
@
@ <hr>
onoff_attribute("Enable /artifact_stats",
"artifact_stats_enable", "artifact_stats_enable", 0, 0);
@ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all
@ users. When disabled (the default) only users with check-in privilege may
@ access the /artifact_stats page.
@ (Property: "artifact_stats_enable")
@ </p>
@
@ <hr>
onoff_attribute("Allow REMOTE_USER authentication",
"remote_user_ok", "remote_user_ok", 0, 0);
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
@ login name of a valid user and no other login credentials are available,
@ then the REMOTE_USER is accepted as an authenticated user.
@ (Property: "remote_user_ok")
@ </p>
@
@ <hr>
onoff_attribute("Allow HTTP_AUTHENTICATION authentication",
"http_authentication_ok", "http_authentication_ok", 0, 0);
@ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment
@ variable or the "Authentication:" HTTP header to find the username and
@ password. This is another way of supporting Basic Authentication.
@ (Property: "http_authentication_ok")
@ </p>
@
@ <hr>
entry_attribute("Login expiration time", 6, "cookie-expire", "cex",
"8766", 0);
@ <p>The number of hours for which a login is valid. This must be a
@ positive number. The default is 8766 hours which is approximately equal
@ to a year.
@ (Property: "cookie-expire")</p>
@ <hr>
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
"5000000", 0);
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
@ to this many bytes, uncompressed. If the client requires more data
@ than this, then the client will issue multiple HTTP requests.
@ Values below 1 million are not recommended. 5 million is a
@ reasonable number. (Property: "max-download")</p>
@ <hr>
entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
"30", 0);
@ <p>Fossil tries to spend less than this many seconds gathering
@ the out-bound data of sync, clone, and pull packets.
@ If the client request takes longer, a partial reply is given similar
@ to the download packet limit. 30s is a reasonable default.
@ (Property: "max-download-time")</p>
@ <a id="slal"></a>
@ <hr>
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
"0.0", 0);
@ <p>Some expensive operations (such as computing tarballs, zip archives,
@ or annotation/blame pages) are prohibited if the load average on the host
@ computer is too large. Set the threshold for disallowing expensive
@ computations here. Set this to 0.0 to disable the load average limit.
@ This limit is only enforced on Unix servers. On Linux systems,
@ access to the /proc virtual filesystem is required, which means this limit
@ might not work inside a chroot() jail.
@ (Property: "max-loadavg")</p>
/* Add the auto-hyperlink settings controls. These same controls
** are also accessible from the /setup_robot page.
*/
@ <hr>
addAutoHyperlinkSettings();
@ <hr>
onoff_attribute("Require a CAPTCHA if not logged in",
"require-captcha", "reqcapt", 1, 0);
@ <p>Require a CAPTCHA for edit operations (appending, creating, or
@ editing wiki or tickets or adding attachments to wiki or tickets)
@ for users who are not logged in. (Property: "require-captcha")</p>
@ <hr>
entry_attribute("Public pages", 30, "public-pages",
"pubpage", "", 0);
@ <p>A comma-separated list of glob patterns for pages that are accessible
@ without needing a login and using the privileges given by the
@ "Default privileges" setting below.
@
@ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
@ the "Default privileges" to include the "o" privilege
@ to give anonymous users read-only permission to the
@ latest version of the embedded documentation in the www/ folder without
@ allowing them to see the rest of the source code.
@ (Property: "public-pages")
@ </p>
@ <hr>
onoff_attribute("Allow users to register themselves",
"self-register", "selfreg", 0, 0);
@ <p>Allow users to register themselves on the /register webpage.
@ A self-registration creates a new entry in the USER table and
@ perhaps also in the SUBSCRIBER table if email notification is
@ enabled.
@ (Property: "self-register")</p>
@ <hr>
onoff_attribute("Allow users to reset their own passwords",
"self-pw-reset", "selfpw", 0, 0);
@ <p>Allow users to request that an email contains a hyperlink to a
@ password reset page be sent to their email address of record. This
@ enables forgetful users to recover their forgotten passwords without
@ administrator intervention.
@ (Property: "self-pw-reset")</p>
@ <hr>
onoff_attribute("Email verification required for self-registration",
"selfreg-verify", "sfverify", 0, 0);
@ <p>If enabled, self-registration creates a new entry in the USER table
@ with only capabilities "7". The default user capabilities are not
@ added until the email address associated with the self-registration
@ has been verified. This setting only makes sense if
@ email notifications are enabled.
@ (Property: "selfreg-verify")</p>
@ <hr>
onoff_attribute("Allow anonymous subscriptions",
"anon-subscribe", "anonsub", 1, 0);
@ <p>If disabled, email notification subscriptions are only allowed
@ for users with a login. If Nobody or Anonymous visit the /subscribe
@ page, they are redirected to /register or /login.
@ (Property: "anon-subscribe")</p>
@ <hr>
entry_attribute("Authorized subscription email addresses", 35,
"auth-sub-email", "asemail", "", 0);
@ <p>This is a comma-separated list of GLOB patterns that specify
@ email addresses that are authorized to subscriptions. If blank
@ (the usual case), then any email address can be used to self-register.
@ This setting is used to limit subscriptions to members of a particular
@ organization or group based on their email address.
@ (Property: "auth-sub-email")</p>
@ <hr>
entry_attribute("Default privileges", 10, "default-perms",
"defaultperms", "u", 0);
@ <p>Permissions given to users that... <ul><li>register themselves using
@ the self-registration procedure (if enabled), or <li>access "public"
@ pages identified by the public-pages glob pattern above, or <li>
@ are users newly created by the administrator.</ul>
@ <p>Recommended value: "u" for Reader.
@ <a href="%R/setup_ucap_list">Capability Key</a>.
@ (Property: "default-perms")
@ </p>
@ <hr>
onoff_attribute("Show javascript button to fill in CAPTCHA",
"auto-captcha", "autocaptcha", 0, 0);
@ <p>When enabled, a button appears on the login screen for user
@ "anonymous" that will automatically fill in the CAPTCHA password.
@ This is less secure than forcing the user to do it manually, but is
@ probably secure enough and it is certainly more convenient for
@ anonymous users. (Property: "auto-captcha")</p>
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
** WEBPAGE: setup_login_group
|
| ︙ | ︙ | |||
758 759 760 761 762 763 764 |
@ </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>
| | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 |
@ </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>
@ </thead><tbody>
|
| ︙ | ︙ | |||
810 811 812 813 814 815 816 |
}
style_set_current_feature("setup");
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%R/setup_timeline" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | | | | | | | | | 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 |
}
style_set_current_feature("setup");
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%R/setup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
onoff_attribute("Allow block-markup in timeline",
"timeline-block-markup", "tbm", 0, 0);
@ <p>In timeline displays, check-in comments can be displayed with or
@ without block markup such as paragraphs, tables, etc.
@ (Property: "timeline-block-markup")</p>
@ <hr>
onoff_attribute("Plaintext comments on timelines",
"timeline-plaintext", "tpt", 0, 0);
@ <p>In timeline displays, check-in comments are displayed literally,
@ without any wiki or HTML interpretation. Use CSS to change
@ display formatting features such as fonts and line-wrapping behavior.
@ (Property: "timeline-plaintext")</p>
@ <hr>
onoff_attribute("Truncate comment at first blank line (Git-style)",
"timeline-truncate-at-blank", "ttb", 0, 0);
@ <p>In timeline displays, check-in comments are displayed only through
@ the first blank line. This is the traditional way to display comments
@ in Git repositories (Property: "timeline-truncate-at-blank")</p>
@ <hr>
onoff_attribute("Break comments at newline characters",
"timeline-hard-newlines", "thnl", 0, 0);
@ <p>In timeline displays, newline characters in check-in comments force
@ a line break on the display.
@ (Property: "timeline-hard-newlines")</p>
@ <hr>
onoff_attribute("Use Universal Coordinated Time (UTC)",
"timeline-utc", "utc", 1, 0);
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
@ Zulu) instead of in local time. On this server, local time is currently
tmDiff = db_double(0.0, "SELECT julianday('now')");
tmDiff = db_double(0.0,
"SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
tmDiff, tmDiff);
sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
if( strcmp(zTmDiff, "0.0")==0 ){
@ the same as UTC and so this setting will make no difference in
@ the display.</p>
}else if( tmDiff<0.0 ){
sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", -tmDiff);
@ %s(zTmDiff) hours behind UTC.</p>
}else{
@ %s(zTmDiff) hours ahead of UTC.</p>
}
@ <p>(Property: "timeline-utc")
@ <hr>
multiple_choice_attribute("Style", "timeline-default-style",
"tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles);
@ <p>The default timeline viewing style, for when the user has not
@ specified an alternative. (Property: "timeline-default-style")</p>
@ <hr>
entry_attribute("Default Number Of Rows", 6, "timeline-default-length",
"tldl", "50", 0);
@ <p>The maximum number of rows to show on a timeline in the absence
@ of a user display preference cookie setting or an explicit n= query
@ parameter. (Property: "timeline-default-length")</p>
@ <hr>
multiple_choice_attribute("Per-Item Time Format", "timeline-date-format",
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
@ in a separate box (using CSS class "timelineDate") whenever the date
@ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats,
@ the complete date and time is shown on every timeline entry using the
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
@ <hr>
entry_attribute("Max timeline comment length", 6,
"timeline-max-comment", "tmc", "0", 0);
@ <p>The maximum length of a comment to be displayed in a timeline.
@ "0" there is no length limit.
@ (Property: "timeline-max-comment")</p>
@ <hr>
entry_attribute("Tooltip dwell time (milliseconds)", 6,
"timeline-dwelltime", "tdt", "100", 0);
@ <br>
entry_attribute("Tooltip close time (milliseconds)", 6,
"timeline-closetime", "tct", "250", 0);
@ <p>The <strong>dwell time</strong> defines how long the mouse pointer
@ should be stationary above an object of the graph before a tooltip
@ appears.<br>
@ The <strong>close time</strong> defines how long the mouse pointer
@ can be away from an object before a tooltip is closed.</p>
@ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br>
@ Set <strong>close time</strong> to "0" to keep tooltips visible until
@ the mouse is clicked elsewhere.<p>
@ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p>
@ <hr>
onoff_attribute("Timestamp hyperlinks to /info",
"timeline-tslink-info", "ttlti", 0, 0);
@ <p>The hyperlink on the timestamp associated with each timeline entry,
@ on the far left-hand side of the screen, normally targets another
@ /timeline page that shows the entry in context. However, if this
@ option is turned on, that hyperlink targets the /info page showing
@ the details of the entry.
@ <p>The /timeline link is the default since it is often useful to
@ see an entry in context, and because that link is not otherwise
@ accessible on the timeline. The /info link is also accessible by
@ double-clicking the timeline node or by clicking on the hash that
@ follows "check-in:" in the supplemental information section on the
@ right of the entry.
@ <p>(Properties: "timeline-tslink-info")
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
** WEBPAGE: setup_settings
|
| ︙ | ︙ | |||
960 961 962 963 964 965 966 |
db_open_local(0);
}
db_begin_transaction();
@ <p>Settings marked with (v) are "versionable" and will be overridden
@ by the contents of managed files named
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
@ If the file for a versionable setting exists, the value cannot be
| | | | | | 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 |
db_open_local(0);
}
db_begin_transaction();
@ <p>Settings marked with (v) are "versionable" and will be overridden
@ by the contents of managed files named
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
@ If the file for a versionable setting exists, the value cannot be
@ changed on this screen.</p><hr><p>
@
@ <form action="%R/setup_settings" method="post"><div>
@ <table border="0"><tr><td valign="top">
login_insert_csrf_secret();
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width==0 ){
int hasVersionableValue = pSet->versionable &&
(db_get_versioned(pSet->name, NULL)!=0);
onoff_attribute("", pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
is_truth(pSet->def), hasVersionableValue);
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
}
}
@ <br><input type="submit" name="submit" value="Apply Changes">
@ </td><td style="width:50px;"></td><td valign="top">
@ <table>
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && !pSet->forceTextArea ){
int hasVersionableValue = pSet->versionable &&
(db_get_versioned(pSet->name, NULL)!=0);
@ <tr><td>
|
| ︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 |
@</table>
@ </td><td style="width:50px;"></td><td valign="top">
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && pSet->forceTextArea ){
int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
if( pSet->versionable ){
| | | | | 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
@</table>
@ </td><td style="width:50px;"></td><td valign="top">
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && pSet->forceTextArea ){
int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
(char*)pSet->def, hasVersionableValue);
@<br>
}
}
@ </td></tr></table>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 |
}
style_set_current_feature("setup");
style_header("WWW Configuration");
db_begin_transaction();
@ <form action="%R/setup_config" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | 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 |
}
style_set_current_feature("setup");
style_header("WWW Configuration");
db_begin_transaction();
@ <form action="%R/setup_config" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
entry_attribute("Project Name", 60, "project-name", "pn", "", 0);
@ <p>A brief project name so visitors know what this site is about.
@ The project name will also be used as the RSS feed title.
@ (Property: "project-name")
@ </p>
@ <hr>
textarea_attribute("Project Description", 3, 80,
"project-description", "pd", "", 0);
@ <p>Describe your project. This will be used in page headers for search
@ engines as well as a short RSS description.
@ (Property: "project-description")</p>
@ <hr>
entry_attribute("Canonical Server URL", 40, "email-url",
"eurl", "", 0);
@ <p>This is the URL used to access this repository as a server.
@ Other repositories use this URL to clone or sync against this repository.
@ This is also the basename for hyperlinks included in email alert text.
@ Omit the trailing "/".
@ If this repo will not be set up as a persistent server and will not
@ be sending email alerts, then leave this entry blank.
@ Suggested value: "%h(g.zBaseURL)"
@ (Property: "email-url")</p>
@ <hr>
entry_attribute("Tarball and ZIP-archive Prefix", 20, "short-project-name",
"spn", "", 0);
@ <p>This is used as a prefix on the names of generated tarballs and
@ ZIP archive. For best results, keep this prefix brief and avoid special
@ characters such as "/" and "\".
@ If no tarball prefix is specified, then the full Project Name above is used.
@ (Property: "short-project-name")
@ </p>
@ <hr>
entry_attribute("Download Tag", 20, "download-tag", "dlt", "trunk", 0);
@ <p>The <a href='%R/download'>/download</a> page is designed to provide
@ a convenient place for newbies
@ to download a ZIP archive or a tarball of the project. By default,
@ the latest trunk check-in is downloaded. Change this tag to something
@ else (ex: release) to alter the behavior of the /download page.
@ (Property: "download-tag")
@ </p>
@ <hr>
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
@ <p>Enter the pathname of the page to display when the "Home" menu
@ option is selected and when no pathname is
@ specified in the URL. For example, if you visit the url:</p>
@
@ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
@
|
| ︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 |
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
| | | | 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 |
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
** WEBPAGE: setup_wiki
|
| ︙ | ︙ | |||
1240 1241 1242 1243 1244 1245 1246 |
}
style_set_current_feature("setup");
style_header("Wiki Configuration");
db_begin_transaction();
@ <form action="%R/setup_wiki" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | | 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 |
}
style_set_current_feature("setup");
style_header("Wiki Configuration");
db_begin_transaction();
@ <form action="%R/setup_wiki" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
"wiki-about", "wiki-about", 1, 0);
@ <p>
@ Associate wiki pages with branches, tags, or checkins, based on
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/"
@ or "tag/" and which continue with the name of an existing branch, check-in
@ or tag are treated specially when this feature is enabled.
@ <ul>
@ <li> <b>branch/</b><i>branch-name</i>
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
@ <li> <b>tag/</b><i>tag-name</i>
@ </ul>
@ (Property: "wiki-about")</p>
@ <hr>
entry_attribute("Allow Unsafe HTML In Markdown", 6,
"safe-html", "safe-html", "", 0);
@ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be
@ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
@ This setting is a string where each character indicates a "type" of
@ document in which to allow unsafe HTML:
@ <ul>
@ <li> <b>b</b> → checked-in files, embedded documentation
@ <li> <b>f</b> → forum posts
@ <li> <b>t</b> → tickets
@ <li> <b>w</b> → wiki pages
@ </ul>
@ Include letters for each type of document for which unsafe HTML should
@ be allowed. For example, to allow unsafe HTML only for checked-in files,
@ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except
@ in forum posts, make this setting be "<b>btw</b>". The default is an
@ empty string which means that Fossil never allows Markdown documents
@ to generate unsafe HTML.
@ (Property: "safe-html")</p>
@ <hr>
@ The current interwiki tag map is as follows:
interwiki_append_map_table(cgi_output_blob());
@ <p>Visit <a href="./intermap">%R/intermap</a> for details or to
@ modify the interwiki tag map.
@ <hr>
onoff_attribute("Use HTML as wiki markup language",
"wiki-use-html", "wiki-use-html", 0, 0);
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
@ but all other wiki formatting will be ignored.</p>
@ <p><strong>CAUTION:</strong> when
@ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
@ No sanitization is done. This means that it is very possible for malicious
@ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
@ <p>This should <strong>only</strong> be enabled when wiki editing is limited
@ to trusted users. It should <strong>not</strong> be used on a publicly
@ editable wiki.</p>
@ (Property: "wiki-use-html")
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
** WEBPAGE: setup_chat
|
| ︙ | ︙ | |||
1324 1325 1326 1327 1328 1329 1330 |
}
style_set_current_feature("setup");
style_header("Chat Configuration");
db_begin_transaction();
@ <form action="%R/setup_chat" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | | | 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 |
}
style_set_current_feature("setup");
style_header("Chat Configuration");
db_begin_transaction();
@ <form action="%R/setup_chat" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
entry_attribute("Initial Chat History Size", 10,
"chat-initial-history", "chatih", "50", 0);
@ <p>When /chat first starts up, it preloads up to this many historical
@ messages.
@ (Property: "chat-initial-history")</p>
@ <hr>
entry_attribute("Minimum Number Of Historical Messages To Retain", 10,
"chat-keep-count", "chatkc", "50", 0);
@ <p>The chat subsystem purges older messages. But it will always retain
@ the N most recent messages where N is the value of this setting.
@ (Property: "chat-keep-count")</p>
@ <hr>
entry_attribute("Maximum Message Age In Days", 10,
"chat-keep-days", "chatkd", "7", 0);
@ <p>Chat message are removed after N days, where N is the value of
@ this setting. N may be fractional. So, for example, to only keep
@ an historical record of chat messages for 12 hours, set this value
@ to 0.5.
@ (Property: "chat-keep-days")</p>
@ <hr>
entry_attribute("Chat Polling Timeout", 10,
"chat-poll-timeout", "chatpt", "420", 0);
@ <p>New chat content is downloaded using the "long poll" technique.
@ HTTP requests are made to /chat-poll which blocks waiting on new
@ content to arrive. But the /chat-poll cannot block forever. It
@ eventual must give up and return an empty message set. This setting
@ determines how long /chat-poll will wait before giving up. The
@ default setting of approximately 7 minutes works well on many systems.
@ Shorter delays might be required on installations that use proxies
@ or web-servers with short timeouts. For best efficiency, this value
@ should be larger rather than smaller.
@ (Property: "chat-poll-timeout")</p>
@ <hr>
entry_attribute("Chat Timeline Robot Username", 15,
"chat-timeline-user", "chatrobot", "", 0);
@ <p>If this setting is not an empty string, then any changes that appear
@ on the timeline are announced in the chatroom under the username
@ supplied. The username does not need to actually exist in the USER table.
@ Suggested username: "chat-robot".
@ (Property: "chat-timeline-user")</p>
@ <hr>
multiple_choice_attribute("Alert sound",
"chat-alert-sound", "snd", azAlerts[0],
count(azAlerts)/2, azAlerts);
@ <p>The sound used in the client-side chat to indicate that a new
@ chat message has arrived.
@ (Property: "chat-alert-sound")</p>
@ <hr/>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
@ <script nonce="%h(style_nonce())">
@ (function(){
@ var w = document.getElementById('idsnd');
@ w.onchange = function(){
@ var audio = new Audio('%s(g.zBaseURL)/builtin/' + w.value);
|
| ︙ | ︙ | |||
1408 1409 1410 1411 1412 1413 1414 |
}
style_set_current_feature("setup");
style_header("Moderator For Wiki And Tickets");
db_begin_transaction();
@ <form action="%R/setup_modreq" method="post"><div>
login_insert_csrf_secret();
| | | | | | 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 |
}
style_set_current_feature("setup");
style_header("Moderator For Wiki And Tickets");
db_begin_transaction();
@ <form action="%R/setup_modreq" method="post"><div>
login_insert_csrf_secret();
@ <hr>
onoff_attribute("Moderate ticket changes",
"modreq-tkt", "modreq-tkt", 0, 0);
@ <p>When enabled, any change to tickets is subject to the approval
@ by a ticket moderator - a user with the "q" or Mod-Tkt privilege.
@ Ticket changes enter the system and are shown locally, but are not
@ synced until they are approved. The moderator has the option to
@ delete the change rather than approve it. Ticket changes made by
@ a user who has the Mod-Tkt privilege are never subject to
@ moderation. (Property: "modreq-tkt")
@
@ <hr>
onoff_attribute("Moderate wiki changes",
"modreq-wiki", "modreq-wiki", 0, 0);
@ <p>When enabled, any change to wiki is subject to the approval
@ by a wiki moderator - a user with the "l" or Mod-Wiki privilege.
@ Wiki changes enter the system and are shown locally, but are not
@ synced until they are approved. The moderator has the option to
@ delete the change rather than approve it. Wiki changes made by
@ a user who has the Mod-Wiki privilege are never subject to
@ moderation. (Property: "modreq-wiki")
@ </p>
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
|
| ︙ | ︙ | |||
1465 1466 1467 1468 1469 1470 1471 |
setup_incr_cfgcnt();
}
style_set_current_feature("setup");
style_header("Edit Ad Unit");
@ <form action="%R/setup_adunit" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | | | | | 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 |
setup_incr_cfgcnt();
}
style_set_current_feature("setup");
style_header("Edit Ad Unit");
@ <form action="%R/setup_adunit" method="post"><div>
login_insert_csrf_secret();
@ <b>Banner Ad-Unit:</b><br>
textarea_attribute("", 6, 80, "adunit", "adunit", "", 0);
@ <br>
@ <b>Right-Column Ad-Unit:</b><br>
textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0);
@ <br>
onoff_attribute("Omit ads to administrator",
"adunit-omit-if-admin", "oia", 0, 0);
@ <br>
onoff_attribute("Omit ads to logged-in users",
"adunit-omit-if-user", "oiu", 0, 0);
@ <br>
onoff_attribute("Temporarily disable all ads",
"adunit-disable", "oall", 0, 0);
@ <br>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Delete Ad-Unit">
@ </div></form>
@ <hr>
@ <b>Ad-Unit Notes:</b><ul>
@ <li>Leave both Ad-Units blank to disable all advertising.
@ <li>The "Banner Ad-Unit" is used for wide pages.
@ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content.
@ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is
@ used on all pages.
@ <li>Properties: "adunit", "adunit-right", "adunit-omit-if-admin", and
|
| ︙ | ︙ | |||
1647 1648 1649 1650 1651 1652 1653 |
db_end_transaction(0);
cgi_redirect("setup_logo");
}
style_set_current_feature("setup");
style_header("Edit Project Logo And Background");
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
@ and looks like this:</p>
| | | | | | | | | | | | | | | | | | 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 |
db_end_transaction(0);
cgi_redirect("setup_logo");
}
style_set_current_feature("setup");
style_header("Edit Project Logo And Background");
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
@ and looks like this:</p>
@ <blockquote><p>
@ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1">
@ </p></blockquote>
@
@ <form action="%R/setup_logo" method="post"
@ enctype="multipart/form-data"><div>
@ <p>The logo is accessible to all users at this URL:
@ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
@ The logo may or may not appear on each
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
@ <a href="setup_skinedit?w=2">header setup</a>.
@ To change the logo image, use the following form:</p>
login_insert_csrf_secret();
@ Logo Image file:
@ <input type="file" name="logoim" size="60" accept="image/*">
@ <p align="center">
@ <input type="submit" name="setlogo" value="Change Logo">
@ <input type="submit" name="clrlogo" value="Revert To Default"></p>
@ <p>(Properties: "logo-image" and "logo-mimetype")
@ </div></form>
@ <hr>
@
@ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
@ and looks like this:</p>
@ <blockquote><p><img src="%R/background/%z(zBgMtime)" \
@ alt="background" border=1>
@ </p></blockquote>
@
@ <form action="%R/setup_logo" method="post"
@ enctype="multipart/form-data"><div>
@ <p>The background image is accessible to all users at this URL:
@ <a href="%s(g.zBaseURL)/background">%s(g.zBaseURL)/background</a>.
@ The background image may or may not appear on each
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
@ <a href="setup_skinedit?w=2">header setup</a>.
@ To change the background image, use the following form:</p>
login_insert_csrf_secret();
@ Background image file:
@ <input type="file" name="bgim" size="60" accept="image/*">
@ <p align="center">
@ <input type="submit" name="setbg" value="Change Background">
@ <input type="submit" name="clrbg" value="Revert To Default"></p>
@ </div></form>
@ <p>(Properties: "background-image" and "background-mimetype")
@ <hr>
@
@ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b>
@ and looks like this:</p>
@ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \
@ alt="icon" border=1>
@ </p></blockquote>
@
@ <form action="%R/setup_logo" method="post"
@ enctype="multipart/form-data"><div>
@ <p>The icon image is accessible to all users at this URL:
@ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>.
@ The icon image may or may not appear on each
@ page depending on the web browser in use and the MIME-Types that it
@ supports for icon images.
@ To change the icon image, use the following form:</p>
login_insert_csrf_secret();
@ Icon image file:
@ <input type="file" name="iconim" size="60" accept="image/*">
@ <p align="center">
@ <input type="submit" name="seticon" value="Change Icon">
@ <input type="submit" name="clricon" value="Revert To Default"></p>
@ </div></form>
@ <p>(Properties: "icon-image" and "icon-mimetype")
@ <hr>
@
@ <p><span class="note">Note:</span> Your browser has probably cached these
@ images, so you may need to press the Reload button before changes will
@ take effect. </p>
style_finish_page();
db_end_transaction(0);
}
|
| ︙ | ︙ | |||
1800 1801 1802 1803 1804 1805 1806 |
"FROM config\n"
"-- ORDER BY mtime DESC; -- optional";
go = 1;
}
@
@ <form method="post" action="%R/admin_sql">
login_insert_csrf_secret();
| | | | 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 |
"FROM config\n"
"-- ORDER BY mtime DESC; -- optional";
go = 1;
}
@
@ <form method="post" action="%R/admin_sql">
login_insert_csrf_secret();
@ SQL:<br>
@ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br>
@ <input type="submit" name="go" value="Run SQL">
@ <input type="submit" name="schema" value="Show Schema">
@ <input type="submit" name="tablelist" value="List Tables">
@ <input type="submit" name="configtab" value="CONFIG Table Query">
@ </form>
if( P("schema") ){
zQ = sqlite3_mprintf(
|
| ︙ | ︙ | |||
1823 1824 1825 1826 1827 1828 1829 |
if( go ){
sqlite3_stmt *pStmt;
int rc;
const char *zTail;
int nCol;
int nRow = 0;
int i;
| | | 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
if( go ){
sqlite3_stmt *pStmt;
int rc;
const char *zTail;
int nCol;
int nRow = 0;
int i;
@ <hr>
login_verify_csrf_secret();
sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
search_sql_setup(g.db);
rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
if( rc!=SQLITE_OK ){
@ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 |
style_header("Raw TH1 Commands");
@ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
@ run by this page. If Tcl integration was enabled at compile-time and
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
@
@ <form method="post" action="%R/admin_th1">
login_insert_csrf_secret();
| | | | | 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 |
style_header("Raw TH1 Commands");
@ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
@ run by this page. If Tcl integration was enabled at compile-time and
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
@
@ <form method="post" action="%R/admin_th1">
login_insert_csrf_secret();
@ TH1:<br>
@ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
@ <input type="submit" name="go" value="Run TH1">
@ </form>
if( go ){
const char *zR;
int rc;
int n;
@ <hr>
login_verify_csrf_secret();
rc = Th_Eval(g.interp, 0, zQ, -1);
zR = Th_GetResult(g.interp, &n);
if( rc==TH_OK ){
@ <pre class="th1result">%h(zR)</pre>
}else{
@ <pre class="th1error">%h(zR)</pre>
|
| ︙ | ︙ | |||
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 |
@ </tbody></table>
if( counter>ofst+limit ){
@ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
}
style_finish_page();
}
/*
** WEBPAGE: srchsetup
**
** Configure the search engine. Requires Admin privilege.
*/
void page_srchsetup(){
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Search Configuration");
@ <form action="%R/srchsetup" method="post"><div>
login_insert_csrf_secret();
@ <div style="text-align:center;font-weight:bold;">
@ Server-specific settings that affect the
@ <a href="%R/search">/search</a> webpage.
@ </div>
| > > > > > > > > > > > > > > > | | | | | | | | | | | > > | | | > | | | 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 |
@ </tbody></table>
if( counter>ofst+limit ){
@ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
}
style_finish_page();
}
/*
** Renders a selection list of values for the search-tokenizer
** setting, using the form field name "ftstok".
*/
static void select_fts_tokenizer(void){
const char *const aTokenizer[] = {
"off", "None",
"porter", "Porter Stemmer",
"trigram", "Trigram"
};
multiple_choice_attribute("FTS Tokenizer", "search-tokenizer",
"ftstok", "off", 3, aTokenizer);
}
/*
** WEBPAGE: srchsetup
**
** Configure the search engine. Requires Admin privilege.
*/
void page_srchsetup(){
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Search Configuration");
@ <form action="%R/srchsetup" method="post"><div>
login_insert_csrf_secret();
@ <div style="text-align:center;font-weight:bold;">
@ Server-specific settings that affect the
@ <a href="%R/search">/search</a> webpage.
@ </div>
@ <hr>
textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
@ <p>The "Document Glob List" is a comma- or newline-separated list
@ of GLOB expressions that identify all documents within the source
@ tree that are to be searched when "Document Search" is enabled.
@ Some examples:
@ <table border=0 cellpadding=2 align=center>
@ <tr><td>*.wiki,*.html,*.md,*.txt<td style="width: 4x;">
@ <td>Search all wiki, HTML, Markdown, and Text files</tr>
@ <tr><td>doc/*.md,*/README.txt,README.txt<td>
@ <td>Search all Markdown files in the doc/ subfolder and all README.txt
@ files.</tr>
@ <tr><td>*<td><td>Search all checked-in files</tr>
@ <tr><td><i>(blank)</i><td>
@ <td>Search nothing. (Disables document search).</tr>
@ </table>
@ <hr>
entry_attribute("Document Branch", 20, "doc-branch", "db", "trunk", 0);
@ <p>When searching documents, use the versions of the files found at the
@ type of the "Document Branch" branch. Recommended value: "trunk".
@ Document search is disabled if blank.
@ <hr>
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
@ <br>
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
@ <br>
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
@ <br>
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
@ <br>
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
@ <br>
onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
if( P("fts0") ){
search_drop_index();
}else if( P("fts1") ){
const char *zTokenizer = PD("ftstok","off");
search_set_tokenizer(zTokenizer);
search_drop_index();
search_create_index();
search_fill_index();
search_update_index(search_restrict(SRCH_ALL));
}
if( search_index_exists() ){
@ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index.
@ The index helps search run faster, especially on large repositories,
@ but takes up space.</p>
select_fts_tokenizer();
@ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
@ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
style_submenu_element("FTS Index Debugging","%R/test-ftsdocs");
}else{
@ <p>The SQLite search index is disabled. All searching will be
@ a full-text scan. This usually works fine, but can be slow for
@ larger repositories.</p>
select_fts_tokenizer();
@ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
}
@ </div></form>
style_finish_page();
}
/*
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
609 610 611 612 613 614 615 |
@ <td class="usetupEditLabel" id="suuid">User ID:</td>
if( uid ){
@ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \
@ name="id" value="%d(uid)"/>\
@ </td>
}else{
@ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \
| | | | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 |
@ <td class="usetupEditLabel" id="suuid">User ID:</td>
if( uid ){
@ <td>%d(uid) <input aria-labelledby="suuid" type="hidden" \
@ name="id" value="%d(uid)"/>\
@ </td>
}else{
@ <td>(new user)<input aria-labelledby="suuid" type="hidden" name="id" \
@ value="0"></td>
}
@ </tr>
@ <tr>
@ <td class="usetupEditLabel" id="sulgn">Login:</td>
if( login_is_special(zLogin) ){
@ <td><b>%h(zLogin)</b></td>
}else{
@ <td><input aria-labelledby="sulgn" type="text" name="login" \
@ value="%h(zLogin)">
if( alert_tables_exist() ){
int sid;
sid = db_int(0, "SELECT subscriberId FROM subscriber"
" WHERE suname=%Q", zLogin);
if( sid>0 ){
@ <a href="%R/alerts?sid=%d(sid)">\
@ (subscription info for %h(zLogin))</a>\
|
| ︙ | ︙ | |||
642 643 644 645 646 647 648 |
@ <tr>
@ <td class="usetupEditLabel">Capabilities:</td>
@ <td width="100%%">
#define B(x) inherit[x]
@ <div class="columns" style="column-width:13em;">
@ <ul style="list-style-type: none;">
if( g.perm.Setup ){
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 |
@ <tr>
@ <td class="usetupEditLabel">Capabilities:</td>
@ <td width="100%%">
#define B(x) inherit[x]
@ <div class="columns" style="column-width:13em;">
@ <ul style="list-style-type: none;">
if( g.perm.Setup ){
@ <li><label><input type="checkbox" name="as"%s(oa['s'])>
@ Setup%s(B('s'))</label>
}
@ <li><label><input type="checkbox" name="aa"%s(oa['a'])>
@ Admin%s(B('a'))</label>
@ <li><label><input type="checkbox" name="au"%s(oa['u'])>
@ Reader%s(B('u'))</label>
@ <li><label><input type="checkbox" name="av"%s(oa['v'])>
@ Developer%s(B('v'))</label>
#if 0 /* Not Used */
@ <li><label><input type="checkbox" name="ad"%s(oa['d'])>
@ Delete%s(B('d'))</label>
#endif
@ <li><label><input type="checkbox" name="ae"%s(oa['e'])>
@ View-PII%s(B('e'))</label>
@ <li><label><input type="checkbox" name="ap"%s(oa['p'])>
@ Password%s(B('p'))</label>
@ <li><label><input type="checkbox" name="ai"%s(oa['i'])>
@ Check-In%s(B('i'))</label>
@ <li><label><input type="checkbox" name="ao"%s(oa['o'])>
@ Check-Out%s(B('o'))</label>
@ <li><label><input type="checkbox" name="ah"%s(oa['h'])>
@ Hyperlinks%s(B('h'))</label>
@ <li><label><input type="checkbox" name="ab"%s(oa['b'])>
@ Attachments%s(B('b'))</label>
@ <li><label><input type="checkbox" name="ag"%s(oa['g'])>
@ Clone%s(B('g'))</label>
@ <li><label><input type="checkbox" name="aj"%s(oa['j'])>
@ Read Wiki%s(B('j'))</label>
@ <li><label><input type="checkbox" name="af"%s(oa['f'])>
@ New Wiki%s(B('f'))</label>
@ <li><label><input type="checkbox" name="am"%s(oa['m'])>
@ Append Wiki%s(B('m'))</label>
@ <li><label><input type="checkbox" name="ak"%s(oa['k'])>
@ Write Wiki%s(B('k'))</label>
@ <li><label><input type="checkbox" name="al"%s(oa['l'])>
@ Moderate Wiki%s(B('l'))</label>
@ <li><label><input type="checkbox" name="ar"%s(oa['r'])>
@ Read Ticket%s(B('r'))</label>
@ <li><label><input type="checkbox" name="an"%s(oa['n'])>
@ New Tickets%s(B('n'))</label>
@ <li><label><input type="checkbox" name="ac"%s(oa['c'])>
@ Append To Ticket%s(B('c'))</label>
@ <li><label><input type="checkbox" name="aw"%s(oa['w'])>
@ Write Tickets%s(B('w'))</label>
@ <li><label><input type="checkbox" name="aq"%s(oa['q'])>
@ Moderate Tickets%s(B('q'))</label>
@ <li><label><input type="checkbox" name="at"%s(oa['t'])>
@ Ticket Report%s(B('t'))</label>
@ <li><label><input type="checkbox" name="ax"%s(oa['x'])>
@ Private%s(B('x'))</label>
@ <li><label><input type="checkbox" name="ay"%s(oa['y'])>
@ Write Unversioned%s(B('y'))</label>
@ <li><label><input type="checkbox" name="az"%s(oa['z'])>
@ Download Zip%s(B('z'))</label>
@ <li><label><input type="checkbox" name="a2"%s(oa['2'])>
@ Read Forum%s(B('2'))</label>
@ <li><label><input type="checkbox" name="a3"%s(oa['3'])>
@ Write Forum%s(B('3'))</label>
@ <li><label><input type="checkbox" name="a4"%s(oa['4'])>
@ WriteTrusted Forum%s(B('4'))</label>
@ <li><label><input type="checkbox" name="a5"%s(oa['5'])>
@ Moderate Forum%s(B('5'))</label>
@ <li><label><input type="checkbox" name="a6"%s(oa['6'])>
@ Supervise Forum%s(B('6'))</label>
@ <li><label><input type="checkbox" name="a7"%s(oa['7'])>
@ Email Alerts%s(B('7'))</label>
@ <li><label><input type="checkbox" name="aA"%s(oa['A'])>
@ Send Announcements%s(B('A'))</label>
@ <li><label><input type="checkbox" name="aC"%s(oa['C'])>
@ Chatroom%s(B('C'))</label>
@ <li><label><input type="checkbox" name="aD"%s(oa['D'])>
@ Enable Debug%s(B('D'))</label>
@ </ul></div>
@ </td>
@ </tr>
@ <tr>
@ <td class="usetupEditLabel">Selected Cap:</td>
@ <td>
@ <span id="usetupEditCapability">(missing JS?)</span>
@ <a href="%R/setup_ucap_list">(key)</a>
@ </td>
@ </tr>
if( !login_is_special(zLogin) ){
@ <tr>
@ <td align="right" id="supw">Password:</td>
if( zPw[0] ){
/* Obscure the password for all users */
@ <td><input aria-labelledby="supw" type="password" autocomplete="off" \
@ name="pw" value="**********">
@ (Leave unchanged to retain password)</td>
}else{
/* Show an empty password as an empty input field */
char *zRPW = fossil_random_password(12);
@ <td><input aria-labelledby="supw" type="password" name="pw" \
@ autocomplete="off" value=""> Password suggestion: %z(zRPW)</td>
}
@ </tr>
}
zGroup = login_group_name();
if( zGroup ){
@ <tr>
@ <td valign="top" align="right">Scope:</td>
@ <td valign="top">
@ <input type="radio" name="all" checked value="0">
@ Apply changes to this repository only.<br>
@ <input type="radio" name="all" value="1">
@ Apply changes to all repositories in the "<b>%h(zGroup)</b>"
@ login group.</td></tr>
}
if( !higherUser ){
if( zDeleteVerify ){
@ <tr>
|
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
allExist = 0;
}
admin_log("Unshunned %Q", p);
p += strlen(p)+1;
}
if( allExist ){
| | | | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
if( !db_exists("SELECT 1 FROM blob WHERE uuid=%Q", p) ){
allExist = 0;
}
admin_log("Unshunned %Q", p);
p += strlen(p)+1;
}
if( allExist ){
@ <p class="noMoreShun">Artifact(s)<br>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ <a href="%R/artifact/%s(p)">%s(p)</a><br>
}
@ are no longer being shunned.</p>
}else{
@ <p class="noMoreShun">Artifact(s)<br>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ %s(p)<br>
}
@ will no longer be shunned. But they may not exist in the repository.
@ It may be necessary to rebuild the repository using the
@ <b>fossil rebuild</b> command-line before the artifact content
@ can pulled in from other repositories.</p>
}
}
|
| ︙ | ︙ | |||
147 148 149 150 151 152 153 |
db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p);
db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid);
db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
}
admin_log("Shunned %Q", p);
p += strlen(p)+1;
}
| | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", p);
db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid);
db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
}
admin_log("Shunned %Q", p);
p += strlen(p)+1;
}
@ <p class="shunned">Artifact(s)<br>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ <a href="%R/artifact/%s(p)">%s(p)</a><br>
}
@ have been shunned. They will no longer be pushed.
@ They will be removed from the repository the next time the repository
@ is rebuilt using the <b>fossil rebuild</b> command-line</p>
}
if( zRcvid ){
nRcvid = atoi(zRcvid);
|
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}
@ </textarea>
| | | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}
@ </textarea>
@ <input type="submit" name="add" value="Shun">
@ </div></form>
@ </blockquote>
@
@ <a name="delshun"></a>
@ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
@ accepted again in the repository. The artifacts content is not
@ restored because the content is unknown. The only change is that
|
| ︙ | ︙ | |||
225 226 227 228 229 230 231 |
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}
@ </textarea>
| | | | | | | 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 |
while( db_step(&q)==SQLITE_ROW ){
@ %s(db_column_text(&q, 0))
}
db_finalize(&q);
}
}
@ </textarea>
@ <input type="submit" name="sub" value="Accept">
@ </div></form>
@ </blockquote>
@
@ <p>Press the Rebuild button below to rebuild the repository. The
@ content of newly shunned artifacts is not purged until the repository
@ is rebuilt. On larger repositories, the rebuild may take minute or
@ two, so be patient after pressing the button.</p>
@
@ <blockquote>
@ <form method="post" action="%R/%s(g.zPath)"><div>
login_insert_csrf_secret();
@ <input type="submit" name="rebuild" value="Rebuild">
@ </div></form>
@ </blockquote>
@
@ <hr><p>Shunned Artifacts:</p>
@ <blockquote><p>
db_prepare(&q,
"SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
" FROM shun ORDER BY uuid");
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
int stillExists = db_column_int(&q, 1);
cnt++;
if( stillExists ){
@ <b><a href="%R/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
}else{
@ <b>%s(zUuid)</b><br>
}
}
if( cnt==0 ){
@ <i>no artifacts are shunned on this server</i>
}
db_finalize(&q);
@ </p></blockquote>
|
| ︙ | ︙ | |||
479 480 481 482 483 484 485 |
if( zDesc==0 ) zDesc = "";
if( cnt==0 ){
@ <tr><th valign="top" align="right">Artifacts:</th>
@ <td valign="top">
}
cnt++;
@ <a href="%R/info/%s(zUuid)">%s(zUuid)</a>
| | | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
if( zDesc==0 ) zDesc = "";
if( cnt==0 ){
@ <tr><th valign="top" align="right">Artifacts:</th>
@ <td valign="top">
}
cnt++;
@ <a href="%R/info/%s(zUuid)">%s(zUuid)</a>
@ %h(zDesc) (size: %d(size))<br>
}
if( cnt>0 ){
@ <p>
if( db_exists(
"SELECT 1 FROM blob WHERE rcvid=%d AND"
" NOT EXISTS (SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)", rcvid)
){
|
| ︙ | ︙ | |||
529 530 531 532 533 534 535 |
int isDeleted = zHash==0;
if( cnt==0 ){
@ <tr><th valign="top" align="right">Unversioned Files:</th>
@ <td valign="top">
}
cnt++;
if( isDeleted ){
| | | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 |
int isDeleted = zHash==0;
if( cnt==0 ){
@ <tr><th valign="top" align="right">Unversioned Files:</th>
@ <td valign="top">
}
cnt++;
if( isDeleted ){
@ %h(zName) (deleted)<br>
}else{
@ <a href="%R/uv/%h(zName)">%h(zName)</a> (size: %d(size))<br>
}
}
if( cnt>0 ){
@ <p><form action='%R/rcvfrom'>
@ <input type="hidden" name="rcvid" value='%d(rcvid)'>
@ <input type="hidden" name="uvdelete" value="1">
if( PB("uvdelete") ){
|
| ︙ | ︙ |
Changes to src/skins.c.
| ︙ | ︙ | |||
536 537 538 539 540 541 542 |
if( cgi_csrf_safe(1) ){
/* Process requests to delete a user-defined skin */
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
style_header("Confirm Custom Skin Delete");
@ <form action="%R/setup_skin_admin" method="post"><div>
@ <p>Deletion of a custom skin is a permanent action that cannot
@ be undone. Please confirm that this is what you want to do:</p>
| | | | | 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
if( cgi_csrf_safe(1) ){
/* Process requests to delete a user-defined skin */
if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
style_header("Confirm Custom Skin Delete");
@ <form action="%R/setup_skin_admin" method="post"><div>
@ <p>Deletion of a custom skin is a permanent action that cannot
@ be undone. Please confirm that this is what you want to do:</p>
@ <input type="hidden" name="sn" value="%h(P("sn"))">
@ <input type="submit" name="del2" value="Confirm - Delete The Skin">
@ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
login_insert_csrf_secret();
@ </div></form>
style_finish_page();
db_end_transaction(1);
return;
}
if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
|
| ︙ | ︙ | |||
624 625 626 627 628 629 630 |
z = aBuiltinSkin[i].zDesc;
@ <tr><td>%d(i+1).<td>%h(z)<td> <td>
if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){
@ (Currently In Use)
seenCurrent = 1;
}else{
@ <form action="%R/setup_skin_admin" method="post">
| | | | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 |
z = aBuiltinSkin[i].zDesc;
@ <tr><td>%d(i+1).<td>%h(z)<td> <td>
if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){
@ (Currently In Use)
seenCurrent = 1;
}else{
@ <form action="%R/setup_skin_admin" method="post">
@ <input type="hidden" name="sn" value="%h(z)">
@ <input type="submit" name="load" value="Install">
if( pAltSkin==&aBuiltinSkin[i] ){
@ (Current override)
}
@ </form>
}
@ </tr>
}
|
| ︙ | ︙ | |||
856 857 858 859 860 861 862 |
@ <input type='hidden' name='sk' value='%d(iSkin)'>
@ <h2>Edit %s(zTitle):</h2>
if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){
db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile);
}
@ <textarea name="%s(zFile)" rows="10" cols="80">\
@ %h(zContent)</textarea>
| | | | | | | | 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 |
@ <input type='hidden' name='sk' value='%d(iSkin)'>
@ <h2>Edit %s(zTitle):</h2>
if( P("submit") && cgi_csrf_safe(0) && (zOrig==0 || strcmp(zOrig,zContent)!=0) ){
db_set_mprintf(zContent, 0, "draft%d-%s",iSkin,zFile);
}
@ <textarea name="%s(zFile)" rows="10" cols="80">\
@ %h(zContent)</textarea>
@ <br>
@ <input type="submit" name="submit" value="Apply Changes">
if( isRevert ){
@ ← Press to complete reversion to "%s(zBasis)"
}else if( fossil_strcmp(zContent,zDflt)!=0 ){
@ <input type="submit" name="revert" value='Revert To "%s(zBasis)"'>
}
@ <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);
|
| ︙ | ︙ |
Changes to src/smtp.c.
| ︙ | ︙ | |||
575 576 577 578 579 580 581 | }while( bMore ); if( iCode!=250 ) return 1; return 0; } /* ** The input is a base email address of the form "local@domain". | | > | | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
}while( bMore );
if( iCode!=250 ) return 1;
return 0;
}
/*
** The input is a base email address of the form "local@domain".
** Return a pointer to just the "domain" part, or 0 if the string
** contains no "@".
*/
const char *domain_of_addr(const char *z){
while( z[0] && z[0]!='@' ) z++;
if( z[0]==0 ) return 0;
return z+1;
}
/*
|
| ︙ | ︙ | |||
621 622 623 624 625 626 627 |
zRelay = find_option("relayhost",0,1);
verify_all_options();
if( g.argc<5 ) usage("EMAIL FROM TO ...");
blob_read_from_file(&body, g.argv[2], ExtFILE);
zFrom = g.argv[3];
nTo = g.argc-4;
azTo = (const char**)g.argv+4;
| | | | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 |
zRelay = find_option("relayhost",0,1);
verify_all_options();
if( g.argc<5 ) usage("EMAIL FROM TO ...");
blob_read_from_file(&body, g.argv[2], ExtFILE);
zFrom = g.argv[3];
nTo = g.argc-4;
azTo = (const char**)g.argv+4;
zFromDomain = domain_of_addr(zFrom);
if( zRelay!=0 && zRelay[0]!= 0) {
smtpFlags |= SMTP_DIRECT;
zToDomain = zRelay;
}else{
zToDomain = domain_of_addr(azTo[0]);
}
p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort);
if( p->zErr ){
fossil_fatal("%s", p->zErr);
}
fossil_print("Connection to \"%s\"\n", p->zHostname);
smtp_client_startup(p);
|
| ︙ | ︙ |
Changes to src/sqlcmd.c.
| ︙ | ︙ | |||
285 286 287 288 289 290 291 |
** is pointed to by the value placed in pzKey must be obtained from malloc.
*/
void fossil_key(const char **pzKey, int *pnKey){
char *zSavedKey = db_get_saved_encryption_key();
char *zKey;
size_t savedKeySize = db_get_saved_encryption_key_size();
| | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
** is pointed to by the value placed in pzKey must be obtained from malloc.
*/
void fossil_key(const char **pzKey, int *pnKey){
char *zSavedKey = db_get_saved_encryption_key();
char *zKey;
size_t savedKeySize = db_get_saved_encryption_key_size();
if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return;
zKey = (char*)malloc( savedKeySize );
if( zKey ){
memcpy(zKey, zSavedKey, savedKeySize);
*pzKey = zKey;
if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
*pnKey = (int)strlen(zKey);
}else{
|
| ︙ | ︙ |
Changes to src/statrep.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
rc = *zRealType;
break;
case 'g':
case 'G':
zRealType = "g";
rc = *zRealType;
break;
case 't':
case 'T':
zRealType = "t";
rc = *zRealType;
break;
case 'w':
case 'W':
| > > > > > > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
rc = *zRealType;
break;
case 'g':
case 'G':
zRealType = "g";
rc = *zRealType;
break;
case 'm':
case 'M':
zRealType = "m";
rc = *zRealType;
break;
case 'n':
case 'N':
zRealType = "n";
rc = *zRealType;
break;
case 't':
case 'T':
zRealType = "t";
rc = *zRealType;
break;
case 'w':
case 'W':
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
if( P("from")!=0 && P("to")!=0 ){
zTimeSpan = mprintf(
" (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))",
P("from"), P("to"));
}else{
zTimeSpan = " 1";
}
| | > > > > > | > | | > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > > > > > > > > | > > > > > > > > > | > > > > > | | 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 |
if( P("from")!=0 && P("to")!=0 ){
zTimeSpan = mprintf(
" (event.mtime BETWEEN julianday(%Q) AND julianday(%Q))",
P("from"), P("to"));
}else{
zTimeSpan = " 1";
}
if( zRealType==0 ){
statsReportTimelineYFlag = "a";
db_multi_exec("CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE %s", zTimeSpan/*safe-for-%s*/);
}else if( rc!='n' && rc!='m' ){
statsReportTimelineYFlag = zRealType;
db_multi_exec("CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE (type GLOB %Q) AND %s",
zRealType, zTimeSpan/*safe-for-%s*/);
}else{
const char *zNot = rc=='n' ? "NOT" : "";
statsReportTimelineYFlag = "ci";
db_multi_exec(
"CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE type='ci' AND %s"
" AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
);
}
return statsReportType = rc;
}
/*
** Returns a string suitable (for a given value of suitable) for
** use in a label with the header of the /reports pages, dependent
** on the 'type' flag. See stats_report_init_view().
** The returned bytes are static.
*/
static const char *stats_report_label_for_type(){
assert( statsReportType && "Must call stats_report_init_view() first." );
switch( statsReportType ){
case 'c':
return "check-ins";
case 'm':
return "merge check-ins";
case 'n':
return "non-merge check-ins";
case 'e':
return "technotes";
case 'f':
return "forum posts";
case 'w':
return "wiki changes";
case 't':
return "ticket changes";
case 'g':
return "tag changes";
default:
return "all types";
}
}
/*
** Implements the "byyear" and "bymonth" reports for /reports.
** If includeMonth is true then it generates the "bymonth" report,
** else the "byyear" report. If zUserName is not NULL then the report is
** restricted to events created by the named user account.
*/
static void stats_report_by_month_year(
char includeMonth, /* 0 for stats-by-year. 1 for stats-by-month */
const char *zUserName /* Only report events by this user */
){
Stmt query = empty_Stmt;
int nRowNumber = 0; /* current TR number */
int nEventTotal = 0; /* Total event count */
int rowClass = 0; /* counter for alternating
row colors */
const char *zTimeLabel = includeMonth ? "Year/Month" : "Year";
char zPrevYear[5] = {0}; /* For keeping track of when
we change years while looping */
int nEventsPerYear = 0; /* Total event count for the
current year */
char showYearTotal = 0; /* Flag telling us when to show
the per-year event totals */
int nMaxEvents = 1; /* for calculating length of graph
bars. */
int iterations = 0; /* number of weeks/months we iterate
over */
char *zCurrentTF; /* The timeframe in which 'now' lives */
double rNowFraction; /* Fraction of 'now' timeframe that has
passed */
int nTFChar; /* Prefix of date() for timeframe */
nTFChar = includeMonth ? 7 : 4;
stats_report_init_view();
db_prepare(&query,
"SELECT substr(date(mtime),1,%d) AS timeframe,"
" count(*) AS eventCount"
" FROM v_reports"
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY timeframe"
" ORDER BY timeframe DESC",
nTFChar, zUserName);
@ <h1>Timeline Events (%s(stats_report_label_for_type()))
@ by year%s(includeMonth ? "/month" : "")
if( zUserName ){
@ for user %h(zUserName)
}
@ </h1>
@ <table border='0' cellpadding='2' cellspacing='0' \
zCurrentTF = db_text(0, "SELECT substr(date(),1,%d)", nTFChar);
if( !includeMonth ){
@ class='statistics-report-table-events sortable' \
@ data-column-types='tnx' data-init-sort='0'>
style_table_sorter();
rNowFraction = db_double(0.5,
"SELECT (unixepoch() - unixepoch('now','start of year'))*1.0/"
" (unixepoch('now','start of year','+1 year') - "
" unixepoch('now','start of year'));");
}else{
@ class='statistics-report-table-events'>
rNowFraction = db_double(0.5,
"SELECT (unixepoch() - unixepoch('now','start of month'))*1.0/"
" (unixepoch('now','start of month','+1 month') - "
" unixepoch('now','start of month'));");
}
@ <thead>
@ <th>%s(zTimeLabel)</th>
@ <th>Events</th>
@ <th width='90%%'><!-- relative commits graph --></th>
@ </thead><tbody>
/*
Run the query twice. The first time we calculate the maximum
number of events for a given row. Maybe someone with better SQL
Fu can re-implement this with a single query.
*/
while( SQLITE_ROW == db_step(&query) ){
int nCount = db_column_int(&query, 1);
if( strcmp(db_column_text(&query,0),zCurrentTF)==0
&& rNowFraction>0.05
){
nCount = (int)(((double)nCount)/rNowFraction);
}
if(nCount>nMaxEvents){
nMaxEvents = nCount;
}
++iterations;
}
db_reset(&query);
while( SQLITE_ROW == db_step(&query) ){
const char *zTimeframe = db_column_text(&query, 0);
const int nCount = db_column_int(&query, 1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 1;
showYearTotal = 0;
if(!nSize) nSize = 1;
if(includeMonth){
/* For Month/year view, add a separator for each distinct year. */
if(!*zPrevYear ||
|
| ︙ | ︙ | |||
292 293 294 295 296 297 298 |
if( zUserName ){
cgi_printf("&u=%t", zUserName);
}
cgi_printf("'>%s</a>", zTimeframe);
}
@ </td><td>%d(nCount)</td>
@ <td>
| > > > > > > > > > > > > > > > | | > | < < < < < < < < < < < | | | 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 |
if( zUserName ){
cgi_printf("&u=%t", zUserName);
}
cgi_printf("'>%s</a>", zTimeframe);
}
@ </td><td>%d(nCount)</td>
@ <td>
if( strcmp(zTimeframe, zCurrentTF)==0
&& rNowFraction>0.05
&& nCount>0
&& nMaxEvents>0
){
/* If the timespan covered by this row contains "now", then project
** the number of changes until the completion of the timespan and
** show a dashed box of that projection. */
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
int nXSize = (100 * nExtra)/nMaxEvents;
@ <span class='statistics-report-graph-line' \
@ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\
@ <span class='statistics-report-graph-extra' \
@ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\
}else{
@ <div class='statistics-report-graph-line' \
@ style='width:%d(nSize)%%;'> </div> \
}
@ </td>
@ </tr>
/*
Potential improvement: calculate the min/max event counts and
use percent-based graph bars.
*/
}
db_finalize(&query);
if(includeMonth && !showYearTotal && *zPrevYear){
/* Add final year total separator. */
rowClass = ++nRowNumber % 2;
@ <tr class='row%d(rowClass)'>
@ <td></td>
@ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td>
@</tr>
}
@ </tbody></table>
if(nEventTotal){
const char *zAvgLabel = includeMonth ? "month" : "year";
int nAvg = iterations ? (nEventTotal/iterations) : 0;
@ <br><div>Total events: %d(nEventTotal)
@ <br>Average per active %s(zAvgLabel): %d(nAvg)
@ </div>
}
}
/*
** Implements the "byuser" view for /reports.
*/
|
| ︙ | ︙ | |||
352 353 354 355 356 357 358 |
"CREATE TEMP VIEW piechart(amt,label) AS"
" SELECT count(*), ifnull(euser,user) FROM v_reports"
" GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;"
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
| | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
"CREATE TEMP VIEW piechart(amt,label) AS"
" SELECT count(*), ifnull(euser,user) FROM v_reports"
" GROUP BY ifnull(euser,user) ORDER BY count(*) DESC;"
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
@ </svg></centre><hr>
}
style_table_sorter();
@ <table class='statistics-report-table-events sortable' border='0' \
@ cellpadding='2' cellspacing='0' data-column-types='tkx' data-init-sort='2'>
@ <thead><tr>
@ <th>User</th>
@ <th>Events</th>
|
| ︙ | ︙ | |||
508 509 510 511 512 513 514 |
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY 2 ORDER BY cast(strftime('%%w', mtime) AS INT);"
, zUserName
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
| | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY 2 ORDER BY cast(strftime('%%w', mtime) AS INT);"
, zUserName
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
@ </svg></centre><hr>
}
style_table_sorter();
@ <table class='statistics-report-table-events sortable' border='0' \
@ cellpadding='2' cellspacing='0' data-column-types='ntnx' data-init-sort='1'>
@ <thead><tr>
@ <th>DoW</th>
@ <th>Day</th>
|
| ︙ | ︙ | |||
585 586 587 588 589 590 591 |
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY 2 ORDER BY hod;",
zUserName
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
| | | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 |
" WHERE ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY 2 ORDER BY hod;",
zUserName
);
if( db_int(0, "SELECT count(*) FROM piechart")>=2 ){
@ <center><svg width=700 height=400>
piechart_render(700, 400, PIE_OTHER|PIE_PERCENT);
@ </svg></centre><hr>
}
style_table_sorter();
@ <table class='statistics-report-table-events sortable' border='0' \
@ cellpadding='2' cellspacing='0' data-column-types='nnx' data-init-sort='1'>
@ <thead><tr>
@ <th>Hour</th>
@ <th>Events</th>
|
| ︙ | ︙ | |||
627 628 629 630 631 632 633 | @ </tbody></table> db_finalize(&query); } /* ** Helper for stats_report_by_month_year(), which generates a list of | | | > > > | > > > > > > > | | | | | > > > > > > | | > > > > > > > > > > > > > > > | | < | > | | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
@ </tbody></table>
db_finalize(&query);
}
/*
** Helper for stats_report_by_month_year(), which generates a list of
** week numbers. The "y" query parameter is the year in format YYYY.
** If zUserName is not NULL then the report is restricted to events
** created by the named user account.
*/
static void stats_report_year_weeks(const char *zUserName){
const char *zYear = P("y"); /* Year for which report shown */
Stmt q;
int nMaxEvents = 1; /* max number of events for
all rows. */
int iterations = 0; /* # of active time periods. */
int rowCount = 0;
int total = 0;
char *zCurrentWeek; /* Current week number */
double rNowFraction = 0.0; /* Fraction of current week that has
** passed */
stats_report_init_view();
style_submenu_sql("y", "Year:",
"WITH RECURSIVE a(b) AS ("
" SELECT substr(date('now'),1,4) UNION ALL"
" SELECT b-1 FROM a"
" WHERE b>0+(SELECT substr(date(min(mtime)),1,4) FROM event)"
") SELECT b, b FROM a ORDER BY b DESC");
if( zYear==0 || strlen(zYear)!=4 ){
zYear = db_text("1970","SELECT substr(date('now'),1,4);");
}
cgi_printf("<br>\n");
db_prepare(&q,
"SELECT DISTINCT strftime('%%W',mtime) AS wk, "
" count(*) AS n "
" FROM v_reports "
" WHERE %Q=substr(date(mtime),1,4) "
" AND mtime < current_timestamp "
" AND ifnull(coalesce(euser,user,'')=%Q,1)"
" GROUP BY wk ORDER BY wk DESC", zYear, zUserName);
@ <h1>Timeline events (%h(stats_report_label_for_type()))
@ for the calendar weeks of %h(zYear)
if( zUserName ){
@ for user %h(zUserName)
}
@ </h1>
zCurrentWeek = db_text(0,
"SELECT strftime('%%W','now') WHERE date() LIKE '%q%%'",
zYear);
if( zCurrentWeek ){
rNowFraction = db_double(0.5,
"SELECT (unixepoch()-unixepoch('now','weekday 0','-7 days'))/604800.0;");
}
style_table_sorter();
cgi_printf("<table class='statistics-report-table-events sortable' "
"border='0' cellpadding='2' width='100%%' "
"cellspacing='0' data-column-types='tnx' data-init-sort='0'>\n");
cgi_printf("<thead><tr>"
"<th>Week</th>"
"<th>Events</th>"
"<th width='90%%'><!-- relative commits graph --></th>"
"</tr></thead>\n"
"<tbody>\n");
while( SQLITE_ROW == db_step(&q) ){
int nCount = db_column_int(&q, 1);
if( zCurrentWeek!=0
&& strcmp(db_column_text(&q,0),zCurrentWeek)==0
&& rNowFraction>0.05
){
nCount = (int)(((double)nCount)/rNowFraction);
}
if(nCount>nMaxEvents){
nMaxEvents = nCount;
}
++iterations;
}
db_reset(&q);
while( SQLITE_ROW == db_step(&q) ){
const char *zWeek = db_column_text(&q,0);
const int nCount = db_column_int(&q,1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 0;
if(!nSize) nSize = 1;
total += nCount;
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
cgi_printf("<td><a href='%R/timeline?yw=%t-%s&n=%d&y=%s",
zYear, zWeek, nCount,
statsReportTimelineYFlag);
if( zUserName ){
cgi_printf("&u=%t",zUserName);
}
cgi_printf("'>%s</a></td>",zWeek);
cgi_printf("<td>%d</td>",nCount);
cgi_printf("<td>");
if( nCount ){
if( zCurrentWeek!=0
&& strcmp(zWeek, zCurrentWeek)==0
&& rNowFraction>0.05
&& nMaxEvents>0
){
/* If the covered covered by this row contains "now", then project
** the number of changes until the completion of the week and
** show a dashed box of that projection. */
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
int nXSize = (100 * nExtra)/nMaxEvents;
@ <span class='statistics-report-graph-line' \
@ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\
@ <span class='statistics-report-graph-extra' \
@ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\
}else{
@ <div class='statistics-report-graph-line' \
@ style='width:%d(nSize)%%;'> </div> \
}
}
cgi_printf("</td></tr>\n");
}
db_finalize(&q);
cgi_printf("</tbody></table>");
if(total){
int nAvg = iterations ? (total/iterations) : 0;
cgi_printf("<br><div>Total events: %d<br>"
"Average per active week: %d</div>",
total, nAvg);
}
}
/*
|
| ︙ | ︙ | |||
783 784 785 786 787 788 789 | /* ** WEBPAGE: reports ** ** Shows activity reports for the repository. ** ** Query Parameters: ** | | > > > > > > > > > | > > > > > > | 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 | /* ** WEBPAGE: reports ** ** Shows activity reports for the repository. ** ** Query Parameters: ** ** view=REPORT_NAME Valid REPORT_NAME values: ** * byyear ** * bymonth ** * byweek ** * byweekday ** * byhour ** * byuser ** * byfile ** * lastchng ** user=NAME Restricts statistics to the given user ** type=TYPE Restricts the report to a specific event type: ** * all (everything), ** * ci (check-in) ** * m (merge check-in), ** * n (non-merge check-in) ** * f (forum post) ** * w (wiki page change) ** * t (ticket change) ** * g (tag added or removed) ** Defaulting to all event types. ** ** The view-specific query parameters include: ** ** view=byweek: ** ** y=YYYY The year to report (default is the server's |
| ︙ | ︙ | |||
820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
{ "By Year", "byyear", RPT_BYYEAR },
{ "By Hour", "byhour", RPT_BYHOUR },
};
static const char *const azType[] = {
"a", "All Changes",
"ci", "Check-ins",
"f", "Forum Posts",
"g", "Tags",
"e", "Tech Notes",
"t", "Tickets",
"w", "Wiki"
};
login_check_credentials();
| > > | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 |
{ "By Year", "byyear", RPT_BYYEAR },
{ "By Hour", "byhour", RPT_BYHOUR },
};
static const char *const azType[] = {
"a", "All Changes",
"ci", "Check-ins",
"f", "Forum Posts",
"m", "Merge check-ins",
"n", "Non-merge check-ins",
"g", "Tags",
"e", "Tech Notes",
"t", "Tickets",
"w", "Wiki"
};
login_check_credentials();
|
| ︙ | ︙ | |||
865 866 867 868 869 870 871 |
);
}
}
style_submenu_element("Stats", "%R/stat");
style_header("Activity Reports");
switch( eType ){
case RPT_BYYEAR:
| | | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 |
);
}
}
style_submenu_element("Stats", "%R/stat");
style_header("Activity Reports");
switch( eType ){
case RPT_BYYEAR:
stats_report_by_month_year(0, zUserName);
break;
case RPT_BYMONTH:
stats_report_by_month_year(1, zUserName);
break;
case RPT_BYWEEK:
stats_report_year_weeks(zUserName);
break;
default:
case RPT_BYUSER:
stats_report_by_user();
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
646 647 648 649 650 651 652 | ** header template lacks a <body> tag, then all of the following is ** prepended. */ static const char zDfltHeader[] = @ <html> @ <head> @ <meta charset="UTF-8"> | | | | | | 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 | ** header template lacks a <body> tag, then all of the following is ** prepended. */ static const char zDfltHeader[] = @ <html> @ <head> @ <meta charset="UTF-8"> @ <base href="$baseurl/$current_page"> @ <meta http-equiv="Content-Security-Policy" content="$default_csp"> @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> @ <title>$<project_name>: $<title></title> @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ @ href="$home/timeline.rss"> @ <link rel="stylesheet" href="$stylesheet_url" type="text/css"> @ </head> @ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> ; /* ** Returns the default page header. */ |
| ︙ | ︙ | |||
815 816 817 818 819 820 821 | zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); @ <!DOCTYPE html> | | | | | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 |
zTitle = vmprintf(zTitleFormat, ap);
va_end(ap);
cgi_destination(CGI_HEADER);
@ <!DOCTYPE html>
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br>\n", -1);
/* Generate the header up through the main menu */
style_init_th1_vars(zTitle);
if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){
Th_Render(zDfltHeader);
}
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1);
Th_Render(zHeader);
if( g.thTrace ) Th_Trace("END_HEADER<br>\n", -1);
Th_Unstore("title"); /* Avoid collisions with ticket field names */
cgi_destination(CGI_BODY);
g.cgiOutput = 1;
headerHasBeenGenerated = 1;
sideboxUsed = 0;
if( g.perm.Debug && P("showqp") ){
@ <div class="debug">
|
| ︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 |
@ </div>
/* Put the footer at the bottom of the page. */
zFooter = skin_get("footer");
if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
style_load_all_js_files();
}
| | | | | 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 |
@ </div>
/* Put the footer at the bottom of the page. */
zFooter = skin_get("footer");
if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){
style_load_all_js_files();
}
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1);
Th_Render(zFooter);
if( g.thTrace ) Th_Trace("END_FOOTER<br>\n", -1);
/* Render trace log if TH1 tracing is enabled. */
if( g.thTrace ){
cgi_append_content("<span class=\"thTrace\"><hr>\n", -1);
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
cgi_append_content("</span>\n", -1);
}
/* Add document end mark if it was not in the footer */
if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){
style_load_all_js_files();
|
| ︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 |
showAll = PB("showall");
style_submenu_checkbox("showall", "Cookies", 0, 0);
style_submenu_element("Stats", "%R/stat");
}
if( isAuth ){
#if !defined(_WIN32)
| | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > | | | 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 |
showAll = PB("showall");
style_submenu_checkbox("showall", "Cookies", 0, 0);
style_submenu_element("Stats", "%R/stat");
}
if( isAuth ){
#if !defined(_WIN32)
@ uid=%d(getuid()), gid=%d(getgid())<br>
#endif
@ g.zBaseURL = %h(g.zBaseURL)<br>
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
@ g.zTop = %h(g.zTop)<br>
@ g.zPath = %h(g.zPath)<br>
@ g.userUid = %d(g.userUid)<br>
@ g.zLogin = %h(g.zLogin)<br>
@ g.isHuman = %d(g.isHuman)<br>
@ g.jsHref = %d(g.jsHref)<br>
if( g.zLocalRoot ){
@ g.zLocalRoot = %h(g.zLocalRoot)<br>
}else{
@ g.zLocalRoot = <i>none</i><br>
}
if( g.nRequest ){
@ g.nRequest = %d(g.nRequest)<br>
}
if( g.nPendingRequest>1 ){
@ g.nPendingRequest = %d(g.nPendingRequest)<br>
}
@ capabilities = %s(find_capabilities(zCap))<br>
if( zCap[0] ){
@ anonymous-adds = %s(find_anon_capabilities(zCap))<br>
}
@ g.zRepositoryName = %h(g.zRepositoryName)<br>
@ load_average() = %f(load_average())<br>
#ifndef _WIN32
@ RSS = %.2f(fossil_rss()/1000000.0) MB</br>
#endif
@ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br>
@ fossil_exe_id() = %h(fossil_exe_id())<br>
if( g.perm.Admin ){
int k;
for(k=0; g.argvOrig[k]; k++){
Blob t;
blob_init(&t, 0, 0);
blob_append_escaped_arg(&t, g.argvOrig[k], 0);
@ argv[%d(k)] = %h(blob_str(&t))<br>
blob_zero(&t);
}
}
@ <hr>
P("HTTP_USER_AGENT");
P("SERVER_SOFTWARE");
cgi_print_all(showAll, 0);
if( showAll && blob_size(&g.httpHeader)>0 ){
@ <hr>
@ <pre>
@ %h(blob_str(&g.httpHeader))
@ </pre>
}
}
if( zErr && zErr[0] ){
style_finish_page();
|
| ︙ | ︙ |
Changes to src/style.chat.css.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
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 .message-widget-content.wide {
/* Special case for when embedding content which we really want to
expand, namely iframes. */
width: 98%;
}
body.chat .message-widget-content label[for] {
margin-left: 0.25em;
| > > | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
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;
word-break: break-word /* so that full hashes wrap on narrow screens */;
}
body.chat .message-widget-content.wide {
/* Special case for when embedding content which we really want to
expand, namely iframes. */
width: 98%;
}
body.chat .message-widget-content label[for] {
margin-left: 0.25em;
|
| ︙ | ︙ | |||
175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
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{
padding: 0;
}
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;
| > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
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{
padding: 0;
margin: 0 auto;
}
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;
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
884 885 886 887 888 889 890 |
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
db_finalize(&q);
| | | 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 |
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
db_finalize(&q);
@ <br>
style_finish_page();
}
/*
** Returns true if the given blob.rid value has the given tag ID
** applied to it, else false.
*/
|
| ︙ | ︙ |
Changes to src/tar.c.
| ︙ | ︙ | |||
244 245 246 247 248 249 250 |
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue);
/* this _must_ be right */
| | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue);
/* this _must_ be right */
if((int)blob_size(&tball.pax) != blen){
fossil_panic("internal error: PAX tar header has bad length");
}
}
/*
** set the header type, calculate the checksum and output
|
| ︙ | ︙ | |||
702 703 704 705 706 707 708 | zName[n] = 0; *pzName = fossil_strdup(&zName[n+1]); return zName; } /* ** WEBPAGE: tarball | | | | | | | | | | > > > | | | | < | | > | | | 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 | zName[n] = 0; *pzName = fossil_strdup(&zName[n+1]); return zName; } /* ** WEBPAGE: tarball ** URL: /tarball/[VERSION/]NAME.tar.gz ** ** Generate a compressed tarball for the check-in specified by VERSION. ** The tarball is called NAME.tar.gz and has a top-level directory called ** NAME. ** ** The optional VERSION element defaults to "trunk" per the r= rules below. ** All of the following URLs are equivalent: ** ** /tarball/release/xyz.tar.gz ** /tarball?r=release&name=xyz.tar.gz ** /tarball/xyz.tar.gz?r=release ** /tarball?name=release/xyz.tar.gz ** ** Query parameters: ** ** name=[CKIN/]NAME The optional CKIN component of the name= parameter ** identifies the check-in from which the tarball is ** constructed. If CKIN is omitted and there is no ** r= query parameter, then use "trunk". NAME is the ** name of the download file. The top-level directory ** in the generated tarball is called by NAME with the ** file extension removed. ** ** r=TAG TAG identifies the check-in that is turned into a ** compressed tarball. The default value is "trunk". ** If r= is omitted and if the name= query parameter ** contains one "/" character then the of part the ** name= value before the / becomes the TAG and the ** part of the name= value after the / is the download ** filename. If no check-in is specified by either ** name= or r=, then "trunk" is used. ** ** in=PATTERN Only include files that match the comma-separate ** list of GLOB patterns in PATTERN, as with ex= ** ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a ** comma-separated list of GLOB patterns, where each ** pattern can optionally be quoted using ".." or '..'. |
| ︙ | ︙ | |||
803 804 805 806 807 808 809 |
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
zKey = blob_str(&cacheKey);
etag_check(ETAG_HASH, zKey);
if( P("debug")!=0 ){
style_header("Tarball Generator Debug Screen");
| | | | | | | 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 |
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
zKey = blob_str(&cacheKey);
etag_check(ETAG_HASH, zKey);
if( P("debug")!=0 ){
style_header("Tarball Generator Debug Screen");
@ zName = "%h(zName)"<br>
@ rid = %d(rid)<br>
if( zInclude ){
@ zInclude = "%h(zInclude)"<br>
}
if( zExclude ){
@ zExclude = "%h(zExclude)"<br>
}
@ zKey = "%h(zKey)"
style_finish_page();
return;
}
if( referred_from_login() ){
style_header("Tarball Download");
@ <form action='%R/tarball/%h(zName).tar.gz'>
cgi_query_parameters_to_hidden();
@ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content
@ of check-in <b>%h(zRid)</b>:
@ <input type="submit" value="Download">
@ </form>
style_finish_page();
return;
}
blob_zero(&tarball);
if( cache_read(&tarball, zKey)==0 ){
tarball_of_checkin(rid, &tarball, zName, pInclude, pExclude, 0);
|
| ︙ | ︙ |
Changes to src/th.c.
| ︙ | ︙ | |||
851 852 853 854 855 856 857 |
zWord = Th_GetResult(interp, &nWord);
thBufferWrite(interp, &strbuf, zWord, nWord);
thBufferAddChar(interp, &strbuf, 0);
thBufferWrite(interp, &lenbuf, &nWord, sizeof(int));
nCount++;
}
}
| | | 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 |
zWord = Th_GetResult(interp, &nWord);
thBufferWrite(interp, &strbuf, zWord, nWord);
thBufferAddChar(interp, &strbuf, 0);
thBufferWrite(interp, &lenbuf, &nWord, sizeof(int));
nCount++;
}
}
assert((int)(lenbuf.nBuf/sizeof(int))==nCount);
assert((pazElem && panElem) || (!pazElem && !panElem));
if( pazElem && rc==TH_OK ){
int i;
char *zElem;
int *anElem;
char **azElem = Th_Malloc(interp,
|
| ︙ | ︙ | |||
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
if( !pValue->zData ){
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
return TH_ERROR;
}
return Th_SetResult(interp, pValue->zData, pValue->nData);
}
/*
** Return true if variable (zVar, nVar) exists.
*/
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
return pValue && (pValue->zData || pValue->pHash);
| > > > > > > > > > > > > > > > > > > > > > | 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 |
if( !pValue->zData ){
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
return TH_ERROR;
}
return Th_SetResult(interp, pValue->zData, pValue->nData);
}
/*
** If interp has a variable with the given name, its value is returned
** and its length is returned via *nOut if nOut is not NULL. If
** interp has no such var then NULL is returned without setting any
** error state and *nOut, if not NULL, is set to -1. The returned value
** is owned by the interpreter and may be invalidated the next time
** the interpreter is modified.
*/
const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName,
int *nOut){
Th_Variable *pValue;
pValue = thFindValue(interp, zVarName, -1, 0, 0, 1, 0);
if( !pValue || !pValue->zData ){
if( nOut!=0 ) *nOut = -1;
return NULL;
}
if( nOut!=0 ) *nOut = pValue->nData;
return pValue->zData;
}
/*
** Return true if variable (zVar, nVar) exists.
*/
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
return pValue && (pValue->zData || pValue->pHash);
|
| ︙ | ︙ |
Changes to src/th.h.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | int Th_ExistsVar(Th_Interp *, const char *, int); int Th_ExistsArrayVar(Th_Interp *, const char *, int); int Th_GetVar(Th_Interp *, const char *, int); int Th_SetVar(Th_Interp *, const char *, int, const char *, int); int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); int Th_UnsetVar(Th_Interp *, const char *, int); typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); /* ** Register new commands. */ int Th_CreateCommand( Th_Interp *interp, | > > > > > > > > > > > > > | 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 |
int Th_ExistsVar(Th_Interp *, const char *, int);
int Th_ExistsArrayVar(Th_Interp *, const char *, int);
int Th_GetVar(Th_Interp *, const char *, int);
int Th_SetVar(Th_Interp *, const char *, int, const char *, int);
int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int);
int Th_UnsetVar(Th_Interp *, const char *, int);
/*
** If interp has a variable with the given name, its value is returned
** and its length is returned via *nOut if nOut is not NULL. If
** interp has no such var then NULL is returned without setting any
** error state and *nOut, if not NULL, is set to 0. The returned value
** is owned by the interpreter and may be invalidated the next time
** the interpreter is modified.
**
** zVarName must be NUL-terminated.
*/
const char * Th_MaybeGetVar(Th_Interp *interp, const char *zVarName,
int *nOut);
typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *);
/*
** Register new commands.
*/
int Th_CreateCommand(
Th_Interp *interp,
|
| ︙ | ︙ |
Changes to src/th_lang.c.
| ︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 |
static int breakpoint_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
| | | | 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 |
static int breakpoint_command(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl
){
static unsigned int cnt = 0;
if( (cnt++)==0xffffffff ) printf("too many TH3 breakpoints\n");
return TH_OK;
}
/*
** Register the built-in th1 language commands with interpreter interp.
** Usually this is called soon after interpreter creation.
*/
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
288 289 290 291 292 293 294 |
){
int rc;
if( argc<2 || argc>3 ){
return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
}
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
if( g.thTrace ){
| | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
){
int rc;
if( argc<2 || argc>3 ){
return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
}
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
if( g.thTrace ){
Th_Trace("enable_output {%.*s} -> %d<br>\n", argl[1],argv[1],enableOutput);
}
return rc;
}
/*
** TH1 command: enable_htmlify ?BOOLEAN?
**
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 |
return Th_WrongNumArgs(interp,
"enable_htmlify [TRACE_LABEL] ?BOOLEAN?");
}
buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1;
Th_SetResultInt(g.interp, buul);
if(argc>1){
if( g.thTrace ){
| | | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
return Th_WrongNumArgs(interp,
"enable_htmlify [TRACE_LABEL] ?BOOLEAN?");
}
buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1;
Th_SetResultInt(g.interp, buul);
if(argc>1){
if( g.thTrace ){
Th_Trace("enable_htmlify {%.*s} -> %d<br>\n",
argl[1],argv[1],buul);
}
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul);
if(!rc){
if(buul){
g.th1Flags &= ~TH_INIT_NO_ENCODE;
}else{
|
| ︙ | ︙ | |||
410 411 412 413 414 415 416 |
/*
** error-reporting counterpart of sendText().
*/
static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
int savedEnable = enableOutput;
enableOutput = 1;
if( forceCgi || g.cgiOutput ){
| | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
/*
** error-reporting counterpart of sendText().
*/
static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
int savedEnable = enableOutput;
enableOutput = 1;
if( forceCgi || g.cgiOutput ){
sendText(pOut, "<hr><p class=\"thmainError\">", -1, 0);
}
sendText(pOut,"ERROR: ", -1, 0);
sendText(pOut,(char*)z, n, 1);
sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
enableOutput = savedEnable;
}
|
| ︙ | ︙ | |||
789 790 791 792 793 794 795 |
for(i=1; rc==1 && i<argc; i++){
if( g.thTrace ){
Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]);
}
rc = login_has_capability((char*)argv[i],argl[i],*(int*)p);
}
if( g.thTrace ){
| | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
for(i=1; rc==1 && i<argc; i++){
if( g.thTrace ){
Th_ListAppend(interp, &zCapList, &nCapList, argv[i], argl[i]);
}
rc = login_has_capability((char*)argv[i],argl[i],*(int*)p);
}
if( g.thTrace ){
Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc);
Th_Free(interp, zCapList);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
906 907 908 909 910 911 912 |
case 't': match |= searchCap & SRCH_TKT; break;
case 'w': match |= searchCap & SRCH_WIKI; break;
}
}
if( !match ) rc = 0;
}
if( g.thTrace ){
| | | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 |
case 't': match |= searchCap & SRCH_TKT; break;
case 'w': match |= searchCap & SRCH_WIKI; break;
}
}
if( !match ) rc = 0;
}
if( g.thTrace ){
Th_Trace("[searchable %#h] => %d<br>\n", argl[1], argv[1], rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH1 command: hasfeature STRING
|
| ︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 |
rc = 1;
}
#endif
else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
rc = 1;
}
if( g.thTrace ){
| | | 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 |
rc = 1;
}
#endif
else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
rc = 1;
}
if( g.thTrace ){
Th_Trace("[hasfeature %#h] => %d<br>\n", argl[1], zArg, rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 |
}
#if defined(FOSSIL_ENABLE_TCL)
if( g.tcl.interp ){
rc = 1;
}
#endif
if( g.thTrace ){
| | | 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 |
}
#if defined(FOSSIL_ENABLE_TCL)
if( g.tcl.interp ){
rc = 1;
}
#endif
if( g.thTrace ){
Th_Trace("[tclReady] => %d<br>\n", rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 |
if( argc!=2 ){
return Th_WrongNumArgs(interp, "anycap STRING");
}
for(i=0; rc==0 && i<argl[1]; i++){
rc = login_has_capability((char*)&argv[1][i],1,0);
}
if( g.thTrace ){
| | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
if( argc!=2 ){
return Th_WrongNumArgs(interp, "anycap STRING");
}
for(i=0; rc==0 && i<argl[1]; i++){
rc = login_has_capability((char*)&argv[1][i],1,0);
}
if( g.thTrace ){
Th_Trace("[anycap %#h] => %d<br>\n", argl[1], argv[1], rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH1 command: combobox NAME TEXT-LIST NUMLINES
|
| ︙ | ︙ | |||
1847 1848 1849 1850 1851 1852 1853 |
return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
}
if( argc==2 ){
if( Th_ToInt(interp, argv[1], argl[1], &n) ){
return TH_ERROR;
}
if( n<1 ) n = 1;
| | | 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 |
return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
}
if( argc==2 ){
if( Th_ToInt(interp, argv[1], argl[1], &n) ){
return TH_ERROR;
}
if( n<1 ) n = 1;
if( n>(int)sizeof(aRand) ) n = sizeof(aRand);
}else{
n = 10;
}
sqlite3_randomness(n, aRand);
encode16(aRand, zOut, n);
Th_SetResult(interp, (const char *)zOut, -1);
return TH_OK;
|
| ︙ | ︙ | |||
1951 1952 1953 1954 1955 1956 1957 |
const char *zCol = sqlite3_column_name(pStmt, i);
int szCol = th_strlen(zCol);
const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
int szVal = sqlite3_column_bytes(pStmt, i);
Th_SetVar(interp, zCol, szCol, zVal, szVal);
}
if( g.thTrace ){
| | | | 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 |
const char *zCol = sqlite3_column_name(pStmt, i);
int szCol = th_strlen(zCol);
const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
int szVal = sqlite3_column_bytes(pStmt, i);
Th_SetVar(interp, zCol, szCol, zVal, szVal);
}
if( g.thTrace ){
Th_Trace("query_eval {<pre>%#h</pre>}<br>\n", argl[2], argv[2]);
}
res = Th_Eval(interp, 0, argv[2], argl[2]);
if( g.thTrace ){
int nTrRes;
char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
Th_Trace("[query_eval] => %h {%#h}<br>\n",
Th_ReturnCodeName(res, 0), nTrRes, zTrRes);
}
if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK;
}
rc = sqlite3_finalize(pStmt);
if( rc!=SQLITE_OK ){
if( noComplain ) return TH_OK;
|
| ︙ | ︙ | |||
2011 2012 2013 2014 2015 2016 2017 |
Th_ErrorMessage(interp, "no value for setting \"", argv[nArg], -1);
rc = TH_ERROR;
}else{
Th_SetResult(interp, 0, 0);
rc = TH_OK;
}
if( g.thTrace ){
| | | 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 |
Th_ErrorMessage(interp, "no value for setting \"", argv[nArg], -1);
rc = TH_ERROR;
}else{
Th_SetResult(interp, 0, 0);
rc = TH_OK;
}
if( g.thTrace ){
Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "",
argl[nArg], argv[nArg], rc);
}
return rc;
}
/*
** TH1 command: glob_match ?-one? ?--? patternList string
|
| ︙ | ︙ | |||
2372 2373 2374 2375 2376 2377 2378 |
{"utime", utimeCmd, 0},
{"verifyCsrf", verifyCsrfCmd, 0},
{"verifyLogin", verifyLoginCmd, 0},
{"wiki", wikiCmd, (void*)&aFlags[0]},
{0, 0, 0}
};
if( g.thTrace ){
| | | | 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 |
{"utime", utimeCmd, 0},
{"verifyCsrf", verifyCsrfCmd, 0},
{"verifyLogin", verifyLoginCmd, 0},
{"wiki", wikiCmd, (void*)&aFlags[0]},
{0, 0, 0}
};
if( g.thTrace ){
Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
}
if( needConfig ){
/*
** This function uses several settings which may be defined in the
** repository and/or the global configuration. Since the caller
** passed a non-zero value for the needConfig parameter, make sure
** the necessary database connections are open prior to continuing.
*/
Th_OpenConfig(!noRepo);
}
if( forceReset || forceTcl || g.interp==0 ){
int created = 0;
int i;
if( g.interp==0 ){
Th_Vtab *pVtab = 0;
#if defined(TH_MEMDEBUG)
if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
pVtab = &vtab;
if( g.thTrace ){
Th_Trace("th1-init MEMDEBUG ENABLED<br>\n");
}
}
#endif
g.interp = Th_CreateInterp(pVtab);
created = 1;
}
if( forceReset || created ){
|
| ︙ | ︙ | |||
2433 2434 2435 2436 2437 2438 2439 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
sendError(0,zResult, nResult, 0);
}
}
if( g.thTrace ){
| | | | | 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
sendError(0,zResult, nResult, 0);
}
}
if( g.thTrace ){
Th_Trace("th1-setup {%h} => %h<br>\n", g.th1Setup,
Th_ReturnCodeName(rc, 0));
}
}
g.th1Flags &= ~TH_INIT_MASK;
g.th1Flags |= (flags & TH_INIT_MASK);
}
/*
** Store a string value in a variable in the interpreter if the variable
** does not already exist.
*/
void Th_MaybeStore(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){
if( g.thTrace ){
Th_Trace("maybe_set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
}
}
/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue ){
if( g.thTrace ){
Th_Trace("set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
}
}
/*
** Appends an element to a TH1 list value. This function is called by the
|
| ︙ | ︙ | |||
2505 2506 2507 2508 2509 2510 2511 |
char *zValue = 0;
int nValue = 0;
int i;
for(i=0; i<nList; i++){
Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1);
}
if( g.thTrace ){
| | | | 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 |
char *zValue = 0;
int nValue = 0;
int i;
for(i=0; i<nList; i++){
Th_ListAppend(g.interp, &zValue, &nValue, pzList[i], -1);
}
if( g.thTrace ){
Th_Trace("set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, nValue);
Th_Free(g.interp, zValue);
}
}
/*
** Store an integer value in a variable in the interpreter.
*/
void Th_StoreInt(const char *zName, int iValue){
Blob value;
char *zValue;
Th_FossilInit(TH_INIT_DEFAULT);
blob_zero(&value);
blob_appendf(&value, "%d", iValue);
zValue = blob_str(&value);
if( g.thTrace ){
Th_Trace("set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
blob_reset(&value);
}
/*
** Unset a variable.
|
| ︙ | ︙ | |||
2671 2672 2673 2674 2675 2676 2677 |
** If the script returned TH_ERROR (e.g. the "command_hook" TH1 command does
** not exist because commands are not being hooked), return TH_OK because we
** do not want to skip executing essential commands unless the called command
** (i.e. "command_hook") explicitly forbids this by successfully returning
** TH_BREAK or TH_CONTINUE.
*/
if( g.thTrace ){
| | | 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 |
** If the script returned TH_ERROR (e.g. the "command_hook" TH1 command does
** not exist because commands are not being hooked), return TH_OK because we
** do not want to skip executing essential commands unless the called command
** (i.e. "command_hook") explicitly forbids this by successfully returning
** TH_BREAK or TH_CONTINUE.
*/
if( g.thTrace ){
Th_Trace("[command_hook {%h}] => %h<br>\n", zName,
Th_ReturnCodeName(rc, 0));
}
/*
** Does our call to Th_FossilInit() result in opening a database? If so,
** clean it up now. This is very important because some commands do not
** expect the repository and/or the configuration ("user") database to be
** open prior to their own code doing so.
|
| ︙ | ︙ | |||
2703 2704 2705 2706 2707 2708 2709 |
if( !Th_AreHooksEnabled() ) return rc;
Th_FossilInit(TH_INIT_HOOK);
Th_Store("cmd_name", zName);
Th_StoreList("cmd_args", g.argv, g.argc);
Th_StoreInt("cmd_flags", cmdFlags);
rc = Th_Eval(g.interp, 0, "command_notify", -1);
if( g.thTrace ){
| | | 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 |
if( !Th_AreHooksEnabled() ) return rc;
Th_FossilInit(TH_INIT_HOOK);
Th_Store("cmd_name", zName);
Th_StoreList("cmd_args", g.argv, g.argc);
Th_StoreInt("cmd_flags", cmdFlags);
rc = Th_Eval(g.interp, 0, "command_notify", -1);
if( g.thTrace ){
Th_Trace("[command_notify {%h}] => %h<br>\n", zName,
Th_ReturnCodeName(rc, 0));
}
/*
** Does our call to Th_FossilInit() result in opening a database? If so,
** clean it up now. This is very important because some commands do not
** expect the repository and/or the configuration ("user") database to be
** open prior to their own code doing so.
|
| ︙ | ︙ | |||
2758 2759 2760 2761 2762 2763 2764 |
** If the script returned TH_ERROR (e.g. the "webpage_hook" TH1 command does
** not exist because commands are not being hooked), return TH_OK because we
** do not want to skip processing essential web pages unless the called
** command (i.e. "webpage_hook") explicitly forbids this by successfully
** returning TH_BREAK or TH_CONTINUE.
*/
if( g.thTrace ){
| | | 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 |
** If the script returned TH_ERROR (e.g. the "webpage_hook" TH1 command does
** not exist because commands are not being hooked), return TH_OK because we
** do not want to skip processing essential web pages unless the called
** command (i.e. "webpage_hook") explicitly forbids this by successfully
** returning TH_BREAK or TH_CONTINUE.
*/
if( g.thTrace ){
Th_Trace("[webpage_hook {%h}] => %h<br>\n", zName,
Th_ReturnCodeName(rc, 0));
}
/*
** Does our call to Th_FossilInit() result in opening a database? If so,
** clean it up now. This is very important because some commands do not
** expect the repository and/or the configuration ("user") database to be
** open prior to their own code doing so.
|
| ︙ | ︙ | |||
2790 2791 2792 2793 2794 2795 2796 |
if( !Th_AreHooksEnabled() ) return rc;
Th_FossilInit(TH_INIT_HOOK);
Th_Store("web_name", zName);
Th_StoreList("web_args", g.argv, g.argc);
Th_StoreInt("web_flags", cmdFlags);
rc = Th_Eval(g.interp, 0, "webpage_notify", -1);
if( g.thTrace ){
| | | 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 |
if( !Th_AreHooksEnabled() ) return rc;
Th_FossilInit(TH_INIT_HOOK);
Th_Store("web_name", zName);
Th_StoreList("web_args", g.argv, g.argc);
Th_StoreInt("web_flags", cmdFlags);
rc = Th_Eval(g.interp, 0, "webpage_notify", -1);
if( g.thTrace ){
Th_Trace("[webpage_notify {%h}] => %h<br>\n", zName,
Th_ReturnCodeName(rc, 0));
}
/*
** Does our call to Th_FossilInit() result in opening a database? If so,
** clean it up now. This is very important because some commands do not
** expect the repository and/or the configuration ("user") database to be
** open prior to their own code doing so.
|
| ︙ | ︙ | |||
2873 2874 2875 2876 2877 2878 2879 |
zResult = (char*)Th_GetResult(g.interp, &n);
sendText(pOut,(char*)zResult, n, encode);
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
sendText(pOut,z, i, 0);
z += i+5;
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
if( g.thTrace ){
| | | | 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 |
zResult = (char*)Th_GetResult(g.interp, &n);
sendText(pOut,(char*)zResult, n, encode);
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
sendText(pOut,z, i, 0);
z += i+5;
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
if( g.thTrace ){
Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z);
}
rc = Th_Eval(g.interp, 0, (const char*)z, i);
if( g.thTrace ){
int nTrRes;
char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
Th_Trace("[render_eval] => %h {%#h}<br>\n",
Th_ReturnCodeName(rc, 0), nTrRes, zTrRes);
}
if( rc!=TH_OK ) break;
z += i;
if( z[0] ){ z += 6; }
i = 0;
}else{
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
"SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0",
TAG_CLOSED);
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
| > > > > > > > > > > > > > > > > > > | 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 |
"SELECT 1 FROM tagxref WHERE rid=$rid AND tagid=%d AND tagtype>0",
TAG_CLOSED);
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Return the text of the unformatted
** forum post given by the RID in the argument.
*/
static void forum_post_content_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int rid = sqlite3_value_int(argv[0]);
Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0);
if( pPost ){
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
manifest_destroy(pPost);
}
}
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
|
| ︙ | ︙ | |||
276 277 278 279 280 281 282 |
}
if( pendingEndTr ){
@ </td></tr>
pendingEndTr = 0;
}
if( fossil_strcmp(zType,"div")==0 ){
if( !prevWasDivider ){
| | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
}
if( pendingEndTr ){
@ </td></tr>
pendingEndTr = 0;
}
if( fossil_strcmp(zType,"div")==0 ){
if( !prevWasDivider ){
@ <tr><td colspan="3"><hr class="timelineMarker"></td></tr>
}
prevWasDivider = 1;
continue;
}
prevWasDivider = 0;
/* Date format codes:
** (0) HH:MM
|
| ︙ | ︙ | |||
492 493 494 495 496 497 498 |
hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_version(zUuid);
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
| | | | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
}else if( (tmFlags & TIMELINE_ARTID)!=0 ){
hyperlink_to_version(zUuid);
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
@ (%z(href("%R/deltachain/%d",rid))%d(rid)←%d(srcId)</a>)
}else{
@ (%z(href("%R/deltachain/%d",rid))%d(rid)</a>)
}
}
}
if( zType[0]!='c' ){
/* Comments for anything other than a check-in are generated by
** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
if( zType[0]=='w' ){
|
| ︙ | ︙ | |||
545 546 547 548 549 550 551 |
if( z[ii]=='\n' ){
for(jj=ii+1; jj<n && z[jj]!='\n' && fossil_isspace(z[jj]); jj++){}
if( z[jj]=='\n' ) break;
}
}
z[ii] = 0;
cgi_printf("%W",z);
| | | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
if( z[ii]=='\n' ){
for(jj=ii+1; jj<n && z[jj]!='\n' && fossil_isspace(z[jj]); jj++){}
if( z[jj]=='\n' ) break;
}
}
z[ii] = 0;
cgi_printf("%W",z);
}else if( mxWikiLen>0 && (int)blob_size(&comment)>mxWikiLen ){
Blob truncated;
blob_zero(&truncated);
blob_append(&truncated, blob_buffer(&comment), mxWikiLen);
blob_append(&truncated, "...", 3);
@ %W(blob_str(&truncated))
blob_reset(&truncated);
drawDetailEllipsis = 0;
|
| ︙ | ︙ | |||
648 649 650 651 652 653 654 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
| | > | > | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
cgi_printf(" id: %z%d←%d</a>",
href("%R/deltachain/%d",rid), rid, srcId);
}else{
cgi_printf(" id: %z%d</a>",
href("%R/deltachain/%d",rid), rid);
}
}
tag_private_status(rid);
if( xExtra ){
xExtra(rid);
}
/* End timelineDetail */
|
| ︙ | ︙ | |||
701 702 703 704 705 706 707 |
int fid = db_column_int(&fchngQuery, 1);
int isDel = fid==0;
const char *zOldName = db_column_text(&fchngQuery, 5);
const char *zOld = db_column_text(&fchngQuery, 4);
const char *zNew = db_column_text(&fchngQuery, 3);
const char *zUnpub = "";
char *zA;
| | > | | > | | 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 |
int fid = db_column_int(&fchngQuery, 1);
int isDel = fid==0;
const char *zOldName = db_column_text(&fchngQuery, 5);
const char *zOld = db_column_text(&fchngQuery, 4);
const char *zNew = db_column_text(&fchngQuery, 3);
const char *zUnpub = "";
char *zA;
char *zId;
if( !inUl ){
@ <ul class="filelist">
inUl = 1;
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(fid);
if( srcId ){
zId = mprintf(" (%z%d←%d</a>) ",
href("%R/deltachain/%d", fid), fid, srcId);
}else{
zId = mprintf(" (%z%d</a>) ",
href("%R/deltachain/%d", fid), fid);
}
}else{
zId = fossil_strdup("");
}
if( (tmFlags & TIMELINE_FRENAMES)!=0 ){
if( !isNew && !isDel && zOldName!=0 ){
@ <li> %h(zOldName) → %h(zFilename)%s(zId)
}
continue;
}
|
| ︙ | ︙ | |||
748 749 750 751 752 753 754 755 756 757 758 759 760 761 |
@ <li>%h(zOldName) → %s(zA)%h(zFilename)%s(zId)</a> %s(zUnpub)
}else{
@ <li>%s(zA)%h(zFilename)</a>%s(zId) %s(zUnpub)
}
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a></li>
}
fossil_free(zA);
}
db_reset(&fchngQuery);
if( inUl ){
@ </ul>
}
}
| > | 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 |
@ <li>%h(zOldName) → %s(zA)%h(zFilename)%s(zId)</a> %s(zUnpub)
}else{
@ <li>%s(zA)%h(zFilename)</a>%s(zId) %s(zUnpub)
}
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a></li>
}
fossil_free(zA);
fossil_free(zId);
}
db_reset(&fchngQuery);
if( inUl ){
@ </ul>
}
}
|
| ︙ | ︙ | |||
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** d=CHECKIN Children and descendants of CHECKIN ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN In conjunction with p=CX, this means show all ** ancestors of CX going back to the time of CHECKIN. ** All qualifying check-ins are shown unless there ** is also an n= or n1= query parameter. ** t=TAG Show only check-ins with the given TAG | > | 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 | ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** d=CHECKIN Children and descendants of CHECKIN ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN In conjunction with p=CX, this means show all ** ancestors of CX going back to the time of CHECKIN. ** All qualifying check-ins are shown unless there ** is also an n= or n1= query parameter. ** t=TAG Show only check-ins with the given TAG |
| ︙ | ︙ | |||
1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 | ** to see all changes for the current week. ** year=YYYY Show only events on the given year. The use "year=0" ** to see all changes for the current year. ** days=N Show events over the previous N days ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". ** bisect Show the check-ins that are in the current bisect ** showid Show RIDs ** showsql Show the SQL text ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If both a= and b= appear then both upper and lower bounds are honored. | > | 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 | ** to see all changes for the current week. ** year=YYYY Show only events on the given year. The use "year=0" ** to see all changes for the current year. ** days=N Show events over the previous N days ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". ** bisect Show the check-ins that are in the current bisect ** oldestfirst Show events oldest first. ** showid Show RIDs ** showsql Show the SQL text ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If both a= and b= appear then both upper and lower bounds are honored. |
| ︙ | ︙ | |||
1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 |
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
/* Set number of rows to display */
z = P("n");
if( z!=0 ){
haveParameterN = 1;
cookie_write_parameter("n","n",0);
}else{
| > > > | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 |
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
/* Set number of rows to display */
z = P("n");
if( z!=0 ){
haveParameterN = 1;
cookie_write_parameter("n","n",0);
}else{
|
| ︙ | ︙ | |||
1778 1779 1780 1781 1782 1783 1784 |
}
cookie_read_parameter("y","y");
zType = P("y");
if( zType==0 ){
zType = g.perm.Read ? "ci" : "all";
cgi_set_parameter("y", zType);
}
| | > > > > > | 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 |
}
cookie_read_parameter("y","y");
zType = P("y");
if( zType==0 ){
zType = g.perm.Read ? "ci" : "all";
cgi_set_parameter("y", zType);
}
if( zType[0]=='a' ||
( g.perm.Read && zType[0]=='c' ) ||
( g.perm.RdTkt && (zType[0]=='t' || zType[0]=='n') ) ||
( g.perm.RdWiki && (zType[0]=='w' || zType[0]=='e') ) ||
( g.perm.RdForum && zType[0]=='f' )
){
cookie_write_parameter("y","y",zType);
}
/* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */
if( P("cf")!=0 ){
zCirca = db_text(0,
"SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)"
|
| ︙ | ︙ | |||
2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 |
addFileGlobDescription(zChng, &desc);
}else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
/* If p= or d= is present, ignore all other parameters other than n= */
char *zUuid;
const char *zCiName;
int np = 0, nd;
const char *zBackTo = 0;
int ridBackTo = 0;
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
if( p_rid && d_rid ){
if( p_rid!=d_rid ) p_rid = d_rid;
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
if( zCiName==0 ) zCiName = zUuid;
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > | > | 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 |
addFileGlobDescription(zChng, &desc);
}else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
/* If p= or d= is present, ignore all other parameters other than n= */
char *zUuid;
const char *zCiName;
int np = 0, nd;
const char *zBackTo = 0;
const char *zFwdTo = 0;
int ridBackTo = 0;
int ridFwdTo = 0;
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
if( p_rid && d_rid ){
if( p_rid!=d_rid ) p_rid = d_rid;
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
if( zCiName==0 ) zCiName = zUuid;
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
Stmt s;
double rStopTime = 9e99;
zFwdTo = P("ft");
if( zFwdTo ){
double rStartDate = db_double(0.0,
"SELECT mtime FROM event WHERE objid=%d", d_rid);
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
if( ridFwdTo==0 ){
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridFwdTo ){
if( !haveParameterN ) nEntry = 0;
rStopTime = db_double(9e99,
"SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
}
}
db_prepare(&s,
"WITH RECURSIVE"
" dx(rid,mtime) AS ("
" SELECT %d, 0"
" UNION"
" SELECT plink.cid, plink.mtime FROM dx, plink"
" WHERE plink.pid=dx.rid"
" AND (:stop>=8e99 OR plink.mtime<=:stop)"
" ORDER BY 2"
" )"
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
d_rid, nEntry<=0 ? -1 : nEntry+1
);
db_bind_double(&s, ":stop", rStopTime);
db_step(&s);
db_finalize(&s);
/* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
nd = db_int(0, "SELECT count(*)-1 FROM ok");
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
if( nd>0 || p_rid==0 ){
blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
}
if( useDividers && !selectedRid ) selectedRid = d_rid;
db_multi_exec("DELETE FROM ok");
}
if( p_rid ){
zBackTo = P("bt");
if( zBackTo ){
double rDateLimit = db_double(0.0,
"SELECT mtime FROM event WHERE objid=%d", p_rid);
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
if( ridBackTo==0 ){
ridBackTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridBackTo && !haveParameterN ) nEntry = 0;
}
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
np = db_int(0, "SELECT count(*)-1 FROM ok");
if( np>0 || nd==0 ){
if( nd>0 ) blob_appendf(&desc, " and ");
blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
db_multi_exec("%s", blob_sql_text(&sql));
}
|
| ︙ | ︙ | |||
2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 |
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>",
href("%R/info?name=%h",zBackTo), zBackTo);
}
}
if( d_rid ){
if( p_rid ){
/* If both p= and d= are set, we don't have the uuid of d yet. */
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
}
| > > > > > > > > > > > > > > > | 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 |
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>",
href("%R/info?name=%h",zBackTo), zBackTo);
if( ridFwdTo && zFwdTo ){
blob_appendf(&desc, " and up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
}
}else if( ridFwdTo ){
if( nd==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an descendant)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zFwdTo), zFwdTo);
}else{
blob_appendf(&desc, " up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
}
if( d_rid ){
if( p_rid ){
/* If both p= and d= are set, we don't have the uuid of d yet. */
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
}
|
| ︙ | ︙ | |||
2482 2483 2484 2485 2486 2487 2488 |
zEType = "forum post";
}
}
if( zUser ){
int n = db_int(0,"SELECT count(*) FROM event"
" WHERE user=%Q OR euser=%Q", zUser, zUser);
if( n<=nEntry ){
| < > > > | > > > > > > > | | > | 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 |
zEType = "forum post";
}
}
if( zUser ){
int n = db_int(0,"SELECT count(*) FROM event"
" WHERE user=%Q OR euser=%Q", zUser, zUser);
if( n<=nEntry ){
nEntry = -1;
}
blob_append_sql(&cond, " AND (event.user=%Q OR event.euser=%Q)",
zUser, zUser);
zThisUser = zUser;
}
if( zSearch ){
if( tmFlags & TIMELINE_FORUMTXT ){
sqlite3_create_function(g.db, "forum_post_content", 1, SQLITE_UTF8,
0, forum_post_content_function, 0, 0);
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%'"
" OR event.brief LIKE '%%%q%%'"
" OR (event.type=='f' AND"
" forum_post_content(event.objid) LIKE '%%%q%%'))",
zSearch, zSearch, zSearch);
}else{
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
zSearch, zSearch);
}
}
rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
if( rAfter>0.0 ){
if( rBefore>0.0 ){
|
| ︙ | ︙ | |||
2610 2611 2612 2613 2614 2615 2616 |
blob_appendf(&desc," plus check-in \"%h\"", zMark);
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
}
addFileGlobDescription(zChng, &desc);
if( rAfter>0.0 ){
if( rBefore>0.0 ){
| | | | | | 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 |
blob_appendf(&desc," plus check-in \"%h\"", zMark);
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
}
addFileGlobDescription(zChng, &desc);
if( rAfter>0.0 ){
if( rBefore>0.0 ){
blob_appendf(&desc, " occurring between %h and %h.<br>",
zAfter, zBefore);
}else{
blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
}
}else if( rBefore>0.0 ){
blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
}else if( rCirca>0.0 ){
blob_appendf(&desc, " occurring around %h.<br>", zCirca);
}
if( zSearch ){
blob_appendf(&desc, " matching \"%h\"", zSearch);
}
if( g.perm.Hyperlink ){
static const char *const azMatchStyles[] = {
"exact", "Exact", "glob", "Glob", "like", "Like", "regexp", "Regexp",
|
| ︙ | ︙ | |||
2695 2696 2697 2698 2699 2700 2701 |
}
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0);
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
| > > > | > | 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 |
}
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0);
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
if( PB("oldestfirst") ){
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
}else{
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
}
if( fossil_islower(desc.aData[0]) ){
desc.aData[0] = fossil_toupper(desc.aData[0]);
}
if( zBrName ){
if( !PB("nowiki")
&& wiki_render_associated("branch", zBrName, WIKIASSOC_ALL)
){
|
| ︙ | ︙ | |||
3417 3418 3419 3420 3421 3422 3423 |
z = db_text(0, "SELECT date(%Q,'+1 day')", zToday);
style_submenu_element("Tomorrow", "%R/thisdayinhistory?today=%t", z);
zStartOfProject = db_text(0,
"SELECT datetime(min(mtime),toLocal(),'startofday') FROM event;"
);
timeline_temp_table();
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
| | | 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 |
z = db_text(0, "SELECT date(%Q,'+1 day')", zToday);
style_submenu_element("Tomorrow", "%R/thisdayinhistory?today=%t", z);
zStartOfProject = db_text(0,
"SELECT datetime(min(mtime),toLocal(),'startofday') FROM event;"
);
timeline_temp_table();
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
for(i=0; i<(int)(sizeof(aYearsAgo)/sizeof(aYearsAgo[0])); i++){
int iAgo = aYearsAgo[i];
char *zThis = db_text(0, "SELECT date(%Q,'-%d years')", zToday, iAgo);
Blob sql;
char *zId;
if( strcmp(zThis, zStartOfProject)<0 ) break;
blob_init(&sql, 0, 0);
blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
&& sqlite3_strnicmp(z0,"sqlite_",7)!=0
&& sqlite3_strnicmp(z0,"fx_",3)!=0
){
goto ticket_schema_error;
}
break;
}
case SQLITE_FUNCTION:
case SQLITE_REINDEX:
case SQLITE_TRANSACTION:
case SQLITE_READ: {
break;
}
default: {
| > | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
&& sqlite3_strnicmp(z0,"sqlite_",7)!=0
&& sqlite3_strnicmp(z0,"fx_",3)!=0
){
goto ticket_schema_error;
}
break;
}
case SQLITE_SELECT:
case SQLITE_FUNCTION:
case SQLITE_REINDEX:
case SQLITE_TRANSACTION:
case SQLITE_READ: {
break;
}
default: {
|
| ︙ | ︙ | |||
730 731 732 733 734 735 736 |
login_check_credentials();
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
if( g.anon.WrTkt || g.anon.ApndTkt ){
style_submenu_element("Edit", "%R/tktedit/%T", PD("name",""));
}
if( g.perm.Hyperlink ){
style_submenu_element("History", "%R/tkthistory/%T", zUuid);
| > | > | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
login_check_credentials();
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
if( g.anon.WrTkt || g.anon.ApndTkt ){
style_submenu_element("Edit", "%R/tktedit/%T", PD("name",""));
}
if( g.perm.Hyperlink ){
style_submenu_element("History", "%R/tkthistory/%T", zUuid);
if( g.perm.Read ){
style_submenu_element("Check-ins", "%R/tkttimeline/%T?y=ci", zUuid);
}
}
if( g.anon.NewTkt ){
style_submenu_element("New Ticket", "%R/tktnew");
}
if( g.anon.ApndTkt && g.anon.Attach ){
style_submenu_element("Attach", "%R/attachadd?tkt=%T&from=%R/tktview/%t",
zUuid, zUuid);
|
| ︙ | ︙ | |||
759 760 761 762 763 764 765 |
}else{
showTimeline = 0;
}
}
if( !showTimeline && g.perm.Hyperlink ){
style_submenu_element("Timeline", "%R/info/%T", zUuid);
}
| | | | | | 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 |
}else{
showTimeline = 0;
}
}
if( !showTimeline && g.perm.Hyperlink ){
style_submenu_element("Timeline", "%R/info/%T", zUuid);
}
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
ticket_init();
initializeVariablesFromCGI();
getAllTicketFields();
initializeVariablesFromDb();
zScript = ticket_viewpage_code();
if( P("showfields")!=0 ) showAllFields();
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
safe_html_context(DOCSRC_TICKET);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
attachment_list(zFullName, "<hr><h2>Attachments:</h2><ul>");
}
style_finish_page();
}
/*
** TH1 command: append_field FIELD STRING
|
| ︙ | ︙ | |||
802 803 804 805 806 807 808 |
){
int idx;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
}
if( g.thTrace ){
| | | 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 |
){
int idx;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
}
if( g.thTrace ){
Th_Trace("append_field %#h {%#h}<br>\n",
argl[1], argv[1], argl[2], argv[2]);
}
for(idx=0; idx<nField; idx++){
if( memcmp(aField[idx].zName, argv[1], argl[1])==0
&& aField[idx].zName[argl[1]]==0 ){
break;
}
|
| ︙ | ︙ | |||
923 924 925 926 927 928 929 |
int nValue;
if( aField[i].zAppend ) continue;
zValue = Th_Fetch(aField[i].zName, &nValue);
if( zValue ){
while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
|| memcmp(zValue, aField[i].zValue, nValue)!=0
| | | 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 |
int nValue;
if( aField[i].zAppend ) continue;
zValue = Th_Fetch(aField[i].zName, &nValue);
if( zValue ){
while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
|| memcmp(zValue, aField[i].zValue, nValue)!=0
||(int)strlen(aField[i].zValue)!=nValue
){
if( memcmp(aField[i].zName, "private_", 8)==0 ){
zValue = db_conceal(zValue, nValue);
blob_appendf(&tktchng, "J %s %s\n", aField[i].zName, zValue);
aUsed[i] = JCARD_PRIVATE;
}else{
blob_appendf(&tktchng, "J %s %#F\n", aField[i].zName, nValue, zValue);
|
| ︙ | ︙ | |||
962 963 964 965 966 967 968 |
const char *zNeedMod = needMod ? "required" : "skipped";
/* If called from /debug_tktnew or /debug_tktedit... */
@ <div style="color:blue">
@ <p>Ticket artifact that would have been submitted:</p>
@ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
@ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
@ </div>
| | | | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
const char *zNeedMod = needMod ? "required" : "skipped";
/* If called from /debug_tktnew or /debug_tktedit... */
@ <div style="color:blue">
@ <p>Ticket artifact that would have been submitted:</p>
@ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote>
@ <blockquote><pre>Moderation would be %h(zNeedMod).</pre></blockquote>
@ </div>
@ <hr>
}else{
if( g.thTrace ){
Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
"}<br>\n",
blob_str(&tktchng));
}
ticket_put(&tktchng, zUuid, aUsed, needMod);
rc = ticket_change(zUuid);
}
finish:
fossil_free( aUsed );
|
| ︙ | ︙ | |||
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 |
** just like /tktnew except that it does not really save the new ticket
** when you press submit - it just prints the ticket artifact at the
** top of the screen.
*/
void tktnew_page(void){
const char *zScript;
char *zNewUuid = 0;
login_check_credentials();
if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
if( P("cancel") ){
cgi_redirect("home");
}
style_set_current_feature("tkt");
style_header("New Ticket");
ticket_standard_submenu(T_ALL_BUT(T_NEW));
| > | > > > > > > > > > > > > > > > | | | 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 |
** just like /tktnew except that it does not really save the new ticket
** when you press submit - it just prints the ticket artifact at the
** top of the screen.
*/
void tktnew_page(void){
const char *zScript;
char *zNewUuid = 0;
int uid;
login_check_credentials();
if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
if( P("cancel") ){
cgi_redirect("home");
}
style_set_current_feature("tkt");
style_header("New Ticket");
ticket_standard_submenu(T_ALL_BUT(T_NEW));
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br>\n", -1);
ticket_init();
initializeVariablesFromCGI();
getAllTicketFields();
initializeVariablesFromDb();
if( g.zPath[0]=='d' ) showAllFields();
form_begin(0, "%R/%s", g.zPath);
login_insert_csrf_secret();
if( P("date_override") && g.perm.Setup ){
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
}
zScript = ticket_newpage_code();
if( g.zLogin && g.zLogin[0] ){
int nEmail = 0;
(void)Th_MaybeGetVar(g.interp, "private_contact", &nEmail);
uid = nEmail>0
? 0 : db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin);
if( uid ){
char * zEmail =
db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d",
uid);
if( zEmail ){
Th_Store("private_contact", zEmail);
fossil_free(zEmail);
}
}
}
Th_Store("login", login_name());
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
(void*)&zNewUuid, 0);
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
return;
}
captcha_generate(0);
@ </form>
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
style_finish_page();
}
/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
|
| ︙ | ︙ | |||
1076 1077 1078 1079 1080 1081 1082 |
}
if( nRec>1 ){
@ <span class="tktError">%d(nRec) tickets begin with:
@ "%h(zName)"</span>
style_finish_page();
return;
}
| | | | | | 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 |
}
if( nRec>1 ){
@ <span class="tktError">%d(nRec) tickets begin with:
@ "%h(zName)"</span>
style_finish_page();
return;
}
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
ticket_init();
getAllTicketFields();
initializeVariablesFromCGI();
initializeVariablesFromDb();
if( g.zPath[0]=='d' ) showAllFields();
form_begin(0, "%R/%s", g.zPath);
@ <input type="hidden" name="name" value="%s(zName)">
login_insert_csrf_secret();
zScript = ticket_editpage_code();
Th_Store("login", login_name());
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
cgi_redirect(mprintf("%R/tktview/%s", zName));
return;
}
captcha_generate(0);
@ </form>
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br>\n", -1);
style_finish_page();
}
/*
** Check the ticket table schema in zSchema to see if it appears to
** be well-formed. If everything is OK, return NULL. If something is
** amiss, then return a pointer to a string (obtained from malloc) that
|
| ︙ | ︙ | |||
1210 1211 1212 1213 1214 1215 1216 |
if( !g.perm.Hyperlink || !g.perm.RdTkt ){
login_needed(g.anon.Hyperlink && g.anon.RdTkt);
return;
}
zUuid = PD("name","");
zType = PD("y","a");
if( zType[0]!='c' ){
| > | > | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 |
if( !g.perm.Hyperlink || !g.perm.RdTkt ){
login_needed(g.anon.Hyperlink && g.anon.RdTkt);
return;
}
zUuid = PD("name","");
zType = PD("y","a");
if( zType[0]!='c' ){
if( g.perm.Read ){
style_submenu_element("Check-ins", "%R/tkttimeline/%T?y=ci", zUuid);
}
}else{
style_submenu_element("Timeline", "%R/tkttimeline/%T", zUuid);
}
style_submenu_element("History", "%R/tkthistory/%s", zUuid);
style_submenu_element("Status", "%R/info/%s", zUuid);
if( zType[0]=='c' ){
zTitle = mprintf("Check-ins Associated With Ticket %h", zUuid);
|
| ︙ | ︙ | |||
1268 1269 1270 1271 1272 1273 1274 |
if( !g.perm.Hyperlink || !g.perm.RdTkt ){
login_needed(g.anon.Hyperlink && g.anon.RdTkt);
return;
}
zUuid = PD("name","");
zTitle = mprintf("History Of Ticket %h", zUuid);
style_submenu_element("Status", "%R/info/%s", zUuid);
| > | > | 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 |
if( !g.perm.Hyperlink || !g.perm.RdTkt ){
login_needed(g.anon.Hyperlink && g.anon.RdTkt);
return;
}
zUuid = PD("name","");
zTitle = mprintf("History Of Ticket %h", zUuid);
style_submenu_element("Status", "%R/info/%s", zUuid);
if( g.perm.Read ){
style_submenu_element("Check-ins", "%R/tkttimeline/%s?y=ci", zUuid);
}
style_submenu_element("Timeline", "%R/tkttimeline/%s", zUuid);
if( P("raw")!=0 ){
style_submenu_element("Decoded", "%R/tkthistory/%s", zUuid);
}else if( g.perm.Admin ){
style_submenu_element("Raw", "%R/tkthistory/%s?raw", zUuid);
}
style_set_current_feature("tkt");
|
| ︙ | ︙ |
Changes to src/tktsetup.c.
| ︙ | ︙ | |||
154 155 156 157 158 159 160 |
}
}
@ <form action="%R/%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <blockquote><p>
| | | | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
}
}
@ <form action="%R/%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <blockquote><p>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ <input type="submit" name="setup" value="Cancel">
@ </p></blockquote>
@ </div></form>
@ <hr>
@ <h2>Default %s(zTitle)</h2>
@ <blockquote><pre>
@ %h(zDfltValue)
@ </pre></blockquote>
style_finish_page();
}
|
| ︙ | ︙ | |||
322 323 324 325 326 327 328 | @ set preview 1 @ } @ </th1> @ <h1 style="text-align: center;">Enter A New Ticket</h1> @ <table cellpadding="5"> @ <tr> @ <td colspan="3"> | | | | | < | | | | | | | | 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 |
@ set preview 1
@ }
@ </th1>
@ <h1 style="text-align: center;">Enter A New Ticket</h1>
@ <table cellpadding="5">
@ <tr>
@ <td colspan="3">
@ Enter a one-line summary of the ticket:<br>
@ <input type="text" name="title" size="60" value="$<title>">
@ </td>
@ </tr>
@
@ <tr>
@ <td align="right">Type:</td>
@ <td align="left"><th1>combobox type $type_choices 1</th1></td>
@ <td align="left">What type of ticket is this?</td>
@ </tr>
@
@ <tr>
@ <td align="right">Version:</td>
@ <td align="left">
@ <input type="text" name="foundin" size="20" value="$<foundin>">
@ </td>
@ <td align="left">In what version or build number do you observe
@ the problem?</td>
@ </tr>
@
@ <tr>
@ <td align="right">Severity:</td>
@ <td align="left"><th1>combobox severity $severity_choices 1</th1></td>
@ <td align="left">How debilitating is the problem? How badly does the problem
@ affect the operation of the product?</td>
@ </tr>
@
@ <tr>
@ <td align="right">EMail:</td>
@ <td align="left">
@ <input name="private_contact" value="$<private_contact>" size="30">
@ </td>
@ <td align="left"><u>Not publicly visible</u>
@ Used by developers to contact you with questions.</td>
@ </tr>
@
@ <tr>
@ <td colspan="3">
@ Enter a detailed description of the problem.
@ For code defects, be sure to provide details on exactly how
@ the problem can be reproduced. Provide as much detail as
@ possible. Format:
@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1>
@ <br>
@ <th1>set nline [linecount $comment 50 10]</th1>
@ <textarea name="icomment" cols="80" rows="$nline"
@ wrap="virtual" class="wikiedit">$<icomment></textarea><br>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="3">
@ Description Preview:<br><hr>
@ <th1>
@ if {$mutype eq "Wiki"} {
@ wiki $icomment
@ } elseif {$mutype eq "Plain Text"} {
@ set r [randhex]
@ wiki "<verbatim-$r>[string trimright $icomment]\n</verbatim-$r>"
@ } elseif {$mutype eq "Markdown"} {
@ html [lindex [markdown "$icomment\n"] 1]
@ } elseif {$mutype eq {[links only]}} {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $icomment]\n</verbatim-$r>"
@ } else {
@ wiki "<nowiki>$icomment\n</nowiki>"
@ }
@ </th1>
@ <hr></td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td><td align="left">
@ <input type="submit" name="preview" value="Preview">
@ </td>
@ <td align="left">See how the description will appear after formatting.</td>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr>
@ <td><td align="left">
@ <input type="submit" name="submit" value="Submit">
@ </td>
@ <td align="left">After filling in the information above, press this
@ button to create the new ticket</td>
@ </tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td><td align="left">
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon and forget this ticket</td>
@ </tr>
@ </table>
;
/*
|
| ︙ | ︙ | |||
530 531 532 533 534 535 536 |
@ set alwaysPlaintext [info exists plaintext]
@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
| | | 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
@ set alwaysPlaintext [info exists plaintext]
@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
@ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n"
@ html "<tr><td colspan='5' class='tktDspValue'>\n"
@ set seenRow 1
@ }
@ html "<span class='tktDspCommenter'>"
@ html "[htmlize $xlogin]"
|
| ︙ | ︙ | |||
614 615 616 617 618 619 620 | @ } @ submit_ticket @ set preview 1 @ } @ </th1> @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Title:</td><td> | | | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 | @ } @ submit_ticket @ set preview 1 @ } @ </th1> @ <table cellpadding="5"> @ <tr><td class="tktDspLabel">Title:</td><td> @ <input type="text" name="title" value="$<title>" size="60"> @ </td></tr> @ @ <tr><td class="tktDspLabel">Status:</td><td> @ <th1>combobox status $status_choices 1</th1> @ </td></tr> @ @ <tr><td class="tktDspLabel">Type:</td><td> |
| ︙ | ︙ | |||
644 645 646 647 648 649 650 | @ <tr><td class="tktDspLabel">Subsystem:</td><td> @ <th1>combobox subsystem $subsystem_choices 1</th1> @ </td></tr> @ @ <th1>enable_output [hascap e]</th1> @ <tr><td class="tktDspLabel">Contact:</td><td> @ <input type="text" name="private_contact" size="40" | | | | | | | | | | 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 |
@ <tr><td class="tktDspLabel">Subsystem:</td><td>
@ <th1>combobox subsystem $subsystem_choices 1</th1>
@ </td></tr>
@
@ <th1>enable_output [hascap e]</th1>
@ <tr><td class="tktDspLabel">Contact:</td><td>
@ <input type="text" name="private_contact" size="40"
@ value="$<private_contact>">
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr><td class="tktDspLabel">Version Found In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>">
@ </td></tr>
@
@ <tr><td colspan="2">
@ Append Remark with format
@ <th1>combobox mutype {HTML {[links only]} Markdown {Plain Text} Wiki} 1</th1>
@ from
@ <input type="text" name="username" value="$<username>" size="30">:<br>
@ <textarea name="icomment" cols="80" rows="15"
@ wrap="virtual" class="wikiedit">$<icomment></textarea>
@ </td></tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br><hr>
@ <th1>
@ if {$mutype eq "Wiki"} {
@ wiki $icomment
@ } elseif {$mutype eq "Plain Text"} {
@ set r [randhex]
@ wiki "<verbatim-$r>\n[string trimright $icomment]\n</verbatim-$r>"
@ } elseif {$mutype eq "Markdown"} {
@ html [lindex [markdown "$icomment\n"] 1]
@ } elseif {$mutype eq {[links only]}} {
@ set r [randhex]
@ wiki "<verbatim-$r links>\n[string trimright $icomment]</verbatim-$r>"
@ } else {
@ wiki "<nowiki>\n[string trimright $icomment]\n</nowiki>"
@ }
@ </th1>
@ <hr>
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td align="right">
@ <input type="submit" name="preview" value="Preview">
@ </td>
@ <td align="left">See how the description will appear after formatting.</td>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr>
@ <td align="right">
@ <input type="submit" name="submit" value="Submit">
@ </td>
@ <td align="left">Apply the changes shown above</td>
@ </tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td align="right">
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon this edit</td>
@ </tr>
@
@ </table>
;
|
| ︙ | ︙ | |||
910 911 912 913 914 915 916 |
}
style_set_current_feature("tktsetup");
style_header("Ticket Display On Timelines");
db_begin_transaction();
@ <form action="%R/tktsetup_timeline" method="post"><div>
login_insert_csrf_secret();
| | | | | | | | 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 |
}
style_set_current_feature("tktsetup");
style_header("Ticket Display On Timelines");
db_begin_transaction();
@ <form action="%R/tktsetup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <hr>
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t",
"title", 0);
@ <p>An SQL expression in a query against the TICKET table that will
@ return the title of the ticket for display purposes.
@ (Property: ticket-title-expr)</p>
@ <hr>
entry_attribute("Ticket Status", 40, "ticket-status-column", "s",
"status", 0);
@ <p>The name of the column in the TICKET table that contains the ticket
@ status in human-readable form. Case sensitive.
@ (Property: ticket-status-column)</p>
@ <hr>
entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
"status='Closed'", 0);
@ <p>An SQL expression that evaluates to true in a TICKET table query if
@ the ticket is closed.
@ (Property: ticket-closed-expr)</p>
@ <hr>
@ <p>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="setup" value="Cancel">
@ </p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
Changes to src/unversioned.c.
| ︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 288 289 290 291 | ** --glob PATTERN Remove files that match ** --like PATTERN Remove files that match ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** ** touch FILE ... Update the TIMESTAMP on all of the listed files ** | > | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | ** --glob PATTERN Remove files that match ** --like PATTERN Remove files that match ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** The remote account requires the 'y' capability. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** ** touch FILE ... Update the TIMESTAMP on all of the listed files ** |
| ︙ | ︙ | |||
462 463 464 465 466 467 468 |
zNoContent
);
}
}
db_finalize(&q);
sqlite3_free(zPattern);
}else if( memcmp(zCmd, "revert", nCmd)==0 ){
| | | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
zNoContent
);
}
}
db_finalize(&q);
sqlite3_free(zPattern);
}else if( memcmp(zCmd, "revert", nCmd)==0 ){
unsigned syncFlags =
unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
g.argv[1] = "sync";
g.argv[2] = "--uv-noop";
sync_unversioned(syncFlags);
}else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
|| memcmp(zCmd, "delete", nCmd)==0 ){
int i;
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
650 651 652 653 654 655 656 |
/*
** Create empty directories specified by the empty-dirs setting.
*/
void ensure_empty_dirs_created(int clearDirTable){
char *zEmptyDirs = db_get("empty-dirs", 0);
if( zEmptyDirs!=0 ){
int i;
| | < < | < < < < | | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
/*
** Create empty directories specified by the empty-dirs setting.
*/
void ensure_empty_dirs_created(int clearDirTable){
char *zEmptyDirs = db_get("empty-dirs", 0);
if( zEmptyDirs!=0 ){
int i;
Glob *pGlob = glob_create(zEmptyDirs);
for(i=0; i<pGlob->nPattern; i++){
const char *zDir = pGlob->azPattern[i];
char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir);
switch( file_isdir(zPath, RepoFILE) ){
case 0: { /* doesn't exist */
fossil_free(zPath);
zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir);
if( file_mkfolder(zPath, RepoFILE, 0, 1)!=0 ) {
fossil_warning("couldn't create directory %s as "
|
| ︙ | ︙ | |||
686 687 688 689 690 691 692 |
}
case 2: { /* exists, but isn't a directory */
fossil_warning("file %s found, but a directory is required "
"by empty-dirs setting", zDir);
}
}
fossil_free(zPath);
| < < | | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 |
}
case 2: { /* exists, but isn't a directory */
fossil_warning("file %s found, but a directory is required "
"by empty-dirs setting", zDir);
}
}
fossil_free(zPath);
}
glob_free(pGlob);
}
}
/*
** Get the manifest record for a given revision, or the current check-out if
** zRevision is NULL.
*/
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
155 156 157 158 159 160 161 | unsigned char zB[30]; int nA = 25; int nB = 0; int i; memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1); memcpy(zA, zOrig, nA+1); assert( nA==(int)strlen((char*)zA) ); | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
unsigned char zB[30];
int nA = 25;
int nB = 0;
int i;
memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1);
memcpy(zA, zOrig, nA+1);
assert( nA==(int)strlen((char*)zA) );
for(i=0; i<(int)sizeof(aSubst); i++) aSubst[i] = i;
printFive(zA);
while( nA>0 ){
int x = randint(nA);
zB[nB++] = zA[x];
zA[x] = zA[--nA];
}
assert( nB==25 );
|
| ︙ | ︙ | |||
753 754 755 756 757 758 759 |
@ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr>
}
if( skip>0 || cnt>n ){
style_submenu_element("All", "%R/access_log?n=10000000");
}
@ </tbody></table>
db_finalize(&q);
| | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 |
@ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr>
}
if( skip>0 || cnt>n ){
style_submenu_element("All", "%R/access_log?n=10000000");
}
@ </tbody></table>
db_finalize(&q);
@ <hr>
@ <form method="post" action="%R/access_log">
@ <label><input type="checkbox" name="delold">
@ Delete all but the most recent 200 entries</input></label>
@ <input type="submit" name="deloldbtn" value="Delete"></input>
@ </form>
@ <form method="post" action="%R/access_log">
@ <label><input type="checkbox" name="delanon">
|
| ︙ | ︙ |
Changes to src/util.c.
| ︙ | ︙ | |||
678 679 680 681 682 683 684 |
&& ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){
zDir = zTempDirA;
}else{
zDir = fossil_getenv("LOCALAPPDATA");
if( zDir==0 ) zDir = ".";
}
#else
| | | | 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 |
&& ( zTempDirA = fossil_path_to_utf8(zTempDirW) )){
zDir = zTempDirA;
}else{
zDir = fossil_getenv("LOCALAPPDATA");
if( zDir==0 ) zDir = ".";
}
#else
for(i=0; i<(int)(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>=(int)(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
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
}
}
}
#ifndef _WIN32
if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){
/* Changing to a symlink takes priority over all other change types. */
chnged = 7;
}else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){
/* Confirm metadata change types. */
if( origPerm==currentPerm ){
chnged = 0;
}else if( currentPerm==PERM_EXE ){
chnged = 6;
}else if( origPerm==PERM_EXE ){
| > > > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
}
}
}
#ifndef _WIN32
if( origPerm!=PERM_LNK && currentPerm==PERM_LNK ){
/* Changing to a symlink takes priority over all other change types. */
chnged = 7;
}else if( origPerm==PERM_LNK && currentPerm!=PERM_LNK ){
/* Ditto, other direction */
chnged = 9;
}else if( chnged==0 || chnged==6 || chnged==7 || chnged==8 || chnged==9 ){
/* Confirm metadata change types. */
if( origPerm==currentPerm ){
chnged = 0;
}else if( currentPerm==PERM_EXE ){
chnged = 6;
}else if( origPerm==PERM_EXE ){
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
610 611 612 613 614 615 616 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
| | | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
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();
}
|
| ︙ | ︙ | |||
1534 1535 1536 1537 1538 1539 1540 |
style_set_current_feature("wiki");
style_header("Create A New Wiki Page");
wiki_standard_submenu(W_ALL_BUT(W_NEW));
@ <p>Rules for wiki page names:</p>
well_formed_wiki_name_rules();
form_begin(0, "%R/wikinew");
@ <p>Name of new wiki page:
| | | | 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 |
style_set_current_feature("wiki");
style_header("Create A New Wiki Page");
wiki_standard_submenu(W_ALL_BUT(W_NEW));
@ <p>Rules for wiki page names:</p>
well_formed_wiki_name_rules();
form_begin(0, "%R/wikinew");
@ <p>Name of new wiki page:
@ <input style="width: 35;" type="text" name="name" value="%h(zName)"><br>
@ %z(href("%R/markup_help"))Markup style</a>:
mimetype_option_menu("text/x-markdown", "mimetype");
@ <br><input type="submit" value="Create">
@ </p></form>
if( zName[0] ){
@ <p><span class="wikiError">
@ "%h(zName)" is not a valid wiki page name!</span></p>
}
style_finish_page();
}
|
| ︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 |
char *zId;
zDate = db_text(0, "SELECT datetime('now')");
zRemark = PD("r","");
zUser = PD("u",g.zLogin);
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
| | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 |
char *zId;
zDate = db_text(0, "SELECT datetime('now')");
zRemark = PD("r","");
zUser = PD("u",g.zLogin);
if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){
zId = db_text(0, "SELECT lower(hex(randomblob(8)))");
blob_appendf(p, "\n\n<hr><div id=\"%s\"><i>On %s UTC %h",
zId, zDate, login_name());
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
blob_appendf(p, " (claiming to be %h)", zUser);
}
blob_appendf(p, " added:</i><br>\n%s</div id=\"%s\">", zRemark, zId);
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
blob_appendf(p, "\n\n------\n*On %s UTC %h", zDate, login_name());
if( zUser[0] && fossil_strcmp(zUser,login_name()) ){
blob_appendf(p, " (claiming to be %h)", zUser);
}
blob_appendf(p, " added:*\n\n%s\n", zRemark);
}else{
|
| ︙ | ︙ | |||
1683 1684 1685 1686 1687 1688 1689 |
@ <p class="generalError">Error: the Sandbox page may not
@ be appended to.</p>
}
if( !isSandbox && P("preview")!=0 ){
Blob preview;
blob_zero(&preview);
appendRemark(&preview, zMimetype);
| | | | | | | | | | | | 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 |
@ <p class="generalError">Error: the Sandbox page may not
@ be appended to.</p>
}
if( !isSandbox && P("preview")!=0 ){
Blob preview;
blob_zero(&preview);
appendRemark(&preview, zMimetype);
@ Preview:<hr>
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&preview, zMimetype);
@ <hr>
blob_reset(&preview);
}
zUser = PD("u", g.zLogin);
form_begin(0, "%R/wikiappend");
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)">
@ <input type="hidden" name="mimetype" value="%h(zMimetype)">
@ Your Name:
@ <input type="text" name="u" size="20" value="%h(zUser)"><br>
zFormat = mimetype_common_name(zMimetype);
@ Comment to append (formatted as %s(zFormat)):<br>
@ <textarea name="r" class="wikiedit" cols="80"
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
@ <br>
@ <input type="submit" name="preview" value="Preview Your Comment">
@ <input type="submit" name="submit" value="Append Your Changes">
@ <input type="submit" name="cancel" value="Cancel">
captcha_generate(0);
@ </form>
manifest_destroy(pWiki);
style_finish_page();
}
/*
|
| ︙ | ︙ | |||
1748 1749 1750 1751 1752 1753 1754 |
" AND tag.tagname='wiki-%q'"
" AND tagxref.tagid=tag.tagid AND tagxref.srcid=event.objid"
" ORDER BY event.mtime DESC",
zPageName
);
@ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2>
form_begin( "id='wh-form'", "%R/wdiff" );
| | | | 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 |
" AND tag.tagname='wiki-%q'"
" AND tagxref.tagid=tag.tagid AND tagxref.srcid=event.objid"
" ORDER BY event.mtime DESC",
zPageName
);
@ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2>
form_begin( "id='wh-form'", "%R/wdiff" );
@ <input id="wh-pid" name="pid" type="radio" hidden>
@ <input id="wh-id" name="id" type="hidden">
@ </form>
@ <style> .wh-clickable { cursor: pointer; } </style>
@ <div class="brlist">
@ <table>
@ <thead><tr>
@ <th>Age</th>
@ <th>Hash</th>
|
| ︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 |
@ <tr class="wh-major" title="%s(zWhen)">
}
/* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */
@ <td>%s(zAge)</td>
fossil_free(zAge);
@ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td>
@ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td>
| | | 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 |
@ <tr class="wh-major" title="%s(zWhen)">
}
/* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */
@ <td>%s(zAge)</td>
fossil_free(zAge);
@ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td>
@ <td><input disabled type="radio" name="baseline" value="%S(zUuid)"/></td>
@ <td>%h(zUser)<span class="wh-iterations" hidden></td>
if( showRid ){
@ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td>
}
@ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td>
@ </tr>
}
@ </tbody></table></div>
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | MARKUP_CENTER, MARKUP_CITE, MARKUP_CODE, MARKUP_COL, MARKUP_COLGROUP, MARKUP_DD, MARKUP_DEL, MARKUP_DFN, MARKUP_DIV, MARKUP_DL, MARKUP_DT, MARKUP_EM, MARKUP_FONT, MARKUP_HTML5_FOOTER, | > | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | MARKUP_CENTER, MARKUP_CITE, MARKUP_CODE, MARKUP_COL, MARKUP_COLGROUP, MARKUP_DD, MARKUP_DEL, MARKUP_DETAILS, MARKUP_DFN, MARKUP_DIV, MARKUP_DL, MARKUP_DT, MARKUP_EM, MARKUP_FONT, MARKUP_HTML5_FOOTER, |
| ︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 | MARKUP_SAMP, MARKUP_HTML5_SECTION, MARKUP_SMALL, MARKUP_SPAN, MARKUP_STRIKE, MARKUP_STRONG, MARKUP_SUB, MARKUP_SUP, MARKUP_TABLE, MARKUP_TBODY, MARKUP_TD, MARKUP_TFOOT, MARKUP_TH, MARKUP_THEAD, | > | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | MARKUP_SAMP, MARKUP_HTML5_SECTION, MARKUP_SMALL, MARKUP_SPAN, MARKUP_STRIKE, MARKUP_STRONG, MARKUP_SUB, MARKUP_SUMMARY, MARKUP_SUP, MARKUP_TABLE, MARKUP_TBODY, MARKUP_TD, MARKUP_TFOOT, MARKUP_TH, MARKUP_THEAD, |
| ︙ | ︙ | |||
284 285 286 287 288 289 290 |
{ "a", MARKUP_A, MUTYPE_HYPERLINK,
AMSK_HREF|AMSK_NAME|AMSK_CLASS|AMSK_TARGET|AMSK_STYLE|
AMSK_TITLE},
{ "abbr", MARKUP_ABBR, MUTYPE_FONT,
AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE },
{ "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE },
{ "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK,
| | | < > > | < < | < | | > > | 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 |
{ "a", MARKUP_A, MUTYPE_HYPERLINK,
AMSK_HREF|AMSK_NAME|AMSK_CLASS|AMSK_TARGET|AMSK_STYLE|
AMSK_TITLE},
{ "abbr", MARKUP_ABBR, MUTYPE_FONT,
AMSK_ID|AMSK_CLASS|AMSK_STYLE|AMSK_TITLE },
{ "address", MARKUP_ADDRESS, MUTYPE_BLOCK, AMSK_STYLE },
{ "article", MARKUP_HTML5_ARTICLE, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "aside", MARKUP_HTML5_ASIDE, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "b", MARKUP_B, MUTYPE_FONT, AMSK_STYLE },
{ "big", MARKUP_BIG, MUTYPE_FONT, AMSK_STYLE },
{ "blockquote", MARKUP_BLOCKQUOTE, MUTYPE_BLOCK, AMSK_STYLE },
{ "br", MARKUP_BR, MUTYPE_SINGLE, AMSK_CLEAR },
{ "center", MARKUP_CENTER, MUTYPE_BLOCK, AMSK_STYLE },
{ "cite", MARKUP_CITE, MUTYPE_FONT, AMSK_STYLE },
{ "code", MARKUP_CODE, MUTYPE_FONT, AMSK_STYLE },
{ "col", MARKUP_COL, MUTYPE_SINGLE,
AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE },
{ "colgroup", MARKUP_COLGROUP, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_COLSPAN|AMSK_WIDTH|AMSK_STYLE},
{ "dd", MARKUP_DD, MUTYPE_LI, AMSK_STYLE },
{ "del", MARKUP_DEL, MUTYPE_FONT, AMSK_STYLE },
{ "details", MARKUP_DETAILS, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "dfn", MARKUP_DFN, MUTYPE_FONT, AMSK_STYLE },
{ "div", MARKUP_DIV, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "dl", MARKUP_DL, MUTYPE_LIST,
AMSK_COMPACT|AMSK_STYLE },
{ "dt", MARKUP_DT, MUTYPE_LI, AMSK_STYLE },
{ "em", MARKUP_EM, MUTYPE_FONT, AMSK_STYLE },
{ "font", MARKUP_FONT, MUTYPE_FONT,
AMSK_COLOR|AMSK_FACE|AMSK_SIZE|AMSK_STYLE },
{ "footer", MARKUP_HTML5_FOOTER, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "h1", MARKUP_H1, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "h2", MARKUP_H2, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "h3", MARKUP_H3, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "h4", MARKUP_H4, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "h5", MARKUP_H5, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "h6", MARKUP_H6, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "header", MARKUP_HTML5_HEADER, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "hr", MARKUP_HR, MUTYPE_SINGLE,
AMSK_ALIGN|AMSK_COLOR|AMSK_SIZE|AMSK_WIDTH|
AMSK_STYLE|AMSK_CLASS },
{ "i", MARKUP_I, MUTYPE_FONT, AMSK_STYLE },
{ "img", MARKUP_IMG, MUTYPE_SINGLE,
AMSK_ALIGN|AMSK_ALT|AMSK_BORDER|AMSK_HEIGHT|
AMSK_HSPACE|AMSK_SRC|AMSK_VSPACE|AMSK_WIDTH|AMSK_STYLE },
{ "ins", MARKUP_INS, MUTYPE_FONT, AMSK_STYLE },
{ "kbd", MARKUP_KBD, MUTYPE_FONT, AMSK_STYLE },
{ "li", MARKUP_LI, MUTYPE_LI,
AMSK_TYPE|AMSK_VALUE|AMSK_STYLE },
{ "nav", MARKUP_HTML5_NAV, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "nobr", MARKUP_NOBR, MUTYPE_FONT, 0 },
{ "nowiki", MARKUP_NOWIKI, MUTYPE_SPECIAL, 0 },
{ "ol", MARKUP_OL, MUTYPE_LIST,
AMSK_START|AMSK_TYPE|AMSK_COMPACT|AMSK_STYLE },
{ "p", MARKUP_P, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "pre", MARKUP_PRE, MUTYPE_BLOCK, AMSK_STYLE },
{ "s", MARKUP_S, MUTYPE_FONT, AMSK_STYLE },
{ "samp", MARKUP_SAMP, MUTYPE_FONT, AMSK_STYLE },
{ "section", MARKUP_HTML5_SECTION, MUTYPE_BLOCK,
AMSK_ID|AMSK_CLASS|AMSK_STYLE },
{ "small", MARKUP_SMALL, MUTYPE_FONT, AMSK_STYLE },
{ "span", MARKUP_SPAN, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "strike", MARKUP_STRIKE, MUTYPE_FONT, AMSK_STYLE },
{ "strong", MARKUP_STRONG, MUTYPE_FONT, AMSK_STYLE },
{ "sub", MARKUP_SUB, MUTYPE_FONT, AMSK_STYLE },
{ "summary", MARKUP_SUMMARY, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
{ "sup", MARKUP_SUP, MUTYPE_FONT, AMSK_STYLE },
{ "table", MARKUP_TABLE, MUTYPE_TABLE,
AMSK_ALIGN|AMSK_BGCOLOR|AMSK_BORDER|AMSK_CELLPADDING|
AMSK_CELLSPACING|AMSK_HSPACE|AMSK_VSPACE|AMSK_CLASS|
AMSK_STYLE },
{ "tbody", MARKUP_TBODY, MUTYPE_BLOCK,
AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE },
|
| ︙ | ︙ | |||
788 789 790 791 792 793 794 |
i = 2;
}else{
p->endTag = 0;
i = 1;
}
j = 0;
while( fossil_isalnum(z[i]) ){
| | | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 |
i = 2;
}else{
p->endTag = 0;
i = 1;
}
j = 0;
while( fossil_isalnum(z[i]) ){
if( j<(int)sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
i++;
}
zTag[j] = 0;
p->iCode = findTag(zTag);
p->iType = aMarkup[p->iCode].iType;
p->nAttr = 0;
c = 0;
|
| ︙ | ︙ | |||
811 812 813 814 815 816 817 |
if( c=='>' ) return 0;
}
while( fossil_isspace(z[i]) ){ i++; }
while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){
int attrOk; /* True to preserve attribute. False to ignore it */
j = 0;
while( fossil_isalnum(z[i]) ){
| | | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
if( c=='>' ) return 0;
}
while( fossil_isspace(z[i]) ){ i++; }
while( c!='>' && p->nAttr<8 && fossil_isalpha(z[i]) ){
int attrOk; /* True to preserve attribute. False to ignore it */
j = 0;
while( fossil_isalnum(z[i]) ){
if( j<(int)sizeof(zTag)-1 ) zTag[j++] = fossil_tolower(z[i]);
i++;
}
zTag[j] = 0;
p->aAttr[p->nAttr].iACode = iACode = findAttr(zTag);
attrOk = iACode!=0 && (seen & aAttribute[iACode].iMask)==0;
while( fossil_isspace(z[i]) ){ z++; }
if( z[i]!='=' ){
|
| ︙ | ︙ | |||
1103 1104 1105 1106 1107 1108 1109 |
int n;
char zU2[HNAME_MAX+1];
db_static_prepare(&q,
"SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2"
);
db_bind_text(&q, ":u", zUuid);
n = (int)strlen(zUuid);
| | | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 |
int n;
char zU2[HNAME_MAX+1];
db_static_prepare(&q,
"SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2"
);
db_bind_text(&q, ":u", zUuid);
n = (int)strlen(zUuid);
if( n>=(int)sizeof(zU2) ) n = sizeof(zU2)-1;
memcpy(zU2, zUuid, n);
zU2[n-1]++;
zU2[n] = 0;
db_bind_text(&q, ":u2", zU2);
rc = db_step(&q);
db_reset(&q);
return rc==SQLITE_ROW;
|
| ︙ | ︙ | |||
1356 1357 1358 1359 1360 1361 1362 |
/* Also ignore the link if various flags are set */
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
zTerm = "</span>";
}
if( zExtra ) fossil_free(zExtra);
| | | 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 |
/* Also ignore the link if various flags are set */
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
zTerm = "</span>";
}
if( zExtra ) fossil_free(zExtra);
assert( (int)strlen(zTerm)<nClose );
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
}
/*
** Check to see if the given parsed markup is the correct
** </verbatim> tag.
*/
|
| ︙ | ︙ |
Changes to src/winhttp.c.
| ︙ | ︙ | |||
568 569 570 571 572 573 574 575 576 577 578 579 580 581 | HANDLE hStoppedEvent; WSADATA wd; DualSocket ds; int idCnt = 0; int iPort = mnPort; Blob options; wchar_t zTmpPath[MAX_PATH]; const char *zSkin; #if USE_SEE const char *zSavedKey = 0; size_t savedKeySize = 0; #endif blob_zero(&options); | > > | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | HANDLE hStoppedEvent; WSADATA wd; DualSocket ds; int idCnt = 0; int iPort = mnPort; Blob options; wchar_t zTmpPath[MAX_PATH]; char *zTempSubDirPath; const char *zTempSubDir = "fossil"; const char *zSkin; #if USE_SEE const char *zSavedKey = 0; size_t savedKeySize = 0; #endif blob_zero(&options); |
| ︙ | ︙ | |||
621 622 623 624 625 626 627 |
if( builtin_get_js_delivery_mode()!=0 /* JS_INLINE==0 may change? */ ){
blob_appendf(&options, " --jsmode ");
blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
}
#if USE_SEE
zSavedKey = db_get_saved_encryption_key();
savedKeySize = db_get_saved_encryption_key_size();
| | | 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
if( builtin_get_js_delivery_mode()!=0 /* JS_INLINE==0 may change? */ ){
blob_appendf(&options, " --jsmode ");
blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
}
#if USE_SEE
zSavedKey = db_get_saved_encryption_key();
savedKeySize = db_get_saved_encryption_key_size();
if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){
blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
zSavedKey, savedKeySize);
}
#endif
if( WSAStartup(MAKEWORD(2,0), &wd) ){
fossil_panic("unable to initialize winsock");
}
|
| ︙ | ︙ | |||
659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
fossil_fatal("unable to open listening socket on any"
" port in the range %d..%d", mnPort, mxPort);
}
}
if( !GetTempPathW(MAX_PATH, zTmpPath) ){
fossil_panic("unable to get path to the temporary directory.");
}
if( g.fHttpTrace ){
zTempPrefix = mprintf("httptrace");
}else{
zTempPrefix = mprintf("%sfossil_server_P%d",
fossil_unicode_to_utf8(zTmpPath), iPort);
}
fossil_print("Temporary files: %s*\n", zTempPrefix);
| > > > > > > | 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
fossil_fatal("unable to open listening socket on any"
" port in the range %d..%d", mnPort, mxPort);
}
}
if( !GetTempPathW(MAX_PATH, zTmpPath) ){
fossil_panic("unable to get path to the temporary directory.");
}
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
}
if( g.fHttpTrace ){
zTempPrefix = mprintf("httptrace");
}else{
zTempPrefix = mprintf("%sfossil_server_P%d",
fossil_unicode_to_utf8(zTmpPath), iPort);
}
fossil_print("Temporary files: %s*\n", zTempPrefix);
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
352 353 354 355 356 357 358 |
goto end_accept_unversioned_file;
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
| > > | > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
goto end_accept_unversioned_file;
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
if( !isWriter ){
blob_appendf(&pXfer->err, "Write permissions for unversioned files missing");
goto end_accept_unversioned_file;
}
/* Make sure we have a valid g.rcvid marker */
content_rcvid_init(0);
/* Check to see if current content really should be overwritten. Ideally,
** a uvfile card should never have been sent unless the overwrite should
** occur. But do not trust the sender. Double-check.
|
| ︙ | ︙ | |||
448 449 450 451 452 453 454 |
if( srcId>0
&& (pXfer->syncPrivate || !content_is_private(srcId))
&& content_get(srcId, &src)
){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
blob_delta_create(&src, pContent, &delta);
size = blob_size(&delta);
| | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
if( srcId>0
&& (pXfer->syncPrivate || !content_is_private(srcId))
&& content_get(srcId, &src)
){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
blob_delta_create(&src, pContent, &delta);
size = blob_size(&delta);
if( size>=(int)blob_size(pContent)-50 ){
size = 0;
}else if( uuid_is_shunned(zUuid) ){
size = 0;
}else{
if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
blob_append(pXfer->pOut, blob_buffer(&delta), size);
|
| ︙ | ︙ | |||
575 576 577 578 579 580 581 |
pUuid = &uuid;
}
if( uuid_is_shunned(blob_str(pUuid)) ){
blob_reset(&uuid);
return;
}
if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
| | | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 |
pUuid = &uuid;
}
if( uuid_is_shunned(blob_str(pUuid)) ){
blob_reset(&uuid);
return;
}
if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
pXfer->mxSend<=(int)blob_size(pXfer->pOut) ){
const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid);
pXfer->nIGotSent++;
blob_reset(&uuid);
return;
}
if( nativeDelta ){
|
| ︙ | ︙ | |||
699 700 701 702 703 704 705 |
static void send_unversioned_file(
Xfer *pXfer, /* Transfer context */
const char *zName, /* Name of unversioned file to be sent */
int noContent /* True to omit the content */
){
Stmt q1;
| | | | 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 |
static void send_unversioned_file(
Xfer *pXfer, /* Transfer context */
const char *zName, /* Name of unversioned file to be sent */
int noContent /* True to omit the content */
){
Stmt q1;
if( (int)blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1;
if( noContent ){
db_prepare(&q1,
"SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q",
zName
);
}else{
db_prepare(&q1,
"SELECT mtime, hash, encoding, sz, content FROM unversioned"
" WHERE name=%Q",
zName
);
}
if( db_step(&q1)==SQLITE_ROW ){
sqlite3_int64 mtime = db_column_int64(&q1, 0);
const char *zHash = db_column_text(&q1, 1);
if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
xfer_cannot_send_sha3_error(pXfer);
db_reset(&q1);
return;
}
if( (int)blob_size(pXfer->pOut)>=pXfer->mxSend ){
/* If we have already reached the send size limit, send a (short)
** uvigot card rather than a uvfile card. This only happens on the
** server side. The uvigot card will provoke the client to resend
** another uvgimme on the next cycle. */
blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
zName, mtime, zHash, db_column_int(&q1,3));
}else{
|
| ︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 |
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
zExtra /*safe-for-%s*/
);
}
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
| | | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 |
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
zExtra /*safe-for-%s*/
);
}
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
if( pXfer->resync && pXfer->mxSend<(int)blob_size(pXfer->pOut) ){
pXfer->resync = db_column_int(&q, 1)-1;
}
}
db_finalize(&q);
if( cnt==0 ) pXfer->resync = 0;
return cnt;
}
|
| ︙ | ︙ | |||
1061 1062 1063 1064 1065 1066 1067 |
** pXfer is a "pragma uv-hash HASH" card.
**
** If HASH is different from the unversioned content hash on this server,
** then send a bunch of uvigot cards, one for each entry unversioned file
** on this server.
*/
static void send_unversioned_catalog(Xfer *pXfer){
| < < | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 |
** pXfer is a "pragma uv-hash HASH" card.
**
** If HASH is different from the unversioned content hash on this server,
** then send a bunch of uvigot cards, one for each entry unversioned file
** on this server.
*/
static void send_unversioned_catalog(Xfer *pXfer){
Stmt uvq;
unversioned_schema();
db_prepare(&uvq,
"SELECT name, mtime, hash, sz FROM unversioned"
);
while( db_step(&uvq)==SQLITE_ROW ){
const char *zName = db_column_text(&uvq,0);
sqlite3_int64 mtime = db_column_int64(&uvq,1);
const char *zHash = db_column_text(&uvq,2);
int sz = db_column_int(&uvq,3);
if( zHash==0 ){ sz = 0; zHash = "-"; }
blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
zName, mtime, zHash, sz);
}
db_finalize(&uvq);
}
|
| ︙ | ︙ | |||
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 |
** Server accepts an unversioned file from the client.
*/
if( blob_eq(&xfer.aToken[0], "uvfile") ){
xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
if( blob_size(&xfer.err) ){
cgi_reset_content();
@ error %T(blob_str(&xfer.err))
nErr++;
break;
}
}else
/* gimme HASH
**
| > | 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 |
** Server accepts an unversioned file from the client.
*/
if( blob_eq(&xfer.aToken[0], "uvfile") ){
xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
if( blob_size(&xfer.err) ){
cgi_reset_content();
@ error %T(blob_str(&xfer.err))
fossil_print("%%%%%%%% xfer.err: '%s'\n", blob_str(&xfer.err));
nErr++;
break;
}
}else
/* gimme HASH
**
|
| ︙ | ︙ | |||
1457 1458 1459 1460 1461 1462 1463 |
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
max = db_int(0, "SELECT max(rid) FROM blob");
| | | 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 |
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
max = db_int(0, "SELECT max(rid) FROM blob");
while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
if( time(NULL) >= xfer.maxTime ) break;
if( iVers>=3 ){
send_compressed_file(&xfer, seqno);
}else{
send_file(&xfer, seqno, 0, 1);
}
seqno++;
|
| ︙ | ︙ | |||
1838 1839 1840 1841 1842 1843 1844 |
db_finalize(&q);
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
| | | 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 |
db_finalize(&q);
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
@ # timestamp %s(zNow) errors %d(nErr)
free(zNow);
db_commit_transaction();
configure_rebuild();
}
/*
|
| ︙ | ︙ | |||
2228 2229 2230 2231 2232 2233 2234 |
send_unversioned_file(&xfer, zName, db_column_int(&uvq,1));
nCardSent++;
nArtifactSent++;
db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
if( syncFlags & SYNC_VERBOSE ){
fossil_print("\rUnversioned-file sent: %s\n", zName);
}
| | | 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 |
send_unversioned_file(&xfer, zName, db_column_int(&uvq,1));
nCardSent++;
nArtifactSent++;
db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
if( syncFlags & SYNC_VERBOSE ){
fossil_print("\rUnversioned-file sent: %s\n", zName);
}
if( (int)blob_size(xfer.pOut)>xfer.mxSend ) break;
}
db_finalize(&uvq);
if( rc==SQLITE_DONE ) uvDoPush = 0;
}
}
/* Lock the current check-out */
|
| ︙ | ︙ | |||
2853 2854 2855 2856 2857 2858 2859 |
fossil_warning("*** time skew *** server is slow by %s",
db_timespan_name(-rSkew));
g.clockSkewSeen = 1;
}
fossil_force_newline();
if( g.zHttpCmd==0 ){
| > | > > > > > > > > | | > | 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 |
fossil_warning("*** time skew *** server is slow by %s",
db_timespan_name(-rSkew));
g.clockSkewSeen = 1;
}
fossil_force_newline();
if( g.zHttpCmd==0 ){
if( syncFlags & SYNC_VERBOSE ){
fossil_print(
"%s done, wire bytes sent: %lld received: %lld remote: %s%s\n",
zOpType, nSent, nRcvd,
(g.url.name && g.url.name[0]!='\0') ? g.url.name : "",
(g.zIpAddr && g.zIpAddr[0]!='\0'
&& fossil_strcmp(g.zIpAddr, g.url.name))
? mprintf(" (%s)", g.zIpAddr) : "");
}else{
fossil_print(
"%s done, wire bytes sent: %lld received: %lld remote: %s\n",
zOpType, nSent, nRcvd, g.zIpAddr);
}
}
if( syncFlags & SYNC_VERBOSE ){
fossil_print(
"Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd);
}
transport_close(&g.url);
transport_global_shutdown(&g.url);
|
| ︙ | ︙ |
Changes to src/xfersetup.c.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
}else{
syncFlags = SYNC_PUSH | SYNC_PULL;
zButton = "Synchronize";
zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.",
g.url.canonical);
}
@ <p>Press the <strong>%h(zButton)</strong> button below to
| | | | 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 |
}else{
syncFlags = SYNC_PUSH | SYNC_PULL;
zButton = "Synchronize";
zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.",
g.url.canonical);
}
@ <p>Press the <strong>%h(zButton)</strong> button below to
@ synchronize with the <em>%h(g.url.canonical)</em> repository now.<br>
@ This may be useful when testing the various transfer scripts.</p>
@ <p>You can use the <code>http -async</code> command in your scripts, but
@ make sure the <code>th1-uri-regexp</code> setting is set first.</p>
if( zWarning ){
@
@ <big><b>%h(zWarning)</b></big>
free(zWarning);
}
@
@ <form method="post" action="%R/%s(g.zPath)"><div>
login_insert_csrf_secret();
@ <input type="submit" name="sync" value="%h(zButton)">
@ </div></form>
@
if( P("sync") ){
user_select();
url_enable_proxy(0);
@ <pre class="xfersetup">
client_sync(syncFlags, 0, 0, 0);
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 |
}
}
@ <form action="%R/%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <p>
| | | | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
}
}
@ <form action="%R/%s(g.zPath)" method="post"><div>
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <p>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ <input type="submit" name="setup" value="Cancel">
@ </p>
@ </div></form>
if ( zDfltValue ){
@ <hr>
@ <h2>Default %s(zTitle)</h2>
@ <blockquote><pre>
@ %h(zDfltValue)
@ </pre></blockquote>
}
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
sqlite3_vfs vfs; /* VFS object */
};
/*
** Ensure that blob pBlob is at least nMin bytes in size.
*/
static void zip_blob_minsize(Blob *pBlob, int nMin){
| | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
sqlite3_vfs vfs; /* VFS object */
};
/*
** Ensure that blob pBlob is at least nMin bytes in size.
*/
static void zip_blob_minsize(Blob *pBlob, int nMin){
if( (int)blob_size(pBlob)<nMin ){
blob_resize(pBlob, nMin);
}
}
/*************************************************************************
** Implementation of "archive" VFS. A VFS designed to store the contents
** of a new database in a Blob. Used to construct sqlar archives in
|
| ︙ | ︙ | |||
439 440 441 442 443 444 445 |
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
sqlite3_bind_text(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
| | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
sqlite3_bind_text(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
unsigned int nIn = blob_size(pFile);
unsigned long int nOut = nIn;
sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
sqlite3_bind_int(p->pInsert, 4, nIn);
zip_blob_minsize(&p->tmp, nIn);
compress( (unsigned char*)
blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
);
if( nOut>=(unsigned long)nIn ){
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(&p->tmp), nOut, SQLITE_STATIC
);
|
| ︙ | ︙ | |||
862 863 864 865 866 867 868 | archive_cmd(ARCHIVE_SQLAR); } /* ** WEBPAGE: sqlar ** WEBPAGE: zip ** | > > > > > | | > < < > | | | | | | | | > > > | | > | | | | > | | 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 | archive_cmd(ARCHIVE_SQLAR); } /* ** WEBPAGE: sqlar ** WEBPAGE: zip ** ** URLs: ** ** /zip/[VERSION/]NAME.zip ** /sqlar/[VERSION/]NAME.sqlar ** ** Generate a ZIP Archive or an SQL Archive for the check-in specified by ** VERSION. The archive is called NAME.zip or NAME.sqlar and has a top-level ** directory called NAME. ** ** The optional VERSION element defaults to "trunk" per the r= rules below. ** All of the following URLs are equivalent: ** ** /zip/release/xyz.zip ** /zip?r=release&name=xyz.zip ** /zip/xyz.zip?r=release ** /zip?name=release/xyz.zip ** ** Query parameters: ** ** name=[CKIN/]NAME The optional CKIN component of the name= parameter ** identifies the check-in from which the archive is ** constructed. If CKIN is omitted and there is no ** r= query parameter, then use "trunk". NAME is the ** name of the download file. The top-level directory ** in the generated archive is called by NAME with the ** file extension removed. ** ** r=TAG TAG identifies the check-in that is turned into an ** SQL or ZIP archive. The default value is "trunk". ** If r= is omitted and if the name= query parameter ** contains one "/" character then the of part the ** name= value before the / becomes the TAG and the ** part of the name= value after the / is the download ** filename. If no check-in is specified by either ** name= or r=, then "trunk" is used. ** ** in=PATTERN Only include files that match the comma-separate ** list of GLOB patterns in PATTERN, as with ex= ** ** ex=PATTERN Omit any file that match PATTERN. PATTERN is a ** comma-separated list of GLOB patterns, where each ** pattern can optionally be quoted using ".." or '..'. |
| ︙ | ︙ | |||
979 980 981 982 983 984 985 |
if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
zKey = blob_str(&cacheKey);
etag_check(ETAG_HASH, zKey);
style_set_current_feature("zip");
if( P("debug")!=0 ){
style_header("%s Archive Generator Debug Screen", zType);
| | | | | | | 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 |
if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
zKey = blob_str(&cacheKey);
etag_check(ETAG_HASH, zKey);
style_set_current_feature("zip");
if( P("debug")!=0 ){
style_header("%s Archive Generator Debug Screen", zType);
@ zName = "%h(zName)"<br>
@ rid = %d(rid)<br>
if( zInclude ){
@ zInclude = "%h(zInclude)"<br>
}
if( zExclude ){
@ zExclude = "%h(zExclude)"<br>
}
@ zKey = "%h(zKey)"
style_finish_page();
return;
}
if( referred_from_login() ){
style_header("%s Archive Download", zType);
@ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'>
cgi_query_parameters_to_hidden();
@ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b>
@ holding the content of check-in <b>%h(zRid)</b>:
@ <input type="submit" value="Download">
@ </form>
style_finish_page();
return;
}
blob_zero(&zip);
if( cache_read(&zip, zKey)==0 ){
zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude, 0);
|
| ︙ | ︙ |
Changes to tools/makemake.tcl.
| ︙ | ︙ | |||
554 555 556 557 558 559 560 | writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" | | | | | | | | | | 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 |
writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
-sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>>
-sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>>
-sENVIRONMENT=web <<<NEXT_LINE>>>
-sMODULARIZE <<<NEXT_LINE>>>
-sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>>
--minify 0
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js
#
# The list of all the targets that do not correspond to real files. This stops
# 'make' from getting confused when someone makes an error in a rule.
#
.PHONY: all install test clean
}]
close $output_file
#
# End of the main.mk output
##############################################################################
##############################################################################
##############################################################################
|
| ︙ | ︙ | |||
2047 2048 2049 2050 2051 2052 2053 2054 2055 |
writeln {"$(OX)\builtin_data.reslist": $(EXTRA_FILES) "$(B)\win\Makefile.msc"}
set redir {>}
foreach s [lsort $extra_files] {
writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@"
set redir {>>}
}
writeln ""
foreach s [lsort $src] {
| > > > > > > | | 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 |
writeln {"$(OX)\builtin_data.reslist": $(EXTRA_FILES) "$(B)\win\Makefile.msc"}
set redir {>}
foreach s [lsort $extra_files] {
writeln "\techo \"\$(SRCDIR)\\${s}\" $redir \$@"
set redir {>>}
}
foreach s [lsort $src] {
set extra_h($s) {}
}
set extra_h(builtin) " \"\$(OX)\\builtin_data.h\""
set extra_h(dispatch) " \"\$(OX)\\page_index.h\""
writeln ""
foreach s [lsort $src] {
writeln "\"\$(OX)\\$s\$O\" : \"\$(OX)\\${s}_.c\" \"\$(OX)\\${s}.h\"$extra_h($s)"
writeln "\t\$(TCC) /Fo\$@ /Fd\$(@D)\\ -c \"\$(OX)\\${s}_.c\"\n"
writeln "\"\$(OX)\\${s}_.c\" : \"\$(SRCDIR)\\$s.c\""
writeln "\t\"\$(OBJDIR)\\translate\$E\" \$** > \$@\n"
}
writeln "\"\$(OX)\\fossil.res\" : \"\$(B)\\win\\fossil.rc\""
writeln "\t\$(RCC) /fo \$@ \$**\n"
|
| ︙ | ︙ |
Changes to tools/mkindex.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 68 69 70 | ** SETTING: auto-shun boolean default=on ** ** New arguments may be added in future releases that set additional ** bits in the eCmdFlags field. ** ** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING: ** become the built-in help text for that command or webpage or setting. ** ** Multiple COMMAND: entries can be attached to the same command, thus ** creating multiple aliases for that command. Similarly, multiple ** WEBPAGE: entries can be attached to the same webpage function, to give ** that page aliases. ** ** For SETTING: entries, the default value for the setting can be specified | > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
** SETTING: auto-shun boolean default=on
**
** New arguments may be added in future releases that set additional
** bits in the eCmdFlags field.
**
** Additional lines of comment after the COMMAND: or WEBPAGE: or SETTING:
** become the built-in help text for that command or webpage or setting.
** Backslashes must be escaped ("\\" in comment yields "\" in the help text.)
**
** Multiple COMMAND: entries can be attached to the same command, thus
** creating multiple aliases for that command. Similarly, multiple
** WEBPAGE: entries can be attached to the same webpage function, to give
** that page aliases.
**
** For SETTING: entries, the default value for the setting can be specified
|
| ︙ | ︙ | |||
512 513 514 515 516 517 518 |
(aEntry[i].eType & CMDFLAG_SENSITIVE)!=0,
zDef, (int)(10-strlen(zDef)), ""
);
if( aEntry[i].zIf ){
printf("#endif\n");
}
}
| | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
(aEntry[i].eType & CMDFLAG_SENSITIVE)!=0,
zDef, (int)(10-strlen(zDef)), ""
);
if( aEntry[i].zIf ){
printf("#endif\n");
}
}
printf("{0,0,0,0,0,0,0}};\n");
}
/*
** Process a single file of input
*/
void process_file(void){
|
| ︙ | ︙ |
Changes to win/Makefile.mingw.mistachkin.
| ︙ | ︙ | |||
563 564 565 566 567 568 569 | $(SRCDIR)/../skins/black_and_white/footer.txt \ $(SRCDIR)/../skins/black_and_white/header.txt \ $(SRCDIR)/../skins/blitz/css.txt \ $(SRCDIR)/../skins/blitz/details.txt \ $(SRCDIR)/../skins/blitz/footer.txt \ $(SRCDIR)/../skins/blitz/header.txt \ $(SRCDIR)/../skins/blitz/ticket.txt \ | < < < < | 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | $(SRCDIR)/../skins/black_and_white/footer.txt \ $(SRCDIR)/../skins/black_and_white/header.txt \ $(SRCDIR)/../skins/blitz/css.txt \ $(SRCDIR)/../skins/blitz/details.txt \ $(SRCDIR)/../skins/blitz/footer.txt \ $(SRCDIR)/../skins/blitz/header.txt \ $(SRCDIR)/../skins/blitz/ticket.txt \ $(SRCDIR)/../skins/darkmode/css.txt \ $(SRCDIR)/../skins/darkmode/details.txt \ $(SRCDIR)/../skins/darkmode/footer.txt \ $(SRCDIR)/../skins/darkmode/header.txt \ $(SRCDIR)/../skins/default/css.txt \ $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
1311 1312 1313 1314 1315 1316 1317 | "$(OX)\browse$O" : "$(OX)\browse_.c" "$(OX)\browse.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" "$(OBJDIR)\translate$E" $** > $@ | | | 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 | "$(OX)\browse$O" : "$(OX)\browse_.c" "$(OX)\browse.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\browse_.c" "$(OX)\browse_.c" : "$(SRCDIR)\browse.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\builtin$O" : "$(OX)\builtin_.c" "$(OX)\builtin.h" "$(OX)\builtin_data.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\builtin_.c" "$(OX)\builtin_.c" : "$(SRCDIR)\builtin.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\bundle$O" : "$(OX)\bundle_.c" "$(OX)\bundle.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\bundle_.c" |
| ︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 | "$(OX)\diffcmd$O" : "$(OX)\diffcmd_.c" "$(OX)\diffcmd.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" "$(OBJDIR)\translate$E" $** > $@ | | | 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 | "$(OX)\diffcmd$O" : "$(OX)\diffcmd_.c" "$(OX)\diffcmd.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\diffcmd_.c" "$(OX)\diffcmd_.c" : "$(SRCDIR)\diffcmd.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\dispatch$O" : "$(OX)\dispatch_.c" "$(OX)\dispatch.h" "$(OX)\page_index.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\dispatch_.c" "$(OX)\dispatch_.c" : "$(SRCDIR)\dispatch.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\doc$O" : "$(OX)\doc_.c" "$(OX)\doc.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\doc_.c" |
| ︙ | ︙ |
Changes to www/aboutcgi.wiki.
1 2 | <title>How CGI Works In Fossil</title> <h2>Introduction</h2><blockquote> | | > | | | | | | | | | | | | 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 |
<title>How CGI Works In Fossil</title>
<h2>Introduction</h2><blockquote>
CGI or "Common Gateway Interface" is a venerable yet reliable technique for
generating dynamic web content. This article gives a quick background on how
CGI works and describes how Fossil can act as a CGI service.
This is a "how it works" guide. This document provides background
information on the CGI protocol so that you can better understand what
is going on behind the scenes. If you just want to set up Fossil
as a CGI server, see the [./server/ | Fossil Server Setup] page. Or
if you want to development CGI-based extensions to Fossil, see
the [./serverext.wiki|CGI Server Extensions] page.
</blockquote>
<h2>A Quick Review Of CGI</h2><blockquote>
An HTTP request is a block of text that is sent by a client application
(usually a web browser) and arrives at the web server over a network
connection. The HTTP request contains a URL that describes the information
being requested. The URL in the HTTP request is typically the same URL
that appears in the URL bar at the top of the web browser that is making
the request. The URL might contain a "?" character followed
query parameters. The HTTP will usually also contain other information
such as the name of the application that made the request, whether or
not the requesting application can accept a compressed reply, POST
parameters from forms, and so forth.
The job of the web server is to interpret the HTTP request and formulate
an appropriate reply.
The web server is free to interpret the HTTP request in any way it wants.
But most web servers follow a similar pattern, described below.
(Note: details may vary from one web server to another.)
Suppose the filename component of the URL in the HTTP request looks like this:
<blockquote><b>/one/two/timeline/four</b></blockquote>
Most web servers will search their content area for files that match
some prefix of the URL. The search starts with <b>/one</b>, then goes to
<b>/one/two</b>, then <b>/one/two/timeline</b>, and finally
<b>/one/two/timeline/four</b> is checked. The search stops at the first
match.
Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an
ordinary file in the content area, then that file is returned as static
content. The "<b>/timeline/four</b>" suffix is silently ignored.
If <b>/one/two</b> is a CGI script (or program), then the web server
executes the <b>/one/two</b> script. The output generated by
the script is collected and repackaged as the HTTP reply.
Before executing the CGI script, the web server will set up various
environment variables with information useful to the CGI script:
<table border=1 cellpadding=5>
<tr><th>Environment<br>Variable<th>Meaning
<tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0"
<tr><td>REQUEST_URI
<td>The input URL from the HTTP request.
<tr><td>SCRIPT_NAME
<td>The prefix of the input URL that matches the CGI script name.
In this example: "/one/two".
<tr><td>PATH_INFO
<td>The suffix of the URL beyond the name of the CGI script.
In this example: "timeline/four".
<tr><td>QUERY_STRING
<td>The query string that follows the "?" in the URL, if there is one.
</table>
There are other CGI environment variables beyond those listed above.
Many Fossil servers implement the
[https://fossil-scm.org/home/test_env/two/three?abc=xyz|test_env]
webpage that shows some of the CGI environment
variables that Fossil pays attention to.
In addition to setting various CGI environment variables, if the HTTP
request contains POST content, then the web server relays the POST content
to standard input of the CGI script.
In summary, the task of the
CGI script is to read the various CGI environment variables and
the POST content on standard input (if any), figure out an appropriate
reply, then write that reply on standard output.
The web server will read the output from the CGI script, reformat it
into an appropriate HTTP reply, and relay the result back to the
requesting application.
The CGI script exits as soon as it generates a single reply.
The web server will (usually) persist and handle multiple HTTP requests,
but a CGI script handles just one HTTP request and then exits.
The above is a rough outline of how CGI works.
There are many details omitted from this brief discussion.
See other on-line CGI tutorials for further information.
</blockquote>
<h2>How Fossil Acts As A CGI Program</h2>
<blockquote>
An appropriate CGI script for running Fossil will look something
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 | for this script. On unix, when you execute a script that starts with a shebang, the operating system runs the program identified by the shebang with a single argument that is the full pathname of the script itself. In our example, the interpreter is Fossil, and the argument might be something like "/var/www/cgi-bin/one/two" (depending on how your particular web server is configured). | | | | | | 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 | for this script. On unix, when you execute a script that starts with a shebang, the operating system runs the program identified by the shebang with a single argument that is the full pathname of the script itself. In our example, the interpreter is Fossil, and the argument might be something like "/var/www/cgi-bin/one/two" (depending on how your particular web server is configured). The Fossil program that is run as the script interpreter is the same Fossil that runs when you type ordinary Fossil commands like "fossil sync" or "fossil commit". But in this case, as soon as it launches, the Fossil program recognizes that the GATEWAY_INTERFACE environment variable is set to "CGI/1.0" and it therefore knows that it is being used as CGI rather than as an ordinary command-line tool, and behaves accordingly. When Fossil recognizes that it is being run as CGI, it opens and reads the file identified by its sole argument (the file named by <code>argv[1]</code>). In our example, the second line of that file tells Fossil the location of the repository it will be serving. Fossil then starts looking at the CGI environment variables to figure out what web page is being requested, generates that one web page, then exits. Usually, the webpage being requested is the first term of the PATH_INFO environment variable. (Exceptions to this rule are noted in the sequel.) For our example, the first term of PATH_INFO is "timeline", which means that Fossil will generate the [/help?cmd=/timeline|/timeline] webpage. With Fossil, terms of PATH_INFO beyond the webpage name are converted into the "name" query parameter. Hence, the following two URLs mean exactly the same thing to Fossil: <ol type='A'> <li> [https://fossil-scm.org/home/info/c14ecc43] <li> [https://fossil-scm.org/home/info?name=c14ecc43] </ol> |
| ︙ | ︙ | |||
147 148 149 150 151 152 153 | <blockquote> The previous example showed how to serve a single Fossil repository using a single CGI script. On a website that wants to serve multiple repositories, one could simply create multiple CGI scripts, one script for each repository. But it is also possible to serve multiple Fossil repositories from a single CGI script. | | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | <blockquote> The previous example showed how to serve a single Fossil repository using a single CGI script. On a website that wants to serve multiple repositories, one could simply create multiple CGI scripts, one script for each repository. But it is also possible to serve multiple Fossil repositories from a single CGI script. If the CGI script for Fossil contains a "directory:" line instead of a "repository:" line, then the argument to "directory:" is the name of a directory that contains multiple repository files, each ending with ".fossil". For example: <blockquote><pre> #!/usr/bin/fossil directory: /home/www/repos |
| ︙ | ︙ | |||
198 199 200 201 202 203 204 |
\_________/\____________/\____________________/ \______/
| | | |
HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING
</pre>
</blockquote>
<h2>Additional CGI Script Options</h2>
<blockquote>
| | | | | | | | | 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 |
\_________/\____________/\____________________/ \______/
| | | |
HTTP_HOST SCRIPT_NAME PATH_INFO QUERY_STRING
</pre>
</blockquote>
<h2>Additional CGI Script Options</h2>
<blockquote>
The CGI script can have additional options used to fine-tune
Fossil's behavior. See the [./cgi.wiki|CGI script documentation]
for details.
</blockquote>
<h2>Additional Observations</h2>
<blockquote><ol type="I">
<li><p>
Fossil does not distinguish between the various HTTP methods (GET, PUT,
DELETE, etc). Fossil figures out what it needs to do purely from the
webpage term of the URI.</p></li>
<li><p>
Fossil does not distinguish between query parameters that are part of the
URI, application/x-www-form-urlencoded or multipart/form-data encoded
parameter that are part of the POST content, and cookies. Each information
source is seen as a space of key/value pairs which are loaded into an
internal property hash table. The code that runs to generate the reply
can then reference various properties values.
Fossil does not care where the value of each property comes from (POST
content, cookies, or query parameters) only that the property exists
and has a value.</p></li>
<li><p>
The "[/help?cmd=ui|fossil ui]" and "[/help?cmd=server|fossil server]" commands
are implemented using a simple built-in web server that accepts incoming HTTP
requests, translates each request into a CGI invocation, then creates a
separate child Fossil process to handle each request. In other words, CGI
is used internally to implement "fossil ui/server".
<br><br>
SCGI is processed using the same built-in web server, just modified
to parse SCGI requests instead of HTTP requests. Each SCGI request is
converted into CGI, then Fossil creates a separate child Fossil
process to handle each CGI request.</p></li>
<li><p>
Fossil is itself often launched using CGI. But Fossil can also then
turn around and launch [./serverext.wiki|sub-CGI scripts to implement
extensions].</p></li>
</ol>
</blockquote>
|
Changes to www/adding_code.wiki.
| ︙ | ︙ | |||
116 117 118 119 120 121 122 | (either to an existing source file, or to a new source file created as described above) according to the following template: <blockquote><verbatim> /* ** COMMAND: xyzzy ** | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
(either to an existing source file, or to a new source file created as
described above) according to the following template:
<blockquote><verbatim>
/*
** COMMAND: xyzzy
**
** Help text goes here. Backslashes must be escaped.
*/
void xyzzy_cmd(void){
/* Implement the command here */
fossil_print("Hello, World!\n");
}
</verbatim></blockquote>
|
| ︙ | ︙ |
Changes to www/alerts.md.
| ︙ | ︙ | |||
364 365 366 367 368 369 370 | occur such that a dot or period in an alert message is at the beginning of a line, you'll get a truncated email message without this option. Statistically, this will happen about once every 70 or so messages, so it is important to give this option if your MTA treats leading dots on a line this way. <a id="msmtp"></a> | | | < | > > | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | occur such that a dot or period in an alert message is at the beginning of a line, you'll get a truncated email message without this option. Statistically, this will happen about once every 70 or so messages, so it is important to give this option if your MTA treats leading dots on a line this way. <a id="msmtp"></a> The [`msmtp`][msmtp] SMTP client is compatible with this protocol if you give it the `-t` option. It’s a useful option on a server hosting a Fossil repository which doesn't otherwise require a separate SMTP server for other purposes, such as because you’ve got a separate provider for your email and merely need a way to let Fossil feed messages into it. 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/ |
| ︙ | ︙ |
Changes to www/backup.md.
| ︙ | ︙ | |||
199 200 201 202 203 204 205 | leak your information. This addition to the prior scripts will encrypt the resulting backup in such a way that the cloud copy is a useless blob of noise to anyone without the key: ---- ```shell | | | > > > > > > > > > > > > > > > > > > > > > | 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 |
leak your information. This addition to the prior scripts will encrypt
the resulting backup in such a way that the cloud copy is a useless blob
of noise to anyone without the key:
----
```shell
iter=152830
pass="h8TixP6Mt6edJ3d6COaexiiFlvAM54auF2AjT7ZYYn"
gd="$HOME/Google Drive/Fossil Backups/$bf.xz.enc"
fossil sql -R ~/museum/backups/"$bf" .dump | xz -9 |
openssl enc -e -aes-256-cbc -pbkdf2 -iter $iter -pass pass:"$pass" -out "$gd"
```
----
If you’re adding this to the first script above, remove the
“`-R repo-name`” bit so you get a dump of the repository backing the
current working directory.
Change the `pass` value to some other long random string, and change the
`iter` value to something in the hundreds of thousands range. A good source for
the first is [here][grcp], and for the second, [here][rint].
You may find posts online written by people recommending millions of
iterations for PBKDF2, but they’re generally talking about this in the
context of memorizable passwords, where adding even one more character
to the password is a significant burden. Given our script’s purely
random maximum-length passphrase, there isn’t much more that increasing
the key derivation iteration count can do for us.
Conversely, if you were to reduce the passphrase to 41 characters, that
would drop the key strength by roughly 2⁶, being the entropy value per
character for using most of printable ASCII in our passphrase. To make
that lost strength up on the PBKDF2 end, you’d have to multiply your
iterations by 2⁶ = 64 times. It’s easier to use a max-length passphrase
in this situation than get crazy with key derivation iteration counts.
(This, by the way, is why the example passphrase above is 42 characters:
with 6 bits of entropy per character, that gives you a key size of 252,
as close as we can get to our chosen encryption algorithm’s 256-bit key
size without going over. If it pleases you to give it 43 random
characters for a passphrase in order to pick up those last four bits of
security, you’re welcome to do so.)
Compressing the data before encrypting it removes redundancies that can
make decryption easier, and it results in a smaller backup than you get
with the previous script alone, at the expense of a lot of CPU time
during the backup. You may wish to switch to a less space-efficient
compression algorithm that takes less CPU power, such as [`lz4`][lz4].
Changing up the compression algorithm also provides some
|
| ︙ | ︙ | |||
284 285 286 287 288 289 290 | [bu]: /help?cmd=backup [grcp]: https://www.grc.com/passwords.htm [hb]: https://brew.sh [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean [lz4]: https://lz4.github.io/lz4/ [pbr]: ./private.wiki | | | 305 306 307 308 309 310 311 312 313 314 315 316 | [bu]: /help?cmd=backup [grcp]: https://www.grc.com/passwords.htm [hb]: https://brew.sh [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean [lz4]: https://lz4.github.io/lz4/ [pbr]: ./private.wiki [rint]: https://www.random.org/integers/?num=1&min=100000&max=1000000&col=5&base=10&format=html&rnd=new [Setup]: ./caps/admin-v-setup.md#apsu [shun]: ./shunning.wiki [tkt]: ./tickets.wiki [uv]: ./unvers.wiki |
Changes to www/blockchain.md.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
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 |
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<br><br>
* **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
|
| ︙ | ︙ |
Changes to www/branching.wiki.
1 2 3 4 5 6 7 | <title>Branching, Forking, Merging, and Tagging</title> <h2>Background</h2> In a simple and perfect world, the development of a project would proceed linearly, as shown in Figure 1. <verbatim type="pikchr center toggle"> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <title>Branching, Forking, Merging, and Tagging</title> <h2>Background</h2> In a simple and perfect world, the development of a project would proceed linearly, as shown in Figure 1. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" arrow same circle same "3" arrow same circle same "4"] box invis "Figure 1" big fit with .n at .3cm below ALL.s |
| ︙ | ︙ | |||
41 42 43 44 45 46 47 | "leaf.") Alas, reality often interferes with the simple linear development of a project. Suppose two programmers make independent modifications to check-in 2. After both changes are committed, the check-in graph looks like Figure 2: <verbatim type="pikchr center toggle"> | | | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | "leaf.") Alas, reality often interferes with the simple linear development of a project. Suppose two programmers make independent modifications to check-in 2. After both changes are committed, the check-in graph looks like Figure 2: <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" circle same "3" at 2nd circle+(.4,.3) arrow from 2nd circle to 3rd circle chop circle same "4" at 2nd circle+(.4,-.3) arrow from 2nd circle to 4th circle chop] box invis "Figure 2" big fit with .n at .3cm below ALL.s |
| ︙ | ︙ | |||
117 118 119 120 121 122 123 | merge]</b> command to merge Bob's changes into her local copy of check-in 3. Without arguments, that command merges all leaves on the current branch. Alice can then verify that the merge is sensible and if so, commit the results as check-in 5. This results in a DAG as shown in Figure 3. <verbatim type="pikchr center toggle"> | | | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | merge]</b> command to merge Bob's changes into her local copy of check-in 3. Without arguments, that command merges all leaves on the current branch. Alice can then verify that the merge is sensible and if so, commit the results as check-in 5. This results in a DAG as shown in Figure 3. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px "1" arrow right 40% circle same "2" circle same "3" at 2nd circle+(.4,.3) arrow from 2nd circle to 3rd circle chop circle same "4" at 2nd circle+(.4,-.3) arrow from 2nd circle to 4th circle chop circle same "5" at 3rd circle+(.4,-.3) |
| ︙ | ︙ | |||
180 181 182 183 184 185 186 | When multiple leaves are desirable, we call this <i>branching</i> instead of <i>forking</i>: Figure 4 shows an example of a project where there are two branches, one for development work and another for testing. <verbatim type="pikchr center toggle"> | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | When multiple leaves are desirable, we call this <i>branching</i> instead of <i>forking</i>: Figure 4 shows an example of a project where there are two branches, one for development work and another for testing. <verbatim type="pikchr center toggle"> ALL: [circle rad 0.1in thickness 1.5px fill white "1" arrow 40% C2: circle same "2" arrow same circle same "3" arrow same C5: circle same "5" arrow same |
| ︙ | ︙ | |||
391 392 393 394 395 396 397 | Tags and properties are used in Fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as Figure 4 but with tags and properties added: <verbatim type="pikchr center toggle"> ALL: [arrowht = 0.07 | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | Tags and properties are used in Fossil to help express the intent, and thus to distinguish between forks and branches. Figure 5 shows the same scenario as Figure 4 but with tags and properties added: <verbatim type="pikchr center toggle"> ALL: [arrowht = 0.07 C1: circle rad 0.1in thickness 1.5px fill white "1" arrow 40% C2: circle same "2" arrow same circle same "3" arrow same C5: circle same "5" arrow same |
| ︙ | ︙ | |||
422 423 424 425 426 427 428 | box fill lightgray "branch=trunk" "sym-trunk" fit with .ne at C1-(0.05,0.3); line color gray from last box.ne to C1 chop box same "branch=test" "sym-test" "bgcolor=blue" "cancel=sym-trunk" fit \ with .n at C4-(0,0.3) line color gray from last box.n to C4 chop box same "sym-release-1.0" "closed" fit with .n at C9-(0,0.3) line color gray from last box.n to C9 chop] | | | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 | box fill lightgray "branch=trunk" "sym-trunk" fit with .ne at C1-(0.05,0.3); line color gray from last box.ne to C1 chop box same "branch=test" "sym-test" "bgcolor=blue" "cancel=sym-trunk" fit \ with .n at C4-(0,0.3) line color gray from last box.n to C4 chop box same "sym-release-1.0" "closed" fit with .n at C9-(0,0.3) line color gray from last box.n to C9 chop] box invis "Figure 5" big fit with .n at 0.2cm below ALL.s </verbatim> A <i>tag</i> is a name that is attached to a check-in. A <i>property</i> is a name/value pair. Internally, Fossil implements tags as properties with a NULL value. So, tags and properties really are much the same thing, and henceforth we will use the word "tag" to mean either a tag or a property. |
| ︙ | ︙ |
Changes to www/build.wiki.
1 2 3 4 | <title>Compiling and Installing Fossil</title> <h2>0.0 Using A Pre-compiled Binary</h2> | | | | | | | | | | | | | | | | | 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 | <title>Compiling and Installing Fossil</title> <h2>0.0 Using A Pre-compiled Binary</h2> [/uv/download.html|Pre-compiled binaries] are available for recent releases. Just download the appropriate executable for your platform and put it on your $PATH. To uninstall, simply delete the executable. To upgrade from an older release, just overwrite the older binary with the newer one. For details about how those binaries are built, see [/wiki?name=Release+Build+How-To | the Release Build How-To wiki page]. <h2>0.1 Executive Summary</h2> Building and installing is very simple. Three steps: <ol> <li> Download and unpack a source tarball or ZIP. <li> <b>./configure; make</b> <li> Move the resulting "fossil" or "fossil.exe" executable to someplace on your $PATH. </ol> <hr> <h2>1.0 Obtaining The Source Code</h2> Fossil is self-hosting, so you can obtain a ZIP archive or tarball containing a snapshot of the <em>latest</em> version directly from Fossil's own fossil repository. Additionally, source archives of <em>released</em> versions of fossil are available from the [/uv/download.html|downloads page]. To obtain a development version of fossil, follow these steps: <ol> <li>Point your web browser to [https://fossil-scm.org/]</li> <li>Click on the [/timeline|Timeline] link at the top of the page.</li> <li>Select a version of of Fossil you want to download. The latest version on the trunk branch is usually a good choice. Click on its link.</li> <li>Finally, click on one of the "Zip Archive" or "Tarball" links, according to your preference. These link will build a ZIP archive or a gzip-compressed tarball of the complete source code and download it to your computer.</li> </ol> <h2>Aside: Is it really safe to use an unreleased development version of the Fossil source code?</h2> Yes! Any check-in on the [/timeline?t=trunk | trunk branch] of the Fossil |
| ︙ | ︙ | |||
72 73 74 75 76 77 78 | rendering this page. It is always safe to use whatever version of the Fossil code you find running on the main Fossil website. <h2>2.0 Compiling</h2> <ol> <li value="5"> | | | | | | | | | | | > | | | > > > > | | > | > | | | | | | | | | 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 | rendering this page. It is always safe to use whatever version of the Fossil code you find running on the main Fossil website. <h2>2.0 Compiling</h2> <ol> <li value="5"> Unpack the ZIP or tarball you downloaded then <b>cd</b> into the directory created.</li> <li><i>(Optional, Debian-compatible Linux only)</i> Make sure you have all the necessary tools and libraries at hand by running: <b>sudo apt install tcl-dev tk libssl-dev zlib1g-dev</b>. <li><i>(Optional, Unix only)</i> Run <b>./configure</b> to construct a makefile. <ol type="a"> <li> The build system for Fossil on Unix-like systems assumes that the OpenSSL development and runtime files are available on your system, because unprotected repositories are trivial to attack otherwise. Indeed, some public Fossil repositories — including Fossil's own — today run in an HTTPS-only mode, so that you can't even do an anonymous clone from them without using the TLS features added to Fossil by OpenSSL. To weaken that stance could allow a [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the middle attack], such as one that substitutes malicious code into your Fossil repository clone. You can force the Fossil build system to avoid searching for, building against, and linking to the OpenSSL library by passing <b>--with-openssl=none</b> to the <tt>configure</tt> script. If you do not have the OpenSSL development libraries on your system, we recommend that you install them, typically via your OS's package manager. The Fossil build system goes to a lot of effort to seek these out wherever they may be found, so that is typically all you need to do. For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL discussion in the "TLS and Fossil" document]. </li> <li> To build a statically linked binary, you can <i>try</i> adding the <b>--static</b> option, but [https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead | it may well not work]. If your platform of choice is affected by this, the simplest workaround we're aware of is to build a Fossil container, then [./containers.md#static | extract the static executable from it]. </li> <li> To enable the native [./th1.md#tclEval | Tcl integration feature] feature, add the <b>--with-tcl=1</b> and <b>--with-tcl-private-stubs=1</b> options. </li> <li> Other configuration options can be seen by running <b>./configure --help</b> </li> </ol> <li>Run "<b>make</b>" to build the "fossil" or "fossil.exe" executable. The details depend on your platform and compiler.</li> <ol type="a"> <li><i>Unix</i> → the configure-generated Makefile should work on all Unix and Unix-like systems. Simply type "<b>make</b>".</li> <li><i>Unix without running "configure"</i> → if you prefer to avoid running configure, you can also use: <b>make -f Makefile.classic</b>. You may want to make minor edits to Makefile.classic to configure the build for your system.</li> <li><i>MinGW / MinGW-w64</i> → The best-supported path is to build via the MinGW specific Makefile under a POSIX build of GNU make: "<b>make -f win/Makefile.mingw</b>".</li> There is limited support for building under MinGW's native Windows port of GNU Make instead by defining the <tt>USE_WINDOWS=1</tt> variable, but it's better to build under MSYS, Cygwin, or WSL on Windows since this mode doesn't take care of cases such as the "openssl" target, which depends on <tt>sed</tt>. We've gone as far down this path as is practical short of breaking cross-compilation under Linux, macOS, and so |
| ︙ | ︙ | |||
165 166 167 168 169 170 171 | <b>make -f win/Makefile.mingw FOSSIL_ENABLE_TCL=1 FOSSIL_ENABLE_TCL_STUBS=1 FOSSIL_ENABLE_TCL_PRIVATE_STUBS=1</b> Alternatively, running <b>./configure</b> under MSYS should give a suitable top-level Makefile. However, options passed to configure that are not applicable on Windows may cause the configuration or compilation to fail (e.g. fusefs, internal-sqlite, etc). | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
<b>make -f win/Makefile.mingw FOSSIL_ENABLE_TCL=1 FOSSIL_ENABLE_TCL_STUBS=1 FOSSIL_ENABLE_TCL_PRIVATE_STUBS=1</b>
Alternatively, running <b>./configure</b> under MSYS should give a
suitable top-level Makefile. However, options passed to configure that are
not applicable on Windows may cause the configuration or compilation to fail
(e.g. fusefs, internal-sqlite, etc).
<li><i>MSVC</i> → Use the MSVC makefile.</li>
Run all of the following from a "x64 Native Tools Command Prompt".
First
change to the "win/" subdirectory ("<b>cd win</b>") then run
"<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
|
| ︙ | ︙ | |||
197 198 199 200 201 202 203 | <blockquote><pre> nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1 </pre></blockquote> <blockquote><pre> buildmsvc.bat FOSSIL_ENABLE_TCL=1 </pre></blockquote> | | | | | > | | > | > | > | | > | 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 | <blockquote><pre> nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1 </pre></blockquote> <blockquote><pre> buildmsvc.bat FOSSIL_ENABLE_TCL=1 </pre></blockquote> <li><i>Cygwin</i> → The same as other Unix-like systems. It is recommended to configure using: "<b>configure --disable-internal-sqlite</b>", making sure you have the "libsqlite3-devel" , "zlib-devel" and "openssl-devel" packages installed first.</li> </ol> </ol> <h2>3.0 Installing</h2> <ol> <li value="9"> The finished binary is named "fossil" (or "fossil.exe" on Windows). Put this binary in a directory that is somewhere on your PATH environment variable. It does not matter where. </li> <li> <b>(Optional:)</b> To uninstall, just delete the binary. </li> </ol> <h2>4.0 Additional Considerations</h2> <ul> <li> If the makefiles that come with Fossil do not work for you, or for some other reason you want to know how to build Fossil manually, then refer to the [./makefile.wiki | Fossil Build Process] document which describes in detail what the makefiles do behind the scenes. </li> <li> The fossil executable is self-contained and stand-alone and usually requires no special libraries or other software to be installed. However, the "--tk" option to the [/help/diff|diff command] requires that Tcl/Tk be installed on the local machine. You can get Tcl/Tk from [http://www.activestate.com/activetcl|ActiveState]. </li> <li> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: <blockquote><pre> TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 </pre></blockquote> </li> </ul> <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> The information on building Fossil inside an [https://opencontainers.org/ | OCI container] is now in |
| ︙ | ︙ |
Changes to www/caps/index.md.
|
| | | > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # Administering User Capabilities (a.k.a. Permissions) Fossil includes a powerful [role-based access control system][rbac] which affects which users have which capabilities(^Some parts of the Fossil code call these “permissions” instead, but since there is [a clear and present risk of confusion](#webonly) with operating system level file permissions in this context, we avoid using that term for Fossil’s RBAC capability flags in these pages.) within a given [served][svr] Fossil repository. We call this the “caps” system for short. Fossil stores a user’s caps as an unordered string of ASCII characters, one capability per, [currently](./impl.md#choices) limited to [alphanumerics][an]. Caps are case-sensitive: “**A**” and “**a**” are different user capabilities. This is a complex topic, so some sub-topics have their own documents: |
| ︙ | ︙ |
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 68 69 70 71 72 73 74 75 76 |
<title>Change Log</title>
<h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
* Enhancements to the [/help?cmd=/timeline|/timeline webpage]: <ol type="a">
<li> Add the ft=TAG query parameter which in combination with d=Y
shows all descendants of Y up to TAG
<li> Enhance the s=PATTERN (search) query parameter so that forum post
text is also searched when the "vfx" query parameter is used
<li> Fix the u= (user) query parameter so that it works with a= and b=
<li> Add the oldestfirst query parameter to show the events in reverse order.
Useful in combination with y=f and vfs and perhaps also u= to show all
forum events in chronological order
<li> For the p=X and bt=Y query parameter combination, if Y is a tag that
identifies multiple check-ins, search backwards in time for Y beginning
at X
</ol>
* Administrators can select to skip sending notifications about new forum
posts.
* If the value N is negative in "--context N" or "-c N" to the various diff
commands, then treat it as infinite and show the complete file content.
* The stock OCI container no longer includes BusyBox, thus no longer
needs to start as root to chroot that power away. That in turn
frees us from needing to build and install the container as root,
since it no longer has to create a private <tt>/dev</tt> tree
inside the jail for Fossil's use.
* Add support for the trigram tokenizer for FTS5 search to enable
searching in Chinese.
* Comment lines (starting with a '#') are now supported inside
[./settings.wiki#versionable|versioned settings].
* Default permissions for anonymous users in new repositories is
changed to "hz".
* The [/help?cmd=status|fossil status] command now detects when a
file used to be a symlink and has been replaced by a regular file.
(It previously checked for the inverse case only.)
* The [/help?cmd=empty-dirs|empty-dirs setting] now reuses the same
parser as the *-glob settings instead of its prior idiosyncratic
parser, allowing quoted whitespace in patterns.
* Enhancements to the [/help?cmd=/reports|/reports webpage]:
<ol type="a">
<li> The by-week, by-month, and by-year options now show an estimated
size of the current week, month, or year as a dashed box.
<li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins".
</ol>
<h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
* Users can request a password reset. This feature is disabledby default. Use
the new [/help?cmd=self-pw-reset|self-pw-reset property] to enable it.
New web pages [/help?cmd=/resetpw|/resetpw] and
[/help?cmd=/reqpwreset|/reqpwreset] added.
* Add the [/help?cmd=repack|fossil repack] command (together with
[/help?cmd=all|fossil all repack]) as a convenient way to optimize the
size of one or all of the repositories on a system.
* Add the ability to put text descriptions on ticket report formats.
* Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
* The [/help?cmd=/chat|/chat page] can now embed fossil-rendered
views of wiki/markdown/pikchr file attachments with the caveat that such
embedding happens in an iframe and thus does not inherit styles and such
from the containing browser window.
* The [/help?cmd=all|fossil all remote] subcommand added to "fossil all".
* Passwords for remembered remote repositories are now stored as irreversible
hashes rather than obscured clear-text, for improved security.
* Add the "nossl" and "nocompress" options to CGI.
* Update search infrastructure from FTS4 to FTS5.
* Add the [/help?cmd=/deltachain|/deltachain] page for debugging purposes.
* Writes to the database are disabled by default if the HTTP request
does not come from the same origin. This enhancement is a defense in depth
measure only; it does not address any known vulnerabilities.
* Improvements to automatic detection and mitigation of attacks from
malicious robots.
<h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
* Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
it is not an empty string, then any changes that would appear on the timeline
are announced in [./chat.md|the chat room].
* The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications]
now contain only an "Unsubscribe" link, and not a link to subscription management.
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
* On file listing pages, sort filenames using the "uintnocase" collating
sequence, so that filenames that contains embedded integers sort in
numeric order even if they contain a different number of digits.
(Example: "fossil_80_..." comes before "fossil_100.png" in the
[/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.)
* Enhancements to the graph layout algorithm design to improve readability
and promote better situational awareness.
| | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
* On file listing pages, sort filenames using the "uintnocase" collating
sequence, so that filenames that contains embedded integers sort in
numeric order even if they contain a different number of digits.
(Example: "fossil_80_..." comes before "fossil_100.png" in the
[/dir?ci=92fd091703a28c07&name=skins/blitz|/skins/blitz] directory listing.)
* Enhancements to the graph layout algorithm design to improve readability
and promote better situational awareness.
* Performance enhancement for the
[./checkin_names.wiki#root|"root:BRANCHNAME" style of tag],
accomplished using a Common Table Expression in the underlying SQL.
* Sort tag listings (command line and webpage) by taking numbers into
consideration so as to cater for tags that follow semantic versioning.
* On the wiki listings, omit by default wiki pages that are associated with
check-ins and branches.
* Add the new "[/help?cmd=describe|fossil describe]" command.
* Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
See corresponding [../test/markdown-test3.md|test cases],
[/wiki?name=branch/markdown-footnotes#il|known limitations] and
[forum:/forumthread/ee1f1597e46ec07a|discussion].
* Add the new special name "start:BRANCH" to refer to the first check-in of
the branch.
|
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
<li> Added the "[/help?cmd=chat|fossil chat pull]" command, available to
administrators only, for backing up the chat conversation.
</ul>
* Promote the test-detach command into the [/help?cmd=detach|detach command].
* For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option,
if no URL is specified then use the last URL from the most recent prior
"fossil pull --from-parent-project".
| | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
<li> Added the "[/help?cmd=chat|fossil chat pull]" command, available to
administrators only, for backing up the chat conversation.
</ul>
* Promote the test-detach command into the [/help?cmd=detach|detach command].
* For "[/help?cmd=pull|fossil pull]" with the --from-parent-project option,
if no URL is specified then use the last URL from the most recent prior
"fossil pull --from-parent-project".
* Add options --project-name and --project-desc to the
"[/help?cmd=init|fossil init]" command.
* The [/help?cmd=/ext|/ext page] generates the SERVER_SOFTWARE environment
variable for clients.
* Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
that it includes the query string. This is how most other systems understand
REQUEST_URI.
* Added the --transport-command option to [/help?cmd=sync|fossil sync]
|
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
<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.
| | | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
<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].
|
| ︙ | ︙ | |||
232 233 234 235 236 237 238 |
* <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
images to be loaded from any URL. All other resources are still
locked down by default.
* The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
setting to determine the content of the main menu.
| | | | | | | | | 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 |
* <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
images to be loaded from any URL. All other resources are still
locked down by default.
* The built-in skins all use the "[/help?cmd=mainmenu|mainmenu]"
setting to determine the content of the main menu.
The ability to edit the
"mainmenu" setting is added on the /Admin/Configuration page.
* The hamburger menu is now available on most of the built-in skins.
* Any built-in skin named "X" can be used instead of the standard
repository skin by adding the URL parameter <tt>skin=X</tt> to the
request. The selection is persisted using the display
preferences cookie unless the "once" query parameter is also
included. The [/skins] page may be used to select a skin.
* The [/cookies] page now gives the user an opportunity to delete
individual cookies. And the /cookies page is linked from the
/sitemap, so that it appears in hamburger menus.
* The [/sitemap] extensions are now specified by a single new
"[/help?cmd=sitemap-extra|sitemap-extra setting]",
rather than a cluster of various
"sitemap-*" settings. The older settings are no longer used.
<b>This change might require minor server configuration
adjustments on servers that use /sitemap extensions.</b>
The /Admin/Configuration page provides the ability to edit
the new "sitemap-extra" setting.
* Added the "--ckout-alias NAME" option to
[/help?cmd=ui|fossil ui], [/help?cmd=server|fossil server], and
[/help?cmd=http|fossil http]. This option causes Fossil to
understand URIs of the form "/doc/NAME/..." as if they were
"[/help?cmd=/doc|/doc/ckout/...]", to facilitate testing of
[./embeddeddoc.wiki|embedded documentation] changes prior to
check-in.
* For diff web pages, if the diff type (unified versus side-by-side)
is not specified by a query parameter, and if the
"[/help?cmd=preferred-diff-type|preferred-diff-type]"
setting is omitted or less than 1, then select the diff type based
on a guess of whether or not the request is coming from a mobile
device. Mobile gets unified and desktop gets side-by-side.
* The various pages which show diffs now have toggles to show/hide
individual diffs.
* Add the "[/help?cmd=preferred-diff-type|preferred-diff-type]"
setting to allow an admin to force a default diff type.
* The "pikchr-background" settings is now available in
"detail.txt" skin files, for better control of Pikchr
colors in inverted color schemes.
* Add the <tt>--list</tt> option to the
[/help?cmd=tarball|tarball],
[/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar]
commands.
* The javascript used to implement the hamburger menu on the
default built-in skin has been made generic so that it is usable
by a variety of skins, and promoted to an ordinary built-in
javascript file.
* New TH1 commands:
"[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
"[/doc/trunk/www/th1.md#capexpr|capexpr]",
"foreach", "lappend", and "string match"
* The [/help/leaves|leaves command] now shows the branch point
of each leaf.
* The [/help?cmd=add|fossil add] command refuses to add files whose
names are reserved by Windows (ex: "aux") unless the --allow-reserved
|
| ︙ | ︙ | |||
316 317 318 319 320 321 322 |
* <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>
This release drops a trigger from the database schema (replacing
it with a TEMP trigger that is created as needed). This
change happens automatically the first time you
add content to a repository using Fossil 2.14 or later. No
| | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
* <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>
This release drops a trigger from the database schema (replacing
it with a TEMP trigger that is created as needed). This
change happens automatically the first time you
add content to a repository using Fossil 2.14 or later. No
action is needed on your part. However, if you upgrade to
version 2.14 and then later downgrade or otherwise use an earlier
version of Fossil, the email notification mechanism may fail
to send out notifications for some events, due to the missing
trigger. If you want to
permanently downgrade an installation, then you should run
"[/help?cmd=rebuild|fossil rebuild]" after the downgrade
to get email notifications working again. If you are not using
|
| ︙ | ︙ | |||
341 342 343 344 345 346 347 |
* The "[/help?cmd=clone|fossil clone]" command is enhanced so that
if the repository filename is omitted, an appropriate name is derived
from the remote URL and the newly cloned repo is opened. This makes
the clone command work more like Git, thus making it easier for
people transitioning from Git.
* Added the --mainbranch option to the [/help?cmd=git|fossil git export]
command.
| | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
* The "[/help?cmd=clone|fossil clone]" command is enhanced so that
if the repository filename is omitted, an appropriate name is derived
from the remote URL and the newly cloned repo is opened. This makes
the clone command work more like Git, thus making it easier for
people transitioning from Git.
* Added the --mainbranch option to the [/help?cmd=git|fossil git export]
command.
* Added the --format option to the
"[/help?cmd=timeline|fossil timeline]" command.
* Enhance the --numstat option on the
"[/help?cmd=diff|fossil diff]" command so that it shows a total
number of lines added and deleted and total number of files
modified.
* Add the "contact" sub-command to [/help?cmd=user|fossil user].
* Added commands "[/help?cmd=all|fossil all git export]" and
|
| ︙ | ︙ | |||
463 464 465 466 467 468 469 |
ancestors of CHECKIN going back to ANCESTOR. For example,
[/timeline?p=202006271506&bt=version-2.11] shows all ancestors
of the checkin that occured on 2020-06-27 15:06 going back to
the 2.11 release.
* Update the built-in SQLite so that the
"[/help?cmd=sql|fossil sql]" command supports new output
modes ".mode box" and ".mode json".
| | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
ancestors of CHECKIN going back to ANCESTOR. For example,
[/timeline?p=202006271506&bt=version-2.11] shows all ancestors
of the checkin that occured on 2020-06-27 15:06 going back to
the 2.11 release.
* Update the built-in SQLite so that the
"[/help?cmd=sql|fossil sql]" command supports new output
modes ".mode box" and ".mode json".
* Add the "<tt>obscure()</tt>" SQL function to the
"[/help?cmd=sql|fossil sql]" command.
* 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
|
| ︙ | ︙ | |||
500 501 502 503 504 505 506 |
<ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
take full advantage of this fix. Fossil will continue
to work without the rebuild, but the new backlinks will be missing.</ul>
* The algorithm for finding the
[./tech_overview.wiki#configloc|location of the configuration database]
is enhanced to be XDG-compliant.
* Add a hide/show feature to
| | | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
<ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
take full advantage of this fix. Fossil will continue
to work without the rebuild, but the new backlinks will be missing.</ul>
* The algorithm for finding the
[./tech_overview.wiki#configloc|location of the configuration database]
is enhanced to be XDG-compliant.
* Add a hide/show feature to
[./wikitheory.wiki#assocwiki|associated wiki] display on
check-in and branch information pages.
* Enhance the "[/help?cmd=info|fossil info]" command so that it
works with no arguments even if not within an open check-out.
* Many improvements to the forum and especially email notification
of forum posts, in response to community feedback after switching
SQLite support from a mailing list over to the forum.
* Minimum length of a self-registered user ID increased from 3 to 6
|
| ︙ | ︙ |
Changes to www/concepts.wiki.
| ︙ | ︙ | |||
157 158 159 160 161 162 163 | of the file and the name of the file as it appears on disk, and thus serves as a mapping from artifact ID to disk name. The artifact ID of the manifest is the identifier for the entire check-in. When you look at a "timeline" of changes in Fossil, the ID associated with each check-in or commit is really just the artifact ID of the manifest for that check-in. | | | | | | | 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 |
of the file and the name of the file as it appears on disk,
and thus serves as a mapping from artifact ID to disk name. The artifact ID
of the manifest is the identifier for the entire check-in. When
you look at a "timeline" of changes in Fossil, the ID associated
with each check-in or commit is really just the artifact ID of the
manifest for that check-in.
The manifest file is not normally a real file on disk. Instead,
the manifest is computed in memory by Fossil whenever it needs it.
However, the "fossil setting manifest on" command will cause the
manifest file to be materialized to disk, if desired. Both Fossil
itself, and SQLite cause the manifest file to be materialized to disk
so that the makefiles for these project can read the manifest and
embed version information in generated binaries.
Fossil automatically generates a manifest whenever you "commit"
a new check-in. So this is not something that you, the developer,
need to worry with. The format of a manifest is intentionally
designed to be simple to parse, so that if
you want to read and interpret a manifest, either by hand or
with a script, that is easy to do. But you will probably never
need to do so.
In addition to identifying all files in the check-in, a
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.
<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>
|
| ︙ | ︙ | |||
433 434 435 436 437 438 439 | </ol> <h2>5.0 Setting Up A Fossil Server</h2> With other configuration management software, setting up a server is a lot of work and normally takes time, patience, and a lot of system knowledge. Fossil is designed to avoid this frustration. Setting up | | < | | | | | | | | | | | | | | | < | 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 |
</ol>
<h2>5.0 Setting Up A Fossil Server</h2>
With other configuration management software, setting up a server is
a lot of work and normally takes time, patience, and a lot of system
knowledge. Fossil is designed to avoid this frustration. Setting up
a server with Fossil is ridiculously easy. You have four options:
# <b>Stand-alone server.</b>
Simply run the [/help?cmd=server|fossil server] or
[/help?cmd=ui|fossil ui] command from the command-line.
<br><br>
# <b>CGI.</b>
Install a 2-line CGI script on a CGI-enabled web-server like Apache.
<br><br>
# <b>SCGI.</b>
Start an SCGI server using the
[/help?cmd=server| fossil server --scgi] command for handling
SCGI requests from web-servers like Nginx.
<br><br>
# <b>Inetd or Stunnel.</b>
Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
directly to the [/help?cmd=http|fossil http] command.
See the [./server/ | How To Configure A Fossil Server] document
for details.
<h2>6.0 Review Of Key Concepts</h2>
<ul>
|
| ︙ | ︙ |
Changes to www/containers.md.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 | [Docker]: https://www.docker.com/ [OCI]: https://opencontainers.org/ ## 1. Quick Start | | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [Docker]: https://www.docker.com/ [OCI]: https://opencontainers.org/ ## 1. Quick Start Fossil ships a `Dockerfile` at the top of its source tree, [here][DF], which you can build like so: ``` $ docker build -t fossil . ``` If the image built successfully, you can create a container from it and test that it runs: |
| ︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | _unversioned_ image called `fossil:latest` and from that a container simply called `fossil`. The unversioned names are more convenient for interactive use, while the versioned ones are good for CI/CD type applications since they avoid a conflict with past versions; it lets you keep old containers around for quick roll-backs while replacing them with fresh ones. ## 2. <a id="storage"></a>Repository Storage Options If you want the container to serve an existing repository, there are at least two right ways to do it. The wrong way is to use the `Dockerfile COPY` command, because by baking the repo into the image at build time, it will become one of the image’s base layers. The end result is that each time you build a container from that image, the repo will be reset to its build-time state. Worse, restarting the container will do the same thing, since the base image | > > | | | | 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 | _unversioned_ image called `fossil:latest` and from that a container simply called `fossil`. The unversioned names are more convenient for interactive use, while the versioned ones are good for CI/CD type applications since they avoid a conflict with past versions; it lets you keep old containers around for quick roll-backs while replacing them with fresh ones. [DF]: /file/Dockerfile ## 2. <a id="storage"></a>Repository Storage Options If you want the container to serve an existing repository, there are at least two right ways to do it. The wrong way is to use the `Dockerfile COPY` command, because by baking the repo into the image at build time, it will become one of the image’s base layers. The end result is that each time you build a container from that image, the repo will be reset to its build-time state. Worse, restarting the container will do the same thing, since the base image layers are immutable. This is almost certainly not what you want. The correct ways put the repo into the _container_ created from the _image_, not in the image itself. ### <a id="repo-inside"></a> 2.1 Storing the Repo Inside the Container The simplest method is to stop the container if it was running, then say: ``` $ docker cp /path/to/my-project.fossil fossil:/museum/repo.fossil $ docker start fossil $ docker exec fossil chown -R 499 /museum ``` That copies the local Fossil repo into the container where the server expects to find it, so that the “start” command causes it to serve from that copied-in file instead. Since it lives atop the immutable base layers, it persists as part of the container proper, surviving restarts. |
| ︙ | ︙ | |||
110 111 112 113 114 115 116 | privileges after it enters the chroot. (See [below](#args) for how to change this default.) You don’t have to restart the server after fixing this with `chmod`: simply reload the browser, and Fossil will try again. ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container | | | | | | | 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 |
privileges after it enters the chroot. (See [below](#args) for how to
change this default.) You don’t have to restart the server after fixing
this with `chmod`: simply reload the browser, and Fossil will try again.
### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container
The simple storage method above has a problem: containers are
designed to be killed off at the slightest cause, rebuilt, and
redeployed. If you do that with the repo inside the container, it gets
destroyed, too. The solution is to replace the “run” command above with
the following:
```
$ docker run \
--publish 9999:8080 \
--name fossil-bind-mount \
--volume ~/museum:/museum \
fossil
```
Because this bind mount maps a host-side directory (`~/museum`) into the
container, you don’t need to `docker cp` the repo into the container at
all. It still expects to find the repository as `repo.fossil` under that
directory, but now both the host and the container can see that repo DB.
Instead of a bind mount, you could instead set up a separate
[volume](https://docs.docker.com/storage/volumes/), at which point you
_would_ need to `docker cp` the repo file into the container.
Either way, files in these mounted directories have a lifetime
independent of the container(s) they’re mounted into. When you need to
rebuild the container or its underlying image — such as to upgrade to a
newer version of Fossil — the external directory remains behind and gets
remapped into the new container when you recreate it with `--volume/-v`.
#### 2.2.1 <a id="wal-mode"></a>WAL Mode Interactions
You might be aware that OCI containers allow mapping a single file into
the repository rather than a whole directory. Since Fossil repositories
are specially-formatted SQLite databases, you might be wondering why we
don’t say things like:
```
--volume ~/museum/my-project.fossil:/museum/repo.fossil
```
That lets us have a convenient file name for the project outside the
container while letting the configuration inside the container refer to
the generic “`/museum/repo.fossil`” name. Why should we have to name
the repo generically on the outside merely to placate the container?
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 | [dbcorr]: https://www.sqlite.org/howtocorrupt.html#_deleting_a_hot_journal [wal]: https://www.sqlite.org/wal.html ## 3. <a id="security"></a>Security | | | | | < | < | | < < < < < < < < < < < < < | < < < < | < < < < < < | < < < < < < < < | | 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 | [dbcorr]: https://www.sqlite.org/howtocorrupt.html#_deleting_a_hot_journal [wal]: https://www.sqlite.org/wal.html ## 3. <a id="security"></a>Security ### 3.1 <a id="chroot"></a>Why Not Chroot? Prior to 2023.03.26, the stock Fossil container relied on [the chroot jail feature](./chroot.md) to wall away the shell and other tools provided by [BusyBox]. It included that as a bare-bones operating system inside the container on the off chance that someone might need it for debugging, but the thing is, Fossil is self-contained, needing none of that power in the main-line use cases. Our weak “you might need it” justification collapsed when we realized you could restore this basic shell environment with a one-line change to the `Dockerfile`, as shown [below](#run). [BusyBox]: https://www.busybox.net/BusyBox.html ### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities The example commands above create the container with [a default set of Linux kernel capabilities][defcap]. Although Docker strips away almost all of the traditional root capabilities by default, and Fossil doesn’t |
| ︙ | ︙ | |||
249 250 251 252 253 254 255 |
* **`CHOWN`**: The Fossil server never even calls `chown(2)`, and our
image build process sets up all file ownership properly, to the
extent that this is possible under the limitations of our
automation.
Curiously, stripping this capability doesn’t affect your ability to
| | | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
* **`CHOWN`**: The Fossil server never even calls `chown(2)`, and our
image build process sets up all file ownership properly, to the
extent that this is possible under the limitations of our
automation.
Curiously, stripping this capability doesn’t affect your ability to
run commands like “`chown -R fossil:fossil /museum`” when
you’re using bind mounts or external volumes — as we recommend
[above](#bind-mount) — because it’s the host OS’s kernel
capabilities that affect the underlying `chown(2)` call in that
case, not those of the container.
If for some reason you did have to change file ownership of
in-container files, it’s best to do that by changing the
|
| ︙ | ︙ | |||
277 278 279 280 281 282 283 |
[backoffice], and then only for processes it created on earlier
runs; it doesn’t need the ability to kill processes created by other
users. You might wish for this ability as an administrator shelled
into the container, but you can pass the “`docker exec --user`”
option to run commands within your container as the legitimate owner
of the process, removing the need for this capability.
| > > > | | < < < | | | > > | > | < | 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 |
[backoffice], and then only for processes it created on earlier
runs; it doesn’t need the ability to kill processes created by other
users. You might wish for this ability as an administrator shelled
into the container, but you can pass the “`docker exec --user`”
option to run commands within your container as the legitimate owner
of the process, removing the need for this capability.
* **`MKNOD`**: As of 2023.03.26, the stock container uses the
runtime’s default `/dev` node tree. Prior to this, we had to create
`/dev/null` and `/dev/urandom` inside [the chroot jail](#chroot),
but even then, these device nodes were created at build time and
were never changed at run time, so we didn’t need this run-time
capability even then.
* **`NET_BIND_SERVICE`**: With containerized deployment, Fossil never
needs the ability to bind the server to low-numbered TCP ports, not
even if you’re running the server in production with TLS enabled and
want the service bound to port 443. It’s perfectly fine to let the
Fossil instance inside the container bind to its default port (8080)
because you can rebind it on the host with the
“`docker create --publish 443:8080`” option. It’s the container’s
_host_ that needs this ability, not the container itself.
(Even the container runtime might not need that capability if you’re
[terminating TLS with a front-end proxy](./ssl.wiki#server). You’re
more likely to say something like “`-p localhost:12345:8080`” and then
configure the reverse proxy to translate external HTTPS calls into
HTTP directed at this internal port 12345.)
* **`NET_RAW`**: Fossil itself doesn’t use raw sockets, and while
you could [swap out the run layer](#run) for something more
functional that *does* make use of raw sockets, there’s little call
for it. The best reason I can come up with is to be able to run
utilities like `ping` and `traceroute`, but since we aren’t doing
anything clever with the networking configuration, there’s no
particularly compelling reason to run these from inside the
container. If you need to ping something, do it on the host.
If we did not take this hard-line stance, an attacker that broke
into the container and gained root privileges might use raw sockets
to do a wide array of bad things to any network the container is
bound to.
|
| ︙ | ︙ | |||
343 344 345 346 347 348 349 | [capchg]: https://stackoverflow.com/a/45752205/142454 ## 4. <a id="static"></a>Extracting a Static Binary Our 2-stage build process uses Alpine Linux only as a build host. Once | | | | | | | | < < < < < | < < < < < < | < < < < < < < | | | | < < | | | | | < | > > > > > > > > > > > > > | > | | > | > > > | > | > | > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | 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 |
[capchg]: https://stackoverflow.com/a/45752205/142454
## 4. <a id="static"></a>Extracting a Static Binary
Our 2-stage build process uses Alpine Linux only as a build host. Once
we’ve got everything reduced to a single static Fossil binary,
we throw all the rest of it away.
A secondary benefit falls out of this process for free: it’s arguably
the easiest way to build a purely static Fossil binary for Linux. Most
modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
back-to-basics nature makes static builds work the way they used to,
back in the day. If that’s all you’re after, you can do so as easily as
this:
```
$ docker build -t fossil .
$ docker create --name fossil-static-tmp fossil
$ docker cp fossil-static-tmp:/bin/fossil .
$ docker container rm fossil-static-tmp
```
The result is six or seven megs, depending on the CPU architecture you
build for. It’s built stripped.
[lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
## 5. <a id="custom" name="args"></a>Customization Points
### <a id="pkg-vers"></a> 5.1 Fossil Version
The default version of Fossil fetched in the build is the version in the
checkout directory at the time you run it. You could override it to get
a release build like so:
```
$ docker build -t fossil --build-arg FSLVER=version-2.20 .
```
Or equivalently, using Fossil’s `Makefile` convenience target:
```
$ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
```
While you could instead use the generic
“`release`” tag here, it’s better to use a specific version number
since container builders cache downloaded files, hoping to
reuse them across builds. If you ask for “`release`” before a new
version is tagged and then immediately after, you might expect to get
two different tarballs, but because the underlying source tarball URL
remains the same when you do that, you’ll end up reusing the
old tarball from cache. This will occur
even if you pass the “`docker build --no-cache`” option.
This is why we default to pulling the Fossil tarball by checkin ID
rather than let it default to the generic “`trunk`” tag: so the URL will
change each time you update your Fossil source tree, forcing the builder to
pull a fresh tarball.
### 5.2 <a id="uids"></a>User & Group IDs
The “`fossil`” user and group IDs inside the container default to 499.
Why? Regular user IDs start at 500 or 1000 on most Unix type systems,
leaving those below it for system users like this Fossil daemon owner.
Since it’s typical for these to start at 0 and go upward, we started at
500 and went *down* one instead to reduce the chance of a conflict to as
close to zero as we can manage.
To change it to something else, say:
```
$ make container-image DBFLAGS='--build-arg UID=501'
```
This is particularly useful if you’re putting your repository on a
separate volume since the IDs “leak” out into the host environment via
file permissions. You may therefore wish them to mean something on both
sides of the container barrier rather than have “499” appear on the host
in “`ls -l`” output.
### 5.3 <a id="cengine"></a>Container Engine
Although the Fossil container build system defaults to Docker, we allow
for use of any OCI container system that implements the same interfaces.
We go into more details about this [below](#light), but
for now, it suffices to point out that you can switch to Podman while
using our `Makefile` convenience targets unchanged by saying:
```
$ make CENGINE=podman container-run
```
### 5.4 <a id="config"></a>Fossil Configuration Options
You can use this same mechanism to enable non-default Fossil
configuration options in your build. For instance, to turn on
the JSON API and the TH1 docs extension:
```
$ make container-image \
DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"'
```
If you also wanted [the Tcl evaluation extension](./th1.md#tclEval),
that brings us to [the next point](#run).
### 5.5 <a id="run"></a>Elaborating the Run Layer
If you want a basic shell environment for temporary debugging of the
running container, that’s easily added. Simply change this line in the
`Dockerfile`…
FROM scratch AS run
…to this:
FROM busybox AS run
Rebuild and redeploy to give your Fossil container a [BusyBox]-based
shell environment that you can get into via:
$ docker exec -it -u fossil $(make container-version) sh
(That command assumes you built it via “`make container`” and are
therefore using its versioning scheme.)
Another case where you might need to replace this bare-bones “`run`”
layer with something more functional is that you’re setting up [email
alerts](./alerts.md) and need some way to integrate with the host’s
[MTA]. There are a number of alternatives in that linked document, so
for the sake of discussion, we’ll say you’ve chosen [Method
2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
extension to push messages into the outbound email queue DB, presumably
bind-mounted into the container.
You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile`
with this:
```
## ---------------------------------------------------------------------
## STAGE 2: Pare that back to the bare essentials, plus Tcl.
## ---------------------------------------------------------------------
FROM alpine AS run
ARG UID=499
ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
COPY --from=builder /tmp/fossil /bin/
COPY tools/email-sender.tcl /bin/
RUN set -x \
&& echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
&& echo "fossil:x:${UID}:fossil" >> /etc/group \
&& install -d -m 700 -o fossil -g fossil log museum \
&& apk add --no-cache tcl sqlite-tcl
```
Build it and test that it works like so:
```
$ make container-run &&
echo 'puts [info patchlevel]' |
docker exec -i $(make container-version) tclsh
8.6.12
```
You should remove the `PATH` override in the “RUN”
stage, since it’s written for the case where everything is in `/bin`.
With these additions, we need the longer `PATH` shown above to have
ready access to them all.
Another useful case to consider is that you’ve installed a [server
extension](./serverext.wiki) and you need an interpreter for that
script. The first option above won’t work except in the unlikely case that
it’s written for one of the bare-bones script interpreters that BusyBox
ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
shell, implementing little more than what POSIX specified in 1989, plus
equally stripped-down versions of `awk` and `sed`.)
Let’s say the extension is written in Python. While you could handle it
the same way we do with the Tcl example above, Python is more
popular, giving us more options. Let’s inject a Python environment into
the stock Fossil container via a suitable “[distroless]” image instead:
```
## ---------------------------------------------------------------------
## STAGE 2: Pare that back to the bare essentials, plus Python.
## ---------------------------------------------------------------------
FROM cgr.dev/chainguard/python:latest
USER root
ARG UID=499
ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
COPY --from=builder /tmp/fossil /bin/
COPY --from=builder /bin/busybox.static /bin/busybox
RUN [ "/bin/busybox", "--install", "/bin" ]
RUN set -x \
&& echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
&& echo "fossil:x:${UID}:fossil" >> /etc/group \
&& install -d -m 700 -o fossil -g fossil log museum
```
You will also have to add `busybox-static` to the APK package list in
STAGE 1 for the `RUN` script at the end of that stage to work, since the
[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
install root-level binaries is why we change `USER` temporarily here.
Build it and test that it works like so:
```
$ make container-run &&
docker exec -i $(make container-version) python --version
3.11.2
```
The compensation for the hassle of using Chainguard over something more
general purpose like Alpine + “`apk add python`”
is huge: we no longer leave a package manager sitting around inside the
container, waiting for some malefactor to figure out how to abuse it.
Beware that there’s a limit to this über-jail’s ability to save you when
you go and provide a more capable OS layer like this. The container
layer should stop an attacker from accessing any files out on the host
that you haven’t explicitly mounted into the container’s namespace, but
it can’t stop them from making outbound network connections or modifying
the repo DB inside the container.
[cgimgs]: https://github.com/chainguard-images/images/tree/main/images
[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
## 6. <a id="light"></a>Lightweight Alternatives to Docker
Those afflicted with sticker shock at seeing the size of a [Docker
Desktop][DD] installation — 1.65 GB here — might’ve immediately
“noped” out of the whole concept of containers. The first thing to
realize is that when it comes to actually serving simple containers like
the ones shown above is that [Docker Engine][DE] suffices, at about a
quarter of the size.
Yet on a small server — say, a $4/month ten gig Digital Ocean droplet —
that’s still a big chunk of your storage budget. It takes ~60:1 overhead
merely to run a Fossil server container? Once again, I wouldn’t
blame you if you noped right on out of here, but if you will be patient,
you will find that there are ways to run Fossil inside a container even
on entry-level cloud VPSes. These are well-suited to running Fossil; you
don’t have to resort to [raw Fossil service][srv] to succeed,
leaving the benefits of containerization to those with bigger budgets.
For the sake of simple examples in this section, we’ll assume you’re
integrating Fossil into a larger web site, such as with our [Debian +
nginx + TLS][DNT] plan. This is why all of the examples below create
the container with this option:
```
--publish 127.0.0.1:9999:8080
```
The assumption is that there’s a reverse proxy running somewhere that
redirects public web hits to localhost port 9999, which in turn goes to
port 8080 inside the container. This use of port
publishing effectively replaces the use of the
“`fossil server --localhost`” option.
For the nginx case, you need to add `--scgi` to these commands, and you
might also need to specify `--baseurl`.
Containers are a fine addition to such a scheme as they isolate the
|
| ︙ | ︙ | |||
524 525 526 527 528 529 530 | [DNT]: ./server/debian/nginx.md [srv]: ./server/ ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down The core of Docker Engine is its [`containerd`][ctrd] daemon and the | | > | | | | < < < < < < < < < | < < < < | < < < < < < < < < < < < < < < < < | | < < < < < | < < > | < < | | | < | < < < | | < < < | < < < < < | < < | < < < < < < < < < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
[DNT]: ./server/debian/nginx.md
[srv]: ./server/
### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down
The core of Docker Engine is its [`containerd`][ctrd] daemon and the
[`runc`][runc] container runtime. Add to this the out-of-core CLI program
[`nerdctl`][nerdctl] and you have enough of the engine to run Fossil
containers. The big things you’re missing are:
* **BuildKit**: The container build engine, which doesn’t matter if
you’re building elsewhere and shipping the images to the target.
A good example is using a container registry as an
intermediary between the build and deployment hosts.
* **SwarmKit**: A powerful yet simple orchestrator for Docker that you
probably aren’t using with Fossil anyway.
In exchange, you get a runtime that’s about half the size of Docker
Engine. The commands are essentially the same as above, but you say
“`nerdctl`” instead of “`docker`”. You might alias one to the other,
because you’re still going to be using Docker to build and ship your
container images.
[ctrd]: https://containerd.io/
[nerdctl]: https://github.com/containerd/nerdctl
[runc]: https://github.com/opencontainers/runc
### 6.2 <a id="podman"></a>Podman
A lighter-weight [rootless][rl] [drop-in replacement][whatis] that
doesn’t give up the image builder is [Podman]. Initially created by
Red Hat and thus popular on that family of OSes, it will run on
any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac]
or [on Windows via WSL2][pmwin].
On Ubuntu 22.04, the installation size is about 38 MiB, roughly a
tenth the size of Docker Engine.
For our purposes here, the only thing that changes relative to the
examples at the top of this document are the initial command:
```
$ podman build -t fossil .
$ podman run --name fossil -p 9999:8080/tcp fossil
```
Your Linux package repo may have a `podman-docker` package which
provides a “`docker`” script that calls “`podman`” for you, eliminating
even the command name difference. With that installed, the `make`
commands above will work with Podman as-is.
The only difference that matters here is that Podman doesn’t have the
same [default Linux kernel capability set](#caps) as Docker, which
affects the `--cap-drop` flags recommended above to:
```
$ podman create \
--name fossil \
--cap-drop CHOWN \
--cap-drop FSETID \
--cap-drop KILL \
--cap-drop NET_BIND_SERVICE \
--cap-drop SETFCAP \
--cap-drop SETPCAP \
--publish 127.0.0.1:9999:8080 \
localhost/fossil
$ podman start fossil
```
[pmmac]: https://podman.io/getting-started/installation.html#macos
[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md
[Podman]: https://podman.io/
[rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
[whatis]: https://podman.io/whatis.html
### 6.3 <a id="nspawn"></a>`systemd-container`
If even the Podman stack is too big for you, the next-best option I’m
aware of is the `systemd-container` infrastructure on modern Linuxes,
available since version 239 or so. Its runtime tooling requires only
|
| ︙ | ︙ | |||
725 726 727 728 729 730 731 | We’ll assume your Fossil repository stores something called “`myproject`” within `~/museum/myproject/repo.fossil`, named according to the reasons given [above](#repo-inside). We’ll make consistent use of this naming scheme in the examples below so that you will be able to replace the “`myproject`” element of the various file and path names. | > > > > | > > > | > > > | < | < | 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 |
We’ll assume your Fossil repository stores something called
“`myproject`” within `~/museum/myproject/repo.fossil`, named according
to the reasons given [above](#repo-inside). We’ll make consistent use of
this naming scheme in the examples below so that you will be able to
replace the “`myproject`” element of the various file and path names.
If you use [the stock `Dockerfile`][DF] to generate your
base image, `nspawn` won’t recognize it as containing an OS unless you
change the “`FROM scratch AS os`” line at the top of the second stage
to something like this:
```
FROM gcr.io/distroless/static-debian11 AS os
```
Using that as a base image provides all the files `nspawn` checks for to
determine whether the container is sufficiently close to a Linux VM for
the following step to proceed:
```
$ make container
$ docker container export $(make container-version) |
machinectl import-tar - myproject
```
Next, create `/etc/systemd/nspawn/myproject.nspawn`:
----
```
[Exec]
WorkingDirectory=/
Parameters=bin/fossil server \
--baseurl https://example.com/myproject \
--create \
--jsmode bundled \
--localhost \
--port 9000 \
--scgi \
--user admin \
museum/repo.fossil
|
| ︙ | ︙ | |||
767 768 769 770 771 772 773 |
CAP_SETFCAP \
CAP_SETPCAP
ProcessTwo=yes
LinkJournal=no
Timezone=no
[Files]
| | | 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 |
CAP_SETFCAP \
CAP_SETPCAP
ProcessTwo=yes
LinkJournal=no
Timezone=no
[Files]
Bind=/home/fossil/museum/myproject:/museum
[Network]
VirtualEthernet=no
```
----
|
| ︙ | ︙ | |||
791 792 793 794 795 796 797 |
* The command given in the `Parameters` directive assumes you’re
setting up [SCGI proxying via nginx][DNT], but with adjustment,
it’ll work with the other repository service methods we’ve
[documented][srv].
* The path in the host-side part of the `Bind` value must point at the
directory containing the `repo.fossil` file referenced in said
| | | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 |
* The command given in the `Parameters` directive assumes you’re
setting up [SCGI proxying via nginx][DNT], but with adjustment,
it’ll work with the other repository service methods we’ve
[documented][srv].
* The path in the host-side part of the `Bind` value must point at the
directory containing the `repo.fossil` file referenced in said
command so that `/museum/repo.fossil` refers to your repo out
on the host for the reasons given [above](#bind-mount).
That being done, we also need a generic `systemd` unit file called
`/etc/systemd/system/fossil@.service`, containing:
----
```
[Unit]
Description=Fossil %i Repo Service
|
| ︙ | ︙ | |||
837 838 839 840 841 842 843 |
public using nginx via SCGI. If you aren’t using a front-end proxy
and want Fossil exposed to the world via HTTPS, you might say this instead in
the `*.nspawn` file:
```
Parameters=bin/fossil server \
--cert /path/to/cert.pem \
| < | | 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 |
public using nginx via SCGI. If you aren’t using a front-end proxy
and want Fossil exposed to the world via HTTPS, you might say this instead in
the `*.nspawn` file:
```
Parameters=bin/fossil server \
--cert /path/to/cert.pem \
--create \
--jsmode bundled \
--port 443 \
--user admin \
museum/repo.fossil
```
You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
to allow Fossil to bind to this low-numbered port.
We use the `systemd` template file feature to allow multiple Fossil
servers running on a single machine, each on a different TCP port,
as when proxying them out as subdirectories of a larger site.
To add another project, you must first clone the base “machine” layer:
```
$ sudo machinectl clone myproject otherthing
```
|
| ︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 | assumptions baked into it. We papered over these problems above, but if you’re using these tools for other purposes on the machine you’re serving Fossil from, you may need to know which assumptions our container violates and the resulting consequences. Some of it we discussed above already, but there’s one big class of problems we haven’t covered yet. It stems from the fact that our stock | | | | 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 |
assumptions baked into it. We papered over these problems above,
but if you’re using these tools for other purposes on the machine
you’re serving Fossil from, you may need to know which assumptions
our container violates and the resulting consequences.
Some of it we discussed above already, but there’s one big class of
problems we haven’t covered yet. It stems from the fact that our stock
container starts a single static executable inside a bare-bones container
rather than “boot” an OS image. That causes a bunch of commands to fail:
* **`machinectl poweroff`** will fail because the container
isn’t running dbus.
* **`machinectl start`** will try to find an `/sbin/init`
program in the rootfs, which we haven’t got. We could
rename `/bin/fossil` to `/sbin/init` and then hack
the chroot scheme to match, but ick. (This, incidentally,
is why we set `ProcessTwo=yes` above even though Fossil is
perfectly capable of running as PID 1, a fact we depend on
in the other methods above.)
* **`machinectl shell`** will fail because there is no login
daemon running, which we purposefully avoided adding by
|
| ︙ | ︙ |
Changes to www/custom_ticket.wiki.
1 2 3 | <title>Customizing The Ticket System</title> <nowiki> <h2>Introduction</h2> | | < | | > < | | > < | | > | 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 |
<title>Customizing The Ticket System</title>
<nowiki>
<h2>Introduction</h2>
This guide will explain how to add the "assigned_to" and "submitted_by" fields
to the ticket system in Fossil, as well as making the system more useful. You
must have "admin" access to the repository to implement these instructions.
<h2>First modify the TICKET table</h2>
<blockquote>
Click on the "Admin" menu, then "Tickets", then "Table". After the other fields
and before the final ")", insert:
<pre>
,
assigned_to TEXT,
opened_by TEXT
</pre>
And "Apply Changes". You have just added two more fields to the ticket
database! NOTE: I won't tell you to "Apply Changes" after each step from here
on out. Now, how do you use these fields?
</blockquote>
<h2>Next add assignees</h2>
<blockquote>
Back to the "Tickets" admin page, and click "Common". Add something like this:
<pre>
set assigned_choices {
unassigned
tom
dick
harriet
}
</pre>
Obviously, choose names corresponding to the logins on your system. The
'unassigned' entry is important, as it prevents you from having a NULL in that
field (which causes problems later when editing).
</blockquote>
<h2>Now modify the 'new ticket' page</h2>
<blockquote>
Back to the "Tickets" admin page, and click "New Ticket Page". This is a little
more tricky. Edit the top part:
<pre>
if {[info exists submit]} {
set status Open
set opened_by $login
set assigned_to "unassigned"
|
| ︙ | ︙ | |||
64 65 66 67 68 69 70 | questions.</td> </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. | | < < | | > < | | > | | < | | > < | 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 |
questions.</td>
</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.
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.
</blockquote>
<h2>Modify the 'view ticket' page</h2>
<blockquote>
Look for the text "Contact:" (about halfway through). Then insert these lines
after the closing tr tag and before the "enable_output" line:
<pre>
<tr>
<td align="right">Assigned to:</td><td bgcolor="#d0d0d0">
$<assigned_to>
</td>
<td align="right">Opened by:</td><td bgcolor="#d0d0d0">
$<opened_by>
</td>
</pre>
This will add a row which displays these two fields, in the event the user has
<a href="./caps/ref.html#w">ticket "edit" capability</a>.
</blockquote>
<h2>Modify the 'edit ticket' page</h2>
<blockquote>
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).
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>
</blockquote>
<h2>What next?</h2>
<blockquote>
Now you can add custom reports which select based on the person to whom the
ticket is assigned. For example, an "Assigned to me" report could be:
<pre>
SELECT
CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc'
WHEN status='Review' THEN '#e8e8e8'
WHEN status='Fixed' THEN '#cfe8bd'
WHEN status='Tested' THEN '#bde5d6'
WHEN status='Deferred' THEN '#cacae5'
ELSE '#c8c8c8' END AS 'bgcolor',
substr(tkt_uuid,1,10) AS '#',
datetime(tkt_mtime) AS 'mtime',
type,
status,
subsystem,
title
FROM ticket
WHERE assigned_to=user()
</pre>
</blockquote>
</nowiki>
|
Changes to www/customskin.md.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 |
Fossil *usually* (but not always - [see below](#override))
generates the initial HTML Header section of a page. The
generated HTML Header will look something like this:
<html>
<head>
| | | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
Fossil *usually* (but not always - [see below](#override))
generates the initial HTML Header section of a page. The
generated HTML Header will look something like this:
<html>
<head>
<base href="...">
<meta http-equiv="Content-Security-Policy" content="....">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>....</title>
<link rel="stylesheet" href="..." type="text/css">
</head>
<body class="FEATURE">
…where `FEATURE` is either the top-level URL element (e.g. `doc`) or a
feature class that groups multiple URLs under a single name such as
`forum` to contain `/forummain`, `/forumpost`, `/forume2`, etc. This
allows per-feature CSS such as
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | approach is to use one of the existing built-in skins as a baseline and make incremental modifications, testing after each step, to obtain the desired result. The skin is controlled by five files: <blockquote><dl> | | | | | | | | | | | | | 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 | approach is to use one of the existing built-in skins as a baseline and make incremental modifications, testing after each step, to obtain the desired result. The skin is controlled by five files: <blockquote><dl> <dt><b>css.txt</b></dt> <dd>The css.txt file is the text of the CSS for Fossil. Fossil might add additional CSS elements after the css.txt file, if it sees that the css.txt omits some CSS components that Fossil needs. But for the most part, the content of the css.txt is the CSS for the page.</dd> <dt><b>details.txt</b><dt> <dd>The details.txt file is short list of settings that control the look and feel, mostly of the timeline. The default details.txt file looks like this: <blockquote><pre> pikchr-background: "" pikchr-fontscale: "" pikchr-foreground: "" pikchr-scale: "" timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 </pre></blockquote> The three "timeline-" settings in details.txt control the appearance of certain aspects of the timeline graph. The number on the right is a boolean - "1" to activate the feature and "0" to disable it. The "white-foreground:" setting should be set to "1" if the page color has light-color text on a darker background, and "0" if the page has dark text on a light-colored background. If the "pikchr-foreground" setting (added in Fossil 2.14) is defined and is not an empty string then it specifies a foreground color to use for [pikchr diagrams](./pikchr.md). The default pikchr foreground color is black, or white if the "white-foreground" boolean is set. The "pikchr-background" settings does the same for the pikchr diagram background color. If the "pikchr-fontscale" and "pikchr-scale" values are not empty strings, then they should be floating point values (close to 1.0) that specify relative scaling of the fonts in pikchr diagrams and other elements of the diagrams, respectively. </dd> <dt><b>footer.txt</b> and <b>header.txt</b></dt> <dd>The footer.txt and header.txt files contain the Content Footer and Content Header respectively. Of these, the Content Header is the most important, as it contains the markup used to generate the banner and menu bar for each page. Both the footer.txt and header.txt file are [processed using TH1](#headfoot) prior to being output as part of the overall web page.</dd> <dt><b>js.txt</b></dt> <dd>The js.txt file is optional. It is intended to be javascript. The complete text of this javascript might be inserted into the Content Footer, after being processed using TH1, using code like the following in the "footer.txt" file: <blockquote><pre> <script nonce="$nonce"> <th1>styleScript</th1> </script> </pre></blockquote> The js.txt file was originally used to insert javascript that controls the hamburger menu in the default skin. More recently, the javascript for the hamburger menu was moved into a separate built-in file. Skins that use the hamburger menu typically cause the javascript to be loaded by including the following TH1 code in the "header.txt" file: <blockquote><pre> |
| ︙ | ︙ |
Changes to www/delta_encoder_algorithm.wiki.
| ︙ | ︙ | |||
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.2 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> |
| ︙ | ︙ |
Changes to www/delta_format.wiki.
1 2 3 | <title>Fossil Delta Format</title> <h1>1.0 Overview</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 45 46 47 48 49 50 51 |
<title>Fossil Delta Format</title>
<h1>1.0 Overview</h1>
Fossil achieves efficient storage and low-bandwidth synchronization
through the use of delta-compression. Instead of storing
or transmitting the complete content of an artifact, Fossil stores or
transmits only the changes relative to a related artifact.
This document describes the delta-encoding format used by Fossil.
The intended audience is developers working on either
<a href="index.wiki">Fossil</a> itself, or on tools compatible with
Fossil. Understanding of this document is <em>not</em> required for
ordinary users of Fossil. This document is an implementation detail.
This document only describes the delta file format. A
[./delta_encoder_algorithm.wiki|separate document] describes one possible
algorithm for generating deltas in this format.
<h2>1.1 Sample Software And Analysis Tools</h2>
The core routines used to generate and apply deltas in this format
are contained in the [../src/delta.c|delta.c] source file. Interface
logic, including "test-*" commands to directly manipulate deltas are
contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions
to create, apply, and analyze deltas are implemented by code in the
[../src/deltafunc.c|deltafunc.c] source file.
The following command-line tools are available to create and apply
deltas and to test the delta logic:
* [/help?cmd=test-delta|fossil test-delta] → Run self-tests of
the delta logic
* [/help?cmd=test-delta-create|fossil test-delta-create X Y] → compute
a delta that converts file X into file Y. Output that delta.
* [/help?cmd=test-delta-apply|fossil test-delta-apply X D] → apply
delta D to input file X and output the result.
* [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute
and delta that converts file X into file Y but instead of writing the
delta to output, write performance information about the delta.
When running the [/help?cmd=sqlite3|fossil sql] command to get an
interactive SQL session connected to the repository, the following
additional SQL functions are provided:
* <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> →
Compute a data that carries blob X into blob Y and return that delta
as a blob.
|
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Header"
box same "Segments"
box same "Trailer"
</verbatim>
| | | | | | | | | | | | | | | | 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 |
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Header"
box same "Segments"
box same "Trailer"
</verbatim>
A delta consists of three parts, a "header", a "trailer", and a
"segment-list" between them.
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.
<h2 id="header">2.1 Header</h2>
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Size"
box same "\"\\n\""
</verbatim>
The header consists of a single number followed by a newline
character (ASCII 0x0a). The number is the length of the target in
bytes.
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.
<h2 id="trailer">2.2 Trailer</h2>
<verbatim type="pikchr">
leftmargin = 0.1
box height 50% "Checksum"
box same "\";\""
</verbatim>
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.
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.
By putting this information at the end of the delta a decoder has
it available immediately after the target has been reconstructed
fully.
<h2 id="slist">2.3 Segment-List</h2>
<verbatim type="pikchr">
leftmargin = 0.1
PART1: [
B1: box height 50% width 15% ""
B2: box same ""
|
| ︙ | ︙ | |||
136 137 138 139 140 141 142 |
box "Length" height 50%
right
box "\":\"" same
box "Bytes" same
] with .nw at previous.sw
</verbatim>
| | | | | | | | | | | | | < < < < | < | 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 |
box "Length" height 50%
right
box "\":\"" same
box "Bytes" same
] with .nw at previous.sw
</verbatim>
The segment-list of a delta describes how to create the target from
the original by a combination of inserting literal byte-sequences and
copying ranges of bytes from the original. This is where the
compression takes place, by encoding the large common parts of
original and target in small copy instructions.
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.
<h3 id="insertlit">2.3.1 Insert Literal</h3>
A literal is specified by two elements, the size of the literal in
bytes, and the bytes of the literal itself.
<verbatim type="pikchr">
leftmargin = 0.1
box "Length" height 50%
box "\":\"" same
box "Bytes" same
</verbatim>
The length is written first, followed by a colon character (ASCII
0x3a), followed by the bytes of the literal.
<h3 id="copyrange">2.3.2 Copy Range</h3>
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.
<verbatim type="pikchr">
leftmargin = 0.1
box "Length" height 50%
box "\"@\"" same
box "Offset" same
box "\",\"" same
</verbatim>
The length is written first, followed by an "at" character (ASCII
0x40), then the offset, followed by a comma (ASCII 0x2c).
<h1 id="intcoding">3.0 Encoding of integers</h1>
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").
The base-64 encoding uses one character for each 6 bits of
the integer to be encoded. The encoding characters are:
<blockquote><pre>
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
</pre></blockquote>
The least significant 6 bits of the integer are encoded by
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.
<h1 id="examples">4.0 Examples</h1>
<h2 id="examplesint">4.1 Integer encoding</h2>
<table border=1>
<tr>
|
| ︙ | ︙ | |||
230 231 232 233 234 235 236 | <td>-1101438770</td> <td>2zMM3E</td> </tr> </table> <h2 id="examplesdelta">4.2 Delta encoding</h2> | | | | | 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 | <td>-1101438770</td> <td>2zMM3E</td> </tr> </table> <h2 id="examplesdelta">4.2 Delta encoding</h2> An example of a delta using the specified encoding is: <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> This can be taken apart into the following parts: <table border=1> <tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr> <tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr> <tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr> <tr><td> </td> <td>2:th </td><td>Literal </td><td> 2 'th' </td></tr> <tr><td> </td> <td>FN@4C, </td><td>Copy </td><td> 983 @ 268 </td></tr> <tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr> <tr><td> </td> <td>1B@Jd, </td><td>Copy </td><td> 75 @ 1256 </td></tr> <tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr> <tr><td> </td> <td>5x@Kt, </td><td>Copy </td><td> 380 @ 1336 </td></tr> <tr><td> </td> <td>6:pieces </td><td>Literal </td><td> 6 'pieces' </td></tr> <tr><td> </td> <td>79@Qt, </td><td>Copy </td><td> 457 @ 1720 </td></tr> <tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr> <tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr> <tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr> </table> The unified diff behind the above delta is <table border=1><tr><td><pre> bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new --- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700 +++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700 @@ -5,7 +5,7 @@ |
| ︙ | ︙ |
Changes to www/encryptedrepos.wiki.
1 2 3 4 5 6 7 8 9 | <title>How To Use Encrypted Repositories</title> <h2>Introduction</h2><blockquote> Fossil can be compiled so that it works with encrypted repositories using the [https://www.sqlite.org/see/doc/trunk/www/readme.wiki|SQLite Encryption Extension]. This technical note explains the process. </blockquote> <h2>Building An Encryption-Enabled Fossil</h2><blockquote> The SQLite Encryption Extension (SEE) is proprietary software and requires [https://sqlite.org/purchase/see|purchasing a license]. | | | > | | | | 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 | <title>How To Use Encrypted Repositories</title> <h2>Introduction</h2><blockquote> Fossil can be compiled so that it works with encrypted repositories using the [https://www.sqlite.org/see/doc/trunk/www/readme.wiki|SQLite Encryption Extension]. This technical note explains the process. </blockquote> <h2>Building An Encryption-Enabled Fossil</h2><blockquote> The SQLite Encryption Extension (SEE) is proprietary software and requires [https://sqlite.org/purchase/see|purchasing a license]. Assuming you have an SEE license, the first step of compiling Fossil to use SEE is to create an SEE-enabled version of the SQLite database source code. This alternative SQLite database source file should be called "sqlite3-see.c" and should be placed in the extsrc/ subfolder of the Fossil sources, right beside the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder beside the original "shell.c". Add the --with-see command-line option to the configuration script to enable the use of SEE on unix-like systems. <blockquote><pre> ./configure --with-see; make </pre></blockquote> To build for Windows using MSVC, add the "USE_SEE=1" argument to the "nmake" command line. <blockquote><pre> nmake -f makefile.msc USE_SEE=1 </pre></blockquote> </blockquote> <h2>Using Encrypted Repositories</h2><blockquote> Any Fossil repositories whose filename ends with ".efossil" is taken to be an encrypted repository. Fossil will prompt for the encryption password and attempt to open the repository database using that password. Every invocation of fossil on an encrypted repository requires retyping the encryption password. To avoid excess password typing, consider using the "fossil shell" command which prompts for the password just once, then reuses it for each subsequent Fossil command entered at the prompt. On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not (currently) work on an encrypted repository. </blockquote> <h2>Additional Security</h2><blockquote> Use the FOSSIL_SECURITY_LEVEL environment for additional protection. <blockquote><pre> export FOSSIL_SECURITY_LEVEL=1 |
| ︙ | ︙ |
Changes to www/faq.tcl.
| ︙ | ︙ | |||
155 156 157 158 159 160 161 | ############################################################################# # Code to actually generate the FAQ # puts "<title>Fossil FAQ</title>" puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n" | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
#############################################################################
# Code to actually generate the FAQ
#
puts "<title>Fossil FAQ</title>"
puts "<h1 align=\"center\">Frequently Asked Questions</h1>\n"
puts "Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n"
puts {<ol>}
for {set i 1} {$i<$cnt} {incr i} {
puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>"
}
puts {</ol>}
puts {<hr>}
|
| ︙ | ︙ |
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 | <title>Fossil FAQ</title> <h1 align="center">Frequently Asked Questions</h1> 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 |
| ︙ | ︙ |
Changes to www/fiveminutes.wiki.
1 2 3 4 5 6 7 8 | <title>Up and running in 5 minutes as a single user</title> <p align="center"><b><i> The following document was contributed by Gilles Ganault on 2013-01-08. </i></b> </p><hr> <h1>Up and running in 5 minutes as a single user</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 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 | <title>Up and running in 5 minutes as a single user</title> <p align="center"><b><i> The following document was contributed by Gilles Ganault on 2013-01-08. </i></b> </p><hr> <h1>Up and running in 5 minutes as a single user</h1> This short document explains the main basic Fossil commands for a single user, i.e. with no additional users, with no need to synchronize with some remote repository, and no need for branching/forking. <h2>Create a new repository</h2> <tt>fossil new c:\test.repo</tt> This will create the new SQLite binary file that holds the repository, i.e. files, tickets, wiki, etc. It can be located anywhere, although it's considered best practice to keep it outside the work directory where you will work on files after they've been checked out of the repository. <h2>Open the repository</h2> <tt>cd c:\temp\test.fossil <br> fossil open c:\test.repo</tt> This will check out the last revision of all the files in the repository, if any, into the current work directory. In addition, it will create a binary file _FOSSIL_ to keep track of changes (on non-Windows systems it is called <tt>.fslckout</tt>). <h2>Add new files</h2> <tt>fossil add .</tt> To tell Fossil to add new files to the repository. The files aren't actually added until you run "<tt>fossil commit</tt>. When using ".", it tells Fossil to add all the files in the current directory recursively, i.e. including all the files in all the subdirectories. Note: To tell Fossil to ignore some extensions: <tt>fossil settings ignore-glob "*.o,*.obj,*.exe" --global</tt> <h2>Remove files that haven't been committed yet</h2> <tt>fossil delete myfile.c</tt> This will simply remove the item from the list of files that were previously added through "<tt>fossil add</tt>". <h2>Check current status</h2> <tt>fossil changes</tt> This shows the list of changes that have been done and will be committed the next time you run "<tt>fossil commit</tt>". It's a useful command to run before running "<tt>fossil commit</tt>" just to check that things are OK before proceeding. <h2>Commit changes</h2> To actually apply the pending changes to the repository, e.g. new files marked for addition, checked-out files that have been edited and must be checked-in, etc. <tt>fossil commit -m "Added stuff"</tt> If no file names are provided on the command-line then all changes will be checked in, otherwise just the listed file(s) will be checked in. <h2>Compare two revisions of a file</h2> If you wish to compare the last revision of a file and its checked out version in your work directory: <tt>fossil gdiff myfile.c</tt> If you wish to compare two different revisions of a file in the repository: <tt>fossil finfo myfile</tt> Note the first hash, which is the hash of the commit when the file was committed. <tt>fossil gdiff --from HASH#1 --to HASH#2 myfile.c</tt> <h2>Cancel changes and go back to previous revision</h2> <tt>fossil revert myfile.c</tt> Fossil does not prompt when reverting a file. It simply reminds the user about the "undo" command, just in case the revert was a mistake. |
Changes to www/foss-cklist.wiki.
1 2 3 | <title>Checklist For Successful Open-Source Projects</title> <nowiki> | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
<title>Checklist For Successful Open-Source Projects</title>
<nowiki>
This checklist is loosely derived from Tom "Spot" Callaway's Fail Score
blog post <a href="http://spot.livejournal.com/308370.html">
http://spot.livejournal.com/308370.html</a> (see also
<a href="http://www.theopensourceway.org/book/The_Open_Source_Way-How_to_tell_if_a_FLOSS_project_is_doomed_to_FAIL.html">[1]</a>).
Tom's original post assigned point scores to the various elements and
by adding together the individual points, the reader is supposed to be able
to judge the likelihood that the project will fail.
The point scores, and the items on the list, clearly reflect Tom's
biases and are not necessarily those of the larger open-source community.
Nevertheless, the policy of the Fossil shall be to strive for a perfect
score.
This checklist is an inversion of Tom's original post in that it strives to
say what the project should do in order to succeed rather than what it
should not do to avoid failure. The point values are omitted.
See also:
<ul>
<li><a href="http://offog.org/articles/packaging/">
http://offog.org/articles/packaging/</a>
<li>
<a href="http://www.gnu.org/prep/standards/standards.html#Managing-Releases">
http://www.gnu.org/prep/standards/standards.html#Managing-Releases</a>
</ul>
<hr>
<ol>
<li>The source code size is less than 100 MB, uncompressed.
<li>The project uses a Version Control System (VCS).
<ol type="a">
<li>The VCS has a working web interface.
<li>There is documentation on how to use the VCS.
<li>The VCS is general-purpose, not something hacked together for this
one project.
</ol>
<li>The project comes with documentation on how to build from source
and that documentation is lucid, correct, and up-to-date.
<li>The project is configurable using an autoconf-generated configure
script, or the equivalent, and does not require:
<ol type="a">
<li>Manually editing flat files
<li>Editing code header files
</ol>
<li>The project should be buildable using commonly available and
standard tools like "make".
<li>The project does not depend on 3rd-party proprietary build tools.
<li>The project is able to dynamically link against standard libraries
such as zlib and libjpeg.
<ol type="a">
<li>The project does not ship with the sources to standard libraries.
<i>(On the Fossil project, we will allow the SQLite library sources
to be included in the source tree as long as a system-installed
SQLite library can be used in its stead.)</i>
<li>The project does not use slightly modified versions of standard
libraries. Any required bug fixes in standard libraries are pushed
back upstream.
</ol>
<li>"make install" works and can be configured to target any
directory of the installer's choosing.
<ol type="a">
<li>The project contains no unconfigurable hard-coded pathnames like
"/opt" or "/usr/local".
<li>After installation, the source tree can be moved or deleted and
the application will continue working.
</ol>
<li>The source code uses only \n for line endings, not \r\n.
<li>The code does not depend on any special compiler features or bugs.
<li>The project has a mailing list and announces releases on
the mailing list.
<li>The project has a bug tracker.
<li>The project has a website.
<li>Release version numbers are in the traditional X.Y or X.Y.Z format.
<li>Releases can be downloaded as tarball using
gzip or bzip2 compression.
<li>Releases unpack into a versioned top-level directory.
(ex: "projectname-1.2.3/").
<li>A statement of license appears at the top of every source code file
and the complete text of the license is included in the source code
tarball.
<li>There are no incompatible licenses in the code.
<li>The project has not been blithely proclaimed "public domain" without
having gone through the tedious and exacting legal steps to actually put it
in the public domain.
<li>There is an accurate change log in the code and on the website.
<li>There is documentation in the code and on the website.
</ol>
|
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
179 180 181 182 183 184 185 | somewhere in your <tt>PATH</tt>. To uninstall it, delete the executable. This policy is particularly useful when running Fossil inside a restrictive container, anything from [./chroot.md | classic chroot jails] to modern [https://en.wikipedia.org/wiki/OS-level_virtualization | OS-level virtualization mechanisms] such as [https://en.wikipedia.org/wiki/Docker_(software) | Docker]. | < | < | > < < | < < | | | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | somewhere in your <tt>PATH</tt>. To uninstall it, delete the executable. This policy is particularly useful when running Fossil inside a restrictive container, anything from [./chroot.md | classic chroot jails] to modern [https://en.wikipedia.org/wiki/OS-level_virtualization | OS-level virtualization mechanisms] such as [https://en.wikipedia.org/wiki/Docker_(software) | Docker]. Our [./containers.md | stock container image] is under 8 MB when uncompressed and running. It contains nothing but a single statically-linked binary. If you build a dynamically linked binary instead, Fossil's on-disk size drops to around 6 MB, and it's dependent only on widespread platform libraries with stable ABIs such as glibc, zlib, and openssl. Full static linking is easier on Windows, so our precompiled Windows binaries are just a ZIP archive containing only "<tt>fossil.exe</tt>". There is no "<tt>setup.exe</tt>" to run. Fossil is easy to build from sources. Just run |
| ︙ | ︙ | |||
356 357 358 359 360 361 362 | embedded into Fossil itself. Fossil's build system and test suite are largely based on Tcl.⁵ All of this is quite portable. About half of Git's code is POSIX C, and about a third is POSIX shell code. This is largely why the so-called "Git for Windows" distributions (both [https://git-scm.com/download/win|first-party] and [https://gitforwindows.org/|third-party]) are actually an | | > | | 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 | embedded into Fossil itself. Fossil's build system and test suite are largely based on Tcl.⁵ All of this is quite portable. About half of Git's code is POSIX C, and about a third is POSIX shell code. This is largely why the so-called "Git for Windows" distributions (both [https://git-scm.com/download/win|first-party] and [https://gitforwindows.org/|third-party]) are actually an [https://www.msys2.org/wiki/Home/|MSYS POSIX portability environment] bundled with all of the Git stuff, because it would be too painful to port Git natively to Windows. Git is a foreign citizen on Windows, speaking to it only through a translator.⁶ While Fossil does lean toward POSIX norms when given a choice — LF-only line endings are treated as first-class citizens over CR+LF, for example — the Windows build of Fossil is truly native. The third-party extensions to Git tend to follow this same pattern. [https://docs.gitlab.com/ee/install/install_methods.html#microsoft-windows | GitLab isn't portable to Windows at all], for example. For that matter, GitLab isn't even officially supported on macOS, the BSDs, or uncommon Linuxes! We have many users who regularly build and run Fossil on all of these systems. <h3 id="vs-linux">2.5 Linux vs. SQLite</h3> |
| ︙ | ︙ | |||
426 427 428 429 430 431 432 | 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: | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 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 |
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:
* <b>Personal engagement:</b> SQLite's developers know each
other by name and work together daily on the project.
* <b>Trust over hierarchy:</b> SQLite's developers check
changes into their local repository, and these are immediately and
automatically synchronized up to the central repository; there is no
"[https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows#_dictator_and_lieutenants_workflow|dictator
and lieutenants]" hierarchy as with Linux kernel contributions. D.
Richard Hipp rarely overrides decisions made by those he has trusted
with commit access on his repositories. Fossil allows you to give
[./caps/admin-v-setup.md|some users] more power over what
they can do with the repository, but Fossil [./caps/index.md#ucap |
only loosely supports] the enforcement of a development organization's
social and power hierarchies. Fossil is a great fit for
[https://en.wikipedia.org/wiki/Flat_organization|flat
organizations].
* <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.
* <b>No rebasing:</b> When your local repo clone syncs changes
up to its parent, those changes are sent exactly as they were
committed locally. [#history|There is no rebasing mechanism in
Fossil, on purpose.]
* <b>Sync over push:</b> Explicit pushes are uncommon in
Fossil-based projects: the default is to rely on
[/help?cmd=autosync|autosync mode] instead, in which each commit
syncs immediately to its parent repository. This is a mode so you
can turn it off temporarily when needed, such as when working
offline. Fossil is still a truly distributed version control system;
it's just that its starting default is to assume you're rarely out
of communication with the parent repo.
<br><br>
This is not merely a reflection of modern always-connected computing
environments. It is a conscious decision in direct support of
SQLite's cathedral development model: we don't want developers going
dark, then showing up weeks later with a massive bolus of changes
for us to integrate all at once.
[https://en.wikipedia.org/wiki/Jim_McCarthy_(author)|Jim McCarthy]
put it well in his book on software project management,
<i>[https://www.amazon.com/dp/0735623198/|Dynamics of Software
Development]</i>: "[https://www.youtube.com/watch?v=oY6BCHqEbyc|Beware
of a guy in a room]."
* <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.
* <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.
* <b>Identical clones:</b> Fossil's autosync system tries to
keep each local clone identical to the repository it cloned
from.
Where Git encourages siloed development, Fossil fights against it.
Fossil places a lot of emphasis on synchronizing everyone's work and on
reporting on the state of the project and the work of its developers, so
that everyone — especially the project leader — can maintain a better
mental picture of what is happening, leading to better situational
awareness.
|
| ︙ | ︙ |
Changes to www/gitusers.md.
1 2 3 4 5 6 7 8 | # Git to Fossil Translation Guide ## Introduction Fossil shares many similarities with Git. In many cases, the sub-commands are identical: [`fossil bisect`][fbis] does essentially the same thing as [`git bisect`][gbis], for example. | > > > > | < | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Git to Fossil Translation Guide ## Introduction Fossil shares many similarities with Git. In many cases, the sub-commands are identical: [`fossil bisect`][fbis] does essentially the same thing as [`git bisect`][gbis], for example. Yet, Fossil is not merely Git with a bunch of commands misspelled. If that were the case, we could give you a two-column translation table which would tell you [how to say things like “`git reset --hard HEAD`”](#reset) in this funny ol’ Fossil dialect of Git and be done. The purpose of this document is to cover all the cases where there is no simple 1:1 mapping, usually because of intentional design differences in Fossil that prevent it from working exactly like Git. We choose to explain these differences since to understand the conversion, you need to know why each difference exists. We focus on practical command examples here, leaving discussions of the philosophical underpinnings that drive these command differences to [another document][fvg]. The [case studies](#cs1) do get a bit philosophical, but it is with the aim of illustrating how these Fossil design differences cause Fossil to behave materially differently from Git in everyday operation. |
| ︙ | ︙ | |||
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
(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
<a id="autosync"></a>
## Autosync
Fossil’s [autosync][wflow] feature, normally enabled, has no
equivalent in Git. If you want Fossil to behave like Git, you can turn
it off:
fossil set autosync 0
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > > > > | | > > | > > > > > > > > > | | | > | > | > | > > > > > > > > > | > > | > > > > > | > > | > > > > > > > > > > | > > > | > > | > > > > > > > > > > > > | > > > > > | > > > > > > > > > | > > > > > > > | < < | > | > | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
(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
<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.
[Fossil is an AP-mode system][capt], which in this case means it works
*very hard* to ensure that all repos are as close to identical as it can
make them under this eventually-consistent design philosophy.
Branch *names* sync automatically in Fossil, not just the
content of those branches. That means this common Git command:
git push origin master
…is simply this in Fossil:
fossil push
Fossil doesn’t need to be told what to push or where to push it: it just
keeps using the same remote server URL you gave it last
until you [tell it to do something different][rem]. It pushes all
branches, not just one named local branch.
[capt]: ./cap-theorem.md
[rem]: /help?cmd=remote
<a id="autosync"></a>
## Autosync
Fossil’s [autosync][wflow] feature, normally enabled, has no
equivalent in Git. If you want Fossil to behave like Git, you can turn
it off:
fossil set autosync 0
Let’s say that you have a typical server-and-workstations model with two
working clones on different machines, that you have disabled autosync,
and that this common sequence then occurs:
1. Alice commits to her local clone and *separately* pushes the change
up to Condor — their central server — in typical Git fashion.
2. Bob does the same.
3. Alice brings Bob’s changes down from Condor with “`fossil pull`,” sees
what he did to their shared working branch, and becomes most wrathful.
(Tsk, tsk.)
We’ll get to what you do about this situation [below](#reset), but for
now let us focus on the fact that disabling autosync makes it easier for
[forks] to occur in the development history. If all three machines had
been online and syncing at the time the sequence above began, Bob would
have been warned in step 2 that committing to the central repo would
create a fork and would be invited to fix it before committing.
Likewise, it would allow Fossil to warn Alice about the new
tip-of-branch commit the next time she triggers an implicit autosync at
step 3, giving her a chance to bring Bob’s changes down in a
non-conflicting manner, allowing work to proceed with minimal fuss.
Fossil, being a distributed version control system, cannot guarantee
that sequence of events. Because it allows Alice’s work to proceed
asynchronously, it gives her the chance to create *another* inadvertent
fork before she can trigger an autosync. This is not a serious problem;
Fossil resolves it the same way as with Bob, by inviting her to fix this
second fork in the same manner as it did with Bob. It gets both parties
back onto a single track as expeditiously as possible by moving the
synchronization point out of the expensive human-time workflow and into
the software system, where it’s cheapest to resolve.
Autosync provides Fossil with 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. You get implicit 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.
3. Because there is [little distinction][bu] between the clones in the Fossil
model — unlike in Git, where clones often quickly diverge from each
other, quite possibly on purpose — the backup advantage applies in inverse
as well: if the remote server falls over dead, one of those with a
clone of that repository can stand it back up, and everyone can get
back to work simply by re-pointing their local repo at the new
remote. If the failed remote comes back later, it can sync with the
new central version, then perhaps take over as the primary source of
truth once again.
[bu]: ./backup.md
[forks]: ./branching.wiki
[setup]: ./caps/admin-v-setup.md#apsu
[wflow]: ./concepts.wiki#workflow
<a id="reset"></a>
## Resetting the Repository
Extending from [the prior item](#syncall), you may correctly infer that
“[delete the project and download a fresh copy][x1597]” has no part in
the Fossil Way. Ideally, you should never find yourself forced into
desperate measures like this:(^Parsing the output of `fossil status` is
usually a mistake since it relies on a potentially unstable interface.
We make no guarantee that there will always be a line beginning with
“`repo`” and that it will be separated from the repository’s file name
by a colon. The simplified example above is also liable to become
confused by whitespace in file names.)
```
$ repo=$(fossil status | grep ^repo | cut -f2 -d:)
$ url=$(fossil remote)
$ fossil close # Stop here and think if it warns you!
$ mv $repo ${repo}.old
$ fossil clone $url $repo
$ fossil open --force $repo
```
What, then, should you as a Git transplant do instead when you find
yourself reaching for “`git reset`”?
Since the correct answer to that depends on why you think it’s a good
solution to your immediate problem, we’ll take our motivating scenario
from the problem setup above, where we discussed Fossil’s [autosync]
feature. Let us further say Alice’s pique results from a belief that
Bob’s commit is objectively wrong-headed and should be expunged
henceforth. Since Fossil goes out of its way to ensure that [commits are
durable][wdm], it should be no further surprise that there is no easier
method to reset Bob’s clone in favor of Alice’s than the above sequence
in Fossil’s command set. Except in extreme situations, we believe that
sort of thing is unnecessary.
Instead, Bob can say something like this:
```
fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip
fossil up trunk
fossil push
```
Unlike in Git, the “`amend`” command doesn’t modify prior committed
artifacts. Bob’s first command doesn’t delete anything, merely tells
Fossil to hide his mistake from timeline views by inserting a few new
records into the local repository to change how the client interprets
the data it finds there henceforth.(^One to change the tag marking this
commit’s branch name to “`MISTAKE`,” one to mark that branch as hidden,
and one to close it to further commits.).
Bob’s second command switches his working directory back to the prior
commit on that branch. We’re presuming it was “`trunk`” for the sake of
the example, but it works for any parent branch name. The command works
because the name “`trunk`” now means something different to Fossil by
virtue of the first command.
Bob’s third command pushes the changes up to the central machine to
inform everyone else of his amendment.(^Amendments don’t autosync in
Fossil because they don’t change any previous commits, allowing the
other clones to continue working safely with their existing commit
hashes.)
In this scheme, Alice then needs to say “`fossil update trunk`” in order
to return her check-out’s parent commit to the previous version lest her
next attempted commit land atop this mistake branch. The fact that Bob
marked the branch as closed will prevent that from going thru, cluing
Alice into what she needs to do to remedy the situation, but that merely
shows why it’s a better workflow if Alice makes the amendment herself:
```
fossil amend --branch MISTAKE --hide --close \
-m "shunt Bob’s erroneous commit off" tip
fossil up trunk
fossil push
```
Then she can fire off an email listing Bob’s assorted failings and go
about her work. This asynchronous workflow solves the problem without
requiring explicit coordination with Bob. When he gets his email, he can
then say “`fossil up trunk`” himself, which by default will trigger an
autosync, pulling down Alice’s amendments and getting him back onto her
development track.
Remember that [branch names need not be unique](#btnames) in Fossil. You
are free to reuse this “`MISTAKE`” branch name as often as you need to.
[autosync]: #autosync
[x1597]: https://xkcd.com/1597/
<a id="trunk"></a>
## The Main Branch Is Called "`trunk`"
In Fossil, the default name for the main branch
is "`trunk`". The "`trunk`" branch in Fossil corresponds to the
|
| ︙ | ︙ | |||
949 950 951 952 953 954 955 |
git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master)
We believe you get such answers to Git help requests in part
because of its lack of an always-up-to-date [index into its log](#log) and in
part because of its “small tools loosely joined” design philosophy. This
sort of command is therefore composed piece by piece:
| | | 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 |
git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master)
We believe you get such answers to Git help requests in part
because of its lack of an always-up-to-date [index into its log](#log) and in
part because of its “small tools loosely joined” design philosophy. This
sort of command is therefore composed piece by piece:
<p style="text-align:center">◆ ◆ ◆</p>
“Oh, I know, I’ll search the rev-list, which outputs commit IDs by
parsing the log backwards from `HEAD`! Easy!”
git rev-list --before=2020-03-17
“Blast! Forgot the commit ID!”
|
| ︙ | ︙ | |||
983 984 985 986 987 988 989 |
“Better. Let’s check it out:”
git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master)
“Success, I guess?”
| | | 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 |
“Better. Let’s check it out:”
git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master)
“Success, I guess?”
<p style="text-align:center">◆ ◆ ◆</p>
This vignette is meant to explain some of Git’s popularity: it rewards
the sort of people who enjoy puzzles, many of whom are software
developers and thus need a tool like Git. Too bad if you’re just a
normal user.
And too bad if you’re a Windows user who doesn’t want to use [Git
|
| ︙ | ︙ | |||
1183 1184 1185 1186 1187 1188 1189 |
Where Fossil really wins is in the next step, making the initial commit
from home:
fossil ci
It’s one short command for Fossil instead of three for Git — or two if
you abbreviate it as “`git commit -a && git push`” — because of Fossil’s
| | | 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 |
Where Fossil really wins is in the next step, making the initial commit
from home:
fossil ci
It’s one short command for Fossil instead of three for Git — or two if
you abbreviate it as “`git commit -a && git push`” — because of Fossil’s
[autosync] feature and deliberate omission of a
[staging feature](#staging).
The “Friday afternoon sync-up” case is simpler, too:
fossil remote work
fossil sync
|
| ︙ | ︙ |
Changes to www/globs.md.
1 2 | # File Name Glob Patterns | < | | | | | | | | > > | | | > | > > > | > | > > > | > | | | 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 |
# File Name Glob Patterns
A [glob pattern][glob] is a text expression that matches one or more
file names using wildcards familiar to most users of a command line.
For example, `*` is a glob that matches any name at all, and
`Readme.txt` is a glob that matches exactly one file. For purposes of
Fossil's globs, a complete path name is just a string,
and the globs do not apply any special meaning to the directory part
of the name. Thus, the glob `*` matches any name, including any
directory prefix, and `*/*` matches a name with _one or more_
directory components.
A glob should not be confused with a [regular expression][regexp] (RE)
even though they use some of the same special characters for similar
purposes. [They are not fully compatible][greinc] pattern
matching languages. Fossil uses globs when matching file names with the
settings described in this document, not REs.
[glob]: https://en.wikipedia.org/wiki/Glob_(programming)
[greinc]: https://unix.stackexchange.com/a/57958/138
[regexp]: https://en.wikipedia.org/wiki/Regular_expression
[Fossil’s `*-glob` settings](#settings) hold one or more patterns to cause Fossil to
give matching named files special treatment. Glob patterns are also
accepted in options to certain commands and as query parameters to
certain Fossil UI web pages. For consistency, settings such as
`empty-dirs` are parsed as a glob even though they aren’t then *applied*
as a glob since it allows [the same syntax rules](#syntax) to apply.
Where Fossil also accepts globs in commands, this handling may interact
with your OS’s command shell or its C runtime system, because they may
have their own glob pattern handling. We will detail such interactions
below.
## <a id="syntax"></a>Syntax
Where Fossil accepts glob patterns, it will usually accept a *list* of
individual patterns separated from the others by whitespace or commas.
The parser allows whitespace and commas in a pattern by quoting _the
entire pattern_ with either single or double quotation marks. Internal
quotation marks are treated literally. Moreover, a pattern that begins
with a quote mark ends when the first instance of the same mark occurs,
_not_ at a whitespace or comma. Thus, this:
"foo bar"qux
…constitutes _two_ patterns rather than one with an embedded space, in
contravention of normal shell quoting rules.
A list matches a file when any pattern in that list matches.
A pattern must consume and
match the *entire* file name to succeed. Partial matches are failed matches.
Most characters in a glob pattern consume a single character of the file
name and must match it exactly. For instance, “a” in a glob simply
matches the letter “a” in the file name unless it is inside a special
character sequence.
Other characters have special meaning, and they may include otherwise
normal characters to give them special meaning:
:Pattern |:Effect
---------------------------------------------------------------------
`*` | Matches any sequence of zero or more characters
`?` | Matches exactly one character
`[...]` | Matches one character from the enclosed list of characters
`[^...]` | Matches one character *not* in the enclosed list
Note that unlike [POSIX globs][pg], these special characters and
sequences are allowed to match `/` directory separators as well as the
initial `.` in the name of a hidden file or directory. This is because
Fossil file names are stored as complete path names. The distinction
between file name and directory name is “underneath” Fossil in this sense.
[pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13
The bracket expressions above require some additional explanation:
* A range of characters may be specified with `-`, so `[a-f]` matches
exactly the same characters as `[abcdef]`. Ranges reflect Unicode
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 | :Pattern |:Effect -------------------------------------------------------------------------------- `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. ## Where Globs are Used | > | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | :Pattern |:Effect -------------------------------------------------------------------------------- `README` | Matches only a file named `README` in the root of the tree. It does not match a file named `src/README` because it does not include any characters that consume (and match) the `src/` part. `*/README` | Matches `src/README`. Unlike Unix file globs, it also matches `src/library/README`. However it does not match the file `README` in the root of the tree. `*README` | Matches `src/README` as well as the file `README` in the root of the tree as well as `foo/bar/README` or any other file named `README` in the tree. However, it also matches `A-DIFFERENT-README` and `src/DO-NOT-README`, or any other file whose name ends with `README`. `src/README` | Matches `src\README` on Windows because all directory separators are rewritten as `/` in the canonical name before the glob is matched. This makes it much easier to write globs that work on both Unix and Windows. `*.[ch]` | Matches every C source or header file in the tree at the root or at any depth. Again, this is (deliberately) different from Unix file globs and Windows wild cards. ## Where Globs are Used ### <a id="settings"></a>Settings that are Globs These settings are all lists of glob patterns: :Setting |:Description -------------------------------------------------------------------------------- `binary-glob` | Files that should be treated as binary files for committing and merging purposes `clean-glob` | Files that the [`clean`][] command will delete without prompting or allowing undo |
| ︙ | ︙ | |||
195 196 197 198 199 200 201 202 | The `ignore-glob` is an example of one setting that frequently grows to be an elaborate list of files that should be ignored by most commands. This is especially true when one (or more) IDEs are used in a project because each IDE has its own ideas of how and where to cache information that speeds up its browsing and building tasks but which need not be preserved in your project's history. | > > > > | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | The `ignore-glob` is an example of one setting that frequently grows to be an elaborate list of files that should be ignored by most commands. This is especially true when one (or more) IDEs are used in a project because each IDE has its own ideas of how and where to cache information that speeds up its browsing and building tasks but which need not be preserved in your project's history. Although the `empty-dirs` setting is not a list of glob patterns as such, it is *parsed* that way for consistency among the settings, allowing [the list parsing rules above](#syntax) to apply. ### <a id="commands"></a>Commands that Refer to Globs Many of the commands that respect the settings containing globs have options to override some or all of the settings. These options are usually named to correspond to the setting they override, such as `--ignore` to override the `ignore-glob` setting. These commands are: * [`add`][] |
| ︙ | ︙ | |||
225 226 227 228 229 230 231 | than archiving the entire checkin. The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that implement or support with web servers provide a mechanism to name some files to serve with static content where a list of glob patterns specifies what content may be served. | | | | | | | | | | | | | | | | | | | | | 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 | than archiving the entire checkin. The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that implement or support with web servers provide a mechanism to name some files to serve with static content where a list of glob patterns specifies what content may be served. [`add`]: /help?cmd=add [`addremove`]: /help?cmd=addremove [`changes`]: /help?cmd=changes [`clean`]: /help?cmd=clean [`commit`]: /help?cmd=commit [`extras`]: /help?cmd=extras [`merge`]: /help?cmd=merge [`settings`]: /help?cmd=settings [`status`]: /help?cmd=status [`touch`]: /help?cmd=touch [`unset`]: /help?cmd=unset [`tarball`]: /help?cmd=tarball [`zip`]: /help?cmd=zip [`http`]: /help?cmd=http [`cgi`]: /help?cmd=cgi [`server`]: /help?cmd=server [`ui`]: /help?cmd=ui ### Web Pages that Refer to Globs The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that names a list of glob patterns defining which files to focus the timeline on. It also has the query parameters `t=TAG` and `r=TAG` that names a tag to focus on, which can be configured with `ms=STYLE` to use a glob pattern to match tag names instead of the default exact match or a couple of other comparison styles. The pages [`/tarball`][] and [`/zip`][] generate compressed archives of a specific checkin. They may be further restricted by query parameters that specify glob patterns that name files to include or exclude rather than taking the entire checkin. [`/timeline`]: /help?cmd=/timeline [`/tarball`]: /help?cmd=/tarball [`/zip`]: /help?cmd=/zip ## Platform Quirks Fossil glob patterns are based on the glob pattern feature of POSIX shells. Fossil glob patterns also have a quoting mechanism, discussed [above](#syntax). Because other parts of your operating system may interpret glob patterns and quotes separately from Fossil, it is often difficult to give glob patterns correctly to Fossil on the command line. Quotes and special characters in glob patterns are likely to be interpreted when given as part of a `fossil` command, causing unexpected behavior. These problems do not affect [versioned settings files](settings.wiki) or Admin → Settings in Fossil UI. Consequently, it is better to |
| ︙ | ︙ | |||
355 356 357 358 359 360 361 |
$ fossil add --ignore "\"REALLY SECRET STUFF.txt\"" READ*
It bears repeating that the two glob patterns here are not interpreted
the same way when running this command from a *subdirectory* of the top
checkout directory as when running it at the top of the checkout tree.
If these files were in a subdirectory of the checkout tree called `doc`
| | | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
$ fossil add --ignore "\"REALLY SECRET STUFF.txt\"" READ*
It bears repeating that the two glob patterns here are not interpreted
the same way when running this command from a *subdirectory* of the top
checkout directory as when running it at the top of the checkout tree.
If these files were in a subdirectory of the checkout tree called `doc`
and that was your current working directory, the command would instead
have to be:
$ fossil add --ignore "'doc/REALLY SECRET STUFF.txt'" READ*
The Fossil glob pattern still needs the `doc/` prefix because
Fossil always interprets glob patterns from the base of the checkout
directory, not from the current working directory as POSIX shells do.
When in doubt, use `fossil status` after running commands like the
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
|
| ︙ | ︙ | |||
530 531 532 533 534 535 536 | With that in mind, translating a `.gitignore` file into `.fossil-settings/ignore-glob` may be possible in many cases. Here are some of features of `.gitignore` and comments on how they relate to Fossil: * "A blank line matches no files...": same in Fossil. | | > > | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
With that in mind, translating a `.gitignore` file into
`.fossil-settings/ignore-glob` may be possible in many cases. Here are
some of features of `.gitignore` and comments on how they relate to
Fossil:
* "A blank line matches no files...": same in Fossil.
* "A line starting with # serves as a comment...": same in Fossil, including
the possibility of escaping an initial `#` with a backslash to allow globs
beginning with a hash.
* "Trailing spaces are ignored unless they are quoted..." is similar
in Fossil. All whitespace before and after a glob is trimmed in
Fossil unless quoted with single or double quotes. Git uses
backslash quoting instead, which Fossil does not.
* "An optional prefix "!" which negates the pattern...": not in
Fossil.
* Git's globs are relative to the location of the `.gitignore` file:
|
| ︙ | ︙ |
Changes to www/glossary.md.
| ︙ | ︙ | |||
197 198 199 200 201 202 203 | move right 0.1 line dotted right until even with previous line.end move right 0.05 box invis "clones of Fossil itself, SQLite, etc." ljust ``` [asdis]: /help?cmd=autosync | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | > > > > > > | < < < < | | | > > > > > > > > > > > > > > > > > > | | | 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 |
move right 0.1
line dotted right until even with previous line.end
move right 0.05
box invis "clones of Fossil itself, SQLite, etc." ljust
```
[asdis]: /help?cmd=autosync
[backup]: ./backup.md
[CAP]: ./cap-theorem.md
[cloned]: /help?cmd=clone
[pull]: /help?cmd=pull
[push]: /help?cmd=push
[svrcmd]: /help?cmd=server
[sync]: /help?cmd=sync
[repository]: #repo
[repositories]: #repo
## Version / Revision / Hash / UUID <a id="version" name="hash"></a>
These terms all mean the same thing: a long hexadecimal
[SHA hash value](./hashes.md) that uniquely identifies a particular
[check-in](#ci).
We’ve listed the alternatives in decreasing preference order:
* **Version** and **revision** are near-synonyms in common usage.
Fossil’s code and documentation use both interchangeably because
Fossil was created to manage the development of the SQLite project,
which formerly used [CVS], the Concurrent Versions System. CVS in
turn started out as a front-end to [RCS], the Revision Control
System, but even though CVS uses “version” in its name, it numbers
check-ins using a system derived from RCS’s scheme, which it calls
“Revisions” in user-facing output. Fossil inherits this confusion
honestly.
* **Hash** refers to the [SHA1 or SHA3-256 hash](./hashes.md) of the
content of the checked-in data, uniquely identifying that version of
the managed files. It is a strictly correct synonym, used more often
in low-level contexts than the term “version.”
* **UUID** is a deprecated term still found in many parts of the
Fossil internals and (decreasingly) its documentation. The problem
with using this as a synonym for a Fossil-managed version of the
managed files is that there are [standards][UUID] defining the
format of a “UUID,” none of which Fossil follows, not even the
[version 4][ruuid] (random) format, the type of UUID closest in
meaning and usage to a Fossil hash.(^A pre-Fossil 2.0 style SHA1
hash is 160 bits, not the 128 bits many people expect for a proper
UUID, and even if you truncate it to 128 bits to create a “good
enough” version prefix, the 6 bits reserved in the UUID format for
the variant code cannot make a correct declaration except by a
random 1:64 chance. The SHA3-256 option allowed in Fossil 2.0 and
higher doesn’t help with this confusion, making a Fossil version
hash twice as large as a proper UUID. Alas, the term will never be
fully obliterated from use since there are columns in the Fossil
repository format that use the obsolete term; we cannot change this
without breaking backwards compatibility.)
You will find all of these synonyms used in the Fossil documentation.
Some day we may settle on a single term, but it doesn’t seem likely.
[CVS]: https://en.wikipedia.org/wiki/Concurrent_Versions_System
[hash]: #version
[RCS]: https://en.wikipedia.org/wiki/Revision_Control_System
[ruuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
[snfs]: https://en.wikipedia.org/wiki/Snapshot_(computer_storage)#File_systems
[UUID]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
[version]: #version
## Check-in <a id="check-in" name="ci"></a>
A [version] of the project’s files that have been committed to the
[repository]; as such, it is sometimes called a “commit” instead. A
check-in is a snapshot of the project at an instant in time, as seen from
a single [check-out’s](#co) perspective. It is sometimes styled
“`CHECKIN`”, especially in command documentation where any
[valid check-in name][ciname] can be used.
* There is a harmless conflation of terms here: any of the various
synonyms for [version] may be used where “check-in” is more accurate,
and vice versa, because there is a 1:1 relation between them. A
check-in *has* a version, but a version suffices to uniquely look up
a particular commit.[^snapshot]
* Combining both sets of synonyms results in a list of terms that is
confusing to new Fossil users, but it’s easy
enough to internalize the concepts. [Committing][commit] creates a
*commit.* It may also be said to create a checked-in *version* of a
particular *revision* of the project’s files, thus creating an
immutable *snapshot* of the project’s state at the time of the
commit. Fossil users find each of these different words for the
same concept useful for expressive purposes among ourselves, but to
Fossil proper, they all mean the same thing.
* Check-ins are immutable.
* Check-ins exist only inside the repository. Contrast a
[check-out](#co).
* Check-ins may have [one or more names][ciname], but only the
[hash] is globally unique, across all time; we call it the check-in’s
canonical name. The other names are either imprecise, contextual, or
change their meaning over time and across [repositories].
[^snapshot]: You may sometimes see the term “snapshot” used as a synonym
for a check-in or the version number identifying said check-in. We
must warn against this usage because there is a potential confusion
here: [the `stash` command][stash] uses the term “snapshot,” as does
[the `undo` system][undo] to make a distinction with check-ins.
Nevertheless, there is a conceptual overlap here between Fossil and
systems that do use the term “snapshot,” the primary distinction being
that Fossil will capture only changes to files you’ve [added][add] to
the [repository], not to everything in [the check-out directory](#co)
at the time of the snapshot. (Thus [the `extras` command][extras].)
Contrast a snapshot taken by a virtual machine system or a
[snapshotting file system][snfs], which captures changes to everything
on the managed storage volume.
[add]: /help?cmd=add
[ciname]: ./checkin_names.wiki
[extras]: /help?cmd=extras
[stash]: /help?cmd=stash
[undo]: /help?cmd=undo
## Check-out <a id="check-out" name="co"></a>
A set of files extracted from a [repository] that represent a
particular [check-in](#ci) of the [project](#project).
* Unlike a check-in, a check-out is mutable. It may start out as a
version of a particular check-in extracted from the repository, but
the user is then free to make modifications to the checked-out
files. Once those changes are formally [committed][commit], they
become a new immutable check-in, derived from its parent check-in.
* You can switch from one check-in to another within a check-out
directory by passing those names to [the `fossil update`
command][update].
* Check-outs relate to repositories in a one-to-many fashion: it is
common to have a single repo clone on a machine but to have it
[open] in [multiple working directories][mwd]. Check-out directories
are associated with the repos they were created from by settings
stored in the check-out directory. This is in the `.fslckout` file on
POSIX type systems, but for historical compatibility reasons, it’s
called `_FOSSIL_` by native Windows builds of Fossil.
(Contrast the Cygwin and WSL Fossil binaries, which use POSIX file
naming rules.)
* In the same way that one cannot extract files from a zip archive
|
| ︙ | ︙ | |||
370 371 372 373 374 375 376 377 | [edoc]: ./embeddeddoc.wiki [fef]: ./fileedit-page.md [fshr]: ./selfhost.wiki [wiki]: ./wikitheory.wiki <div style="height:50em" id="this-space-intentionally-left-blank"></div> | > > > > > > > > > > > > > > > | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 | [edoc]: ./embeddeddoc.wiki [fef]: ./fileedit-page.md [fshr]: ./selfhost.wiki [wiki]: ./wikitheory.wiki ## <a id="cap"></a>Capability Fossil includes a powerful [role-based access control system][rbac] which affects which users have permission to do certain things within a given [repository]. You can read more about this complex topic [here](./caps/). Some people — and indeed certain parts of Fossil’s own code — use the term “permissions” instead, but since [operating system file permissions also play into this](./caps/#webonly), we prefer the term “capabilities” (or “caps” for short) when talking about Fossil’s RBAC system to avoid a confusion here. [rbac]: https://en.wikipedia.org/wiki/Role-based_access_control <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
Changes to www/image-format-vs-repo-size.md.
1 2 3 4 5 | # Image Format vs Fossil Repo Size ## The Problem Fossil has a [delta compression][dc] feature which removes redundant | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Image Format vs Fossil Repo Size
## The Problem
Fossil has a [delta compression][dc] feature which removes redundant
information from a file relative to its parent on check-in.[^delta-prgs]
That delta is then [zlib][zl]-compressed before being stored
in the Fossil repository database file.
Storing pre-compressed data files in a Fossil repository defeats both of
these space-saving measures:
1. Binary data compression algorithms turn the file data into
pseudorandom noise.[^prn]
Typical data compression algorithms are not [hash functions][hf],
where the goal is that a change to each bit in the input has a
statistically even chance of changing every bit in the output, but
because they do approach that pathological condition, pre-compressed
data tends to defeat Fossil’s delta compression algorithm, there
being so little correlation between two different outputs from the
|
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | itself? It really doesn’t, as far as point 2 above goes, but point 1 causes the Fossil repository to balloon out of proportion to the size of the input data change on each checkin. This article will illustrate that problem, quantify it, and give a solution to it. [dc]: ./delta_format.wiki [hf]: https://en.wikipedia.org/wiki/Hash_function | < | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | itself? It really doesn’t, as far as point 2 above goes, but point 1 causes the Fossil repository to balloon out of proportion to the size of the input data change on each checkin. This article will illustrate that problem, quantify it, and give a solution to it. [dc]: ./delta_format.wiki [hf]: https://en.wikipedia.org/wiki/Hash_function [zl]: http://www.zlib.net/ ## <a id="formats"></a>Affected File Formats In this article’s core experiment, we use 2D image file formats, but this article’s advice also applies to many other file types. For just a |
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
4. Change a random pixel in the image to a random RGB value, save that
image, check it in, and remember the new Fossil repo size.
5. Iterate on step 4 some number of times — currently 10 — and remember
the Fossil repo size at each step.
| | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
4. Change a random pixel in the image to a random RGB value, save that
image, check it in, and remember the new Fossil repo size.
5. Iterate on step 4 some number of times — currently 10 — and remember
the Fossil repo size at each step.
6. Repeat the above steps for BMP, PNG, and TIFF.[^tiff-cmp]
7. Create a bar chart showing how the Fossil repository size changes
with each checkin.
We chose to use JupyterLab for this because it makes it easy for you to
modify the notebook to try different things. Want to see how the
results change with a different image size? Easy, change the `size`
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | [nbd]: ./image-format-vs-repo-size.ipynb [nbp]: https://nbviewer.jupyter.org/urls/fossil-scm.org/fossil/doc/trunk/www/image-format-vs-repo-size.ipynb [wp]: http://wand-py.org/ ## <a id="results"></a>Results | | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
[nbd]: ./image-format-vs-repo-size.ipynb
[nbp]: https://nbviewer.jupyter.org/urls/fossil-scm.org/fossil/doc/trunk/www/image-format-vs-repo-size.ipynb
[wp]: http://wand-py.org/
## <a id="results"></a>Results
Running the notebook gives a bar chart something like[^variance] this:

There are a few key things we want to draw your attention to in that
chart:
* BMP and uncompressed TIFF are nearly identical in size for all
checkins, and the repository growth rate is negligible past the
first commit.[^size-jump] We owe this economy to Fossil’s delta compression
feature: it is encoding each of those single-pixel changes in a very
small amount of repository space.
* The JPEG and PNG bars increase by large amounts on most checkins
even though each checkin *also* encodes only a *single-pixel change*.
* The size of the first checkin in the BMP and TIFF cases is roughly
|
| ︙ | ︙ | |||
156 157 158 159 160 161 162 | ## <a id="makefile"></a>Automated Recompression Since programs that produce and consume binary-compressed data files often make it either difficult or impossible to work with the uncompressed form, we want an automated method for producing the uncompressed form to make Fossil happy while still having the compressed form to keep our content creation applications happy. This `Makefile` | | | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
## <a id="makefile"></a>Automated Recompression
Since programs that produce and consume binary-compressed data files
often make it either difficult or impossible to work with the
uncompressed form, we want an automated method for producing the
uncompressed form to make Fossil happy while still having the compressed
form to keep our content creation applications happy. This `Makefile`
should[^makefile] do that for BMP, PNG, SVG, and XLSX files:
.SUFFIXES: .bmp .png .svg .svgz
.svgz.svg:
gzip -dc < $< > $@
.svg.svgz:
|
| ︙ | ︙ | |||
192 193 194 195 196 197 198 |
doc-big.pdf: doc-small.pdf
qpdf --stream-data=uncompress $@ $<
This `Makefile` allows you to treat the compressed version as the
process input, but to actually check in only the changes against the
uncompressed version by typing “`make`” before “`fossil ci`”. This is
not actually an extra step in practice, since if you’ve got a
| | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
doc-big.pdf: doc-small.pdf
qpdf --stream-data=uncompress $@ $<
This `Makefile` allows you to treat the compressed version as the
process input, but to actually check in only the changes against the
uncompressed version by typing “`make`” before “`fossil ci`”. This is
not actually an extra step in practice, since if you’ve got a
`Makefile`-based project, you should be building — and testing — it
before checking each change in anyway!
Because this technique is based on dependency rules, only the necessary
files are generated on each `make` command.
You only have to run “`make reconstitute`” *once* after opening a fresh
Fossil checkout to produce those compressed sources. After that, you
|
| ︙ | ︙ | |||
237 238 239 240 241 242 243 |
output. Since the file name extension is the same either way, we
treat the compressed PDF as the source to the process, yielding an
automatically-uncompressed PDF for the benefit of Fossil. Unlike
with the Excel case, there is no simple “file base name to directory
name” mapping, so we just created the `-big` to `-small` name scheme
here.
| < | < < | < > | > | > | > | < < > | | 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 |
output. Since the file name extension is the same either way, we
treat the compressed PDF as the source to the process, yielding an
automatically-uncompressed PDF for the benefit of Fossil. Unlike
with the Excel case, there is no simple “file base name to directory
name” mapping, so we just created the `-big` to `-small` name scheme
here.
[^delta-prgs]:
This problem is not Fossil-specific. Several other programs also do
delta compression, so they’ll also be affected by this problem:
[rsync][rs], [Unison][us], [Git][git], etc. You should take this
article’s advice when using all such programs, not just Fossil.
When using file copying and synchronization programs *without* delta
compression, on the other hand, it’s best to use the most
highly-compressed file format you can tolerate, since they copy the
whole file any time any bit of it changes.
[^prn]:
In fact, a good way to gauge the effectiveness of a given
compression scheme is to run its output through the same sort of
tests we use to gauge how “random” a given [PRNG][prng] is. Another
way to look at it is that if there is a discernible pattern in the
output of a compression scheme, that constitutes *information* (in
[the technical sense of that word][ith]) that could be further
compressed.
[^tiff-cmp]:
We're using *uncompressed* TIFF here, not [LZW][lzw]- or
Zip-compressed TIFF, either of which would give similar results to
PNG, which is always zlib-compressed.
[^variance]:
The raw data changes somewhat from one run to the next due to the
use of random noise in the image to make the zlib/PNG compression
more difficult, and the random pixel changes. Those test design
choices make this a [Monte Carlo experiment][mce]. We’ve found that
the overall character of the results doesn’t change from one run to
the next.
[^size-jump]:
It’s not clear to me why there is a one-time jump in size for BMP
and TIFF past the first commit. I suspect it is due to the SQLite
indices being initialized for the first time.
Page size inflation might have something to do with it as well,
though we tried to control that by rebuilding the initial DB with a
minimal page size. If you re-run the program often enough, you will
sometimes see the BMP or TIFF bar jump higher than the other, again
likely due to one of the repos crossing a page boundary.
Another curious artifact in the data is that the BMP is slightly
larger than for the TIFF. This goes against expectation because a
low-tech format like BMP should have a small edge in this test
because TIFF metadata includes the option for multiple timestamps,
UUIDs, etc., which bloat the checkin size by creating many small
deltas.
[^makefile]:
The `Makefile` above is not battle-tested. Please report bugs and
needed extensions [on the forum][for].
[for]: https://fossil-scm.org/forum/forumpost/15e677f2c8
[git]: https://git-scm.com/
[ith]: https://en.wikipedia.org/wiki/Information_theory
[lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
[prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator
[rs]: https://rsync.samba.org/
[us]: http://www.cis.upenn.edu/~bcpierce/unison/
|
Changes to www/index.wiki.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 | <li> [./changes.wiki | Change Log] <li> [../COPYRIGHT-BSD2.txt | License] <li> [./userlinks.wiki | User Links] <li> [./hacker-howto.wiki | Hacker How-To] <li> [./fossil-v-git.wiki | Fossil vs. Git] <li> [./permutedindex.html | Doc Index] </ul> | | | | | 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 |
<li> [./changes.wiki | Change Log]
<li> [../COPYRIGHT-BSD2.txt | License]
<li> [./userlinks.wiki | User Links]
<li> [./hacker-howto.wiki | Hacker How-To]
<li> [./fossil-v-git.wiki | Fossil vs. Git]
<li> [./permutedindex.html | Doc Index]
</ul>
<p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p>
</div>
Fossil is a simple, high-reliability, distributed software configuration
management system with these advanced features:
1. <b>Project Management</b> -
In addition to doing [./concepts.wiki | distributed version control]
like Git and Mercurial,
Fossil also supports [./bugtheory.wiki | bug tracking],
[./wikitheory.wiki | wiki], [./forum.wiki | forum],
[./alerts.md|email alerts], [./chat.md | chat], and
[./event.wiki | technotes].
2. <b>Built-in Web Interface</b> -
Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible],
and intuitive [./webui.wiki | web interface]
with a rich variety of information pages
([./webpage-ex.md|examples]) promoting situational awareness.
<br><br>
This entire website is just a running instance of Fossil.
The pages you see here are all [./wikitheory.wiki | wiki] or
[./embeddeddoc.wiki | embedded documentation] or (in the case of
the [/uv/download.html|download] page)
[./unvers.wiki | unversioned files].
When you clone Fossil from one of its
[./selfhost.wiki | self-hosting repositories],
|
| ︙ | ︙ | |||
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.22 ([/timeline?c=version-2.22|2023-05-31])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_22|Change Summary]
* [/timeline?p=version-2.22&bt=version-2.21&y=ci|Check-ins in version 2.22]
* [/timeline?df=version-2.22&y=ci|Check-ins derived from the 2.22 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].
|
| ︙ | ︙ |
Added www/json-api/api-settings.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 |
# JSON API: /settings
([⬑JSON API Index](index.md))
Jump to:
* [Fetch Settings](#get)
* [Set Settings](#set)
---
<a id="get"></a>
# Fetch Settings
**Status:** Implemented 20230120
**Required permissions:** "o"
**Request:** `/json/settings/get[?version=X]`
**Response payload example:**
```json
{
"access-log":{
"versionable":false,
"sensitive":false,
"defaultValue":"off",
"valueSource":null,
"value":null
},
...
"binary-glob":{
"versionable":true,
"sensitive":false,
"defaultValue":null,
"valueSource":"versioned",
"value":"*.gif\n*.ico\n*.jpg\n*.odp\n*.dia\n*.pdf\n*.png\n*.wav..."
},
...
"web-browser":{
"versionable":false,
"sensitive":true,
"defaultValue":null,
"valueSource":"repo",
"value":"firefox"
}
}
```
Each key in the payload is the name of a fossil-recognized setting,
modeled as an object. The keys of that are:
- `defaultValue`: the setting's default value, or `null` if no default
is defined.
- `value`: The current value of that setting.
- `valueSource`: one of (`"repo"`, `"checkout"`, `"versioned"`, or
`null`), specifying the data source where the setting was found. The
settings sources are checked in this order and the first one found
is the result:
- If `version=X` is provided, check-in `X` is searched for a
versionable-settings file. If found, its value is used and
`valueSource` will be `"versioned"`. If `X` is not a checkin, an
error response is produced with code `FOSSIL-3006`.
- If a versionable setting is found in the current checkout, its
value is used and `valueSource` will be `"versioned"`
- If the setting is found in checkout database's `vvar` table, its
value is used and `valueSource` will be `"checkout"`.
- If the setting is found in repository's `config` table, its
value is used and `valueSource` will be `"repo"`.
- If no value is found, `null` is used for both the `value` and
`valueSource` results. Note that _global settings are never
checked_ because they can leak information which have nothing
specifically to do with the given repository.
- `sensitive`: a value which fossil has flagged as sensitive can only
be fetched by a Setup user. For other users, they will always have
a `value` and `valueSource` of `null`.
- `versionable`: `true` if the setting is tagged as versionable, else
`false`.
Note that settings are internally stored as strings, even if they're
semantically treated as numbers. The way settings are stored and
handled does not give us enough information to recognize their exact
data type here so they are passed on as-is.
<a id="set"></a>
# Set Settings
**Status:** Implemented 20230120
**Required permissions:** "s"
**Request:** `/json/settings/set`
This call requires that the input payload be an object containing a
mapping of fossil-known configuration keys (case-sensitive) to
values. For example:
```json
{
"editor": "emacs",
"admin-log": true,
"auto-captcha": false
}
```
It iterates through each property, which must have a data type of
`null`, boolean, number, or string. A value of `null` _unsets_
(deletes) the setting. Boolean values are stored as integer 0
or 1. All other types are stored as-is. It only updates the
`repository.config` database and never updates a checkout or global
config database, nor is it capable of updating versioned settings
(^Updating versioned settings requires creating a full check-in.).
It has no result payload but this may be changed in the future it
practice shows that it should return something specific.
Error responses include:
- `FOSSIL-2002`: called without "setup" permissions.
- `FOSSIL-3002`: called without a payload object.
- `FOSSIL-3001`: passed an unknown config option.
- `FOSSIL-3000`: a value has an unsupported data type.
If an error is triggered, any settings made by this call up until that
point are discarded.
|
Changes to www/json-api/index.md.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 | * [Checkout Status](api-checkout.md) * [Config](api-config.md) * [Diffs](api-diff.md) * [Directory Listing](api-dir.md) * [File Info](api-finfo.md) * [The Obligatory Misc. Category](api-misc.md) * [Repository Stats](api-stat.md) * [SQL Query](api-query.md) * [Tags](api-tag.md) * [Tickets](api-ticket.md) * [Timeline](api-timeline.md) * [User Management](api-user.md) * [Version](api-version.md) * [Wiki](api-wiki.md) | > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | * [Checkout Status](api-checkout.md) * [Config](api-config.md) * [Diffs](api-diff.md) * [Directory Listing](api-dir.md) * [File Info](api-finfo.md) * [The Obligatory Misc. Category](api-misc.md) * [Repository Stats](api-stat.md) * [Settings](api-settings.md) * [SQL Query](api-query.md) * [Tags](api-tag.md) * [Tickets](api-ticket.md) * [Timeline](api-timeline.md) * [User Management](api-user.md) * [Version](api-version.md) * [Wiki](api-wiki.md) |
Changes to www/mirrortogithub.md.
1 2 3 4 5 6 | # How To Mirror A Fossil Repository On GitHub Beginning with Fossil version 2.9, you can mirror a Fossil-based project on GitHub (with [limitations](./mirrorlimitations.md)) by following these steps: | < | | < | < | | > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < | | | | | < | < < | 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 |
# How To Mirror A Fossil Repository On GitHub
Beginning with Fossil version 2.9, you can mirror a Fossil-based
project on GitHub (with [limitations](./mirrorlimitations.md))
by following these steps:
1. Create an account on GitHub if you do not have one already. Log
into that account.
2. Create a new project. GitHub will ask you if you want to prepopulate
your project with various things like a README file. Answer "no" to
everything. You want a completely blank project. GitHub will then
supply you with a URL for your project that will look something
like this:
https://github.com/username/project.git
3. Back on your workstation, move to a checkout for your Fossil
project and type:
<blockquote>
<pre>
$ fossil git export /path/to/git/repo --autopush \
https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git
</pre>
</blockquote>
In place of the <code>/path/to...</code> argument above, put in
some directory name that is <i>outside</i> of your Fossil checkout. If
you keep multiple Fossil checkouts in a directory of their own,
consider using <code>../git-mirror</code> to place the Git export
mirror alongside them, for example. Fossil will create this
directory if necessary. This directory will become a Git
repository that holds a translation of your Fossil repository.
The <code>--autopush</code> option tells Fossil that you want to
push the Git translation up to GitHub every time it is updated.
The URL parameter is the same as the one GitHub gave you, but with
your GitHub <font color="orange">username</font> and <font
color="red">password</font> added.
If your GitHub account uses two-factor authentication (2FA), you
will have to <a href="https://github.com/settings/tokens">generate
a personal access token</a> and use that in place of your actual
password in the URL. This token should have “repo” scope enabled,
only.
You can also run the command above outside of any open checkout of
your project by supplying the “<code>-R repository</code>”
option.
4. Get some coffee. Depending on the size of your project, the
initial "<code>fossil git export</code>" command in the previous
step might run for several minutes.
5. And you are done! Assuming everything worked, your project is now
mirrored on GitHub.
6. Whenever you update your project, simply run this command to update
the mirror:
$ fossil git export
Unlike with the first time you ran that command, you don’t need
the remaining arguments, because Fossil remembers those things.
Subsequent mirror updates should usually happen in a fraction of
a second.
7. To see the status of your mirror, run:
$ fossil git status
## Notes:
* Unless you specify --force, the mirroring only happens if the Fossil
repo has changed, with Fossil reporting "no changes", because Fossil
does not care about the success or failure of the mirror run. If a mirror
run failed (for example, due to an incorrect password, or a transient
|
| ︙ | ︙ |
Changes to www/mkindex.tcl.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
backup.md {Backing Up a Remote Fossil Repository}
blame.wiki {The Annotate/Blame Algorithm Of Fossil}
blockchain.md {Is Fossil A Blockchain?}
branching.wiki {Branching, Forking, Merging, and Tagging}
bugtheory.wiki {Bug Tracking In Fossil}
build.wiki {Compiling and Installing Fossil}
cap-theorem.md {Fossil and the CAP Theorem}
| | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
backup.md {Backing Up a Remote Fossil Repository}
blame.wiki {The Annotate/Blame Algorithm Of Fossil}
blockchain.md {Is Fossil A Blockchain?}
branching.wiki {Branching, Forking, Merging, and Tagging}
bugtheory.wiki {Bug Tracking In Fossil}
build.wiki {Compiling and Installing Fossil}
cap-theorem.md {Fossil and the CAP Theorem}
caps/ {Administering User Capabilities (a.k.a. Permissions)}
caps/admin-v-setup.md {Differences Between Setup and Admin Users}
caps/ref.html {User Capability Reference}
cgi.wiki {CGI Script Configuration Options}
changes.wiki {Fossil Changelog}
chat.md {Fossil Chat}
checkin_names.wiki {Check-in And Version Names}
checkin.wiki {Check-in Checklist}
|
| ︙ | ︙ | |||
151 152 153 154 155 156 157 |
}
set permindex [lsort -dict -index 0 $permindex]
set out [open permutedindex.html w]
fconfigure $out -encoding utf-8 -translation lf
puts $out \
"<div class='fossil-doc' data-title='Index Of Fossil Documentation'>"
puts $out {
| < | < | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
}
set permindex [lsort -dict -index 0 $permindex]
set out [open permutedindex.html w]
fconfigure $out -encoding utf-8 -translation lf
puts $out \
"<div class='fossil-doc' data-title='Index Of Fossil Documentation'>"
puts $out {
<form action='$ROOT/docsrch' method='GET' style="text-align:center">
<input type="text" name="s" size="40" autofocus>
<input type="submit" value="Search Docs">
</form>
<h2>Primary Documents:</h2>
<ul>
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
<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>
|
| ︙ | ︙ |
Changes to www/newrepo.wiki.
1 2 | <title>How To Create A New Fossil Repository</title> | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <title>How To Create A New Fossil Repository</title> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how to get up and running with fossil. But once you're running, what can you do with it? This document will walk you through the process of creating a fossil repository, populating it with files, and then sharing it over the web. The first thing we need to do is create a fossil repository file: <verbatim> stephan@ludo:~/fossil$ fossil new demo.fossil project-id: 9d8ccff5671796ee04e60af6932aa7788f0a990a server-id: 145fe7d71e3b513ac37ac283979d73e12ca04bfe |
| ︙ | ︙ |
Changes to www/permutedindex.html.
1 2 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> | < | < | | 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 | <div class='fossil-doc' data-title='Index Of Fossil Documentation'> <form action='$ROOT/docsrch' method='GET' style="text-align:center"> <input type="text" name="s" size="40" autofocus> <input type="submit" value="Search Docs"> </form> <h2>Primary Documents:</h2> <ul> <li> <a href='quickstart.wiki'>Quick-start Guide</a> <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> <ul><li><a href='$ROOT/wiki?name=Release Build How-To'>Release Build How-To</a>, a.k.a. how deliverables are built</li></ul> </li> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> <li> <a href='https://fossil-scm.org/fossil-book/'>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.k.a. Permissions)</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> <li><a href="branching.wiki">Branching, Forking, Merging, and Tagging</a></li> <li><a href="bugtheory.wiki">Bug Tracking In Fossil</a></li> <li><a href="cgi.wiki">CGI Script Configuration Options</a></li> <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> |
| ︙ | ︙ |
Changes to www/pop.wiki.
1 2 3 | <title>Principles Of Operation</title> <h1 align="center">Principles Of Operation</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 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 |
<title>Principles Of Operation</title>
<h1 align="center">Principles Of Operation</h1>
This page attempts to define the foundational principals upon
which Fossil is built.
* A project consists of source files, wiki pages, and
trouble tickets, and control files (collectively "artifacts").
All historical copies of all artifacts
are saved. The project maintains an audit
trail.
* A project resides in one or more repositories. Each
repository is administered and operates independently
of the others.
* Each repository has both global and local state. The
global state is common to all repositories (or at least
has the potential to be shared in common when the
repositories are fully synchronized). The local state
for each repository is private to that repository.
The global state represents the content of the project.
The local state identifies the authorized users and
access policies for a particular repository.
* The global state of a repository is an unordered
collection of artifacts. Each artifact is named by a
cryptographic hash (SHA1 or SHA3-256) encoded in
lowercase hexadecimal.
In many contexts, the name can be
abbreviated to a unique prefix. A five- or six-character
prefix usually suffices to uniquely identify a file.
* Because artifacts are named by a cryptographic hash, all artifacts
are immutable. Any change to the content of an artifact also
changes the hash that forms the artifacts name, thus
creating a new artifact. Both the old original version of the
artifact and the new change are preserved under different names.
* It is theoretically possible for two artifacts with different
content to share the same hash. But finding two such
artifacts is so incredibly difficult and unlikely that we
consider it to be an impossibility.
* The signature of an artifact is the cryptographic hash of the
artifact itself, exactly as it would appear in a disk file. No prefix
or meta-information about the artifact is added before computing
the hash. So you can
always find the signature of a file by using the
"sha1sum" or "sha3sum" or similar command-line utilities.
* The artifacts that comprise the global state of a repository
are the complete global state of that repository. The SQLite
database that holds the repository contains additional information
about linkages between artifacts, but all of that added information
can be discarded and reconstructed by rescanning the content
artifacts.
* Two repositories for the same project can synchronize
their global states simply by sharing artifacts. The local
state of repositories is not normally synchronized or
shared.
* Every check-in has a special file at the top-level
named "manifest" which is an index of all other files in
that check-in. The manifest is automatically created and
maintained by the system.
* The <a href="fileformat.wiki">file formats</a>
used by Fossil are all very simple so that with access
to the original content files, one can easily reconstruct
the content of a check-in without the need for any
special tools or software.
|
Changes to www/qandc.wiki.
1 2 3 4 | <title>Questions And Criticisms</title> <nowiki> <h1 align="center">Questions And Criticisms</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 |
<title>Questions And Criticisms</title>
<nowiki>
<h1 align="center">Questions And Criticisms</h1>
This page is a collection of real questions and criticisms that were
raised against Fossil early in its history (circa 2008).
This page is old and has not been kept up-to-date. See the
</nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.
<b>Fossil sounds like a lot of reinvention of the wheel.
Why create your own DVCS when you could have reused mercurial?</b>
<blockquote>
I wrote fossil because none of the
other available DVCSes met my needs. If the other DVCSes do
meet your needs, then you might not need fossil. But they
don't meet mine, and so fossil is necessary for me.
Features provided by fossil that one does not get with other
DVCSes include:
<ol>
<li> Integrated <a href="wikitheory.wiki">wiki</a>. </li>
<li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li>
<li> Immutable artifacts </li>
<li> Self-contained, stand-alone executable that can be run in
a <a href="http://en.wikipedia.org/wiki/Chroot">chroot jail</a> </li>
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 | the massive user base of git or mercurial. </blockquote> <b>Fossil looks like the bug tracker that would be in your Linksys Router's administration screen.</b> <blockquote> | | | | | | | | | | 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 |
the massive user base of git or mercurial.
</blockquote>
<b>Fossil looks like the bug tracker that would be in your
Linksys Router's administration screen.</b>
<blockquote>
I take a pragmatic approach to software: form follows function.
To me, it is more important to have a reliable, fast, efficient,
enduring, and simple DVCS than one that looks pretty.
On the other hand, if you have patches that improve the appearance
of Fossil without seriously compromising its reliability, performance,
and/or maintainability, I will be happy to accept them. Fossil is
self-hosting. Send email to request a password that will let
you push to the main fossil repository.
</blockquote>
<b>It would be useful to have a separate application that
keeps the bug-tracking database in a versioned file. That file can
then be pushed and pulled along with the rest repository.</b>
<blockquote>
Fossil already <u>does</u> push and pull bugs along with the files
in your repository.
But fossil does <u>not</u> track bugs as files in the source tree.
That approach to bug tracking was rejected for three reasons:
<ol>
<li> Check-ins in fossil are immutable. So if
tickets were part of the check-in, then there would be no way to add
new tickets to a check-in as new bugs are discovered.
<li> Any project of reasonable size and complexity will generate thousands
and thousands of tickets, and we do not want all those ticket files
cluttering the source tree.
<li> We want tickets to be managed from the web interface and to have a
permission system that is distinct from check-in permissions.
In other words, we do not want to restrict the creation and editing
of tickets to developers with check-in privileges and an installed
copy of the fossil executable. Casual passers-by on the internet should
be permitted to create tickets.
</ol>
These points are reiterated in the opening paragraphs of
the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.
</blockquote>
<b>Fossil is already the name of a plan9 versioned
append-only filesystem.</b>
<blockquote>
I did not know that. Perhaps they selected the name for the same reason that
|
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | <b>I am dubious of the benefits of including wikis and bug trackers directly in the VCS - either they are under-featured compared to full software like Trac, or the VCS is massively bloated compared to Subversion or Bazaar.</b> <blockquote> | | | | | | 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 | <b>I am dubious of the benefits of including wikis and bug trackers directly in the VCS - either they are under-featured compared to full software like Trac, or the VCS is massively bloated compared to Subversion or Bazaar.</b> <blockquote> I have no doubt that Trac has many features that fossil lacks. But that is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, sqlite, and so forth) in order to run fossil. Fossil runs just fine in a chroot jail all by itself. And the self-contained fossil executable is much less than 1MB in size. (Update 2015-01-12: Fossil has grown in the years since the previous sentence was written but is still much less than 2MB according to "size" when compiled using -Os on x64 Linux.) Fossil is the very opposite of bloat. </blockquote> </nowiki> |
Changes to www/quickstart.wiki.
1 2 3 | <title>Fossil Quick Start Guide</title> <h1 align="center">Fossil Quick Start</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 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 |
<title>Fossil Quick Start Guide</title>
<h1 align="center">Fossil Quick Start</h1>
This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly
and painlessly.
<h2 id="install">Installing</h2>
Fossil is a single self-contained C program. You need to
either download a
[https://fossil-scm.org/home/uv/download.html|precompiled
binary]
or <a href="build.wiki">compile it yourself</a> from sources.
Install Fossil by putting the fossil binary
someplace on your $PATH.
You can test that Fossil is present and working like this:
<blockquote>
<b>
fossil version<br>
<tt>This is fossil version 2.13 [309af345ab] 2020-09-28 04:02:55 UTC</tt><br>
</b>
</blockquote>
<h2 id="workflow" name="fslclone">General Work Flow</h2>
Fossil works with repository files (a database in a single file with the project's
complete history) and with checked-out local trees (the working directory
you use to do your work).
(See [./glossary.md | the glossary] for more background.)
The workflow looks like this:
<ul>
<li>Create or clone a repository file. ([/help/init|fossil init] or
[/help/clone | fossil clone])
<li>Check out a local tree. ([/help/open | fossil open])
<li>Perform operations on the repository (including repository
configuration).
</ul>
Fossil can be entirely driven from the command line. Many features
can also be conveniently accessed from the built-in web user interface.
The following sections give a brief overview of these
operations.
<h2 id="new">Starting A New Project</h2>
To start a new project with fossil create a new empty repository
this way: ([/help/init | more info])
<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>
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
"cloning".
Clone a remote repository as follows: ([/help/clone | more info])
<blockquote>
<b>fossil clone</b> <i>URL repository-filename</i>
</blockquote>
The <i>URL</i> specifies the fossil repository
you want to clone. The <i>repository-filename</i> is the new local
filename into which the cloned repository will be written. For
example, to clone the source code of Fossil itself:
<blockquote>
<b>fossil clone https://fossil-scm.org/ myclone.fossil</b>
</blockquote>
|
| ︙ | ︙ | |||
92 93 94 95 96 97 98 |
Vacuuming the database... <br>
project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333<br>
server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br>
admin-user: exampleuser (password is "yoWgDR42iv")><br>
</tt></b>
</blockquote>
| | | | | | | | | | | | | | | | | | | | | | | | > > | | | | > | > > | | | | | 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 |
Vacuuming the database... <br>
project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333<br>
server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42<br>
admin-user: exampleuser (password is "yoWgDR42iv")><br>
</tt></b>
</blockquote>
If the remote repository requires a login, include a
userid in the URL like this:
<blockquote>
<b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b>
</blockquote>
You will be prompted separately for the password.
Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid.
For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget")
If you are behind a restrictive firewall, you might need
to <a href="#proxy">specify an HTTP proxy</a>.
A Fossil repository is a single disk file. Instead of cloning,
you can just make a copy of the repository file (for example, using
"scp"). Note, however, that the repository file contains auxiliary
information above and beyond the versioned files, including some
sensitive information such as password hashes and email addresses. If you
want to share Fossil repositories directly by copying, consider running the
[/help/scrub|fossil scrub] command to remove sensitive information
before transmitting the file.
<h2 id="import">Importing From Another Version Control System</h2>
Rather than start a new project, or clone an existing Fossil project,
you might prefer to
<a href="./inout.wiki">import an existing Git project</a>
into Fossil using the [/help/import | fossil import] command.
You can even decide to export your project back into git using the
[/help/git | fossil git] command, which is how the Fossil project maintains
[https://github.com/drhsqlite/fossil-mirror | its public GitHub mirror]. There
is no limit to the number of times a tree can be imported and exported between
Fossil and git.
The [https://git-scm.com/docs/git-fast-export|Git fast-export format] has become
a popular way to move files between version management systems, including from
[https://www.mercurial-scm.org/|Mercurial].
Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.
<h2 id="checkout">Checking Out A Local Tree</h2>
To work on a project in fossil, you need to check out a local
copy of the source tree. Create the directory you want to be
the root of your tree and cd into that directory. Then
do this: ([/help/open | more info])
<blockquote>
<b>fossil open </b><i> repository-filename</i>
</blockquote>
for example:
<blockquote>
<b><tt>
fossil open ../myclone.fossil<br>
BUILD.txt<br>
COPYRIGHT-BSD2.txt<br>
README.md<br>
︙<br>
</tt></b>
</blockquote>
(or "fossil open ..\myclone.fossil" on Windows).
This leaves you with the newest version of the tree
checked out.
From anywhere underneath the root of your local tree, you
can type commands like the following to find out the status of
your local tree:
<blockquote>
<b>[/help/info | fossil info]</b><br>
<b>[/help/status | fossil status]</b><br>
<b>[/help/changes | fossil changes]</b><br>
<b>[/help/diff | fossil diff]</b><br>
<b>[/help/timeline | fossil timeline]</b><br>
<b>[/help/ls | fossil ls]</b><br>
<b>[/help/branch | fossil branch]</b><br>
</blockquote>
If you created a new repository using "fossil init" some commands will not
produce much output.
Note that Fossil allows you to make multiple check-outs in
separate directories from the same repository. This enables you,
for example, to do builds from multiple branches or versions at
the same time without having to generate extra clones.
To switch a checkout between different versions and branches,
use:<
<blockquote>
<b>[/help/update | fossil update]</b><br>
<b>[/help/checkout | fossil checkout]</b><br>
</blockquote>
[/help/update | update] honors the "autosync" option and
does a "soft" switch, merging any local changes into the target
version, whereas [/help/checkout | checkout] does not
automatically sync and does a "hard" switch, overwriting local
changes if told to do so.
<h2 id="changes">Making and Committing Changes</h2>
To add new files to your project or remove existing ones, use these
commands:
<blockquote>
<b>[/help/add | fossil add]</b> <i>file...</i><br>
<b>[/help/rm | fossil rm]</b> <i>file...</i><br>
<b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
</blockquote>
The command:
<blockquote>
<b>
[/help/changes | fossil changes]</b>
</blockquote>
lists files that have changed since the last commit to the repository. For
example, if you edit the file "README.md":
<blockquote>
<b>
fossil changes<br>
EDITED README.md
</b>
</blockquote>
To see exactly what change was made you can use the command
<b>[/help/diff | fossil diff]</b>:
<blockquote>
<b>
fossil diff <br><tt>
Index: README.md<br>
============================================================<br>
--- README.md<br>
+++ README.md<br>
@@ -1,5 +1,6 @@<br>
+Made some changes to the project<br>
# Original text<br>
</tt></b>
</blockquote>
"fossil diff" shows the difference between your tree on disk now and as
the tree was when you last committed changes. If you haven't committed
yet, then it shows the difference relative to the tip-of-trunk commit in
the repository, being what you get when you "fossil open" a repository
without specifying a version, populating the working directory.
To see the most recent changes made to the repository by other users, use "fossil timeline" to
find out the most recent commit, and then "fossil diff" between that commit and the
current tree:
<blockquote>
<b>
fossil timeline <br><tt>
=== 2021-03-28 === <br>
03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk) <br>
=== 2021-03-27 === <br>
23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk) <br>
|
| ︙ | ︙ | |||
267 268 269 270 271 272 273 |
# Original text<br>
</tt></b>
</blockquote>
"current" is an alias for the checkout version, so the command
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
| | | | | | > | | | | | | | | | | | | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
# Original text<br>
</tt></b>
</blockquote>
"current" is an alias for the checkout version, so the command
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
To commit your changes to a local-only repository:
<blockquote>
<b>
fossil commit </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt>
# Enter a commit message for this check-in. Lines beginning with # are ignored.<br>
#<br>
# user: exampleuser<br>
# tags: trunk<br>
#<br>
# EDITED README.md<br>
Edited file to add description of code changes<br>
New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br>
</tt></b>
</blockquote>
You will be prompted for check-in comments using whatever editor
is specified by your VISUAL or EDITOR environment variable. If none is
specified Fossil uses line-editing in the terminal.
To commit your changes to a repository that was cloned from a remote
repository, you give the same command, but the results are different.
Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a
single-stage commit that sends all changes committed to the local
repository immediately on to the remote parent repository. This only
works if you have write permission to the remote respository.
<h2 id="naming">Naming of Files, Checkins, and Branches</h2>
Fossil deals with information artifacts. This Quickstart document only deals
with files and collections of files, but be aware there are also tickets, wiki pages and more.
Every artifact in Fossil has a universally-unique hash id, and may also have a
human-readable name.
The following are all equivalent ways of identifying a Fossil file,
checkin or branch artifact:
<ul>
<li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a
<li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters
<li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash
</ul>
A special convenience branch is "trunk", which is Fossil's default branch name for
the first checkin, and the default for any time a branch name is needed but not
specified.
This will get you started on identifying checkins. The
<a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
how timestamps can also be used.
<h2 id="config">Configuring Your Local Repository</h2>
When you create a new repository, either by cloning an existing
project or create a new project of your own, you usually want to do some
local configuration. This is easily accomplished using the web-server
that is built into fossil. Start the fossil web server like this:
([/help/ui | more info])
<blockquote>
<b>fossil ui </b><i> repository-filename</i>
</blockquote>
You can omit the <i>repository-filename</i> from the command above
if you are inside a checked-out local tree.
This starts a web server then automatically launches your
web browser and makes it point to this web server. If your system
has an unusual configuration, fossil might not be able to figure out
how to start your web browser. In that case, first tell fossil
where to find your web browser using a command like this:
<blockquote>
<b>fossil setting web-browser </b><i> path-to-web-browser</i>
</blockquote>
By default, fossil does not require a login for HTTP connections
coming in from the IP loopback address 127.0.0.1. You can, and perhaps
should, change this after you create a few users.
When you are finished configuring, just press Control-C or use
the <b>kill</b> command to shut down the mini-server.
<h2 id="sharing">Sharing Changes</h2>
When [./concepts.wiki#workflow|autosync] is turned off,
the changes you [/help/commit | commit] are only
on your local repository.
To share those changes with other repositories, do:
<blockquote>
<b>[/help/push | fossil push]</b> <i>URL</i>
</blockquote>
Where <i>URL</i> is the http: URL of the server repository you
want to share your changes with. If you omit the <i>URL</i> argument,
fossil will use whatever server you most recently synced with.
The [/help/push | push] command only sends your changes to others. To
Receive changes from others, use [/help/pull | pull]. Or go both ways at
once using [/help/sync | sync]:
<blockquote>
<b>[/help/pull | fossil pull]</b> <i>URL</i><br>
<b>[/help/sync | fossil sync]</b> <i>URL</i>
</blockquote>
When you pull in changes from others, they go into your repository,
not into your checked-out local tree. To get the changes into your
local tree, use [/help/update | update]:
<blockquote>
<b>[/help/update | fossil update]</b> <i>VERSION</i>
</blockquote>
The <i>VERSION</i> can be the name of a branch or tag or any
abbreviation to the 40-character
artifact identifier for a particular check-in, or it can be a
date/time stamp. ([./checkin_names.wiki | more info])
If you omit
the <i>VERSION</i>, then fossil moves you to the
latest version of the branch your are currently on.
The default behavior is for [./concepts.wiki#workflow|autosync] to
be turned on. That means that a [/help/pull|pull] automatically occurs
when you run [/help/update|update] and a [/help/push|push] happens
automatically after you [/help/commit|commit]. So in normal practice,
the push, pull, and sync commands are rarely used. But it is important
to know about them, all the same.
<blockquote>
<b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
</blockquote>
Is similar to update except that it does not honor the autosync
setting, nor does it merge in local changes - it prefers to overwrite
them and fails if local changes exist unless the <tt>--force</tt>
flag is used.
<h2 id="branch" name="merge">Branching And Merging</h2>
Use the --branch option to the [/help/commit | commit] command
to start a new branch. Note that in Fossil, branches are normally
created when you commit, not before you start editing. You can
use the [/help/branch | branch new] command to create a new branch
before you start editing, if you want, but most people just wait
until they are ready to commit.
To merge two branches back together, first
[/help/update | update] to the branch you want to merge into.
Then do a [/help/merge|merge] of the other branch that you want to incorporate
the changes from. For example, to merge "featureX" changes into "trunk"
do this:
<blockquote>
<b>fossil [/help/update|update] trunk</b><br>
<b>fossil [/help/merge|merge] featureX</b><br>
<i># make sure the merge didn't break anything...</i><br>
<b>fossil [/help/commit|commit]
</blockquote>
The argument to the [/help/merge|merge] command can be any of the
version identifier forms that work for [/help/update|update].
([./checkin_names.wiki|more info].)
The merge command has options to cherry-pick individual
changes, or to back out individual changes, if you don't want to
do a full merge.
The merge command puts all changes in your working check-out.
No changes are made to the repository.
You must run [/help/commit|commit] separately
to add the merge changes into your repository to make them persistent
and so that your coworkers can see them.
But before you do that, you will normally want to run a few tests
to verify that the merge didn't cause logic breaks in your code.
The same branch can be merged multiple times without trouble. Fossil
automatically keeps up with things and avoids conflicts when doing
multiple merges. So even if you have merged the featureX branch
into trunk previously, you can do so again and Fossil will automatically
know to pull in only those changes that have occurred since the previous
merge.
If a merge or update doesn't work out (perhaps something breaks or
there are many merge conflicts) then you back up using:
<blockquote>
<b>[/help/undo | fossil undo]</b>
</blockquote>
This will back out the changes that the merge or update made to the
working checkout. There is also a [/help/redo|redo] command if you undo by
mistake. Undo and redo only work for changes that have
not yet been checked in using commit and there is only a single
level of undo/redo.
<h2 id="server">Setting Up A Server</h2>
Fossil can act as a stand-alone web server using one of these
commands:
<blockquote>
<b>[/help/server | fossil server]</b> <i>repository-filename</i><br>
<b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
</blockquote>
The <i>repository-filename</i> can be omitted when these commands
are run from within an open check-out, which is a particularly useful
shortcut with the <b>fossil ui</b> command.
The <b>ui</b> command is intended for accessing the web user interface
from a local desktop. (We sometimes call this mode "Fossil UI.")
The <b>ui</b> command differs from the
<b>server</b> command by binding to the loopback IP
address only (thus making the web UI visible only on the
local machine) and by automatically starting your default web browser,
pointing it at the running UI
server. The localhost restriction exists because it also gives anyone
who can access the resulting web UI full control over the
repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful
Setup capabliity].)
For cross-machine collaboration, use the <b>server</b> command instead,
which binds on all IP addresses, does not try to start a web browser,
and enforces [./caps/ | Fossil's role-based access control system].
Servers are also easily configured as:
<ul>
<li>[./server/any/inetd.md|inetd]
<li>[./server/debian/service.md|systemd]
<li>[./server/any/cgi.md|CGI]
<li>[./server/any/scgi.md|SCGI]
</ul>
…along with [./server/#matrix | several other options].
The [./selfhost.wiki | self-hosting fossil repositories] use
CGI.
You might <i>need</i> to set up a server, whether you know it yet or
not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server]
article for details.
<h2 id="proxy">HTTP Proxies</h2>
If you are behind a restrictive firewall that requires you to use
an HTTP proxy to reach the internet, then you can configure the proxy
in three different ways. You can tell fossil about your proxy using
a command-line option on commands that use the network,
<b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.
<blockquote>
<b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i>
</blockquote>
It is annoying to have to type in the proxy URL every time you
sync your project, though, so you can make the proxy configuration
persistent using the [/help/setting | setting] command:
<blockquote>
<b>fossil setting proxy </b><i>Proxy-URL</i>
</blockquote>
Or, you can set the "<b>http_proxy</b>" environment variable:
<blockquote>
<b>export http_proxy=</b><i>Proxy-URL</i>
</blockquote>
To stop using the proxy, do:
<blockquote>
<b>fossil setting proxy off</b>
</blockquote>
Or unset the environment variable. The fossil setting for the
HTTP proxy takes precedence over the environment variable and the
command-line option overrides both. If you have a persistent
proxy setting that you want to override for a one-time sync, that
is easily done on the command-line. For example, to sync with
a co-worker's repository on your LAN, you might type:
<blockquote>
<b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
</blockquote>
<h2 id="links">Other Resources</h2>
|
| ︙ | ︙ |
Changes to www/quotes.wiki.
| ︙ | ︙ | |||
143 144 145 146 147 148 149 | <blockquote> <i>viablepanic at [https://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/c0p30b4?utm_source=share&utm_medium=web2x&context=3]</i> </blockquote> <li>In the fossil community - and hence in fossil itself - development history is pretty much sacrosanct. The very name "fossil" was to chosen to reflect the unchanging nature of things in that history. | | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | <blockquote> <i>viablepanic at [https://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/c0p30b4?utm_source=share&utm_medium=web2x&context=3]</i> </blockquote> <li>In the fossil community - and hence in fossil itself - development history is pretty much sacrosanct. The very name "fossil" was to chosen to reflect the unchanging nature of things in that history. <br><br> In git (or rather, the git community), the development history is part of the published aspect of the project, so it provides tools for rearranging that history so you can present what you "should" have done rather than what you actually did. <blockquote> <i>Mike Meyer on the Fossil mailing list, 2011-10-04</i> </blockquote> |
| ︙ | ︙ |
Changes to www/rebaseharm.md.
| ︙ | ︙ | |||
196 197 198 199 200 201 202 | branch and from the mainline, whereas in the rebase case diff(C6,C5\') shows only the feature branch changes. But that argument is comparing apples to oranges, since the two diffs do not have the same baseline. The correct way to see only the feature branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). | | > | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
branch and from the mainline, whereas in the rebase case
diff(C6,C5\') shows only the feature branch changes.
But that argument is comparing apples to oranges, since the two diffs
do not have the same baseline. The correct way to see only the feature
branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7).
<table border="1" cellpadding="5" cellspacing="0"
style="margin-left:auto; margin-right:auto">
<tr><th>Rebase<th>Merge<th>What You See
<tr><td>diff(C2,C5\')<td>diff(C2,C7)<td>Commingled branch and mainline changes
<tr><td>diff(C6,C5\')<td>diff(C6,C7)<td>Branch changes only
</table>
Remember: C7 and C5\' are bit-for-bit identical, so the output of the
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
|
| ︙ | ︙ |
Changes to www/server/any/althttpd.md.
1 2 3 4 5 6 7 8 9 10 | # Serving via althttpd [Althttpd][althttpd] is a light-weight web server that has been used to implement the SQLite and Fossil websites for well over a decade. Althttpd strives for simplicity, security, ease of configuration, and low resource usage. To set up a Fossil server as CGI on a host running the althttpd web server, follow these steps. <ol> | | | | | | 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 |
# Serving via althttpd
[Althttpd][althttpd]
is a light-weight web server that has been used to implement the SQLite and
Fossil websites for well over a decade. Althttpd strives for simplicity,
security, ease of configuration, and low resource usage.
To set up a Fossil server as CGI on a host running the althttpd web
server, follow these steps.
<ol>
<li>Get the althttpd webserver running on the host. This is easily
done by following the [althttpd documentation][althttpd].
<li>Create a CGI script for your Fossil repository. The script will
be typically be two lines of code that look something like this:
~~~
#!/usr/bin/fossil
repository: /home/yourlogin/fossils/project.fossil
~~~
Modify the filenames to conform to your system, of course. The
CGI script accepts [other options][cgi] besides the
repository:" line. You can add in other options as you desire,
but the single "repository:" line is normally all that is needed
to get started.
<li>Make the CGI script executable.
<li>Verify that the fossil repository file and the directory that contains
the repository are both writable by whatever user the web server is
running and.
</ol>
And you are done. Visit the URL that corresponds to the CGI script
you created to start using your Fossil server.
|
| ︙ | ︙ |
Changes to www/server/debian/nginx.md.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | `/etc/nginx/local/example.com` contains the configuration for `*.example.com` and its alias `*.example.net`; and `local/foo.net` contains the configuration for `*.foo.net`. The configuration for our `example.com` web site, stored in `/etc/nginx/sites-enabled/local/example.com` is: | > > | | | > | | | | | | | | | | | < | | > | | | | | | > > > > > > | > > > > > > | | > > > > > > > > > > > > > > > > > > | | | | 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 |
`/etc/nginx/local/example.com` contains the configuration for
`*.example.com` and its alias `*.example.net`; and `local/foo.net`
contains the configuration for `*.foo.net`.
The configuration for our `example.com` web site, stored in
`/etc/nginx/sites-enabled/local/example.com` is:
----
server {
server_name .example.com .example.net "";
include local/generic;
include local/code;
access_log /var/log/nginx/example.com-https-access.log;
error_log /var/log/nginx/example.com-https-error.log;
# Bypass Fossil for the static documentation generated from
# our source code by Doxygen, so it merges into the embedded
# doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
# these generated files actually be stored in the repo. This
# also lets us set aggressive caching on these docs, since
# they rarely change.
location /code/doc/html {
root /var/www/example.com/code/doc/html;
location ~* \.(html|ico|css|js|gif|jpg|png)$ {
add_header Vary Accept-Encoding;
access_log off;
expires 7d;
}
}
# Redirect everything under /code to the Fossil instance
location /code {
include local/code;
# Extended caching for URLs that include unique IDs
location ~ "/(artifact|doc|file|raw)/[0-9a-f]{40,64}" {
add_header Cache-Control "public, max-age=31536000, immutable";
include local/code;
access_log off;
}
# Lesser caching for URLs likely to be quasi-static
location ~* \.(css|gif|ico|js|jpg|png)$ {
add_header Vary Accept-Encoding;
include local/code;
access_log off;
expires 7d;
}
}
}
----
As you can see, this is a pure extension of [the basic nginx service
configuration for SCGI][scgii], showing off a few ideas you might want to
try on your own site, such as static asset proxying.
You also need a `local/code` file containing:
include scgi_params;
scgi_pass 127.0.0.1:12345;
scgi_param SCRIPT_NAME "/code";
We separate that out because nginx refuses to inherit certain settings
between nested location blocks, so rather than repeat them, we extract
them to this separate file and include it from both locations where it’s
needed. You see this above where we set far-future expiration dates on
files served by Fossil via URLs that contain hashes that change when the
content changes. It tells your browser that the content of these URLs
can never change without the URL itself changing, which makes your
Fossil-based site considerably faster.
Similarly, the `local/generic` file referenced above helps us reduce unnecessary
repetition among the multiple sites this configuration hosts:
root /var/www/$host;
listen 80;
listen [::]:80;
charset utf-8;
There are some configuration directives that nginx refuses to substitute
variables into, citing performance considerations, so there is a limit
to how much repetition you can squeeze out this way. One such example
are the `access_log` and `error_log` directives, which follow an obvious
pattern from one host to the next. Sadly, you must tolerate some
repetition across `server { }` blocks when setting up multiple domains
on a single server.
The configuration for `foo.net` is similar.
See [the nginx docs](https://nginx.org/en/docs/) for more ideas.
|
| ︙ | ︙ |
Changes to www/server/debian/service.md.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | ## Containerized Service Two of the methods for running [containerized Fossil][cntdoc] integrate with `systemd`, potentially obviating the more direct methods below: * If you take [the Podman method][podman] of running containerized | | | | | | > | > < | 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 |
## Containerized Service
Two of the methods for running [containerized Fossil][cntdoc] integrate
with `systemd`, potentially obviating the more direct methods below:
* If you take [the Podman method][podman] of running containerized
Fossil, it opens the `podman generate systemd` option for you, as
exemplified in [the `fslsrv` script][fslsrv] used on this author’s
public Fossil-based web site. That script pulls its container images
from [my Docker Hub repo][dhrepo] to avoid the need for my public
Fossil server to have build tools and a copy of the Fossil source
tree. You’re welcome to use my images as-is, or you may use these
tools to bounce custom builds up through a separate container image
repo you manage.
* If you’re willing to give up [a lot of features][nsweak] relative to
Podman, and you’re willing to tolerate a lot more manual
administrivia, [the nspawn method][nspawn] has a lot less overhead,
being a direct feature of `systemd` itself.
Both of these options provide [better security][cntsec] than running
Fossil directly under `systemd`, among [other benefits][cntdoc].
[cntdoc]: ../../containers.md
[cntsec]: ../../containers.md#security
[dhrepo]: https://hub.docker.com/r/tangentsoft/fossil
[fslsrv]: https://tangentsoft.com/fossil/dir?name=bin
[nspawn]: ../../containers.md#nspawn
[nsweak]: ../../containers.md#nspawn-weaknesses
[podman]: ../../containers.md#podman
## User Service
A fun thing you can easily do with `systemd` that you can’t directly do
with older technologies like `inetd` and `xinetd` is to set a server up
as a “user” service.
|
| ︙ | ︙ | |||
200 201 202 203 204 205 206 |
ExecStart=/home/fossil/bin/fossil http repo.fossil
StandardInput=socket
[Install]
WantedBy=multi-user.target
```
| < < | | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
ExecStart=/home/fossil/bin/fossil http repo.fossil
StandardInput=socket
[Install]
WantedBy=multi-user.target
```
Notice that we haven’t told `systemd` which user and group to run Fossil
under. Since this is a system-level service definition, that means it
will run as root, which then causes Fossil to [automatically drop into a
`chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory`
we’ve configured above, shortly after each `fossil http` call starts.
The `Restart*` directives we had in the user service configuration above
are unnecessary for this method, since Fossil isn’t supposed to remain
running under it. Each HTTP hit starts one Fossil instance, which
handles that single client’s request and then immediately shuts down.
Next, you need to tell `systemd` to reload its system-level
|
| ︙ | ︙ |
Changes to www/server/index.html.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 | <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 | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | <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 multiple 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> |
| ︙ | ︙ | |||
169 170 171 172 173 174 175 | <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> | | | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | <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">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 |
| ︙ | ︙ |
Changes to www/server/whyuseaserver.wiki.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | to ensure that (in the limit) all participating peers see the same content. <h2>But, a Server Can Be Useful</h2> Fossil does not require a server, but a server can be very useful. Here are a few reasons to set up a Fossil server for your project: | | > | > | > | > | > | > | > | > | > | | | > | 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 |
to ensure that (in the limit) all participating peers see the same content.
<h2>But, a Server Can Be Useful</h2>
Fossil does not require a server, but a server can be very useful.
Here are a few reasons to set up a Fossil server for your project:
1. <b>A server works as a complete project website.</b>
Fossil does more than just version control. It also supports
[../tickets.wiki|trouble-tickets],
[../wikitheory.wiki|wiki],
a [../chat.md|developer chat room], and a [../forum.wiki|forum].
The [../embeddeddoc.wiki|embedded documentation]
feature provides a great mechanism for providing project documentation.
The [../unvers.wiki|unversioned files] feature is a convenient way
to host builds and downloads on the project website.
2. <b>A server gives developers a common point of rendezvous for
syncing their work.</b>
It is possible for developers to synchronize peer-to-peer but
that requires the developers coordinate the sync, which in turn
requires that the developers both want to sync at the same moment.
A server alleviates this time dependency by allowing each developer
to sync whenever it is convenient. For example, a developer may
choose to automatically sync
after each commit and before each update. Developers all stay
in sync with each other without having to interrupt each other
constantly to set up a peer-to-peer sync.
3. <b>A server provides project leaders with up-to-date status.</b>
Project coordinators and BDFLs can click on a link or two at the
central Fossil server for a project and quickly tell what is
going on. They can do this from anywhere — even from their phones
— without needing to actually sync to the device they are using.
4. <b>A server provides automatic off-site backups.</b>
A Fossil server is an automatic remote backup for all the work
going into a project. ([../backup.md | Within limits].)
You can even set up multiple servers at
multiple sites with automatic synchronization between them for
added redundancy. Such a setup means that no work is lost due
to a single machine failure.
5. <b>A server consolidates [https://www.sqlite.org/howtocorrupt.html
| SQLite corruption risk mitigation] to a single point.</b>
The concerns in section 1 of that document assume you have direct
access to the central DB files, which isn't the case when the
server is remote and secure against tampering.
Section 2 is about file locking, which concerns disappear when Fossil's
on the other side of an HTTP boundary and your server is set up
properly.
Sections 3.1, 4 thru 6, and 8 apply to all Fossil configurations,
but setting up a server lets you address the risks
in a single place. Once a given commit is
sync'd to the server, you can be reasonably sure any client-side
corruption can be fixed with a fresh clone. Ultimately, this
is an argument for off-machine backups, which returns us to reason
#4 above.
Sections 3.2 and the entirety of section 7 are no concern with
Fossil at all, since it's primarily written by the creator and
primary maintainer of SQLite, so you can be certain Fossil doesn't
actively pursue coding strategies known to risk database corruption.
For another take on this topic, see the article
"[https://sqlite.org/useovernet.html | SQLite Over a Network,
Caveats and Considerations]". Fossil runs in rollback mode by
default per recommendation #3 at the end of that article, and a
Fossil server operates as a network proxy for the underlying
SQLite repository DB per recommendation #2. This <i>may</i> permit
you to safely switch it into WAL mode (<b>fossil rebuild --wal</b>)
depending on the underlying storage used by the server itself.
6. <b>A server allows [../caps/ | Fossil's RBAC system] to work.</b>
The role-based access control (RBAC) system in Fossil only works
when the remote system is on the other side of an HTTP barrier.
([../caps/#webonly | Details].) If you want its benefits, you need
a Fossil server setup of some kind.
|
Changes to www/server/windows/service.md.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 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. | > > > > > > > > > > > > | 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 | 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 ``` ### Choice of Directory Considerations When the Fossil server will be used at times that files may be locked during virus scanning, it is prudent to arrange that its directory used for temporary files is exempted from such scanning. Ordinarily, this will be a subdirectory named "fossil" in the temporary directory given by the Windows GetTempPath(...) API, [namely](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks) the value of the first existing environment variable from `%TMP%`, `%TEMP%`, `%USERPROFILE%`, and `%SystemRoot%`; you can look for their actual values in your system by accessing the `/test_env` webpage. Excluding this subdirectory will avoid certain rare failures where the fossil.exe process is unable to use the directory normally during a scan. ### <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. |
| ︙ | ︙ |
Changes to www/settings.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>Fossil Settings</title> <h2>Using Fossil Settings</h2> Settings control the behaviour of fossil. They are set with the <tt>fossil settings</tt> command, or through the web interface in the Settings page in the Admin section. For a list of all settings, view the Settings page, or type <tt>fossil help settings</tt> from the command line. | | | | | | | | 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 | <title>Fossil Settings</title> <h2>Using Fossil Settings</h2> Settings control the behaviour of fossil. They are set with the <tt>fossil settings</tt> command, or through the web interface in the Settings page in the Admin section. For a list of all settings, view the Settings page, or type <tt>fossil help settings</tt> from the command line. <h3 id="repo">Repository settings</h3> Settings are set on a per-repository basis. When you clone a repository, a subset of settings are copied to your local repository. If you make a change to a setting on your local repository, it is not synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If you make a change on the server, you need to manually make the change on all repositories which are cloned from this repository. You can also set a setting globally on your local machine. The value will be used for all repositories cloned to your machine, unless overridden explicitly in a particular repository. Global settings can be set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> command. <h3 id="versionable">"Versionable" settings</h3> Most of the settings control the behaviour of fossil on your local machine, largely acting to reflect your preference on how you want to use Fossil, how you communicate with the server, or options for hosting a repository on the web. However, for historical reasons, some settings affect how you work with versioned files. These are <tt>clean-glob</tt>, <tt>binary-glob</tt>, <tt>crlf-glob</tt> (and its alias <tt>crnl-glob</tt>), <tt>empty-dirs</tt>, <tt>encoding-glob</tt>, <tt>ignore-glob</tt>, <tt>keep-glob</tt>, <tt>manifest</tt>, and <tt>mimetypes</tt>. The most important is <tt>ignore-glob</tt> which specifies which files should be ignored when looking for unmanaged files with the <tt>extras</tt> command. Because these options can change over time, and the inconvenience of replicating changes, these settings are "versionable". As well as being able to be set using the <tt>settings</tt> command or the web interface, you can create versioned files in the <tt>.fossil-settings</tt> |
| ︙ | ︙ |
Changes to www/shunning.wiki.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | foil spammers up front], legally problematic check-ins should range from rare to nonexistent, and you have to go way out of your way to force Fossil to insert bad control artifacts. Therefore, before we get to methods of permanently deleting content from a Fossil repos, let's give some alternatives that usually suffice, which don't damage the project's fossil record: | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | < | 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 |
foil spammers up front], legally problematic check-ins should range from
rare to nonexistent, and you have to go way out of your way to force
Fossil to insert bad control artifacts. Therefore, before we get to
methods of permanently deleting content from a Fossil repos, let's give
some alternatives that usually suffice, which don't damage the project's
fossil record:
* When a forum post or wiki article is "deleted," what actually
happens is that a new empty version is added to the Fossil repository.
The web interface interprets this
as "deleted," but the prior version remains available if you go
digging for it.
* When you close a ticket, it's marked in a way that causes it
to not show up in the normal ticket reports. You usually want to
give it a Resolution such as "Rejected" when this happens, plus
possibly a comment explaining why you're closing it. This is all new
information added to the ticket, not deletion.
* When you <tt>fossil rm</tt> a file, a new manifest is
checked into the repository with the same file list as for the prior
version minus the "removed" file. The file is still present in the
repository; it just isn't part of that version forward on that
branch.
* If you make a bad check-in, you can shunt it off to the side
by amending it to put it on a different branch, then continuing
development on the prior branch:
<br><br>
<code>$ fossil amend abcd1234 --branch BOGUS --hide<br>
$ fossil up trunk</code>
<br><br>
The first command moves check-in ID <tt>abcd1234</tt> (and any
subsequent check-ins on that branch!) to a branch called
<tt>BOGUS</tt>, then hides it so it doesn't show up on the
timeline. You can call this branch anything you like, and you can
re-use the same name as many times as you like. No content is
actually deleted: it's just shunted off to the side and hidden away.
You might find it easier to do this from the Fossil web UI in
the "edit" function for a check-in.
<br><br>
The second command returns to the last good check-in on that branch
so you can continue work from that point.
* When the check-in you want to remove is followed by good
check-ins on the same branch, you can't use the previous method,
because it will move the good check-ins, too. The solution is:
<br><br>
<tt>$ fossil merge --backout abcd1234</tt>
<br><br>
That creates a diff in the check-out directory that backs out the
bad check-in <tt>abcd1234</tt>. You then fix up any merge conflicts,
build, test, etc., then check the reverting change into the
repository. Again, nothing is actually deleted; you're just adding
more information to the repository which corrects a prior
check-in.
<h2>Exception: Non-versioned Content</h2>
It is normal and expected to delete data which is not versioned, such as
usernames and passwords in the user table. The [/help/scrub|fossil scrub]
command will remove all sensitive non-versioned data from a repository.
|
| ︙ | ︙ |
Changes to www/ssl.wiki.
| ︙ | ︙ | |||
138 139 140 141 142 143 144 |
<pre>
SSL verification failed: unable to get local issuer certificate
</pre>
Fossil relies on the OpenSSL library to have some way to check a trusted
list of CA signing keys. There are two common ways this fails:
| | | > | | | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
<pre>
SSL verification failed: unable to get local issuer certificate
</pre>
Fossil relies on the OpenSSL library to have some way to check a trusted
list of CA signing keys. There are two common ways this fails:
# The OpenSSL library Fossil is linked to doesn't have a CA
signing key set at all, so that it initially trusts no certificates
at all.
# The OpenSSL library does have a CA cert set, but your Fossil server's
TLS certificate was signed by a CA that isn't in that set.
A common reason to fall into the second trap is that you're using
certificates signed by a local private CA, as often happens in large
enterprises. You can solve this sort of problem by getting your local
CA's signing certificate in PEM format and pointing OpenSSL at it:
<pre>
|
| ︙ | ︙ | |||
270 271 272 273 274 275 276 | "<tt>http</tt>" URIs to Fossil, so Fossil issues a redirect, so the browser fetches the page again, causing Fossil to see an "<tt>http</tt>" URI again, so it issues a redirect...'round and 'round it goes until the web browser detects it's in a redirect loop and gives up. This problem prevents you from getting back into the Admin UI to fix it, but there are several ways to fix it: | | | > | | > | | | | 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 |
"<tt>http</tt>" URIs to Fossil, so Fossil issues a redirect, so the browser
fetches the page again, causing Fossil to see an "<tt>http</tt>" URI again, so
it issues a redirect...'round and 'round it goes until the web browser
detects it's in a redirect loop and gives up. This problem prevents you
from getting back into the Admin UI to fix it, but there are several
ways to fix it:
# <b>Reset via CLI.</b> You can turn the setting back off from the
CLI with the command "<tt>fossil -R /path/to/repo.fossil set
redirect-to-https 0</tt>". (Currently doesn't work.)
# <b>Backup first.</b> This setting is stored in the Fossil
repository, so if you make a backup first <i>on the server</i>, you
can restore the repo file if enabling this feature creates a
redirect loop.
# <b>Download, fix, and restore.</b> You can copy the remote
repository file down to a local machine, use <tt>fossil ui</tt> to
fix the setting, and then upload it to the repository server
again.
It's best to enforce TLS-only access at the front-end proxy level
anyway. It not only avoids the problem entirely, it can be significantly
more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way
to achieve this.
<h2>Terminology Note</h2>
This document is called <tt>ssl.wiki</tt> for historical reasons. The
TLS protocol was originally called SSL, and it went through several
revisions before being replaced by TLS. Years before this writing, SSL
|
| ︙ | ︙ |
Changes to www/sync.wiki.
1 2 | <title>The Fossil Sync Protocol</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 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 |
<title>The Fossil Sync Protocol</title>
This document describes the wire protocol used to synchronize
content between two Fossil repositories.
<h2>1.0 Overview</h2>
The global state of a fossil repository consists of an unordered
collection of artifacts. Each artifact is identified by a cryptographic
hash of its content, expressed as a lower-case hexadecimal string.
Synchronization is the process of sharing artifacts between
repositories so that all repositories have copies of all artifacts. Because
artifacts are unordered, the order in which artifacts are received
is unimportant. It is assumed that the hash names
of artifacts are unique - that every artifact has a different hash.
To a first approximation, synchronization proceeds by sharing lists
of hashes for available artifacts, then sharing the content of artifacts
whose names are missing from one side or the other of the connection.
In practice, a repository might contain millions of artifacts. The list of
hash names for this many artifacts can be large. So optimizations are
employed that usually reduce the number of hashes that need to be
shared to a few hundred.
Each repository also has local state. The local state determines
the web-page formatting preferences, authorized users, ticket formats,
and similar information that varies from one repository to another.
The local state is not usually transferred during a sync. Except,
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>
The "bag of artifacts" data model used by 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
Fossil predates that research by at least 4 years. But it is nice to
know that theorists have now proven that the underlying data model of
Fossil can provide strongly-consistent replicas using only
peer-to-peer communication and without any kind of central
authority.
If you are already familiar with CRDTs and were wondering if Fossil
used them, the answer is "yes". We just don't call them by that name.
<h2>2.0 Transport</h2>
All communication between client and server is via HTTP requests.
The server is listening for incoming HTTP requests. The client
issues one or more HTTP requests and receives replies for each
request.
The server might be running as an independent server
using the <b>server</b> command, or it might be launched from
inetd or xinetd using the <b>http</b> command. Or the server might
be launched from CGI.
(See "[./server/|How To Configure A Fossil Server]" for details.)
The specifics of how the server listens
for incoming HTTP requests is immaterial to this protocol.
The important point is that the server is listening for requests and
the client is the issuer of the requests.
A single push, pull, or sync might involve multiple HTTP requests.
The client maintains state between all requests. But on the server
side, each request is independent. The server does not preserve
any information about the client from one request to the next.
Note: Throughout this article, we use the terms "server" and "client"
to represent the listener and initiator of the interaction, respectively.
Nothing in this protocol requires that the server actually be a back-room
processor housed in a datacenter, nor does the client need to be a desktop
or handheld device. For the purposes of this article "client" simply means
the repository that initiates the conversation and "server" is the repository
that responds. Nothing more.
<h4>2.0.1 HTTPS Transport</h4>
In the current implementation of Fossil, the server only
understands HTTP requests. The client can send either
clear-text HTTP requests or encrypted HTTPS requests. But when
HTTPS requests are sent, they first must be decrypted by a web server
or proxy before being passed to the Fossil server. This limitation
may be relaxed in a future release.
<h4>2.0.2 SSH Transport</h4>
When doing a sync using an "ssh:..." URL, the same HTTP transport protocol
is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh]
to start an instance of the [/help?cmd=test-http|fossil test-http] command
running on the remote machine. It then sends HTTP requests and gets back HTTP
replies over the SSH connection, rather than sending and receiving over an
internet socket. To see the specific "ssh" command that the Fossil client
runs in order to set up a connection, add either of the the "--httptrace" or
"--sshtrace" options to the "fossil sync" command line.
<h4>2.0.3 FILE Transport</h4>
When doing a sync using a "file:..." URL, the same HTTP protocol is
still used. But instead of sending each HTTP request over a socket or
via SSH, the HTTP request is written into a temporary file. The client
then invokes the [/help?cmd=http|fossil http] command in a subprocess
to process the request and and generate a reply. The client then reads
the HTTP reply out of a temporary file on disk, and deletes the two
temporary files. To see the specific "fossil http" command that is run
in order to implement the "file:" transport, add the "--httptrace"
option to the "fossil sync" command.
<h3>2.1 Server Identification</h3>
The server is identified by a URL argument that accompanies the
push, pull, or sync command on the client. (As a convenience to
users, the URL can be omitted on the client command and the same URL
from the most recent push, pull, or sync will be reused. This saves
typing in the common case where the client does multiple syncs to
the same server.)
The client modifies the URL by appending the method name "<b>/xfer</b>"
to the end. For example, if the URL specified on the client command
line is
<blockquote>
https://fossil-scm.org/fossil
</blockquote>
Then the URL that is really used to do the synchronization will
be:
<blockquote>
https://fossil-scm.org/fossil/xfer
</blockquote>
<h3>2.2 HTTP Request Format</h3>
The client always sends a POST request to the server. The
general format of the POST request is as follows:
<blockquote><pre>
POST /fossil/xfer HTTP/1.0
Host: fossil-scm.hwaci.com:80
Content-Type: application/x-fossil
Content-Length: 4216
<i>content...</i>
</pre></blockquote>
In the example above, the pathname given after the POST keyword
on the first line is a copy of the URL pathname. The Host: parameter
is also taken from the URL. The content type is always either
"application/x-fossil" or "application/x-fossil-debug". The "x-fossil"
content type is the default. The only difference is that "x-fossil"
content is compressed using zlib whereas "x-fossil-debug" is sent
uncompressed.
A typical reply from the server might look something like this:
<blockquote><pre>
HTTP/1.0 200 OK
Date: Mon, 10 Sep 2007 12:21:01 GMT
Connection: close
Cache-control: private
Content-Type: application/x-fossil; charset=US-ASCII
Content-Length: 265
<i>content...</i>
</pre></blockquote>
The content type of the reply is always the same as the content type
of the request.
<h2>3.0 Fossil Synchronization Content</h2>
A synchronization request between a client and server consists of
one or more HTTP requests as described in the previous section. This
section details the "x-fossil" content type.
<h3>3.1 Line-oriented Format</h3>
The x-fossil content type consists of zero or more "cards". Cards
are separated by the newline character ("\n"). Leading and trailing
whitespace on a card is ignored. Blank cards are ignored.
Each card is divided into zero or more space separated tokens.
The first token on each card is the operator. Subsequent tokens
are arguments. The set of operators understood by servers is slightly
different from the operators understood by clients, though the two
are very similar.
<h3>3.2 Login Cards</h3>
Every message from client to server begins with one or more login
cards. Each login card has the following format:
<blockquote>
<b>login</b> <i>userid nonce signature</i>
</blockquote>
The userid is the name of the user that is requesting service
from the server. The nonce is the SHA1 hash of the remainder of
the message - all text that follows the newline character that
terminates the login card. The signature is the SHA1 hash of
the concatenation of the nonce and the users password.
For each login card, the server looks up the user and verifies
that the nonce matches the SHA1 hash of the remainder of the
message. It then checks the signature hash to make sure the
signature matches. If everything
checks out, then the client is granted all privileges of the
specified user.
Privileges are cumulative. There can be multiple successful
login cards. The session privileges are the bit-wise OR of the
privileges of each individual login.
<h3>3.3 File Cards</h3>
Artifacts are transferred using either "file" cards, or "cfile"
or "uvfile" cards.
The name "file" card comes from the fact that most artifacts correspond to
files that are under version control.
The "cfile" name is an abbreviation for "compressed file".
The "uvfile" name is an abbreviation for "unversioned file".
<h4>3.3.1 Ordinary File Cards</h4>
For sync protocols, artifacts are transferred using "file"
cards. File cards come in two different formats depending
on whether the artifact is sent directly or as a delta from some
other artifact.
<blockquote>
<b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i><br>
<b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
</blockquote>
File cards are followed by in-line "payload" data.
The content of the artifact
or the artifact delta is the first <i>size</i> bytes of the
x-fossil content that immediately follow the newline that
terminates the file card.
The first argument of a file card is the ID of the artifact that
is being transferred. The artifact ID is the lower-case hexadecimal
representation of the name hash for the artifact.
The last argument of the file card is the number of bytes of
payload that immediately follow the file card. If the file
card has only two arguments, that means the payload is the
complete content of the artifact. If the file card has three
arguments, then the payload is a delta and second argument is
the ID of another artifact that is the source of the delta.
File cards are sent in both directions: client to server and
server to client. A delta might be sent before the source of
the delta, so both client and server should remember deltas
and be able to apply them when their source arrives.
<h4>3.3.2 Compressed File Cards</h4>
A client that sends a clone protocol version "3" or greater will
receive artifacts as "cfile" cards while cloning. This card was
introduced to improve the speed of the transfer of content by sending the
compressed artifact directly from the server database to the client.
Compressed File cards are similar to File cards, sharing the same
in-line "payload" data characteristics and also the same treatment of
direct content or delta content. Cfile cards come in two different formats
depending on whether the artifact is sent directly or as a delta from
some other artifact.
<blockquote>
<b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
<b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i><br>
</blockquote>
The first argument of the cfile card is the ID of the artifact that
is being transferred. The artifact ID is the lower-case hexadecimal
representation of the name hash for the artifact. The second argument of
the cfile card is the original size in bytes of the artifact. The last
argument of the cfile card is the number of compressed bytes of payload
that immediately follow the cfile card. If the cfile card has only
three arguments, that means the payload is the complete content of the
artifact. If the cfile card has four arguments, then the payload is a
delta and the second argument is the ID of another artifact that is the
source of the delta and the third argument is the original size of the
delta artifact.
Unlike file cards, cfile cards are only sent in one direction during a
clone from server to client for clone protocol version "3" or greater.
<h4>3.3.3 Private artifacts</h4>
"Private" content consist of artifacts that are not normally synced.
However, private content will be synced when the
the [/help?cmd=sync|fossil sync] command includes the "--private" option.
Private content is marked by a "private" card:
<blockquote>
<b>private</b>
</blockquote>
The private card has no arguments and must directly precede a
file card that contains the private content.
<h4>3.3.4 Unversioned File Cards</h4>
Unversioned content is sent in both directions (client to server and
server to client) using "uvfile" cards in the following format:
<blockquote>
<b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
</blockquote>
The <i>name</i> field is the name of the unversioned file. The
<i>mtime</i> is the last modification time of the file in seconds
since 1970. The <i>hash</i> field is the hash of the content
for the unversioned file, or "<b>-</b>" for deleted content.
The <i>size</i> field is the (uncompressed) size of the content
in bytes. The <i>flags</i> field is an integer which is interpreted
as an array of bits. The 0x0004 bit of <i>flags</i> indicates that
the <i>content</i> is to be omitted. The content might be omitted if
it is too large to transmit, or if the sender merely wants to update the
modification time of the file without changing the files content.
The <i>content</i> is the (uncompressed) content of the file.
The receiver should only accept the uvfile card if the hash and
size match the content and if the mtime is newer than any existing
instance of the same file held by the receiver. The sender will not
normally transmit a uvfile card unless all these constraints are true,
but the receiver should double-check.
A server should only accept uvfile cards if the login user has
the "y" write-unversioned permission.
Servers send uvfile cards in response to uvgimme cards received from
the client. Clients send uvfile cards when they determine that the server
needs the content based on uvigot cards previously received from the server.
<h3>3.4 Push and Pull Cards</h3>
Among the first cards in a client-to-server message are
the push and pull cards. The push card tells the server that
the client is pushing content. The pull card tells the server
that the client wants to pull content. In the event of a sync,
both cards are sent. The format is as follows:
<blockquote>
<b>push</b> <i>servercode projectcode</i><br>
<b>pull</b> <i>servercode projectcode</i>
</blockquote>
The <i>servercode</i> argument is the repository ID for the
client. The <i>projectcode</i> is the identifier
of the software project that the client repository contains.
The projectcode for the client and server must match in order
for the transaction to proceed.
The server will also send a push card back to the client
during a clone. This is how the client determines what project
code to put in the new repository it is constructing.
The <i>servercode</i> argument is currently unused.
<h3>3.5 Clone Cards</h3>
A clone card works like a pull card in that it is sent from
client to server in order to tell the server that the client
wants to pull content. The clone card comes in two formats. Older
clients use the no-argument format and newer clients use the
two-argument format.
<blockquote>
<b>clone</b><br>
<b>clone</b> <i>protocol-version sequence-number</i>
</blockquote>
<h4>3.5.1 Protocol 3</h4>
The latest clients send a two-argument clone message with a
protocol version of "3". (Future versions of Fossil might use larger
protocol version numbers.) Version "3" of the protocol enhanced version
"2" by introducing the "cfile" card which is intended to speed up clone
operations. Instead of sending "file" cards, the server will send "cfile"
cards
<h4>3.5.2 Protocol 2</h4>
The sequence-number sent is the number
of artifacts received so far. For the first clone message, the
sequence number is 0. The server will respond by sending file
cards for some number of artifacts up to the maximum message size.
The server will also send a single "clone_seqno" card to the client
so that the client can know where the server left off.
<blockquote>
<b>clone_seqno</b> <i>sequence-number</i>
</blockquote>
The clone message in subsequent HTTP requests for the same clone
operation will use the sequence-number from the
clone_seqno of the previous reply.
In response to an initial clone message, the server also sends the client
a push message so that the client can discover the projectcode for
this project.
<h4>3.5.3 Legacy Protocol</h4>
Older clients send a clone card with no argument. The server responds
to a blank clone card by sending an "igot" card for every artifact in the
repository. The client will then issue "gimme" cards to pull down all the
content it needs.
The legacy protocol works well for smaller repositories (50MB with 50,000
artifacts) but is too slow and unwieldy for larger repositories.
The version 2 protocol is an effort to improve performance. Further
performance improvements with higher-numbered clone protocols are
possible in future versions of Fossil.
<h3>3.6 Igot Cards</h3>
An igot card can be sent from either client to server or from
server to client in order to indicate that the sender holds a copy
of a particular artifact. The format is:
<blockquote>
<b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
</blockquote>
The first argument of the igot card is the ID of the artifact that
the sender possesses.
The receiver of an igot card will typically check to see if
it also holds the same artifact and if not it will request the artifact
using a gimme card in either the reply or in the next message.
If the second argument exists and is "1", then the artifact
identified by the first argument is private on the sender and should
be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
The name "igot" comes from the English slang expression "I got" meaning
"I have".
<h4>3.6.1 Unversioned Igot Cards</h4>
Zero or more "uvigot" cards are sent from server to client when
synchronizing unversioned content. The format of a uvigot card is
as follows:
<blockquote>
<b>uvigot</b> <i>name mtime hash size</i>
</blockquote>
The <i>name</i> argument is the name of an unversioned file.
The <i>mtime</i> is the last modification time of the unversioned file
in seconds since 1970.
The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file
content, or "<b>-</b>" if the file has been deleted.
The <i>size</i> is the uncompressed size of the file in bytes.
When the server sees a "pragma uv-hash" card for which the hash
does not match, it sends uvigot cards for every unversioned file that it
holds. The client will use this information to figure out which
unversioned files need to be synchronized.
The server might also send a uvigot card when it receives a uvgimme card
but its reply message size is already oversized and hence unable to hold
the usual uvfile reply.
When a client receives a "uvigot" card, it checks to see if the
file needs to be transferred from client to server or from server to client.
If a client-to-server transmission is needed, the client schedules that
transfer to occur on a subsequent HTTP request. If a server-to-client
transfer is needed, then the client sends a "uvgimme" card back to the
server to request the file content.
<h3>3.7 Gimme Cards</h3>
A gimme card is sent from either client to server or from server
to client. The gimme card asks the receiver to send a particular
artifact back to the sender. The format of a gimme card is this:
<blockquote>
<b>gimme</b> <i>artifact-id</i>
</blockquote>
The argument to the gimme card is the ID of the artifact that
the sender wants. The receiver will typically respond to a
gimme card by sending a file card in its reply or in the next
message.
The "gimme" name means "give me". The imperative "give me" is
pronounced as if it were a single word "gimme" in some dialects of
English (including the dialect spoken by the original author of Fossil).
<h4>3.7.1 Unversioned Gimme Cards</h4>
Sync synchronizing unversioned content, the client may send "uvgimme"
cards to the server. A uvgimme card requests that the server send
unversioned content to the client. The format of a uvgimme card is
as follows:
<blockquote>
<b>uvgimme</b> <i>name</i>
</blockquote>
The <i>name</i> is the name of the unversioned file found on the
server that the client would like to have. When a server sees a
uvgimme card, it normally responses with a uvfile card, though it might
also send another uvigot card if the HTTP reply is already oversized.
<h3>3.8 Cookie Cards</h3>
A cookie card can be used by a server to record a small amount
of state information on a client. The server sends a cookie to the
client. The client sends the same cookie back to the server on
its next request. The cookie card has a single argument which
is its payload.
<blockquote>
<b>cookie</b> <i>payload</i>
</blockquote>
The client is not required to return the cookie to the server on
its next request. Or the client might send a cookie from a different
server on the next request. So the server must not depend on the
cookie and the server must structure the cookie payload in such
a way that it can tell if the cookie it sees is its own cookie or
a cookie from another server. (Typically the server will embed
its servercode as part of the cookie.)
<h3>3.9 Request-Configuration Cards</h3>
A request-configuration or "reqconfig" card is sent from client to
server in order to request that the server send back "configuration"
data. "Configuration" data is information about users or website
appearance or other administrative details which are not part of the
persistent and versioned state of the project. For example, the "name"
of the project, the default Cascading Style Sheet (CSS) for the web-interface,
and the project logo displayed on the web-interface are all configuration
data elements.
The reqconfig card is normally sent in response to the
"fossil configuration pull" command. The format is as follows:
<blockquote>
<b>reqconfig</b> <i>configuration-name</i>
</blockquote>
As of 2018-06-04, the configuration-name must be one of the
following values:
<table border=0 align="center">
<tr><td valign="top">
<ul>
<li> css
<li> header
|
| ︙ | ︙ | |||
620 621 622 623 624 625 626 | <li> @reportfmt <li> @user <li> @concealed <li> @shun </ul></td></tr> </table> | | | | | | | | | | | | < | < | | < | | | < | | < | | < | < | < < | | | | | | | | | | | | | | | | | | | | | | | | | | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 |
<li> @reportfmt
<li> @user
<li> @concealed
<li> @shun
</ul></td></tr>
</table>
New configuration-names are likely to be added in future releases of
Fossil. If the server receives a configuration-name that it does not
understand, the entire reqconfig card is silently ignored. The reqconfig
card might also be ignored if the user lacks sufficient privilege to
access the requested information.
The configuration-names that begin with an alphabetic character refer
to values in the "config" table of the server database. For example,
the "logo-image" configuration item refers to the project logo image
that is configured on the Admin page of the [./webui.wiki | web-interface].
The value of the configuration item is returned to the client using a
"config" card.
If the configuration-name begins with "@", that refers to a class of
values instead of a single value. The content of these configuration items
is returned in a "config" card that contains pure SQL text that is
intended to be evaluated by the client.
The @user and @concealed configuration items contain sensitive information
and are ignored for clients without sufficient privilege.
<h3>3.10 Configuration Cards</h3>
A "config" card is used to send configuration information from client
to server (in response to a "fossil configuration push" command) or
from server to client (in response to a "fossil configuration pull" or
"fossil clone" command). The format is as follows:
<blockquote>
<b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i>
</blockquote>
The server will only accept a config card if the user has
"Admin" privilege. A client will only accept a config card if
it had sent a corresponding reqconfig card in its request.
The content of the configuration item is used to overwrite the
corresponding configuration data in the receiver.
<h3>3.11 Pragma Cards</h3>
The client may try to influence the behavior of the server by
issuing a pragma card:
<blockquote>
<b>pragma</i> <i>name value...</i>
</blockquote>
The "pragma" card has at least one argument which is the pragma name.
The pragma name defines what the pragma does.
A pragma might have zero or more "value" arguments
depending on the pragma name.
New pragma names may be added to the protocol from time to time
in order to enhance the capabilities of Fossil.
Unknown pragmas are silently ignored, for backwards compatibility.
The following are the known pragma names as of 2019-06-30:
<ol>
<li><b>send-private</b> The send-private pragma instructs the server to send all of its
private artifacts to the client. The server will only obey this
request if the user has the "x" or "Private" privilege.
<li><b>send-catalog</b> The send-catalog pragma instructs the server to transmit igot
cards for every known artifact. This can help the client and server
to get back in synchronization after a prior protocol error. The
"--verily" option to the [/help?cmd=sync|fossil sync] command causes
the send-catalog pragma to be transmitted.
<li><b>uv-hash</b> <i>HASH</i> The uv-hash pragma is sent from client to server to provoke a
synchronization of unversioned content. The <i>HASH</i> is a SHA1
hash of the names, modification times, and individual hashes of all
unversioned files on the client. If the unversioned content hash
from the client does not match the unversioned content hash on the
server, then the server will reply with either a "pragma uv-push-ok"
or "pragma uv-pull-only" card followed by one "uvigot" card for
each unversioned file currently held on the server. The collection
of "uvigot" cards sent in response to a "uv-hash" pragma is called
the "unversioned catalog". The client will used the unversioned
catalog to figure out which files (if any) need to be synchronized
between client and server and send appropriate "uvfile" or "uvgimme"
cards on the next HTTP request.
If a client sends a uv-hash pragma and does not receive back
either a uv-pull-only or uv-push-ok pragma, that means that the
content on the server exactly matches the content on the client and
no further synchronization is required.
<li><b>uv-pull-only</b></i> A server sends the uv-pull-only 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 but that content can only be transferred
from server to client. The server is unwilling to accept content from
the client because the client login lacks the "write-unversioned"
permission.
<li><b>uv-push-ok</b></i> 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.
<li><b>ci-lock</b> <i>CHECKIN-HASH CLIENT-ID</i> 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><b>ci-lock-fail</b> <i>LOGIN MTIME</i> 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
arguments are intended to provide information to the client to
help it generate a more useful error message.
<li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after
a successful commit. This instructs the server to release
any lock on any check-in previously held by that client.
The ci-unlock pragma helps to avoid false-positive lock warnings
that might arise if a check-in is aborted and then restarted
on a branch.
</ol>
<h3>3.12 Comment Cards</h3>
Any card that begins with "#" (ASCII 0x23) is a comment card and
is silently ignored.
<h3>3.13 Message and Error Cards</h3>
If the server discovers anything wrong with a request, it generates
an error card in its reply. When the client sees the error card,
it displays an error message to the user and aborts the sync
operation. An error card looks like this:
<blockquote>
<b>error</b> <i>error-message</i>
</blockquote>
The error message is English text that is encoded in order to
be a single token.
A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A
newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash
(ASCII 0x5C) is represented as two backslashes "\\". Apart from
space and newline, no other whitespace characters nor any
unprintable characters are allowed in
the error message.
The server can also send a message card that also prints a
message on the client console, but which is not an error:
<blockquote>
<b>message</b> <i>message-text</i>
</blockquote>
The message-text uses the same format as an error message.
<h3>3.14 Unknown Cards</h3>
If either the client or the server sees a card that is not
described above, then it generates an error and aborts.
<h2>4.0 Phantoms And Clusters</h2>
When a repository knows that an artifact exists and knows the ID of
that artifact, but it does not know the artifact content, then it stores that
artifact as a "phantom". A repository will typically create a phantom when
it receives an igot card for an artifact that it does not hold or when it
receives a file card that references a delta source that it does not
hold. When a server is generating its reply or when a client is
generating a new request, it will usually send gimme cards for every
phantom that it holds.
A cluster is a special artifact that tells of the existence of other
artifacts. Any artifact in the repository that follows the syntactic rules
of a cluster is considered a cluster.
A cluster is line oriented. Each line of a cluster
is a card. The cards are separated by the newline ("\n") character.
Each card consists of a single character card type, a space, and a
single argument. No extra whitespace and no trailing or leading
whitespace is allowed. All cards in the cluster must occur in
strict lexicographical order.
A cluster consists of one or more "M" cards followed by a single
"Z" card. Each M card holds an argument which is an artifact ID for an
artifact in the repository. The Z card has a single argument which is the
lower-case hexadecimal representation of the MD5 checksum of all
preceding M cards up to and included the newline character that
occurred just before the Z that starts the Z card.
Any artifact that does not match the specifications of a cluster
exactly is not a cluster. There must be no extra whitespace in
the artifact. There must be one or more M cards. There must be a
single Z card with a correct MD5 checksum. And all cards must
be in strict lexicographical order.
<h3>4.1 The Unclustered Table</h3>
Every repository maintains a table named "<b>unclustered</b>"
which records the identity of every artifact and phantom it holds that is not
mentioned in a cluster. The entries in the unclustered table can
be thought of as leaves on a tree of artifacts. Some of the unclustered
artifacts will be other clusters. Those clusters may contain other clusters,
which might contain still more clusters, and so forth. Beginning
with the artifacts in the unclustered table, one can follow the chain
of clusters to find every artifact in the repository.
<h2>5.0 Synchronization Strategies</h2>
<h3>5.1 Pull</h3>
A typical pull operation proceeds as shown below. Details
of the actual implementation may very slightly but the gist of
a pull is captured in the following steps:
<ol>
<li>The client sends login and pull cards.
<li>The client sends a cookie card if it has previously received a cookie.
<li>The client sends gimme cards for every phantom that it holds.
<hr>
<li>The server checks the login password and rejects the session if
|
| ︙ | ︙ | |||
875 876 877 878 879 880 881 | <li>The client adds the content of file cards to its repository. <li>The client creates a phantom for every igot card in the server reply that mentions an artifact that the client does not possess. <li>The client creates a phantom for the delta source of file cards when the delta source is an artifact that the client does not possess. </ol> | | | | | | | | | | | | 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 | <li>The client adds the content of file cards to its repository. <li>The client creates a phantom for every igot card in the server reply that mentions an artifact that the client does not possess. <li>The client creates a phantom for the delta source of file cards when the delta source is an artifact that the client does not possess. </ol> These ten steps represent a single HTTP round-trip request. The first three steps are the processing that occurs on the client to generate the request. The middle four steps are processing that occurs on the server to interpret the request and generate a reply. And the last three steps are the processing that the client does to interpret the reply. During a pull, the client will keep sending HTTP requests until it holds all artifacts that exist on the server. Note that the server tries to limit the size of its reply message to something reasonable (usually about 1MB) so that it might stop sending file cards as described in step (6) if the reply becomes too large. Step (5) is the only way in which new clusters can be created. By only creating clusters on the server, we hope to minimize the amount of overlap between clusters in the common configuration where there is a single server and many clients. The same synchronization protocol will continue to work even if there are multiple servers or if servers and clients sometimes change roles. The only negative effects of these unusual arrangements is that more than the minimum number of clusters might be generated. <h3>5.2 Push</h3> A typical push operation proceeds roughly as shown below. As with a pull, the actual implementation may vary slightly. <ol> <li>The client sends login and push cards. <li>The client sends file cards for any artifacts that it holds that have never before been pushed - artifacts that come from local check-ins. <li>If this is the second or later cycle in a push, then the client sends file cards for any gimme cards that the server sent |
| ︙ | ︙ | |||
927 928 929 930 931 932 933 | it does not possess. <li>The server issues gimme cards for all phantoms. <hr> <li>The client remembers the gimme cards from the server so that it can generate file cards in reply on the next cycle. </ol> | | | | | | | | | | 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 |
it does not possess.
<li>The server issues gimme cards for all phantoms.
<hr>
<li>The client remembers the gimme cards from the server so that it
can generate file cards in reply on the next cycle.
</ol>
As with a pull, the steps of a push operation repeat until the
server knows all artifacts that exist on the client. Also, as with
pull, the client attempts to keep the size of the request from
growing too large by suppressing file cards once the
size of the request reaches 1MB.
<h3 id="sync">5.3 Sync</h3>
A sync is just a pull and a push that happen at the same time.
The first three steps of a pull are combined with the first five steps
of a push. Steps (4) through (7) of a pull are combined with steps
(5) through (8) of a push. And steps (8) through (10) of a pull
are combined with step (9) of a push.
<h3>5.4 Unversioned File Sync</h3>
"Unversioned files" are files held in the repository
where only the most recent version of the file is kept rather than
the entire change history. Unversioned files are intended to be
used to store ephemeral content, such as compiled binaries of the
most recent release.
Unversioned files are identified by name and timestamp (mtime).
Only the most recent version of each file (the version with
the largest mtime value) is retained.
Unversioned files are synchronized using the
[/help?cmd=unversioned|fossil unversioned sync] command.
A schematic of an unversioned file synchronization is as follows:
<ol>
<li>The client sends a "pragma uv-hash" card to the server. The argument
to the uv-hash pragma is a hash of all filesnames, mtimes, and
content hashes for the unversioned files held by the client.
<hr>
<li>If the unversioned content hash from the client matches the unversioned
|
| ︙ | ︙ | |||
979 980 981 982 983 984 985 |
then sends appropriate "uvgimme" or "uvfile" cards back to the
server.
<hr>
<li>The server updates its unversioned file store with received "uvfile"
cards and answers "uvgimme" cards with "uvfile" cards in its reply.
</ol>
| | | | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
then sends appropriate "uvgimme" or "uvfile" cards back to the
server.
<hr>
<li>The server updates its unversioned file store with received "uvfile"
cards and answers "uvgimme" cards with "uvfile" cards in its reply.
</ol>
The last two steps might be repeated multiple
times if there is more unversioned content to be transferred than will
fit comfortably in a single HTTP request.
<h2>6.0 Summary</h2>
Here are the key points of the synchronization protocol:
<ol>
<li>The client sends one or more PUSH HTTP requests to the server.
The request and reply content type is "application/x-fossil".
<li>HTTP request content is compressed using zlib.
<li>The content of request and reply consists of cards with one
card per line.
|
| ︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 | cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> <h2>7.0 Troubleshooting And Debugging Hints</h2> | | | | | | 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 |
cluster and send igot messages for those artifacts.
<li>Repositories keep track of all the phantoms they hold and send
gimme messages for those artifacts.
</ol>
<h2>7.0 Troubleshooting And Debugging Hints</h2>
If you run the [/help?cmd=sync|fossil sync] command
(or [/help?cmd=pull|pull] or [/help?cmd=push|push] or
[/help?cmd=clone|clone]) with the --httptrace option, Fossil
will keep a copy of each HTTP request and reply in files
named:
<ul>
<li> <tt>http-request-</tt><i>N</i><tt>.txt</tt>
<li> <tt>http-reply-</tt><i>N</i><tt>.txt</tt>
</ul>
In the above, <i>N</i> is an integer that increments with each
round-trip. If you are having trouble on the server side,
you can run the "[/help?cmd=test-http|fossil test-http]" command in a
debugger using one the "http-request-N.txt" files as input and
single step through the processing performed by the server.
The "--transport-command CMD" option on [/help?cmd=sync|fossil sync]
(and similar) causes the external program "CMD" to be used to move
the sync message to the server and retrieve the sync reply. The
CMD is given three arguments:
<ol>
<li> The URL of the server
<li> The name of a temporary file that contains the output-bound sync
protocol text, with the HTTP headers
<li> The name of a temporary file into which the CMD should write the
reply sync protocol text, again without any HTTP headers
</ol>
In a complex debugging situation, you can run the command
"fossil sync --transport-command ./debugging_script" where
"debugging_script" is some script of your own that invokes
the anomolous behavior your are trying to debug.
|
Changes to www/unvers.wiki.
1 2 3 4 | <title>Unversioned Content</title> <h1 align="center">Unversioned Content</h1> "Unversioned content" or "unversioned files" are | | | | | | > | | | | | | < | | | < > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <title>Unversioned Content</title> <h1 align="center">Unversioned Content</h1> "Unversioned content" or "unversioned files" are files stored in a Fossil repository without history, meaning it retains the newest version of each such file, and that alone. Though it omits history, Fossil does sync unversioned content between repositories. In the event of a conflict during a sync, it retains the most recent version of each unversioned file, discrding older versions. Unversioned files are useful for storing ephemeral content such as builds or frequently changing web pages. We store the [https://fossil-scm.org/home/uv/download.html|download] page of the self-hosting Fossil repository as unversioned content, for example. <h2>Accessing Unversioned Files</h2> Unversioned files are <u>not</u> a part of a check-out. Unversioned files are intended to be accessible as web pages using URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". In other words, the URI method "<b>uv</b>" (short for "unversioned") followed by the name of the unversioned file will retrieve the content of the file. The MIME type is inferred from the filename suffix. The content of unversioned files can also be retrieved using the [/help?cmd=unversioned|fossil unvers cat <i>FILENAME</i>] command. A list of all unversioned files on a server can be seen using the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example]). <h2>Syncing Unversioned Files</h2> Unversioned content does not sync between repositories by default. One must request it via commands such as: <blockquote><pre> fossil sync <b>-u</b> fossil clone <b>-u</b> <i>URL local-repo-name</i> fossil unversioned sync </pre></blockquote> The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] commands will synchronize unversioned content if and only if they're given the "-u" (or "--unversioned") command-line option. The [/help?cmd=unversioned|fossil unversioned sync] command synchronizes the unversioned content without synchronizing anything else. Notice that the "-u" option does not work on [/help?cmd=push|fossil push] or [/help?cmd=pull|fossil pull]. The "-u" option is only available on "sync" and "clone". A rough equivalent of an unversioned pull would be the [/help?cmd=unversioned|fossil unversioned revert] command. The "unversioned revert" command causes the unversioned content on the local repository to overwritten by the unversioned content found on the remote repository. Beware that because unversioned file sync is an uncommonly dangerous capability — there being no history to revert to in the case of human error — even the all-powerful Fossil "setup" user does not get unversioned file sync capability by default. See [./caps/admin-v-setup.md#dcap | this] for the full details, but the short-and-sweet takeaway is that a user needs the "y" capability on the remote before they can sync unversioned content. Until then, you're allowed to add such files to your local repo, but they will not sync. <h2>Implementation Details</h2> <i>(This section outlines the current implementation of unversioned files. This is not an interface spec and hence subject to change.)</i> Unversioned content is stored in the repository in the "unversioned" table: |
| ︙ | ︙ | |||
75 76 77 78 79 80 81 | hash TEXT, -- SHA1 hash of uncompressed content sz INTEGER, -- Size of uncompressed content encoding INT, -- 0: plaintext 1: zlib compressed content BLOB -- File content ); </pre></blockquote> | > | | | | | | > > > > | > > | | | | 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 | hash TEXT, -- SHA1 hash of uncompressed content sz INTEGER, -- Size of uncompressed content encoding INT, -- 0: plaintext 1: zlib compressed content BLOB -- File content ); </pre></blockquote> Fossil does not create the table ahead of need. If there are no unversioned files in the repository, the "unversioned" table will not exist. Consequently, one simple way to purge all unversioned content from a repository is to run: <blockquote><pre> fossil sql "DROP TABLE unversioned; VACUUM;" </pre></blockquote> Lacking history for unversioned files, Fossil does not attempt delta compression on them. Fossil servers exchange unversioned content whole; it does not attempt to "diff" your local version against the remote and send only the changes. We point tihs out because one use-case for unversioned content is to send large, frequently-changing files. Appreciate the consequences before making each change. There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>". The first is the regular HTTP payload compression step, done on all syncs. The second is that Fossil sends SHA1 hash exchanges to determine when it can avoid sending duplicate content over the wire unnecessarily. See the [./sync.wiki|synchronization protocol documentation] for further information. |
Changes to www/wikitheory.wiki.
1 2 3 4 5 6 7 8 | <title>Wiki In Fossil</title> <h2>Introduction</h2> Fossil uses [/wiki_rules | Fossil wiki markup] and/or [/md_rules | Markdown markup] for many things: * Stand-alone wiki pages. * Description and comments in [./bugtheory.wiki | bug reports]. | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<title>Wiki In Fossil</title>
<h2>Introduction</h2>
Fossil uses [/wiki_rules | Fossil wiki markup] and/or
[/md_rules | Markdown markup] for many things:
* Stand-alone wiki pages.
* Description and comments in [./bugtheory.wiki | bug reports].
* Check-in comments. (For historical reasons, these must
currently be in fossil-wiki text format.)
* [./embeddeddoc.wiki | Embedded documentation] files whose
name ends in ".wiki" or ".md" or ".markdown".
* [./event.wiki | Technical notes].
* [./forum.wiki | Forum messages].
* Auxiliary notes on check-ins and branches.
The [/wiki_rules | formatting rules for fossil wiki]
|
| ︙ | ︙ | |||
58 59 60 61 62 63 64 | use the exact same markup. Some projects may choose to use both forms of documentation at the same time. Because the same format is used, it is trivial to move a file from wiki to embedded documentation or back again as the project evolves. <h2>Bug-reports and check-in comments and Forum messages</h2> | | | > | | > > > | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | use the exact same markup. Some projects may choose to use both forms of documentation at the same time. Because the same format is used, it is trivial to move a file from wiki to embedded documentation or back again as the project evolves. <h2>Bug-reports and check-in comments and Forum messages</h2> The comments on check-ins, forum posts, 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. Minor caveat: check-in messages are currently limited to the fossil-wiki format. <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: |
| ︙ | ︙ |