Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Sync with trunk. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | diff-word-wrap |
Files: | files | file ages | folders |
SHA3-256: |
b97f0310f042682afda72fea562fdde5 |
User & Date: | florian 2024-09-06 10:57:00.000 |
Context
2024-09-06
| ||
10:58 | Add 'overflow-x: hidden' to the table cells containing the diff text to ensure "unbreakable" text (like long runs of spaces) is truncated. ... (check-in: ddf26dd3 user: florian tags: diff-word-wrap) | |
10:57 | Sync with trunk. ... (check-in: b97f0310 user: florian tags: diff-word-wrap) | |
2024-09-05
| ||
09:19 | In the 'unsaved changes' error of the patch command, make it explicit that the patch cannot be applied (because it otherwise comes across as informational, not an error). ... (check-in: 8c5faa36 user: stephan tags: trunk) | |
2024-08-25
| ||
17:48 | Change an assert() to a fossil_fatal() to make the error message appear in the web UI. ... (check-in: 3aad57dd user: florian tags: diff-word-wrap) | |
Changes
Changes to auto.def.
︙ | ︙ | |||
743 744 745 746 747 748 749 | # cctest_function check for "getpass()" with no args and fail if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { define FOSSIL_HAVE_GETPASS 1 msg-result "Found getpass() with unistd.h" } # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE | | > < | 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 | # cctest_function check for "getpass()" with no args and fail if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { define FOSSIL_HAVE_GETPASS 1 msg-result "Found getpass() with unistd.h" } # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE if {![cc-check-functions getloadavg] || ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} { define FOSSIL_OMIT_LOAD_AVERAGE 1 msg-result "Load average support unavailable" } # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars if {![cc-check-functions getpassphrase]} { # Haiku needs this cc-check-function-in-lib getpass bsd } cc-check-function-in-lib sin m # Check for the FuseFS library if {[opt-bool fusefs]} { if {[opt-bool static]} { msg-result "FuseFS support disabled due to -static" } elseif {[cc-check-function-in-lib fuse_mount fuse]} { define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS define FOSSIL_HAVE_FUSEFS 1 msg-result "FuseFS support enabled" } } ######################################################################## # Checks the compiler for compile_commands.json support. # |
︙ | ︙ |
Changes to autosetup/README.autosetup.
|
| | | 1 2 3 4 5 6 7 8 | README.autosetup created by autosetup v0.7.2 This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. *.tcl files in this directory are optional modules which can be loaded with the 'use' directive. |
︙ | ︙ |
Changes to autosetup/autosetup.
1 2 3 4 5 6 7 8 | #!/bin/sh # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@" # Note that the version has a trailing + on unreleased versions | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/bin/sh # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@" # Note that the version has a trailing + on unreleased versions set autosetup(version) 0.7.2 # Can be set to 1 to debug early-init problems set autosetup(debug) [expr {"--debug" in $argv}] ################################################################## # # Main flow of control, option handling |
︙ | ︙ |
Changes to autosetup/autosetup-find-tclsh.
1 2 3 4 5 6 7 8 9 10 11 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh in current dir from source # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) # If an argument is given, use that as the test instead of autosetup-test-tclsh d="`dirname "$0"`" for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh in current dir from source # Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works) # If an argument is given, use that as the test instead of autosetup-test-tclsh d="`dirname "$0"`" for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do { $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do { $cc -o jimsh0 "$d/jimsh0.c"; } 2>&1 >/dev/null || continue ./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0 done echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." echo false |
Changes to autosetup/jimsh0.c.
1 2 3 4 5 6 7 8 9 10 11 12 | /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ #define JIM_TCL_COMPAT #define JIM_ANSIC #define JIM_REGEXP #define HAVE_NO_AUTOCONF #define _JIMAUTOCONF_H #define TCL_LIBRARY "." #define jim_ext_bootstrap #define jim_ext_aio #define jim_ext_readdir #define jim_ext_regexp #define jim_ext_file | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ #define JIM_TCL_COMPAT #define JIM_ANSIC #define JIM_REGEXP #define HAVE_NO_AUTOCONF #define JIM_TINY #define _JIMAUTOCONF_H #define TCL_LIBRARY "." #define jim_ext_bootstrap #define jim_ext_aio #define jim_ext_readdir #define jim_ext_regexp #define jim_ext_file |
︙ | ︙ | |||
58 59 60 61 62 63 64 | #define HAVE_SYS_TIME_H #define HAVE_DIRENT_H #define HAVE_UNISTD_H #define HAVE_UMASK #define HAVE_PIPE #define _FILE_OFFSET_BITS 64 #endif | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #define HAVE_SYS_TIME_H #define HAVE_DIRENT_H #define HAVE_UNISTD_H #define HAVE_UMASK #define HAVE_PIPE #define _FILE_OFFSET_BITS 64 #endif #define JIM_VERSION 83 #ifndef JIM_WIN32COMPAT_H #define JIM_WIN32COMPAT_H #ifdef __cplusplus extern "C" { |
︙ | ︙ | |||
572 573 574 575 576 577 578 | unsigned int i, j; } Jim_PrngState; typedef struct Jim_Interp { Jim_Obj *result; int unused_errorLine; Jim_Obj *currentFilenameObj; | | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | unsigned int i, j; } Jim_PrngState; typedef struct Jim_Interp { Jim_Obj *result; int unused_errorLine; Jim_Obj *currentFilenameObj; int break_level; int maxCallFrameDepth; int maxEvalDepth; int evalDepth; int returnCode; int returnLevel; int exitCode; long id; |
︙ | ︙ | |||
714 715 716 717 718 719 720 721 722 723 724 725 726 727 | JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj); JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv); #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj); JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags); JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); | > > > > > > > > | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 | JIM_EXPORT int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj); JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv); #define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj); JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags); JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr); JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *fileNameObj, int lineNumber); JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); |
︙ | ︙ | |||
2341 2342 2343 2344 2345 2346 2347 | neededLen -= retval; } continue; } if (JimCheckStreamError(interp, af)) { return JIM_ERR; } | < | < | 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 | neededLen -= retval; } continue; } if (JimCheckStreamError(interp, af)) { return JIM_ERR; } break; } return JIM_OK; } static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen) { |
︙ | ︙ | |||
2615 2616 2617 2618 2619 2620 2621 | aio_consume(af->readbuf, nl - pt + 1); break; } offset = len; len = af->fops->reader(af, buf, AIO_BUF_LEN, nb); if (len <= 0) { | < < | | < < | < | 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 | aio_consume(af->readbuf, nl - pt + 1); break; } offset = len; len = af->fops->reader(af, buf, AIO_BUF_LEN, nb); if (len <= 0) { break; } Jim_AppendString(interp, af->readbuf, buf, len); } aio_set_nonblocking(af, nb); if (!nl && aio_eof(af)) { objPtr = af->readbuf; |
︙ | ︙ | |||
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 | #define MAX_SUB_MATCHES 50 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int regcomp_flags = 0; int regexec_flags = 0; int opt_all = 0; int offset = 0; regex_t *regex; const char *p; | > | > > | | | | 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 | #define MAX_SUB_MATCHES 50 int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int regcomp_flags = 0; int regexec_flags = 0; int opt_all = 0; int opt_command = 0; int offset = 0; regex_t *regex; const char *p; int result = JIM_OK; regmatch_t pmatch[MAX_SUB_MATCHES + 1]; int num_matches = 0; int i, j, n; Jim_Obj *varname; Jim_Obj *resultObj; Jim_Obj *cmd_prefix = NULL; Jim_Obj *regcomp_obj = NULL; const char *source_str; int source_len; const char *replace_str = NULL; int replace_len; const char *pattern; int option; enum { OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END }; static const char * const options[] = { "-nocase", "-line", "-all", "-start", "-command", "--", NULL }; if (argc < 4) { wrongNumArgs: Jim_WrongNumArgs(interp, 1, argv, "?-switch ...? exp string subSpec ?varName?"); return JIM_ERR; |
︙ | ︙ | |||
3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 | if (++i == argc) { goto wrongNumArgs; } if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { return JIM_ERR; } break; } } if (argc - i != 3 && argc - i != 4) { goto wrongNumArgs; } | > > > > > > > | > > > > > > > > > > > | > | 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 | if (++i == argc) { goto wrongNumArgs; } if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) { return JIM_ERR; } break; case OPT_COMMAND: opt_command = 1; break; } } if (argc - i != 3 && argc - i != 4) { goto wrongNumArgs; } regcomp_obj = Jim_DuplicateObj(interp, argv[i]); Jim_IncrRefCount(regcomp_obj); regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags); if (!regex) { Jim_DecrRefCount(interp, regcomp_obj); return JIM_ERR; } pattern = Jim_String(argv[i]); source_str = Jim_GetString(argv[i + 1], &source_len); if (opt_command) { cmd_prefix = argv[i + 2]; if (Jim_ListLength(interp, cmd_prefix) == 0) { Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1); Jim_DecrRefCount(interp, regcomp_obj); return JIM_ERR; } Jim_IncrRefCount(cmd_prefix); } else { replace_str = Jim_GetString(argv[i + 2], &replace_len); } varname = argv[i + 3]; resultObj = Jim_NewStringObj(interp, "", 0); if (offset) { if (offset < 0) { |
︙ | ︙ | |||
3889 3890 3891 3892 3893 3894 3895 3896 | break; } num_matches++; Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so); | > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 | break; } num_matches++; Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so); if (opt_command) { Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix); for (j = 0; j < MAX_SUB_MATCHES; j++) { if (pmatch[j].rm_so == -1) { break; } else { Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so); Jim_ListAppendElement(interp, cmdListObj, srcObj); } } Jim_IncrRefCount(cmdListObj); result = Jim_EvalObj(interp, cmdListObj); Jim_DecrRefCount(interp, cmdListObj); if (result != JIM_OK) { goto cmd_error; } Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1); } else { for (j = 0; j < replace_len; j++) { int idx; int c = replace_str[j]; if (c == '&') { idx = 0; } else if (c == '\\' && j < replace_len) { c = replace_str[++j]; if ((c >= '0') && (c <= '9')) { idx = c - '0'; } else if ((c == '\\') || (c == '&')) { Jim_AppendString(interp, resultObj, replace_str + j, 1); continue; } else { Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2); continue; } } else { Jim_AppendString(interp, resultObj, replace_str + j, 1); continue; } if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) { Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so, pmatch[idx].rm_eo - pmatch[idx].rm_so); } } } p += pmatch[0].rm_eo; n -= pmatch[0].rm_eo; |
︙ | ︙ | |||
3954 3955 3956 3957 3958 3959 3960 3961 | regexec_flags = 0; } } while (n); Jim_AppendString(interp, resultObj, p, -1); | > > | | | | | | | | | | | | | > > > > > > > > > > | 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 | regexec_flags = 0; } } while (n); Jim_AppendString(interp, resultObj, p, -1); cmd_error: if (result == JIM_OK) { if (argc - i == 4) { result = Jim_SetVariable(interp, varname, resultObj); if (result == JIM_OK) { Jim_SetResultInt(interp, num_matches); } else { Jim_FreeObj(interp, resultObj); } } else { Jim_SetResult(interp, resultObj); result = JIM_OK; } } else { Jim_FreeObj(interp, resultObj); } if (opt_command) { Jim_DecrRefCount(interp, cmd_prefix); } Jim_DecrRefCount(interp, regcomp_obj); return result; } int Jim_regexpInit(Jim_Interp *interp) { Jim_PackageProvideCheck(interp, "regexp"); |
︙ | ︙ | |||
6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 | pt = strptime(Jim_String(argv[0]), options.format, &tm); if (pt == 0 || *pt != 0) { Jim_SetResultString(interp, "Failed to parse time according to format", -1); return JIM_ERR; } Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm)); return JIM_OK; } #endif static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv) | > | 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 | pt = strptime(Jim_String(argv[0]), options.format, &tm); if (pt == 0 || *pt != 0) { Jim_SetResultString(interp, "Failed to parse time according to format", -1); return JIM_ERR; } tm.tm_isdst = options.gmt ? 0 : -1; Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm)); return JIM_OK; } #endif static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv) |
︙ | ︙ | |||
6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 | Jim_posixInit(interp); Jim_clockInit(interp); Jim_arrayInit(interp); Jim_stdlibInit(interp); Jim_tclcompatInit(interp); return JIM_OK; } #define JIM_OPTIMIZATION #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> | > > | 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 | Jim_posixInit(interp); Jim_clockInit(interp); Jim_arrayInit(interp); Jim_stdlibInit(interp); Jim_tclcompatInit(interp); return JIM_OK; } #ifndef JIM_TINY #define JIM_OPTIMIZATION #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <ctype.h> |
︙ | ︙ | |||
6790 6791 6792 6793 6794 6795 6796 | #define JIM_DEBUG_PANIC #endif #define JIM_INTEGER_SPACE 24 | > | > | 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 | #define JIM_DEBUG_PANIC #endif #define JIM_INTEGER_SPACE 24 #if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST) static const char *jim_tt_name(int type); #endif #ifdef JIM_DEBUG_PANIC static void JimPanicDump(int fail_condition, const char *fmt, ...); #define JimPanic(X) JimPanicDump X #else #define JimPanic(X) #endif |
︙ | ︙ | |||
6826 6827 6828 6829 6830 6831 6832 | static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); static int JimSign(jim_wide w); static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen); static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len); static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv); static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr); | < | 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 | static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv); static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); static int JimSign(jim_wide w); static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen); static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len); static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv); static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr); static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); #define JIM_DICT_SUGAR 100 |
︙ | ︙ | |||
7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 | const char *tend; int tline; int tt; int eof; int inquote; int comment; struct JimParseMissing missing; }; static int JimParseScript(struct JimParserCtx *pc); static int JimParseSep(struct JimParserCtx *pc); static int JimParseEol(struct JimParserCtx *pc); static int JimParseCmd(struct JimParserCtx *pc); static int JimParseQuote(struct JimParserCtx *pc); | > | 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 | const char *tend; int tline; int tt; int eof; int inquote; int comment; struct JimParseMissing missing; const char *errmsg; }; static int JimParseScript(struct JimParserCtx *pc); static int JimParseSep(struct JimParserCtx *pc); static int JimParseEol(struct JimParserCtx *pc); static int JimParseCmd(struct JimParserCtx *pc); static int JimParseQuote(struct JimParserCtx *pc); |
︙ | ︙ | |||
9505 9506 9507 9508 9509 9510 9511 | void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue; Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); } | < < < < < < < < < < < | 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 | void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue; Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); } static const Jim_ObjType scriptLineObjType = { "scriptline", NULL, NULL, NULL, JIM_NONE, }; |
︙ | ︙ | |||
9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 | int linenr; int missing; } ScriptObj; static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static int JimParseCheckMissing(Jim_Interp *interp, int ch); static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr); void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { int i; struct ScriptObj *script = (void *)objPtr->internalRep.ptr; if (--script->inUse != 0) | > | 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 | int linenr; int missing; } ScriptObj; static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static int JimParseCheckMissing(Jim_Interp *interp, int ch); static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr); static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script); void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { int i; struct ScriptObj *script = (void *)objPtr->internalRep.ptr; if (--script->inUse != 0) |
︙ | ︙ | |||
9791 9792 9793 9794 9795 9796 9797 | while (wordtokens--) { const ParseToken *t = &tokenlist->list[i++]; token->type = t->type; token->objPtr = JimMakeScriptObj(interp, t); Jim_IncrRefCount(token->objPtr); | | | 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 | while (wordtokens--) { const ParseToken *t = &tokenlist->list[i++]; token->type = t->type; token->objPtr = JimMakeScriptObj(interp, t); Jim_IncrRefCount(token->objPtr); Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line); token++; } } if (lineargs == 0) { token--; } |
︙ | ︙ | |||
9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 | msg = "missing quote"; break; } Jim_SetResultString(interp, msg, -1); return JIM_ERR; } static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, ParseTokenList *tokenlist) { int i; struct ScriptToken *token; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 | msg = "missing quote"; break; } Jim_SetResultString(interp, msg, -1); return JIM_ERR; } Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr) { int line; Jim_Obj *fileNameObj; if (objPtr->typePtr == &sourceObjType) { fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; line = objPtr->internalRep.sourceValue.lineNumber; } else if (objPtr->typePtr == &scriptObjType) { ScriptObj *script = JimGetScript(interp, objPtr); fileNameObj = script->fileNameObj; line = script->firstline; } else { fileNameObj = interp->emptyObj; line = 1; } *lineptr = line; return fileNameObj; } void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *fileNameObj, int lineNumber) { JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object")); Jim_FreeIntRep(interp, objPtr); Jim_IncrRefCount(fileNameObj); objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; objPtr->internalRep.sourceValue.lineNumber = lineNumber; objPtr->typePtr = &sourceObjType; } static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, ParseTokenList *tokenlist) { int i; struct ScriptToken *token; |
︙ | ︙ | |||
9879 9880 9881 9882 9883 9884 9885 | static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int scriptTextLen; const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); struct JimParserCtx parser; struct ScriptObj *script; ParseTokenList tokenlist; | > | | < < < < < < | < | 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 | static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int scriptTextLen; const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); struct JimParserCtx parser; struct ScriptObj *script; ParseTokenList tokenlist; Jim_Obj *fileNameObj; int line; fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); ScriptTokenListInit(&tokenlist); JimParserInit(&parser, scriptText, scriptTextLen, line); while (!parser.eof) { JimParseScript(&parser); ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); script = Jim_Alloc(sizeof(*script)); memset(script, 0, sizeof(*script)); script->inUse = 1; script->fileNameObj = fileNameObj; Jim_IncrRefCount(script->fileNameObj); script->missing = parser.missing.ch; script->linenr = parser.missing.line; ScriptObjAddTokens(interp, script, &tokenlist); |
︙ | ︙ | |||
11384 11385 11386 11387 11388 11389 11390 11391 11392 11393 | Jim_DecrRefCount(i, i->stackTrace); Jim_DecrRefCount(i, i->errorProc); Jim_DecrRefCount(i, i->unknown); Jim_DecrRefCount(i, i->defer); Jim_DecrRefCount(i, i->nullScriptObj); Jim_DecrRefCount(i, i->currentFilenameObj); Jim_InterpIncrProcEpoch(i); | > > < | 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 | Jim_DecrRefCount(i, i->stackTrace); Jim_DecrRefCount(i, i->errorProc); Jim_DecrRefCount(i, i->unknown); Jim_DecrRefCount(i, i->defer); Jim_DecrRefCount(i, i->nullScriptObj); Jim_DecrRefCount(i, i->currentFilenameObj); Jim_FreeHashTable(&i->commands); Jim_InterpIncrProcEpoch(i); #ifdef JIM_REFERENCES Jim_FreeHashTable(&i->references); #endif Jim_FreeHashTable(&i->packages); Jim_Free(i->prngState); Jim_FreeHashTable(&i->assocData); if (i->traceCmdObj) { |
︙ | ︙ | |||
11586 11587 11588 11589 11590 11591 11592 | Jim_IncrRefCount(stackTraceObj); Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = stackTraceObj; interp->errorFlag = 1; } | | > > > > > > > | | | | > | 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 | Jim_IncrRefCount(stackTraceObj); Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = stackTraceObj; interp->errorFlag = 1; } static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script) { if (!interp->errorFlag) { int i; Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0); if (interp->procLevel == 0 && script) { Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); Jim_ListAppendElement(interp, stackTrace, script->fileNameObj); Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr)); Jim_ListAppendElement(interp, stackTrace, interp->emptyObj); } else { for (i = 0; i <= interp->procLevel; i++) { Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i); if (frame) { JimAddStackFrame(interp, frame, stackTrace); } } } JimSetStackTrace(interp, stackTrace); } } int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc, |
︙ | ︙ | |||
12286 12287 12288 12289 12290 12291 12292 | Jim_Free(dict); return JIM_OK; } | < < < < < | < < | 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 | Jim_Free(dict); return JIM_OK; } fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr); Jim_IncrRefCount(fileNameObj); str = Jim_GetString(objPtr, &strLen); Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &listObjType; |
︙ | ︙ | |||
12315 12316 12317 12318 12319 12320 12321 | while (!parser.eof) { Jim_Obj *elementPtr; JimParseList(&parser); if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) continue; elementPtr = JimParserGetTokenObj(interp, &parser); | | | 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 | while (!parser.eof) { Jim_Obj *elementPtr; JimParseList(&parser); if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) continue; elementPtr = JimParserGetTokenObj(interp, &parser); Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline); ListAppendElement(objPtr, elementPtr); } } Jim_DecrRefCount(interp, fileNameObj); return JIM_OK; } |
︙ | ︙ | |||
12370 12371 12372 12373 12374 12375 12376 | Jim_Obj *command; Jim_Interp *interp; enum { JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_INTEGER, JIM_LSORT_REAL, | | > | 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 | Jim_Obj *command; Jim_Interp *interp; enum { JIM_LSORT_ASCII, JIM_LSORT_NOCASE, JIM_LSORT_INTEGER, JIM_LSORT_REAL, JIM_LSORT_COMMAND, JIM_LSORT_DICT } type; int order; Jim_Obj **indexv; int indexc; int unique; int (*subfn)(Jim_Obj **, Jim_Obj **); }; |
︙ | ︙ | |||
12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 12413 12414 12415 | return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; } static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order; } static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { jim_wide lhs = 0, rhs = 0; if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 | return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; } static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order; } static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { const char *left = Jim_String(*lhsObj); const char *right = Jim_String(*rhsObj); while (1) { if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) { jim_wide lint, rint; char *lend, *rend; lint = jim_strtoull(left, &lend); rint = jim_strtoull(right, &rend); if (lint != rint) { return JimSign(lint - rint) * sort_info->order; } if (lend -left != rend - right) { return JimSign((lend - left) - (rend - right)) * sort_info->order; } left = lend; right = rend; } else { int cl, cr; left += utf8_tounicode_case(left, &cl, 1); right += utf8_tounicode_case(right, &cr, 1); if (cl != cr) { return JimSign(cl - cr) * sort_info->order; } if (cl == 0) { return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; } } } } static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { jim_wide lhs = 0, rhs = 0; if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK || Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) { |
︙ | ︙ | |||
12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 | break; case JIM_LSORT_REAL: fn = ListSortReal; break; case JIM_LSORT_COMMAND: fn = ListSortCommand; break; default: fn = NULL; JimPanic((1, "ListSort called with invalid sort type")); return -1; } if (info->indexc) { | > > > | 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 | break; case JIM_LSORT_REAL: fn = ListSortReal; break; case JIM_LSORT_COMMAND: fn = ListSortCommand; break; case JIM_LSORT_DICT: fn = ListSortDict; break; default: fn = NULL; JimPanic((1, "ListSort called with invalid sort type")); return -1; } if (info->indexc) { |
︙ | ︙ | |||
12565 12566 12567 12568 12569 12570 12571 12572 12573 12574 12575 12576 12577 12578 | static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) { int currentLen = listPtr->internalRep.listValue.len; int requiredLen = currentLen + elemc; int i; Jim_Obj **point; if (requiredLen > listPtr->internalRep.listValue.maxLen) { if (currentLen) { requiredLen *= 2; } ListEnsureLength(listPtr, requiredLen); | > > > > > | 12689 12690 12691 12692 12693 12694 12695 12696 12697 12698 12699 12700 12701 12702 12703 12704 12705 12706 12707 | static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) { int currentLen = listPtr->internalRep.listValue.len; int requiredLen = currentLen + elemc; int i; Jim_Obj **point; if (elemc == 0) { return; } if (requiredLen > listPtr->internalRep.listValue.maxLen) { if (currentLen) { requiredLen *= 2; } ListEnsureLength(listPtr, requiredLen); |
︙ | ︙ | |||
14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 | #undef OPRINIT_ATTR #define JIM_EXPR_OPERATORS_NUM \ (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) static int JimParseExpression(struct JimParserCtx *pc) { while (1) { while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { if (*pc->p == '\n') { pc->linenr++; } pc->p++; | > > | 14459 14460 14461 14462 14463 14464 14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 | #undef OPRINIT_ATTR #define JIM_EXPR_OPERATORS_NUM \ (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) static int JimParseExpression(struct JimParserCtx *pc) { pc->errmsg = NULL; while (1) { while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { if (*pc->p == '\n') { pc->linenr++; } pc->p++; |
︙ | ︙ | |||
14380 14381 14382 14383 14384 14385 14386 14387 14388 14389 14390 14391 14392 14393 | return JimParseCmd(pc); case '$': if (JimParseVar(pc) == JIM_ERR) return JimParseExprOperator(pc); else { if (pc->tt == JIM_TT_EXPRSUGAR) { return JIM_ERR; } return JIM_OK; } break; case '0': case '1': | > | 14511 14512 14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 14523 14524 14525 | return JimParseCmd(pc); case '$': if (JimParseVar(pc) == JIM_ERR) return JimParseExprOperator(pc); else { if (pc->tt == JIM_TT_EXPRSUGAR) { pc->errmsg = "nesting expr in expr is not allowed"; return JIM_ERR; } return JIM_OK; } break; case '0': case '1': |
︙ | ︙ | |||
14524 14525 14526 14527 14528 14529 14530 14531 14532 14533 14534 14535 14536 14537 14538 14539 14540 14541 | int len = pc->len - bestLen; while (len && isspace(UCHAR(*p))) { len--; p++; } if (*p != '(') { return JIM_ERR; } } pc->tend = pc->p + bestLen - 1; pc->p += bestLen; pc->len -= bestLen; pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP; return JIM_OK; } | > < < < < < < < < < < < < < < < < < < < < < < < < < | 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 14671 14672 14673 14674 14675 14676 14677 14678 14679 14680 14681 | int len = pc->len - bestLen; while (len && isspace(UCHAR(*p))) { len--; p++; } if (*p != '(') { pc->errmsg = "function requires parentheses"; return JIM_ERR; } } pc->tend = pc->p + bestLen - 1; pc->p += bestLen; pc->len -= bestLen; pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP; return JIM_OK; } static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType exprObjType = { "expression", |
︙ | ︙ | |||
14865 14866 14867 14868 14869 14870 14871 | } if (!objPtr) { objPtr = Jim_NewStringObj(interp, t->token, t->len); if (t->type == JIM_TT_CMD) { | | | 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 | } if (!objPtr) { objPtr = Jim_NewStringObj(interp, t->token, t->len); if (t->type == JIM_TT_CMD) { Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line); } } node = builder->next++; node->objPtr = objPtr; Jim_IncrRefCount(node->objPtr); |
︙ | ︙ | |||
14963 14964 14965 14966 14967 14968 14969 | struct ExprTree *expr; ParseTokenList tokenlist; int line; Jim_Obj *fileNameObj; int rc = JIM_ERR; | < < < < < | < < > > > > > > > > | > > | | 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 15089 15090 15091 15092 15093 15094 15095 15096 15097 15098 15099 15100 15101 15102 15103 15104 15105 15106 15107 15108 15109 15110 15111 15112 15113 15114 15115 15116 15117 15118 15119 15120 15121 15122 15123 15124 15125 15126 15127 15128 15129 15130 | struct ExprTree *expr; ParseTokenList tokenlist; int line; Jim_Obj *fileNameObj; int rc = JIM_ERR; fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line); Jim_IncrRefCount(fileNameObj); exprText = Jim_GetString(objPtr, &exprTextLen); ScriptTokenListInit(&tokenlist); JimParserInit(&parser, exprText, exprTextLen, line); while (!parser.eof) { if (JimParseExpression(&parser) != JIM_OK) { ScriptTokenListFree(&tokenlist); Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr); if (parser.errmsg) { Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL); } expr = NULL; goto err; } ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } #ifdef DEBUG_SHOW_EXPR_TOKENS { int i; printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj)); for (i = 0; i < tokenlist.count; i++) { printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type), tokenlist.list[i].len, tokenlist.list[i].token); } } #endif if (tokenlist.count <= 1) { Jim_SetResultString(interp, "empty expression", -1); rc = JIM_ERR; } else { rc = JimParseCheckMissing(interp, parser.missing.ch); } if (rc != JIM_OK) { ScriptTokenListFree(&tokenlist); Jim_DecrRefCount(interp, fileNameObj); return rc; } expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj); ScriptTokenListFree(&tokenlist); |
︙ | ︙ | |||
15856 15857 15858 15859 15860 15861 15862 15863 | { JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object")); int ret; Jim_Obj *nargv[7]; Jim_Obj *traceCmdObj = interp->traceCmdObj; Jim_Obj *resultObj = Jim_GetResult(interp); | > > > > | > | | | 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978 15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 | { JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object")); int ret; Jim_Obj *nargv[7]; Jim_Obj *traceCmdObj = interp->traceCmdObj; Jim_Obj *resultObj = Jim_GetResult(interp); ScriptObj *script = NULL; if (interp->evalFrame->scriptObj) { script = JimGetScript(interp, interp->evalFrame->scriptObj); } nargv[0] = traceCmdObj; nargv[1] = Jim_NewStringObj(interp, type, -1); nargv[2] = script ? script->fileNameObj : interp->emptyObj; nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1); nargv[4] = resultObj; nargv[5] = argv[0]; nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1); interp->traceCmdObj = NULL; |
︙ | ︙ | |||
15984 15985 15986 15987 15988 15989 15990 | retcode = JimCallProcedure(interp, cmdPtr, objc, objv); } else { interp->cmdPrivData = cmdPtr->u.native.privData; retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); } if (retcode == JIM_ERR) { | | | 16100 16101 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 | retcode = JimCallProcedure(interp, cmdPtr, objc, objv); } else { interp->cmdPrivData = cmdPtr->u.native.privData; retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); } if (retcode == JIM_ERR) { JimSetErrorStack(interp, NULL); } } if (tailcallObj) { Jim_DecrRefCount(interp, tailcallObj); tailcallObj = NULL; |
︙ | ︙ | |||
16019 16020 16021 16022 16023 16024 16025 | interp->cmdPrivData = prevPrivData; interp->evalDepth--; out: JimDecrCmdRefCount(interp, cmdPtr); if (retcode == JIM_ERR) { | | > | 16135 16136 16137 16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 | interp->cmdPrivData = prevPrivData; interp->evalDepth--; out: JimDecrCmdRefCount(interp, cmdPtr); if (retcode == JIM_ERR) { JimSetErrorStack(interp, NULL); } if (interp->framePtr->tailcallObj) { JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd); Jim_DecrRefCount(interp, interp->framePtr->tailcallObj); interp->framePtr->tailcallCmd = NULL; interp->framePtr->tailcallObj = NULL; } return retcode; } int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int i, retcode; Jim_EvalFrame frame; for (i = 0; i < objc; i++) Jim_IncrRefCount(objv[i]); JimPushEvalFrame(interp, &frame, NULL); retcode = JimInvokeCommand(interp, objc, objv); JimPopEvalFrame(interp); |
︙ | ︙ | |||
16179 16180 16181 16182 16183 16184 16185 | objPtr->typePtr = &interpolatedObjType; objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; Jim_IncrRefCount(intv[2]); } else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) { | > > | | 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 | objPtr->typePtr = &interpolatedObjType; objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr; objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2]; Jim_IncrRefCount(intv[2]); } else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) { int line; Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line); Jim_SetSourceInfo(interp, objPtr, fileNameObj, line); } s = objPtr->bytes = Jim_Alloc(totlen + 1); objPtr->length = totlen; for (i = 0; i < tokens; i++) { if (intv[i]) { |
︙ | ︙ | |||
16246 16247 16248 16249 16250 16251 16252 | if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { return JimEvalObjList(interp, scriptObjPtr); } Jim_IncrRefCount(scriptObjPtr); script = JimGetScript(interp, scriptObjPtr); if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) { | | | 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 | if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { return JimEvalObjList(interp, scriptObjPtr); } Jim_IncrRefCount(scriptObjPtr); script = JimGetScript(interp, scriptObjPtr); if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) { JimSetErrorStack(interp, script); Jim_DecrRefCount(interp, scriptObjPtr); return JIM_ERR; } Jim_SetEmptyResult(interp); token = script->token; |
︙ | ︙ | |||
16418 16419 16420 16421 16422 16423 16424 | Jim_Free(argv); argv = sargv; } } if (retcode == JIM_ERR) { | | | 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 | Jim_Free(argv); argv = sargv; } } if (retcode == JIM_ERR) { JimSetErrorStack(interp, NULL); } JimPopEvalFrame(interp); Jim_FreeIntRep(interp, scriptObjPtr); scriptObjPtr->typePtr = &scriptObjType; Jim_SetIntRepPtr(scriptObjPtr, script); |
︙ | ︙ | |||
16646 16647 16648 16649 16650 16651 16652 | { int retval; Jim_Obj *scriptObjPtr; scriptObjPtr = Jim_NewStringObj(interp, script, -1); Jim_IncrRefCount(scriptObjPtr); if (filename) { | | | 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 | { int retval; Jim_Obj *scriptObjPtr; scriptObjPtr = Jim_NewStringObj(interp, script, -1); Jim_IncrRefCount(scriptObjPtr); if (filename) { Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno); } retval = Jim_EvalObj(interp, scriptObjPtr); Jim_DecrRefCount(interp, scriptObjPtr); return retval; } int Jim_Eval(Jim_Interp *interp, const char *script) |
︙ | ︙ | |||
16728 16729 16730 16731 16732 16733 16734 | scriptObjPtr = JimReadTextFile(interp, filename); if (!scriptObjPtr) { return JIM_ERR; } filenameObj = Jim_NewStringObj(interp, filename, -1); | | | 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 | scriptObjPtr = JimReadTextFile(interp, filename); if (!scriptObjPtr) { return JIM_ERR; } filenameObj = Jim_NewStringObj(interp, filename, -1); Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1); oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj); retcode = Jim_EvalObj(interp, scriptObjPtr); JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj); |
︙ | ︙ | |||
16769 16770 16771 16772 16773 16774 16775 | } if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { if (JimParseVar(pc) == JIM_OK) { return; } pc->tstart = pc->p; | | > > | 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 | } if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { if (JimParseVar(pc) == JIM_OK) { return; } pc->tstart = pc->p; pc->p++; pc->len--; } while (pc->len) { if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) { break; } if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) { break; |
︙ | ︙ | |||
17272 17273 17274 17275 17276 17277 17278 | Jim_SetEmptyResult(interp); return JIM_OK; } static int JimCheckLoopRetcode(Jim_Interp *interp, int retval) { if (retval == JIM_BREAK || retval == JIM_CONTINUE) { | | | 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 17403 17404 17405 17406 17407 | Jim_SetEmptyResult(interp); return JIM_OK; } static int JimCheckLoopRetcode(Jim_Interp *interp, int retval) { if (retval == JIM_BREAK || retval == JIM_CONTINUE) { if (--interp->break_level > 0) { return 1; } } return 0; } |
︙ | ︙ | |||
17462 17463 17464 17465 17466 17467 17468 | } evalstart: #endif while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { retval = Jim_EvalObj(interp, argv[4]); | > > > | < < < < | 17583 17584 17585 17586 17587 17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 17601 17602 17603 17604 | } evalstart: #endif while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { retval = Jim_EvalObj(interp, argv[4]); if (JimCheckLoopRetcode(interp, retval)) { immediate++; break; } if (retval == JIM_OK || retval == JIM_CONTINUE) { JIM_IF_OPTIM(evalnext:) retval = Jim_EvalObj(interp, argv[3]); if (retval == JIM_OK || retval == JIM_CONTINUE) { JIM_IF_OPTIM(testcond:) retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); } } } |
︙ | ︙ | |||
18325 18326 18327 18328 18329 18330 18331 | } static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) { static const char * const options[] = { "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", | | | > > | 18445 18446 18447 18448 18449 18450 18451 18452 18453 18454 18455 18456 18457 18458 18459 18460 18461 18462 18463 18464 18465 18466 18467 18468 18469 18470 18471 | } static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) { static const char * const options[] = { "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique", "-stride", "-dictionary", NULL }; enum { OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE, OPT_STRIDE, OPT_DICT }; Jim_Obj *resObj; int i; int retCode; int shared; long stride = 1; Jim_Obj **elements; int listlen; struct lsort_info info; if (argc < 2) { wrongargs: Jim_WrongNumArgs(interp, 1, argv, "?options? list"); return JIM_ERR; |
︙ | ︙ | |||
18362 18363 18364 18365 18366 18367 18368 18369 18370 18371 18372 18373 18374 18375 | if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) return JIM_ERR; switch (option) { case OPT_ASCII: info.type = JIM_LSORT_ASCII; break; case OPT_NOCASE: info.type = JIM_LSORT_NOCASE; break; case OPT_INTEGER: info.type = JIM_LSORT_INTEGER; break; case OPT_REAL: | > > > | 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 18497 18498 18499 18500 | if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) return JIM_ERR; switch (option) { case OPT_ASCII: info.type = JIM_LSORT_ASCII; break; case OPT_DICT: info.type = JIM_LSORT_DICT; break; case OPT_NOCASE: info.type = JIM_LSORT_NOCASE; break; case OPT_INTEGER: info.type = JIM_LSORT_INTEGER; break; case OPT_REAL: |
︙ | ︙ | |||
18416 18417 18418 18419 18420 18421 18422 18423 18424 | goto badindex; } i++; break; } } resObj = argv[argc - 1]; if (stride > 1) { Jim_Obj *tmpListObj; | > > > > > > > < < < | 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 18561 18562 18563 18564 18565 | goto badindex; } i++; break; } } resObj = argv[argc - 1]; JimListGetElements(interp, resObj, &listlen, &elements); if (listlen <= 1) { Jim_SetResult(interp, resObj); return JIM_OK; } if (stride > 1) { Jim_Obj *tmpListObj; int i; if (listlen % stride) { Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1); return JIM_ERR; } tmpListObj = Jim_NewListObj(interp, NULL, 0); Jim_IncrRefCount(tmpListObj); |
︙ | ︙ | |||
18610 18611 18612 18613 18614 18615 18616 | } if (argc == 2) { long level; int ret = Jim_GetLong(interp, argv[1], &level); if (ret != JIM_OK) { return ret; } | | | 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 | } if (argc == 2) { long level; int ret = Jim_GetLong(interp, argv[1], &level); if (ret != JIM_OK) { return ret; } interp->break_level = level; } return retcode; } static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { |
︙ | ︙ | |||
20328 20329 20330 20331 20332 20333 20334 | Jim_DecrRefCount(interp, interp->currentFilenameObj); interp->currentFilenameObj = argv[2]; } Jim_SetResult(interp, interp->currentFilenameObj); return JIM_OK; case INFO_SOURCE:{ | < > | < < < < < < < | < < | < < | 20457 20458 20459 20460 20461 20462 20463 20464 20465 20466 20467 20468 20469 20470 20471 20472 20473 20474 20475 20476 20477 20478 20479 20480 20481 20482 20483 20484 20485 20486 20487 20488 | Jim_DecrRefCount(interp, interp->currentFilenameObj); interp->currentFilenameObj = argv[2]; } Jim_SetResult(interp, interp->currentFilenameObj); return JIM_OK; case INFO_SOURCE:{ Jim_Obj *resObjPtr; Jim_Obj *fileNameObj; if (argc == 4) { Jim_SubCmdArgError(interp, ct, argv[0]); return JIM_ERR; } if (argc == 5) { jim_wide line; if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) { return JIM_ERR; } resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2])); Jim_SetSourceInfo(interp, resObjPtr, argv[3], line); } else { int line; fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line); resObjPtr = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, resObjPtr, fileNameObj); Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); } Jim_SetResult(interp, resObjPtr); return JIM_OK; } |
︙ | ︙ | |||
23642 23643 23644 23645 23646 23647 23648 23649 23650 23651 23652 23653 23654 23655 23656 23657 23658 23659 23660 23661 23662 23663 23664 23665 23666 23667 | Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); } else { filenameObj = Jim_NewStringObj(interp, filename_template, -1); } mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); #ifdef HAVE_MKSTEMP fd = mkstemp(filenameObj->bytes); #else if (mktemp(filenameObj->bytes) == NULL) { fd = -1; } else { fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC); } #endif umask(mask); if (fd < 0) { Jim_SetResultErrno(interp, Jim_String(filenameObj)); Jim_FreeNewObj(interp, filenameObj); return -1; } if (unlink_file) { remove(Jim_String(filenameObj)); | > > > > | 23760 23761 23762 23763 23764 23765 23766 23767 23768 23769 23770 23771 23772 23773 23774 23775 23776 23777 23778 23779 23780 23781 23782 23783 23784 23785 23786 23787 23788 23789 | Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); } else { filenameObj = Jim_NewStringObj(interp, filename_template, -1); } #ifdef HAVE_UMASK mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); #endif #ifdef HAVE_MKSTEMP fd = mkstemp(filenameObj->bytes); #else if (mktemp(filenameObj->bytes) == NULL) { fd = -1; } else { fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC); } #endif #ifdef HAVE_UMASK umask(mask); #endif if (fd < 0) { Jim_SetResultErrno(interp, Jim_String(filenameObj)); Jim_FreeNewObj(interp, filenameObj); return -1; } if (unlink_file) { remove(Jim_String(filenameObj)); |
︙ | ︙ | |||
24256 24257 24258 24259 24260 24261 24262 24263 24264 24265 24266 24267 24268 24269 | if (Jim_InitStaticExtensions(interp) != JIM_OK) { JimPrintErrorMessage(interp); } Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0); Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); retcode = Jim_initjimshInit(interp); if (argc == 1) { if (retcode == JIM_ERR) { JimPrintErrorMessage(interp); } | > > > > > | 24378 24379 24380 24381 24382 24383 24384 24385 24386 24387 24388 24389 24390 24391 24392 24393 24394 24395 24396 | if (Jim_InitStaticExtensions(interp) != JIM_OK) { JimPrintErrorMessage(interp); } Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0); Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0"); #ifdef USE_LINENOISE Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1"); #else Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0"); #endif retcode = Jim_initjimshInit(interp); if (argc == 1) { if (retcode == JIM_ERR) { JimPrintErrorMessage(interp); } |
︙ | ︙ |
Changes to extsrc/cson_amalgamation.c.
︙ | ︙ | |||
1433 1434 1435 1436 1437 1438 1439 | #endif #if defined(__cplusplus) extern "C" { #endif | < | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 | #endif #if defined(__cplusplus) extern "C" { #endif /** This type holds the "vtbl" for type-specific operations when working with cson_value objects. All cson_values of a given logical type share a pointer to a single library-internal instance of this class. */ |
︙ | ︙ | |||
1684 1685 1686 1687 1688 1689 1690 | && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) ) ? 1 : 0; } char const * cson_rc_string(int rc) { | | | | > | 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 | && ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) ) ? 1 : 0; } char const * cson_rc_string(int rc) { switch(rc){ #define CHECK(N) case CSON_RC_##N: return #N; CHECK(OK); CHECK(ArgError); CHECK(RangeError); CHECK(TypeError); CHECK(IOError); CHECK(AllocError); CHECK(NYIError); CHECK(InternalError); CHECK(UnsupportedError); CHECK(NotFoundError); CHECK(UnknownError); CHECK(Parse_INVALID_CHAR); CHECK(Parse_INVALID_KEYWORD); CHECK(Parse_INVALID_ESCAPE_SEQUENCE); CHECK(Parse_INVALID_UNICODE_SEQUENCE); CHECK(Parse_INVALID_NUMBER); CHECK(Parse_NESTING_DEPTH_REACHED); CHECK(Parse_UNBALANCED_COLLECTION); CHECK(Parse_EXPECTED_KEY); CHECK(Parse_EXPECTED_COLON); default: return "UnknownError"; } #undef CHECK } /** If CSON_LOG_ALLOC is true then the cson_malloc/realloc/free() routines will log a message to stderr. */ |
︙ | ︙ | |||
1852 1853 1854 1855 1856 1857 1858 | ++cv->refcount; } } #if 0 int cson_value_refcount_set( cson_value * cv, unsigned short rc ) { | | | | | 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 | ++cv->refcount; } } #if 0 int cson_value_refcount_set( cson_value * cv, unsigned short rc ) { if( NULL == cv ) return CSON_RC_ArgError; else { cv->refcount = rc; return 0; } } #endif int cson_value_add_reference( cson_value * cv ) { if( NULL == cv ) return CSON_RC_ArgError; else if( (cv->refcount+1) < cv->refcount ) { return CSON_RC_RangeError; } else { cson_refcount_incr( cv ); return 0; } } |
︙ | ︙ | |||
2333 2334 2335 2336 2337 2338 2339 | val->api->cleanup(val); *val = cson_value_undef; val->refcount = rc; } } } | | | | | | | | 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 | val->api->cleanup(val); *val = cson_value_undef; val->refcount = rc; } } } static cson_value * cson_value_array_alloc(void) { cson_value * v = cson_value_new(CSON_TYPE_ARRAY,0); if( NULL != v ) { cson_array * ar = CSON_ARRAY(v); assert(NULL != ar); *ar = cson_array_empty; } return v; } static cson_value * cson_value_object_alloc(void) { cson_value * v = cson_value_new(CSON_TYPE_OBJECT,0); if( NULL != v ) { cson_object * obj = CSON_OBJ(v); assert(NULL != obj); *obj = cson_object_empty; } return v; } cson_value * cson_value_new_object(void) { return cson_value_object_alloc(); } cson_object * cson_new_object(void) { return cson_value_get_object( cson_value_new_object() ); } cson_value * cson_value_new_array(void) { return cson_value_array_alloc(); } cson_array * cson_new_array(void) { return cson_value_get_array( cson_value_new_array() ); } /** Frees kvp->key and kvp->value and sets them to NULL, but does not free kvp. If !kvp then this is a no-op. |
︙ | ︙ | |||
2500 2501 2502 2503 2504 2505 2506 | int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ) { int rc; enum { BufSize = 1024 * 4 }; char rbuf[BufSize]; size_t total = 0; unsigned int rlen = 0; | | | 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 | int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ) { int rc; enum { BufSize = 1024 * 4 }; char rbuf[BufSize]; size_t total = 0; unsigned int rlen = 0; if( ! dest || ! src ) return CSON_RC_ArgError; dest->used = 0; while(1) { rlen = BufSize; rc = src( state, rbuf, &rlen ); if( rc ) break; total += rlen; |
︙ | ︙ | |||
2528 2529 2530 2531 2532 2533 2534 | } return rc; } int cson_data_source_FILE( void * state, void * dest, unsigned int * n ) { FILE * f = (FILE*) state; | | | | | | 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 | } return rc; } int cson_data_source_FILE( void * state, void * dest, unsigned int * n ) { FILE * f = (FILE*) state; if( ! state || ! n || !dest ) return CSON_RC_ArgError; else if( !*n ) return CSON_RC_RangeError; *n = (unsigned int)fread( dest, 1, *n, f ); if( !*n ) { return feof(f) ? 0 : CSON_RC_IOError; } return 0; } int cson_parse_FILE( cson_value ** tgt, FILE * src, cson_parse_opt const * opt, cson_parse_info * err ) { return cson_parse( tgt, cson_data_source_FILE, src, opt, err ); } int cson_value_fetch_bool( cson_value const * val, char * v ) { /** FIXME: move the to-bool operation into cson_value_api, like we do in the C++ API. */ if( ! val || !val->api ) return CSON_RC_ArgError; else { int rc = 0; char b = 0; switch( val->api->typeID ) { case CSON_TYPE_ARRAY: |
︙ | ︙ | |||
2586 2587 2588 2589 2590 2591 2592 | case CSON_TYPE_DOUBLE: { cson_double_t d = 0.0; cson_value_fetch_double( val, &d ); b = (0.0==d) ? 0 : 1; break; } default: | | | | 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 | case CSON_TYPE_DOUBLE: { cson_double_t d = 0.0; cson_value_fetch_double( val, &d ); b = (0.0==d) ? 0 : 1; break; } default: rc = CSON_RC_TypeError; break; } if( v ) *v = b; return rc; } } char cson_value_get_bool( cson_value const * val ) { char i = 0; cson_value_fetch_bool( val, &i ); return i; } int cson_value_fetch_integer( cson_value const * val, cson_int_t * v ) { if( ! val || !val->api ) return CSON_RC_ArgError; else { cson_int_t i = 0; int rc = 0; switch(val->api->typeID) { case CSON_TYPE_UNDEF: |
︙ | ︙ | |||
2639 2640 2641 2642 2643 2644 2645 | i = (cson_int_t)d; break; } case CSON_TYPE_STRING: case CSON_TYPE_ARRAY: case CSON_TYPE_OBJECT: default: | | | | 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 | i = (cson_int_t)d; break; } case CSON_TYPE_STRING: case CSON_TYPE_ARRAY: case CSON_TYPE_OBJECT: default: rc = CSON_RC_TypeError; break; } if(!rc && v) *v = i; return rc; } } cson_int_t cson_value_get_integer( cson_value const * val ) { cson_int_t i = 0; cson_value_fetch_integer( val, &i ); return i; } int cson_value_fetch_double( cson_value const * val, cson_double_t * v ) { if( ! val || !val->api ) return CSON_RC_ArgError; else { cson_double_t d = 0.0; int rc = 0; switch(val->api->typeID) { case CSON_TYPE_UNDEF: |
︙ | ︙ | |||
2685 2686 2687 2688 2689 2690 2691 | } case CSON_TYPE_DOUBLE: { cson_double_t const* dv = CSON_DBL(val); d = dv ? *dv : 0.0; break; } default: | | | | | 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 | } case CSON_TYPE_DOUBLE: { cson_double_t const* dv = CSON_DBL(val); d = dv ? *dv : 0.0; break; } default: rc = CSON_RC_TypeError; break; } if(v) *v = d; return rc; } } cson_double_t cson_value_get_double( cson_value const * val ) { cson_double_t i = 0.0; cson_value_fetch_double( val, &i ); return i; } int cson_value_fetch_string( cson_value const * val, cson_string ** dest ) { if( ! val || ! dest ) return CSON_RC_ArgError; else if( ! cson_value_is_string(val) ) return CSON_RC_TypeError; else { if( dest ) *dest = CSON_STR(val); return 0; } } |
︙ | ︙ | |||
2725 2726 2727 2728 2729 2730 2731 | char const * cson_value_get_cstr( cson_value const * val ) { return cson_string_cstr( cson_value_get_string(val) ); } int cson_value_fetch_object( cson_value const * val, cson_object ** obj ) { | | | | | | | | | | 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 | char const * cson_value_get_cstr( cson_value const * val ) { return cson_string_cstr( cson_value_get_string(val) ); } int cson_value_fetch_object( cson_value const * val, cson_object ** obj ) { if( ! val ) return CSON_RC_ArgError; else if( ! cson_value_is_object(val) ) return CSON_RC_TypeError; else { if(obj) *obj = CSON_OBJ(val); return 0; } } cson_object * cson_value_get_object( cson_value const * v ) { cson_object * obj = NULL; cson_value_fetch_object( v, &obj ); return obj; } int cson_value_fetch_array( cson_value const * val, cson_array ** ar) { if( ! val ) return CSON_RC_ArgError; else if( !cson_value_is_array(val) ) return CSON_RC_TypeError; else { if(ar) *ar = CSON_ARRAY(val); return 0; } } cson_array * cson_value_get_array( cson_value const * v ) { cson_array * ar = NULL; cson_value_fetch_array( v, &ar ); return ar; } cson_kvp * cson_kvp_alloc(void) { cson_kvp * kvp = (cson_kvp*)cson_malloc(sizeof(cson_kvp),"cson_kvp"); if( kvp ) { *kvp = cson_kvp_empty; } return kvp; } int cson_array_append( cson_array * ar, cson_value * v ) { if( !ar || !v ) return CSON_RC_ArgError; else if( (ar->list.count+1) < ar->list.count ) return CSON_RC_RangeError; else { if( !ar->list.alloced || (ar->list.count == ar->list.alloced-1)) { unsigned int const n = ar->list.count ? (ar->list.count*2) : 7; if( n > cson_value_list_reserve( &ar->list, n ) ) { return CSON_RC_AllocError; } } return cson_array_set( ar, ar->list.count, v ); } } #if 0 |
︙ | ︙ | |||
2830 2831 2832 2833 2834 2835 2836 | #endif cson_value * cson_value_new_bool( char v ) { return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE]; } | | | | | 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 | #endif cson_value * cson_value_new_bool( char v ) { return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE]; } cson_value * cson_value_true(void) { return &CSON_SPECIAL_VALUES[CSON_VAL_TRUE]; } cson_value * cson_value_false(void) { return &CSON_SPECIAL_VALUES[CSON_VAL_FALSE]; } cson_value * cson_value_null(void) { return &CSON_SPECIAL_VALUES[CSON_VAL_NULL]; } cson_value * cson_new_int( cson_int_t v ) { return cson_value_new_integer(v); |
︙ | ︙ | |||
2915 2916 2917 2918 2919 2920 2921 | cson_value * cson_value_new_string( char const * str, unsigned int len ) { return cson_string_value( cson_new_string(str, len) ); } int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v ) { | | | | | | | | | | 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 | cson_value * cson_value_new_string( char const * str, unsigned int len ) { return cson_string_value( cson_new_string(str, len) ); } int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v ) { if( !ar) return CSON_RC_ArgError; if( pos >= ar->list.count ) return CSON_RC_RangeError; else { if(v) *v = ar->list.list[pos]; return 0; } } cson_value * cson_array_get( cson_array const * ar, unsigned int pos ) { cson_value *v = NULL; cson_array_value_fetch(ar, pos, &v); return v; } int cson_array_length_fetch( cson_array const * ar, unsigned int * v ) { if( ! ar || !v ) return CSON_RC_ArgError; else { if(v) *v = ar->list.count; return 0; } } unsigned int cson_array_length_get( cson_array const * ar ) { unsigned int i = 0; cson_array_length_fetch(ar, &i); return i; } int cson_array_reserve( cson_array * ar, unsigned int size ) { if( ! ar ) return CSON_RC_ArgError; else if( size <= ar->list.alloced ) { /* We don't want to introduce a can of worms by trying to handle the cleanup from here. */ return 0; } else { return (ar->list.alloced > cson_value_list_reserve( &ar->list, size )) ? CSON_RC_AllocError : 0 ; } } int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v ) { if( !ar || !v ) return CSON_RC_ArgError; else if( (ndx+1) < ndx) /* overflow */return CSON_RC_RangeError; else { unsigned const int len = cson_value_list_reserve( &ar->list, ndx+1 ); if( len <= ndx ) return CSON_RC_AllocError; else { cson_value * old = ar->list.list[ndx]; if( old ) { if(old == v) return 0; else cson_value_free(old); |
︙ | ︙ | |||
3074 3075 3076 3077 3078 3079 3080 | } } #endif int cson_object_unset( cson_object * obj, char const * key ) { | | | | 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 | } } #endif int cson_object_unset( cson_object * obj, char const * key ) { if( ! obj || !key || !*key ) return CSON_RC_ArgError; else { unsigned int ndx = 0; cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx ); if( ! kvp ) { return CSON_RC_NotFoundError; } assert( obj->kvp.count > 0 ); assert( obj->kvp.list[ndx] == kvp ); cson_kvp_free( kvp ); obj->kvp.list[ndx] = NULL; { /* if my brain were bigger i'd use memmove(). */ unsigned int i = ndx; |
︙ | ︙ | |||
3107 3108 3109 3110 3111 3112 3113 | #endif return 0; } } int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ) { | | | | 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 | #endif return 0; } } int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ) { if( !obj || !key ) return CSON_RC_ArgError; else if( NULL == v ) return cson_object_unset( obj, cson_string_cstr(key) ); else { char const * cKey; cson_value * vKey; cson_kvp * kvp; vKey = cson_string_value(key); assert(vKey && (key==CSON_STR(vKey))); if( vKey == CSON_VCAST(obj) ){ return CSON_RC_ArgError; } cKey = cson_string_cstr(key); kvp = cson_object_search_impl( obj, cKey, NULL ); if( kvp ) { /* "I told 'em we've already got one!" */ if( kvp->key != vKey ){ cson_value_free( kvp->key ); |
︙ | ︙ | |||
3140 3141 3142 3143 3144 3145 3146 | return 0; } if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { /* reserve space */ unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) { | | | | 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 | return 0; } if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { /* reserve space */ unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) { return CSON_RC_AllocError; } } { /* insert new item... */ int rc = 0; kvp = cson_kvp_alloc(); if( ! kvp ) { return CSON_RC_AllocError; } rc = cson_kvp_list_append( &obj->kvp, kvp ); if( 0 != rc ) { cson_kvp_free(kvp); } else |
︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 | return rc; } } } int cson_object_set( cson_object * obj, char const * key, cson_value * v ) { | | | | 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 | return rc; } } } int cson_object_set( cson_object * obj, char const * key, cson_value * v ) { if( ! obj || !key || !*key ) return CSON_RC_ArgError; else if( NULL == v ) { return cson_object_unset( obj, key ); } else { cson_string * cs = cson_new_string(key,strlen(key)); if(!cs) return CSON_RC_AllocError; else { int const rc = cson_object_set_s(obj, cs, v); if(rc) cson_value_free(cson_string_value(cs)); return rc; } } |
︙ | ︙ | |||
3235 3236 3237 3238 3239 3240 3241 | #endif return rc; } } /** @internal If p->node is-a Object then value is inserted into the object | | | | 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 | #endif return rc; } } /** @internal If p->node is-a Object then value is inserted into the object using p->key. In any other case CSON_RC_InternalError is returned. Returns CSON_RC_AllocError if an allocation fails. Returns 0 on success. On error, parsing must be ceased immediately. Ownership of val is ALWAYS TRANSFERED to this function. If this function fails, val will be cleaned up and destroyed. (This simplifies error handling in the core parser.) */ |
︙ | ︙ | |||
3265 3266 3267 3268 3269 3270 3271 | extra alloc/strcpy of the key data. */ if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) ) { cson_value_free(val); | | | | 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 | extra alloc/strcpy of the key data. */ if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) { if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) ) { cson_value_free(val); return CSON_RC_AllocError; } } kvp = cson_kvp_alloc(); if( ! kvp ) { cson_value_free(val); return CSON_RC_AllocError; } kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; assert(0 == kvp->key->refcount); cson_refcount_incr(kvp->key); p->ckey = NULL; kvp->value = val; cson_refcount_incr( val ); |
︙ | ︙ | |||
3294 3295 3296 3297 3298 3299 3300 | ++p->totalValueCount; } return rc; } else { if(val) cson_value_free(val); | | | 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 | ++p->totalValueCount; } return rc; } else { if(val) cson_value_free(val); return p->errNo = CSON_RC_InternalError; } } /** @internal Pushes val into the current object/array parent node, depending on the |
︙ | ︙ | |||
3335 3336 3337 3338 3339 3340 3341 | ++p->totalValueCount; } return rc; } else { /* WTF? */ assert( 0 && "Internal error in cson_parser code" ); | | | | | 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 | ++p->totalValueCount; } return rc; } else { /* WTF? */ assert( 0 && "Internal error in cson_parser code" ); return p->errNo = CSON_RC_InternalError; } } /** Callback for JSON_parser API. Reminder: it returns 0 (meaning false) on error! */ static int cson_parse_callback( void * cx, int type, JSON_value const * value ) { cson_parser * p = (cson_parser *)cx; int rc = 0; #define ALLOC_V(T,V) cson_value * v = cson_value_new_##T(V); if( ! v ) { rc = CSON_RC_AllocError; break; } switch(type) { case JSON_T_ARRAY_BEGIN: case JSON_T_OBJECT_BEGIN: { cson_value * obja = (JSON_T_ARRAY_BEGIN == type) ? cson_value_new_array() : cson_value_new_object(); if( ! obja ) { p->errNo = CSON_RC_AllocError; break; } if( 0 != rc ) break; if( ! p->root ) { p->root = p->node = obja; rc = cson_array_append( &p->stack, obja ); |
︙ | ︙ | |||
3393 3394 3395 3396 3397 3398 3399 | } break; } case JSON_T_ARRAY_END: case JSON_T_OBJECT_END: { if( 0 == p->stack.list.count ) { | | | 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 | } break; } case JSON_T_ARRAY_END: case JSON_T_OBJECT_END: { if( 0 == p->stack.list.count ) { rc = CSON_RC_RangeError; break; } #if CSON_OBJECT_PROPS_SORT if( cson_value_is_object(p->node) ) {/* kludge: the parser uses custom cson_object property insertion as a malloc/strcpy-reduction optimization. Because of that, we have to sort the property list |
︙ | ︙ | |||
3479 3480 3481 3482 3483 3484 3485 | break; } case JSON_T_KEY: { assert(!p->ckey); p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length ); if( ! p->ckey ) { | | | | | | | | | | | | | | | | | 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 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 | break; } case JSON_T_KEY: { assert(!p->ckey); p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length ); if( ! p->ckey ) { rc = CSON_RC_AllocError; break; } ++p->totalKeyCount; break; } case JSON_T_STRING: { cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length ); rc = ( NULL == v ) ? CSON_RC_AllocError : cson_parser_push_value( p, v ); break; } default: assert(0); rc = CSON_RC_InternalError; break; } #undef ALLOC_V return ((p->errNo = rc)) ? 0 : 1; } /** Converts a JSON_error code to one of the cson_rc values. */ static int cson_json_err_to_rc( JSON_error jrc ) { switch(jrc) { case JSON_E_NONE: return 0; case JSON_E_INVALID_CHAR: return CSON_RC_Parse_INVALID_CHAR; case JSON_E_INVALID_KEYWORD: return CSON_RC_Parse_INVALID_KEYWORD; case JSON_E_INVALID_ESCAPE_SEQUENCE: return CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE; case JSON_E_INVALID_UNICODE_SEQUENCE: return CSON_RC_Parse_INVALID_UNICODE_SEQUENCE; case JSON_E_INVALID_NUMBER: return CSON_RC_Parse_INVALID_NUMBER; case JSON_E_NESTING_DEPTH_REACHED: return CSON_RC_Parse_NESTING_DEPTH_REACHED; case JSON_E_UNBALANCED_COLLECTION: return CSON_RC_Parse_UNBALANCED_COLLECTION; case JSON_E_EXPECTED_KEY: return CSON_RC_Parse_EXPECTED_KEY; case JSON_E_EXPECTED_COLON: return CSON_RC_Parse_EXPECTED_COLON; case JSON_E_OUT_OF_MEMORY: return CSON_RC_AllocError; default: return CSON_RC_InternalError; } } /** @internal Cleans up all contents of p but does not free p. To properly take over ownership of the parser's root node on a successful parse: - Copy p->root's pointer and set p->root to NULL. - Eventually free up p->root with cson_value_free(). If you do not set p->root to NULL, p->root will be freed along with any other items inserted into it (or under it) during the parsing process. */ static int cson_parser_clean( cson_parser * p ) { if( ! p ) return CSON_RC_ArgError; else { if( p->p ) { delete_JSON_parser(p->p); p->p = NULL; } |
︙ | ︙ | |||
3572 3573 3574 3575 3576 3577 3578 | { unsigned char ch[2] = {0,0}; cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty; int rc = 0; unsigned int len = 1; cson_parse_info info = info_ ? *info_ : cson_parse_info_empty; cson_parser p = cson_parser_empty; | | | | | 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 | { unsigned char ch[2] = {0,0}; cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty; int rc = 0; unsigned int len = 1; cson_parse_info info = info_ ? *info_ : cson_parse_info_empty; cson_parser p = cson_parser_empty; if( ! tgt || ! src ) return CSON_RC_ArgError; { JSON_config jopt = {0}; init_JSON_config( &jopt ); jopt.allow_comments = opt.allowComments; jopt.depth = opt.maxDepth; jopt.callback_ctx = &p; jopt.handle_floats_manually = 0; jopt.callback = cson_parse_callback; p.p = new_JSON_parser(&jopt); if( ! p.p ) { return CSON_RC_AllocError; } } do { /* FIXME: buffer the input in multi-kb chunks. */ len = 1; ch[0] = 0; rc = src( state, ch, &len ); if( 0 != rc ) break; else if( !len /* EOF */ ) break; ++info.length; if('\n' == ch[0]) { ++info.line; info.col = 0; } if( ! JSON_parser_char(p.p, ch[0]) ) { rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) ); if(0==rc) rc = p.errNo; if(0==rc) rc = CSON_RC_InternalError; info.errorCode = rc; break; } if( '\n' != ch[0]) ++info.col; } while(1); if( info_ ) { |
︙ | ︙ | |||
3628 3629 3630 3631 3632 3633 3634 | return rc; } if( ! JSON_parser_done(p.p) ) { rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) ); cson_parser_clean(&p); if(0==rc) rc = p.errNo; | | | 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 | return rc; } if( ! JSON_parser_done(p.p) ) { rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) ); cson_parser_clean(&p); if(0==rc) rc = p.errNo; if(0==rc) rc = CSON_RC_InternalError; } else { cson_value * root = p.root; p.root = NULL; cson_parser_clean(&p); if( root ) |
︙ | ︙ | |||
3650 3651 3652 3653 3654 3655 3656 | root node to keep it from being cleaned up prematurely. */; *tgt = root; } else { /* then can happen on empty input. */ | | | 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 | root node to keep it from being cleaned up prematurely. */; *tgt = root; } else { /* then can happen on empty input. */ rc = CSON_RC_UnknownError; } } return rc; } /** The UTF code was originally taken from sqlite3's public-domain |
︙ | ︙ | |||
3769 3770 3771 3772 3773 3774 3775 | here we would effectively change the data type from string to object. */ static int cson_str_to_json( char const * str, unsigned int len, char escapeFwdSlash, cson_data_dest_f f, void * state ) { | | | 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 | here we would effectively change the data type from string to object. */ static int cson_str_to_json( char const * str, unsigned int len, char escapeFwdSlash, cson_data_dest_f f, void * state ) { if( NULL == f ) return CSON_RC_ArgError; else if( !str || !*str || (0 == len) ) { /* special case for 0-length strings. */ return f( state, "\"\"", 2 ); } else { unsigned char const * pos = (unsigned char const *)str; |
︙ | ︙ | |||
3875 3876 3877 3878 3879 3880 3881 | else { /* UTF: transform it to \uXXXX */ #if defined(CSON_FOSSIL_MODE) assume_latin1: #endif memset(ubuf,0,UBLen); if(ch <= 0xFFFF){ | | | | | | | | | 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 | else { /* UTF: transform it to \uXXXX */ #if defined(CSON_FOSSIL_MODE) assume_latin1: #endif memset(ubuf,0,UBLen); if(ch <= 0xFFFF){ rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x",ch); if( rc != 6 ) { rc = CSON_RC_RangeError; break; } rc = f( state, ubuf, 6 ); }else{ /* encode as a UTF16 surrogate pair */ /* http://unicodebook.readthedocs.org/en/latest/unicode_encodings.html#surrogates */ ch -= 0x10000; rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x\\u%04x", (0xd800 | (ch>>10)), (0xdc00 | (ch & 0x3ff))); if( rc != 12 ) { rc = CSON_RC_RangeError; break; } rc = f( state, ubuf, 12 ); } continue; } } if( 0 == rc ) { rc = f(state, "\"", 1 ); } return rc; } } int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter ) { if( ! obj || !iter ) return CSON_RC_ArgError; else { iter->obj = obj; iter->pos = 0; return 0; } } |
︙ | ︙ | |||
3934 3935 3936 3937 3938 3939 3940 | } return rc; } } static int cson_output_null( cson_data_dest_f f, void * state ) { | | | | | > | < < | | | > | < < | | 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 | } return rc; } } static int cson_output_null( cson_data_dest_f f, void * state ) { if( !f ) return CSON_RC_ArgError; else { return f(state, "null", 4); } } static int cson_output_bool( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return CSON_RC_ArgError; else { char const v = cson_value_get_bool(src); return f(state, v ? "true" : "false", v ? 4 : 5); } } static int cson_output_integer( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return CSON_RC_ArgError; else if( !cson_value_is_integer(src) ) return CSON_RC_TypeError; else { enum { BufLen = 100 }; char b[BufLen]; int rc; memset( b, 0, BufLen ); rc = snprintf( b, (size_t)BufLen, "%"CSON_INT_T_PFMT, cson_value_get_integer(src) ); return ( rc<=0 ) ? CSON_RC_RangeError : f( state, b, (unsigned int)rc ) ; } } static int cson_output_double( cson_value const * src, cson_data_dest_f f, void * state ) { if( !f ) return CSON_RC_ArgError; else if( !cson_value_is_double(src) ) return CSON_RC_TypeError; else { enum { BufLen = 128 /* this must be relatively large or huge doubles can cause us to overrun here, resulting in stack-smashing errors. */}; char b[BufLen]; int rc; memset( b, 0, BufLen ); rc = snprintf( b, (size_t)BufLen, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(src) ); if( rc<=0 ) return CSON_RC_RangeError; else if(1) { /* Strip trailing zeroes before passing it on... */ unsigned int urc = (unsigned int)rc; char * pos = b + urc - 1; for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc ) { *pos = 0; |
︙ | ︙ | |||
4010 4011 4012 4013 4014 4015 4016 | } return 0; } } static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state ) { | | | | 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 | } return 0; } } static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state ) { if( !f ) return CSON_RC_ArgError; else if( ! cson_value_is_string(src) ) return CSON_RC_TypeError; else { cson_string const * str = cson_value_get_string(src); assert( NULL != str ); return cson_str_to_json(cson_string_cstr(str), str->length, escapeFwdSlash, f, state); } } |
︙ | ︙ | |||
4073 4074 4075 4076 4077 4078 4079 | on src->api->typeID. Returns 0 on success. */ static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { | | | 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 | on src->api->typeID. Returns 0 on success. */ static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( ! src || !f || !src->api ) return CSON_RC_ArgError; else { int rc = 0; assert(fmt); switch( src->api->typeID ) { case CSON_TYPE_UNDEF: |
︙ | ︙ | |||
4103 4104 4105 4106 4107 4108 4109 | case CSON_TYPE_ARRAY: rc = cson_output_array( src, f, state, fmt, level ); break; case CSON_TYPE_OBJECT: rc = cson_output_object( src, f, state, fmt, level ); break; default: | | | | | | 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 | case CSON_TYPE_ARRAY: rc = cson_output_array( src, f, state, fmt, level ); break; case CSON_TYPE_OBJECT: rc = cson_output_object( src, f, state, fmt, level ); break; default: rc = CSON_RC_TypeError; break; } return rc; } } static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( !src || !f || !fmt ) return CSON_RC_ArgError; else if( ! cson_value_is_array(src) ) return CSON_RC_TypeError; else if( level > fmt->maxDepth ) return CSON_RC_RangeError; else { int rc; unsigned int i; cson_value const * v; char doIndent = fmt->indentation ? 1 : 0; cson_array const * ar = cson_value_get_array(src); |
︙ | ︙ | |||
4175 4176 4177 4178 4179 4180 4181 | : rc; } } static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { | | | | | 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 | : rc; } } static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state, cson_output_opt const * fmt, unsigned int level ) { if( !src || !f || !fmt ) return CSON_RC_ArgError; else if( ! cson_value_is_object(src) ) return CSON_RC_TypeError; else if( level > fmt->maxDepth ) return CSON_RC_RangeError; else { int rc; unsigned int i; cson_kvp const * kvp; char doIndent = fmt->indentation ? 1 : 0; cson_object const * obj = cson_value_get_object(src); |
︙ | ︙ | |||
4265 4266 4267 4268 4269 4270 4271 | rc = f(state, "\n", 1); } return rc; } int cson_data_dest_FILE( void * state, void const * src, unsigned int n ) { | | | | 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 | rc = f(state, "\n", 1); } return rc; } int cson_data_dest_FILE( void * state, void const * src, unsigned int n ) { if( ! state ) return CSON_RC_ArgError; else if( !src || !n ) return 0; else { return ( 1 == fwrite( src, n, 1, (FILE*) state ) ) ? 0 : CSON_RC_IOError; } } int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * fmt ) { int rc = 0; if( fmt ) |
︙ | ︙ | |||
4298 4299 4300 4301 4302 4303 4304 | fflush( dest ); } return rc; } int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ) { | | | | | | 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 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 | fflush( dest ); } return rc; } int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ) { if( !src || !dest ) return CSON_RC_ArgError; else { FILE * f = fopen(dest,"wb"); if( !f ) return CSON_RC_IOError; else { int const rc = cson_output_FILE( src, f, fmt ); fclose(f); return rc; } } } int cson_parse_filename( cson_value ** tgt, char const * src, cson_parse_opt const * opt, cson_parse_info * err ) { if( !src || !tgt ) return CSON_RC_ArgError; else { FILE * f = fopen(src, "r"); if( !f ) return CSON_RC_IOError; else { int const rc = cson_parse_FILE( tgt, f, opt, err ); fclose(f); return rc; } } |
︙ | ︙ | |||
4347 4348 4349 4350 4351 4352 4353 | /** A cson_data_source_f() implementation which requires the state argument to be a properly populated (cson_data_source_StringSource_t*). */ static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n ) { | | | | | | | | 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 | /** A cson_data_source_f() implementation which requires the state argument to be a properly populated (cson_data_source_StringSource_t*). */ static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n ) { if( !state || !n || !dest ) return CSON_RC_ArgError; else if( !*n ) return 0 /* ignore this */; else { unsigned int i; cson_data_source_StringSource_t * ss = (cson_data_source_StringSource_t*) state; unsigned char * tgt = (unsigned char *)dest; for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt ) { *tgt = *ss->pos; } *n = i; return 0; } } int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len, cson_parse_opt const * opt, cson_parse_info * err ) { if( ! tgt || !src ) return CSON_RC_ArgError; else if( !*src || (len<2/*2==len of {} and []*/) ) return CSON_RC_RangeError; else { cson_data_source_StringSource_t ss; ss.str = ss.pos = src; ss.end = src + len; return cson_parse( tgt, cson_data_source_StringSource, &ss, opt, err ); } } int cson_parse_buffer( cson_value ** tgt, cson_buffer const * buf, cson_parse_opt const * opt, cson_parse_info * err ) { return ( !tgt || !buf || !buf->mem || !buf->used ) ? CSON_RC_ArgError : cson_parse_string( tgt, (char const *)buf->mem, buf->used, opt, err ); } int cson_buffer_reserve( cson_buffer * buf, cson_size_t n ) { if( ! buf ) return CSON_RC_ArgError; else if( 0 == n ) { cson_free(buf->mem, "cson_buffer::mem"); *buf = cson_buffer_empty; return 0; } else if( buf->capacity >= n ) { return 0; } else { unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" ); if( ! x ) return CSON_RC_AllocError; memset( x + buf->used, 0, n - buf->used ); buf->mem = x; buf->capacity = n; ++buf->timesExpanded; return 0; } } |
︙ | ︙ | |||
4432 4433 4434 4435 4436 4437 4438 | cson_data_dest_f() implementation, used by cson_output_buffer(). arg MUST be a (cson_buffer*). This function appends n bytes at position arg->used, expanding the buffer as necessary. */ static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n ) { | | | | | 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 | cson_data_dest_f() implementation, used by cson_output_buffer(). arg MUST be a (cson_buffer*). This function appends n bytes at position arg->used, expanding the buffer as necessary. */ static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n ) { if( !arg ) return CSON_RC_ArgError; else if( ! n ) return 0; else { cson_buffer * sb = (cson_buffer*)arg; char const * data = (char const *)data_; cson_size_t npos = sb->used + n; unsigned int i; if( npos >= sb->capacity ) { const cson_size_t oldCap = sb->capacity; const cson_size_t asz = npos * 2; if( asz < npos ) return CSON_RC_ArgError; /* overflow */ else if( 0 != cson_buffer_reserve( sb, asz ) ) return CSON_RC_AllocError; assert( (sb->capacity > oldCap) && "Internal error in memory buffer management!" ); /* make sure it gets NUL terminated. */ memset( sb->mem + oldCap, 0, (sb->capacity - oldCap) ); } for( i = 0; i < n; ++i, ++sb->used ) { sb->mem[sb->used] = data[i]; |
︙ | ︙ | |||
4531 4532 4533 4534 4535 4536 4537 | for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ } *end = pos; return (pos > *inp) ? 1 : 0; } int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path ) { | | | | | | 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 | for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ } *end = pos; return (pos > *inp) ? 1 : 0; } int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path ) { if( ! obj || !path ) return CSON_RC_ArgError; else if( !*path || !*(1+path) ) return CSON_RC_RangeError; else return cson_object_fetch_sub(obj, tgt, path+1, *path); } int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char sep ) { if( ! obj || !path ) return CSON_RC_ArgError; else if( !*path || !sep ) return CSON_RC_RangeError; else { char const * beg = path; char const * end = NULL; int rc; unsigned int i, len; unsigned int tokenCount = 0; |
︙ | ︙ | |||
4563 4564 4565 4566 4567 4568 4569 | else { ++tokenCount; beg = end; end = NULL; } } | | | | | | | 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 | else { ++tokenCount; beg = end; end = NULL; } } if( 0 == tokenCount ) return CSON_RC_RangeError; beg = path; end = NULL; for( i = 0; i < tokenCount; ++i, beg=end, end=NULL ) { rc = cson_next_token( &beg, sep, &end ); assert( 1 == rc ); assert( beg != end ); assert( end > beg ); len = end - beg; if( len > (BufSize-1) ) return CSON_RC_RangeError; memset( buf, 0, len + 1 ); memcpy( buf, beg, len ); buf[len] = 0; cv = cson_object_get( curObj, buf ); if( NULL == cv ) return CSON_RC_NotFoundError; else if( i == (tokenCount-1) ) { if(tgt) *tgt = cv; return 0; } else if( cson_value_is_object(cv) ) { curObj = cson_value_get_object(cv); assert((NULL != curObj) && "Detected mis-management of internal memory!"); } /* TODO: arrays. Requires numeric parsing for the index. */ else { return CSON_RC_NotFoundError; } } assert( i == tokenCount ); return CSON_RC_NotFoundError; } } cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep ) { cson_value * v = NULL; cson_object_fetch_sub( obj, &v, path, sep ); |
︙ | ︙ | |||
4836 4837 4838 4839 4840 4841 4842 | v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); | | > | > | | 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 | v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); v = cson_strdup( cson_string_cstr( jstr ), slen ); break; } case CSON_TYPE_INTEGER: { char buf[BufSize] = {0}; if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } case CSON_TYPE_DOUBLE: { char buf[BufSize] = {0}; if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } default: break; |
︙ | ︙ | |||
4889 4890 4891 4892 4893 4894 4895 | v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); | | > | > | | 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 | v = cson_strdup( "null", 4 ); break; } case CSON_TYPE_STRING: { cson_string const * jstr = cson_value_get_string(orig); unsigned const int slen = cson_string_length_bytes( jstr ); assert( NULL != jstr ); v = cson_strdup( cson_string_cstr( jstr ), slen ); break; } case CSON_TYPE_INTEGER: { char buf[BufSize] = {0}; if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } case CSON_TYPE_DOUBLE: { char buf[BufSize] = {0}; if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT, cson_value_get_double(orig)) ) { v = cson_strdup( buf, strlen(buf) ); } break; } default: break; |
︙ | ︙ | |||
4986 4987 4988 4989 4990 4991 4992 | int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ cson_object_iterator iter = cson_object_iterator_empty; int rc; char const replace = (flags & CSON_MERGE_REPLACE); char const recurse = !(flags & CSON_MERGE_NO_RECURSE); cson_kvp const * kvp; | | | 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 | int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ cson_object_iterator iter = cson_object_iterator_empty; int rc; char const replace = (flags & CSON_MERGE_REPLACE); char const recurse = !(flags & CSON_MERGE_NO_RECURSE); cson_kvp const * kvp; if((!dest || !src) || (dest==src)) return CSON_RC_ArgError; rc = cson_object_iter_init( src, &iter ); if(rc) return rc; while( (kvp = cson_object_iter_next(&iter) ) ) { cson_string * key = cson_kvp_key(kvp); cson_value * val = cson_kvp_value(kvp); cson_value * check = cson_object_get_s( dest, key ); |
︙ | ︙ | |||
5050 5051 5052 5053 5054 5055 5056 | int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ){ cson_object * o = NULL; int rc = 0; int i = 0; | | | | 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 | int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ){ cson_object * o = NULL; int rc = 0; int i = 0; if(argc<1 || !argc || !tgt) return CSON_RC_ArgError; o = *tgt ? *tgt : cson_new_object(); if(count) *count = 0; for( i = 0; i < argc; ++i ){ char const * arg = argv[i]; char const * key = arg; char const * pos; cson_string * k = NULL; cson_value * v = NULL; if('-' != *arg) continue; while('-'==*key) ++key; if(!*key) continue; pos = key; while( *pos && ('=' != *pos)) ++pos; k = cson_new_string(key, pos-key); if(!k){ rc = CSON_RC_AllocError; break; } if(!*pos){ /** --key */ v = cson_value_true(); }else{ /** --key=...*/ assert('=' == *pos); ++pos /*skip '='*/; |
︙ | ︙ | |||
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); | | | 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 | 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>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/cson_amalgamation.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #ifdef FOSSIL_ENABLE_JSON #ifndef CSON_FOSSIL_MODE #define CSON_FOSSIL_MODE #endif /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ /** @page page_cson cson JSON API cson (pronounced "season") is an object-oriented C API for generating and consuming JSON (http://www.json.org) data. Its main claim to fame is that it can parse JSON from, and output it to, damned near anywhere. The i/o routines use a callback function to fetch/emit JSON data, allowing clients to easily plug in their own implementations. Implementations are provided for string- and FILE-based i/o. | > > | | < < | 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 | #ifdef FOSSIL_ENABLE_JSON #ifndef CSON_FOSSIL_MODE #define CSON_FOSSIL_MODE #endif /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ #include <stdarg.h> /** @page page_cson cson JSON API cson (pronounced "season") is an object-oriented C API for generating and consuming JSON (http://www.json.org) data. Its main claim to fame is that it can parse JSON from, and output it to, damned near anywhere. The i/o routines use a callback function to fetch/emit JSON data, allowing clients to easily plug in their own implementations. Implementations are provided for string- and FILE-based i/o. Project home page: https://fossil.wanderinghorse.net/r/cson Author: Stephan Beal (https://www.wanderinghorse.net/home/stephan/) License: Dual Public Domain/MIT The full license text is at the bottom of the main header file (cson.h). Examples of how to use the library are scattered throughout the API documentation, in the test.c file in the source repo, and in the wiki on the project's home page. */ #if defined(__cplusplus) extern "C" { #endif #if defined(_WIN32) || defined(_WIN64) |
︙ | ︙ | |||
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 | @see cson_value_null() @see cson_value_free() @see cson_value_type_id() */ /** @var cson_rc This object defines the error codes used by cson. Library routines which return int values almost always return a value from this structure. None of the members in this struct have published values except for the OK member, which has the value 0. All other values might be incidentally defined where clients can see them, but the numbers might change from release to release, so clients should only use the symbolic names. Client code is expected to access these values via the shared cson_rc object, and use them as demonstrated here: @code int rc = cson_some_func(...); if( 0 == rc ) {...success...} else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... } else if( cson_rc.AllocError == rc ) { ... allocation error ... } ... @endcode The entries named Parse_XXX are generally only returned by cson_parse() and friends. */ /** @struct cson_rc_ See \ref cson_rc for details. */ static const struct cson_rc_ { /** The generic success value. Guaranteed to be 0. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @see cson_value_null() @see cson_value_free() @see cson_value_type_id() */ /** @var cson_rc Deprecated: clients are encouraged to use the CSON_RC_xxx values which correspond to cson_rc.xxx, as those are more efficient. Some docs and code may still refer to cson_rc, though. This object defines the error codes used by cson. Library routines which return int values almost always return a value from this structure. None of the members in this struct have published values except for the OK member, which has the value 0. All other values might be incidentally defined where clients can see them, but the numbers might change from release to release, so clients should only use the symbolic names. Client code is expected to access these values via the shared cson_rc object, and use them as demonstrated here: @code int rc = cson_some_func(...); if( 0 == rc ) {...success...} else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... } else if( cson_rc.AllocError == rc ) { ... allocation error ... } ... @endcode Or with the preferred/newer method: @code int rc = cson_some_func(...); switch(rc){ case 0: ...success...; case CSON_RC_ArgError: ... some argument was wrong ... case CSON_RC_AllocError: ... allocation error ... ... } @endcode The entries named Parse_XXX are generally only returned by cson_parse() and friends. @deprecated */ /** The CSON_RC_xxx values are intended to replace the older cson_rc.xxx values. */ enum cson_rc_values { /** The generic success value. Guaranteed to be 0. */ CSON_RC_OK = 0, /** Signifies an error in one or more arguments (e.g. NULL where it is not allowed). */ CSON_RC_ArgError, /** Signifies that some argument is not in a valid range. */ CSON_RC_RangeError, /** Signifies that some argument is not of the correct logical cson type. */ CSON_RC_TypeError, /** Signifies an input/ouput error. */ CSON_RC_IOError, /** Signifies an out-of-memory error. */ CSON_RC_AllocError, /** Signifies that the called code is "NYI" (Not Yet Implemented). */ CSON_RC_NYIError, /** Signifies that an internal error was triggered. If it happens, please report this as a bug! */ CSON_RC_InternalError, /** Signifies that the called operation is not supported in the current environment. e.g. missing support from 3rd-party or platform-specific code. */ CSON_RC_UnsupportedError, /** Signifies that the request resource could not be found. */ CSON_RC_NotFoundError, /** Signifies an unknown error, possibly because an underlying 3rd-party API produced an error and we have no other reasonable error code to convert it to. */ CSON_RC_UnknownError, /** Signifies that the parser found an unexpected character. */ CSON_RC_Parse_INVALID_CHAR, /** Signifies that the parser found an invalid keyword (possibly an unquoted string). */ CSON_RC_Parse_INVALID_KEYWORD, /** Signifies that the parser found an invalid escape sequence. */ CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE, /** Signifies that the parser found an invalid Unicode character sequence. */ CSON_RC_Parse_INVALID_UNICODE_SEQUENCE, /** Signifies that the parser found an invalid numeric token. */ CSON_RC_Parse_INVALID_NUMBER, /** Signifies that the parser reached its maximum defined parsing depth before finishing the input. */ CSON_RC_Parse_NESTING_DEPTH_REACHED, /** Signifies that the parser found an unclosed object or array. */ CSON_RC_Parse_UNBALANCED_COLLECTION, /** Signifies that the parser found an key in an unexpected place. */ CSON_RC_Parse_EXPECTED_KEY, /** Signifies that the parser expected to find a colon but found none (e.g. between keys and values in an object). */ CSON_RC_Parse_EXPECTED_COLON }; /** @struct cson_rc_ See \ref cson_rc for details. */ static const struct cson_rc_ { /** The generic success value. Guaranteed to be 0. */ |
︙ | ︙ | |||
395 396 397 398 399 400 401 | const int Parse_EXPECTED_KEY; /** Signifies that the parser expected to find a colon but found none (e.g. between keys and values in an object). */ const int Parse_EXPECTED_COLON; } cson_rc = { | | | | | | | | | | | | | | | | | | | | | | 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 | const int Parse_EXPECTED_KEY; /** Signifies that the parser expected to find a colon but found none (e.g. between keys and values in an object). */ const int Parse_EXPECTED_COLON; } cson_rc = { CSON_RC_OK, CSON_RC_ArgError, CSON_RC_RangeError, CSON_RC_TypeError, CSON_RC_IOError, CSON_RC_AllocError, CSON_RC_NYIError, CSON_RC_InternalError, CSON_RC_UnsupportedError, CSON_RC_NotFoundError, CSON_RC_UnknownError, CSON_RC_Parse_INVALID_CHAR, CSON_RC_Parse_INVALID_KEYWORD, CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE, CSON_RC_Parse_INVALID_UNICODE_SEQUENCE, CSON_RC_Parse_INVALID_NUMBER, CSON_RC_Parse_NESTING_DEPTH_REACHED, CSON_RC_Parse_UNBALANCED_COLLECTION, CSON_RC_Parse_EXPECTED_KEY, CSON_RC_Parse_EXPECTED_COLON }; /** Returns the string form of the cson_rc code corresponding to rc, or some unspecified, non-NULL string if it is an unknown code. The returned bytes are static and do not changing during the |
︙ | ︙ | |||
669 670 671 672 673 674 675 | The srcState argument is ignored by this function but is passed on to src, so any output-destination-specific state can be stored there and accessed via the src callback. Non-parse error conditions include: | | | | | | 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 | The srcState argument is ignored by this function but is passed on to src, so any output-destination-specific state can be stored there and accessed via the src callback. Non-parse error conditions include: - (!tgt) or !src: CSON_RC_ArgError - CSON_RC_AllocError can happen at any time during the input phase Here's a complete example of using a custom input source: @code // Internal type to hold state for a JSON input string. typedef struct { char const * str; // start of input string char const * pos; // current internal cursor position char const * end; // logical EOF (one-past-the-end) } StringSource; // cson_data_source_f() impl which uses StringSource. static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n ) { StringSource * ss = (StringSource*) state; unsigned int i; unsigned char * tgt = (unsigned char *)dest; if( ! ss || ! n || !dest ) return CSON_RC_ArgError; else if( !*n ) return CSON_RC_RangeError; for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt ) { *tgt = *ss->pos; } *n = i; |
︙ | ︙ | |||
746 747 748 749 750 751 752 | */ int cson_parse_FILE( cson_value ** tgt, FILE * src, cson_parse_opt const * opt, cson_parse_info * info ); /** Convenience wrapper around cson_parse_FILE() which opens the given filename. | | | | | 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 | */ int cson_parse_FILE( cson_value ** tgt, FILE * src, cson_parse_opt const * opt, cson_parse_info * info ); /** Convenience wrapper around cson_parse_FILE() which opens the given filename. Returns CSON_RC_IOError if the file cannot be opened. @see cson_parse_FILE() */ int cson_parse_filename( cson_value ** tgt, char const * src, cson_parse_opt const * opt, cson_parse_info * info ); /** Uses an internal helper class to pass src through cson_parse(). See that function for the return value and argument semantics. src must be a string containing JSON code, at least len bytes long, and the parser will attempt to parse exactly len bytes from src. If len is less than 2 (the minimum length of a legal top-node JSON object) then CSON_RC_RangeError is returned. */ int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len, cson_parse_opt const * opt, cson_parse_info * info ); /** Outputs the given value as a JSON-formatted string, sending all output to the given callback function. It is intended for top-level objects or arrays, but can be used with any cson_value. If opt is NULL then default options (the values defined in cson_output_opt_empty) are used. If opt->maxDepth is exceeded while traversing the value tree, CSON_RC_RangeError is returned. The destState parameter is ignored by this function and is passed on to the dest function. Returns 0 on success. On error, any amount of output might have been generated before the error was triggered. |
︙ | ︙ | |||
814 815 816 817 818 819 820 | for FILE output. @see cson_output_filename() */ int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); /** Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying | | | 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | for FILE output. @see cson_output_filename() */ int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); /** Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying any existing contents. Returns CSON_RC_IOError if the file cannot be opened. @see cson_output_FILE() */ int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ); /** Returns the virtual type of v, or CSON_TYPE_UNDEF if !v. |
︙ | ︙ | |||
945 946 947 948 949 950 951 | Similar to cson_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and | | | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | Similar to cson_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and CSON_RC_TypeError is returned. The error may normally be safely ignored, but it is provided for those wanted to know whether a direct conversion was possible. integer: *v is set to the int value and 0 is returned. double: *v is set to the value truncated to int and 0 is returned. */ |
︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 | Ensures that ar has allocated space for at least the given number of entries. This never shrinks the array and never changes its logical size, but may pre-allocate space in the array for storing new (as-yet-unassigned) values. Returns 0 on success, or non-zero on error: | | | | | 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 | Ensures that ar has allocated space for at least the given number of entries. This never shrinks the array and never changes its logical size, but may pre-allocate space in the array for storing new (as-yet-unassigned) values. Returns 0 on success, or non-zero on error: - If ar is NULL: CSON_RC_ArgError - If allocation fails: CSON_RC_AllocError */ int cson_array_reserve( cson_array * ar, unsigned int size ); /** If ar is not NULL, sets *v (if v is not NULL) to the length of the array and returns 0. Returns CSON_RC_ArgError if ar is NULL. */ int cson_array_length_fetch( cson_array const * ar, unsigned int * v ); /** Simplified form of cson_array_length_fetch() which returns 0 if ar is NULL. */ |
︙ | ︙ | |||
1241 1242 1243 1244 1245 1246 1247 | This is functionally equivalent to cson_array_set(ar,cson_array_length_get(ar),v), but this implementation has slightly different array-preallocation policy (it grows more eagerly). Returns 0 on success, non-zero on error. Error cases include: | | | | | 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 | This is functionally equivalent to cson_array_set(ar,cson_array_length_get(ar),v), but this implementation has slightly different array-preallocation policy (it grows more eagerly). Returns 0 on success, non-zero on error. Error cases include: - ar or v are NULL: CSON_RC_ArgError - Array cannot be expanded to hold enough elements: CSON_RC_AllocError. - Appending would cause a numeric overlow in the array's size: CSON_RC_RangeError. (However, you'll get an AllocError long before that happens!) On error ownership of v is NOT modified, and the caller may still need to clean it up. See cson_array_set() for the details. */ int cson_array_append( cson_array * ar, cson_value * v ); |
︙ | ︙ | |||
1447 1448 1449 1450 1451 1452 1453 | The key may be encoded as ASCII or UTF8. Results are undefined with other encodings, and the errors won't show up here, but may show up later, e.g. during output. Returns 0 on success, non-0 on error. It has the following error cases: | | | | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 | The key may be encoded as ASCII or UTF8. Results are undefined with other encodings, and the errors won't show up here, but may show up later, e.g. during output. Returns 0 on success, non-0 on error. It has the following error cases: - CSON_RC_ArgError: obj or key are NULL or strlen(key) is 0. - CSON_RC_AllocError: an out-of-memory error On error ownership of v is NOT modified, and the caller may still need to clean it up. For example, the following code will introduce a leak if this function fails: @code cson_object_set( myObj, "foo", cson_value_new_integer(42) ); |
︙ | ︙ | |||
1498 1499 1500 1501 1502 1503 1504 | */ int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); /** Removes a property from an object. If obj contains the given key, it is removed and 0 is returned. If | | | | 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 | */ int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); /** Removes a property from an object. If obj contains the given key, it is removed and 0 is returned. If it is not found, CSON_RC_NotFoundError is returned (which can normally be ignored by client code). CSON_RC_ArgError is returned if obj or key are NULL or key has a length of 0. Returns 0 if the given key is found and removed. This is functionally equivalent calling cson_object_set(obj,key,NULL). */ |
︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 | obj is the object to search. path is a delimited string, where the delimiter is the given separator character. This function searches for the given path, starting at the given object and traversing its properties as the path specifies. If a given part of the | | | | | | | 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 | obj is the object to search. path is a delimited string, where the delimiter is the given separator character. This function searches for the given path, starting at the given object and traversing its properties as the path specifies. If a given part of the path is not found, then this function fails with CSON_RC_NotFoundError. If it finds the given path, it returns the value by assiging *tgt to it. If tgt is NULL then this function has no side-effects but will return 0 if the given path is found within the object, so it can be used to test for existence without fetching it. Returns 0 if it finds an entry, CSON_RC_NotFoundError if it finds no item, and any other non-zero error code on a "real" error. Errors include: - obj or path are NULL: CSON_RC_ArgError - separator is 0, or path is an empty string or contains only separator characters: CSON_RC_RangeError - There is an upper limit on how long a single path component may be (some "reasonable" internal size), and CSON_RC_RangeError is returned if that length is violated. Limitations: - It has no way to fetch data from arrays this way. i could imagine, e.g., a path of "subobj.subArray.0" for |
︙ | ︙ | |||
1675 1676 1677 1678 1679 1680 1681 | Array properties in dest are NOT recursed for merging - they are either replaced or left as-is, depending on whether flags contains he CSON_MERGE_REPLACE bit. Returns 0 on success. The error conditions are: | | | 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 | Array properties in dest are NOT recursed for merging - they are either replaced or left as-is, depending on whether flags contains he CSON_MERGE_REPLACE bit. Returns 0 on success. The error conditions are: - dest or src are NULL or (dest==src) returns CSON_RC_ArgError. - dest or src contain cyclic references - this will likely cause a crash due to endless recursion. Potential TODOs: - Add a flag to copy clones, not the original values. |
︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 | /** Empty-initialized cson_object_iterator object. */ extern const cson_object_iterator cson_object_iterator_empty; /** Initializes the given iterator to point at the start of obj's | | | 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 | /** Empty-initialized cson_object_iterator object. */ extern const cson_object_iterator cson_object_iterator_empty; /** Initializes the given iterator to point at the start of obj's properties. Returns 0 on success or CSON_RC_ArgError if !obj or !iter. obj must outlive iter, or results are undefined. Results are also undefined if obj is modified while the iterator is active. @see cson_object_iter_next() */ |
︙ | ︙ | |||
1889 1890 1891 1892 1893 1894 1895 | On success 0 is returned and the contents of buf.mem are guaranteed to be NULL-terminated. On error the buffer might contain partial contents, and it should not be used except to free its contents. On error non-zero is returned. Errors include: | | | | 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 | On success 0 is returned and the contents of buf.mem are guaranteed to be NULL-terminated. On error the buffer might contain partial contents, and it should not be used except to free its contents. On error non-zero is returned. Errors include: - Invalid arguments: CSON_RC_ArgError - Buffer cannot be expanded (runs out of memory): CSON_RC_AllocError Example usage: @code cson_buffer buf = cson_buffer_empty; // optional: cson_buffer_reserve(&buf, 1024 * 10); int rc = cson_output_buffer( myValue, &buf, NULL ); |
︙ | ︙ | |||
1997 1998 1999 2000 2001 2002 2003 | invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: | | | | 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 | invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: - dest or src are NULL (CSON_RC_ArgError) - Allocation error (CSON_RC_AllocError) - src() returns an error code Whether or not the state parameter may be NULL depends on the src implementation requirements. On success dest will contain the contents read from the input |
︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 | cson_value_free(), after which the value must be considered, from the perspective of that client code, to be destroyed (though it will not be if there are still other live references to it). cson_value_free() will not _actually_ destroy the value until its reference count drops to 0. Returns 0 on success. The only error conditions are if v is NULL | | | | 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 | cson_value_free(), after which the value must be considered, from the perspective of that client code, to be destroyed (though it will not be if there are still other live references to it). cson_value_free() will not _actually_ destroy the value until its reference count drops to 0. Returns 0 on success. The only error conditions are if v is NULL (CSON_RC_ArgError) or if the reference increment would overflow (CSON_RC_RangeError). In theory a client would get allocation errors long before the reference count could overflow (assuming those reference counts come from container insertions, as opposed to via this function). Insider notes which clients really need to know: For shared/constant value instances, such as those returned by |
︙ | ︙ | |||
2236 2237 2238 2239 2240 2241 2242 | /** Calculates the approximate in-memory-allocated size of v, recursively if it is a container type, with the following caveats and limitations: | | | 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 | /** Calculates the approximate in-memory-allocated size of v, recursively if it is a container type, with the following caveats and limitations: If a given value is reference counted and encountered multiple times within a traversed container, each reference is counted at full cost. We have no way of knowing if a given reference has been visited already and whether it should or should not be counted, so we pessimistically count them even though the _might_ not really count for the given object tree (it depends on where the other open references live). |
︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 | - If (*tgt!=NULL) (i.e., the caller provides his own object) then it might contain partial results. */ int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ); /* LICENSE This software's source code, including accompanying documentation and demonstration applications, are licensed under the following conditions... | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | - If (*tgt!=NULL) (i.e., the caller provides his own object) then it might contain partial results. */ int cson_parse_argv_flags( int argc, char const * const * argv, cson_object ** tgt, unsigned int * count ); /** Return values for the cson_pack() and cson_unpack() interfaces. */ enum cson_pack_retval { /** Signals an out-of-memory error. */ CSON_PACK_ALLOC_ERROR = -1, /** Signals a syntax error in the format string. */ CSON_PACK_ARG_ERROR = -2, /** Signals an that an internal error has occurred. This indicates a bug in this library. */ CSON_PACK_INTERNAL_ERROR = -3, /** Signals that the JSON document does not validate agains the format string passed to cson_unpack(). */ CSON_PACK_VALIDATION_ERROR = -4 }; /** Construct arbitrarily complex JSON documents from native C types. Create a new object or array and add or merge the passed values and properties to it according to the supplied format string. fmt is a format string, it must at least contain an array or object specifier as its root value. Format specifiers start with a percent sign '\%' followed by one or more modifiers and a type character. Object properties are specified as key-value pairs where the key is specified as a string and passed as an argument of const char *. Any space, tab, carriage return, line feed, colon and comma characters between format specifiers are ignored. | Type | Description | | :--: | :---------- | | s | creates either a property name or a string value, in case of the former the corresponding argument is a pointer to const char which is a sequence of bytes specifying the name of the property that is to be created, in case of the latter the corresponding argument is a pointer to const char | | d | creates an integer value, the corresponding argument is an int | | i | ^ | | f | creates a floating point value, the corresponding argument is a double | | b | creates a boolean value, the corresponding argument is an int | | N | creates a null value | | [...] | creates an array, the corresponding argument is a pointer to a cson_array | | {...} | creates an array, the corresponding argument is a pointer to a cson_object | | Modifier | Description | | :------: | :---------- | | l | specifies that the following d or i specifier applies to an argument which is a pointer to long | | ll | specifies that the following d or i specifier applies to an argument which is a pointer to cson_int_t | | Short Form | Expands to | :--------: | :--------- | | {...} | %*{...} | | [...] | %*[...] | | \%D | \%lld | Returns 0 on success. The error conditions are: - CSON_PACK_ARG_ERROR: fmt contains a syntax error - CSON_PACK_ALLOC_ERROR: a memory allocation failed - CSON_PACK_INTERNAL_ERROR: an internal error has occurred, this is a bug in cson Example: @code cson_value * root_value; cson_array * arr; ... rc = cson_pack( root_value, "{%s: %d, %s: %[]}", "foo", 42, "bar", arr ); if( 0 != rc ) { ... error ... } @endcode */ int cson_pack( cson_value **root_valuep, const char *fmt, ... ); /** Same as cson_pack() except that it takes a va_list instead of a variable number of arguments. */ int cson_vpack( cson_value **root_valuep, const char *fmt, va_list args ); /** Iterate over the given object or array and convert an arbitrary number of JSON values into their native C types or validates them according to the given format string fmt. fmt is a format string, it must at least contain an array or object specifier as its root value. Format specifiers start with a percent sign '\%' followed by one or more modifiers and a type character. Object properties are specified as key-value pairs where the key is specified as a string and passed as an argument of const char *. Any space, tab, carriage return, line feed, colon and comma characters between format specifiers are ignored. | Type | Description | | :--: | :---------- | | s | matches a either a property name or a string value, in case of the former the corresponding argument is a pointer to const char which is a sequence of bytes specifying the name of the property that is to be matched, in case of the latter the corresponding argument is a pointer to a pointer to const char unless the 'm' modifier is specified where the the corresponding argument is a pointer to a pointer to char | | d | matches an integer value and must be used in with the "ll" modifier, the corresponding argument is a pointer to cson_int_t | | i | ^ | | f | matches a floating point value, the corresponding argument is a pointer to double | | b | matches a boolean value, the corresponding argument is a pointer to int | | N | matches a null value | | [...] | matches an array, the corresponding argument is a pointer to a pointer to a cson_array | | {...} | matches an array, the corresponding argument is a pointer to a pointer to a cson_object | | Modifier | Description | | :------: | :---------- | | ? | specifies that the property reffered to by the given property name is optional | | * | suppresses assignment, only check for the presence and type of the specified value | | m | allocates a memory buffer for the extracted string | | ll | specifies that the following d or i specifier applies to an argument which is a pointer to cson_int_t | | Short Form | Expands to | :--------: | :--------- | | {...} | %*{...} | | [...] | %*[...] | | \%D | \%lld | Returns 0 on success. The error conditions are: - CSON_PACK_ARG_ERROR: fmt contains a syntax error - CSON_PACK_ALLOC_ERROR: a memory allocation failed - CSON_PACK_VALIDATION_ERROR: validation failed, the JSON document structure differs from that described by the format string - CSON_PACK_INTERNAL_ERROR: an internal error has occurred, this indicates a bug in this library. Example: @code cson_value * root_value; cson_int_t x = 0; cson_array * arr = NULL; const char *str = NULL; ... rc = cson_unpack( root_value, "{%s: %d, %s: %[], %?s: %s}", "foo", &x, "bar", &arr, "baz", &str ); if( rc < 3 && rc >= 0 ) { ... optional property is missing ... } else if ( CSON_PACK_ALLOC_ERROR == rc ) { ... out of memory error ... } else if ( CSON_PACK_VALIDATION_ERROR == rc ) { ... unexpected JSON document structure ... } else if ( rc ) { ... internal error ... } @endcode */ int cson_unpack( cson_value *root_value, const char *fmt, ... ); /** Same as cson_unpack() except that it takes a va_list instead of a variable number of arguments. */ int cson_vunpack( cson_value *root_value, const char *fmt, va_list args ); /* LICENSE This software's source code, including accompanying documentation and demonstration applications, are licensed under the following conditions... |
︙ | ︙ | |||
2414 2415 2416 2417 2418 2419 2420 | #define CSON_ENABLE_SQLITE3 1 # else #define CSON_ENABLE_SQLITE3 1 # endif #endif #if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */ | | | 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 | #define CSON_ENABLE_SQLITE3 1 # else #define CSON_ENABLE_SQLITE3 1 # endif #endif #if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */ #include "sqlite3.h" #if defined(__cplusplus) extern "C" { #endif /** Converts a single value from a single 0-based column index to its JSON |
︙ | ︙ |
Changes to extsrc/shell.c.
︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 270 271 272 273 | #ifdef SQLITE_SHELL_FIDDLE /* Deselect most features from the console I/O package for Fiddle. */ # define SQLITE_CIO_NO_REDIRECT # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE #endif /************************* Begin ../ext/consio/console_io.h ******************/ /* ** 2023 November 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: | > | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | #ifdef SQLITE_SHELL_FIDDLE /* Deselect most features from the console I/O package for Fiddle. */ # define SQLITE_CIO_NO_REDIRECT # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE # define SQLITE_CIO_NO_FLUSH #endif /************************* Begin ../ext/consio/console_io.h ******************/ /* ** 2023 November 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
︙ | ︙ | |||
440 441 442 443 444 445 446 447 448 | oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ #ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept); #endif /* ** Collect input like fgets(...) with special provisions for input | > > > > > > > > | < | | | 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 | oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ #ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept); #endif /* ** Flush the given output stream. Return non-zero for success, else 0. */ #if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) SQLITE_INTERNAL_LINKAGE int fFlushBuffer(FILE *pfOut); #endif /* ** Collect input like fgets(...) with special provisions for input ** from the console on such platforms as require same. Newline ** translation may be done as set by set{Binary,Text}Mode(). ** As a convenience, pfIn==NULL is treated as stdin. */ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); /* Like fGetsUtf8 except stream is always the designated input. */ /* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ |
︙ | ︙ | |||
1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | # endif return (int)fwrite(cBuf, 1, nAccept, pfOut); # if CIO_WIN_WC_XLATE } # endif } # ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept){ FILE *pfErr; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); # if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ return conZstrEmit(ppst, cBuf, nAccept); }else { # endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 | # endif return (int)fwrite(cBuf, 1, nAccept, pfOut); # if CIO_WIN_WC_XLATE } # endif } /* ** Flush the given output stream. Return non-zero for success, else 0. */ #if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE) SQLITE_INTERNAL_LINKAGE int fFlushBuffer(FILE *pfOut){ # if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE) return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0; # else return fflush(pfOut); # endif } #endif #if CIO_WIN_WC_XLATE \ && !defined(SHELL_OMIT_FIO_DUPE) \ && defined(SQLITE_USE_ONLY_WIN32) static struct FileAltIds { int fd; HANDLE fh; } altIdsOfFile(FILE *pf){ struct FileAltIds rv = { _fileno(pf) }; union { intptr_t osfh; HANDLE fh; } fid = { (rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE }; rv.fh = fid.fh; return rv; } SQLITE_INTERNAL_LINKAGE size_t cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){ size_t rv = 0; struct FileAltIds fai = altIdsOfFile(pf); int fmode = _setmode(fai.fd, _O_BINARY); _setmode(fai.fd, fmode); while( rv < ocnt ){ size_t nbo = osz; while( nbo > 0 ){ DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo; BOOL wrc = TRUE; BOOL genCR = (fmode & _O_TEXT)!=0; if( genCR ){ const char *pnl = (const char*)memchr(buf, '\n', nbo); if( pnl ) nbo = pnl - (const char*)buf; else genCR = 0; } if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0); if( genCR && wrc ){ wrc = WriteFile(fai.fh, "\r\n", 2, 0,0); ++dwno; /* Skip over the LF */ } if( !wrc ) return rv; buf = (const char*)buf + dwno; nbo += dwno; } ++rv; } return rv; } SQLITE_INTERNAL_LINKAGE char * cfGets(char *cBuf, int n, FILE *pf){ int nci = 0; struct FileAltIds fai = altIdsOfFile(pf); int fmode = _setmode(fai.fd, _O_BINARY); BOOL eatCR = (fmode & _O_TEXT)!=0; _setmode(fai.fd, fmode); while( nci < n-1 ){ DWORD nr; if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break; if( nr>0 && (!eatCR || cBuf[nci]!='\r') ) nci += nr; } if( nci < n ) cBuf[nci] = 0; return (nci>0)? cBuf : 0; } # else # define cfWrite(b,os,no,f) fwrite(b,os,no,f) # define cfGets(b,n,f) fgets(b,n,f) # endif # ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept){ FILE *pfErr; PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); # if CIO_WIN_WC_XLATE if( pstReachesConsole(ppst) ){ return conZstrEmit(ppst, cBuf, nAccept); }else { # endif return (int)cfWrite(cBuf, 1, nAccept, pfErr); # if CIO_WIN_WC_XLATE } # endif } # endif /* defined(CONSIO_EPUTB) */ SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ |
︙ | ︙ | |||
1219 1220 1221 1222 1223 1224 1225 | if( noc > 0 ){ cBuf[noc] = 0; return cBuf; }else return 0; # endif }else{ # endif | | | 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 | if( noc > 0 ){ cBuf[noc] = 0; return cBuf; }else return 0; # endif }else{ # endif return cfGets(cBuf, ncMax, pfIn); # if CIO_WIN_WC_XLATE } # endif } #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ #if defined(_MSC_VER) |
︙ | ︙ | |||
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 | # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ /* These next 3 macros are for emitting formatted output. When complaints | > | > | 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 | # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) # define fflush(s) fFlushBuffer(s); #else /* For Fiddle, all console handling and emit redirection is omitted. */ /* These next 3 macros are for emitting formatted output. When complaints * from the WASM build are issued for non-formatted output, when a mere * string literal is to be emitted, the ?putz(z) forms should be used. * (This permits compile-time checking of format string / argument mismatch.) */ # define oputf(fmt, ...) printf(fmt,__VA_ARGS__) # define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) # define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) /* These next 3 macros are for emitting simple string literals. */ # define oputz(z) fputs(z,stdout) # define eputz(z) fputs(z,stderr) # define sputz(fp,z) fputs(z,fp) # define oputb(buf,na) fwrite(buf,1,na,stdout) # undef fflush #endif /* True if the timer is enabled */ static int enableTimer = 0; /* A version of strcmp() that works with NULL values */ static int cli_strcmp(const char *a, const char *b){ |
︙ | ︙ | |||
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 | */ static char *shell_strncpy(char *dest, const char *src, size_t n){ size_t i; for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i]; dest[i] = 0; return dest; } /* ** Optionally disable dynamic continuation prompt. ** Unless disabled, the continuation prompt shows open SQL lexemes if any, ** or open parentheses level if non-zero, or continuation prompt as set. ** This facility interacts with the scanner and process_input() where the ** below 5 macros are used. | > > > > > > > > | 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 | */ static char *shell_strncpy(char *dest, const char *src, size_t n){ size_t i; for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i]; dest[i] = 0; return dest; } /* ** strcpy() workalike to squelch an unwarranted link-time warning ** from OpenBSD. */ static void shell_strcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } /* ** Optionally disable dynamic continuation prompt. ** Unless disabled, the continuation prompt shows open SQL lexemes if any, ** or open parentheses level if non-zero, or continuation prompt as set. ** This facility interacts with the scanner and process_input() where the ** below 5 macros are used. |
︙ | ︙ | |||
1583 1584 1585 1586 1587 1588 1589 | || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){ return continuePrompt; }else{ if( dynPrompt.zScannerAwaits ){ size_t ncp = strlen(continuePrompt); size_t ndp = strlen(dynPrompt.zScannerAwaits); if( ndp > ncp-3 ) return continuePrompt; | | | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 | || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){ return continuePrompt; }else{ if( dynPrompt.zScannerAwaits ){ size_t ncp = strlen(continuePrompt); size_t ndp = strlen(dynPrompt.zScannerAwaits); if( ndp > ncp-3 ) return continuePrompt; shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' '; shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4); }else{ if( dynPrompt.inParenLevel>9 ){ shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4); }else if( dynPrompt.inParenLevel<0 ){ |
︙ | ︙ | |||
4679 4680 4681 4682 4683 4684 4685 | ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This file contains code to implement the percentile(Y,P) SQL function | | | 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 | ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This file contains code to implement the percentile(Y,P) SQL function ** and similar as described below: ** ** (1) The percentile(Y,P) function is an aggregate function taking ** exactly two arguments. ** ** (2) If the P argument to percentile(Y,P) is not the same for every ** row in the aggregate then an error is thrown. The word "same" ** in the previous sentence means that the value differ by less |
︙ | ︙ | |||
4728 4729 4730 4731 4732 4733 4734 | ** ** (12) The percentile(Y,P) is implemented as a single C99 source-code ** file that compiles into a shared-library or DLL that can be loaded ** into SQLite using the sqlite3_load_extension() interface. ** ** (13) A separate median(Y) function is the equivalent percentile(Y,50). ** | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | > > > | > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < | > | | > | | > < | | > | | | < | | | > | > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > | | | | > > > | | > > | | | | > > | < < > | < < < < < < | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | > > | > > > | | | | > > | | > > | | > > | > > > | > > > > > > > | < | | < | > | < | < < < | 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 | ** ** (12) The percentile(Y,P) is implemented as a single C99 source-code ** file that compiles into a shared-library or DLL that can be loaded ** into SQLite using the sqlite3_load_extension() interface. ** ** (13) A separate median(Y) function is the equivalent percentile(Y,50). ** ** (14) A separate percentile_cont(Y,P) function is equivalent to ** percentile(Y,P/100.0). In other words, the fraction value in ** the second argument is in the range of 0 to 1 instead of 0 to 100. ** ** (15) A separate percentile_disc(Y,P) function is like ** percentile_cont(Y,P) except that instead of returning the weighted ** average of the nearest two input values, it returns the next lower ** value. So the percentile_disc(Y,P) will always return a value ** that was one of the inputs. ** ** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and ** percentile_disc(Y,P) can be used as window functions. ** ** Differences from standard SQL: ** ** * The percentile_cont(X,P) function is equivalent to the following in ** standard SQL: ** ** (percentile_cont(P) WITHIN GROUP (ORDER BY X)) ** ** The SQLite syntax is much more compact. The standard SQL syntax ** is also supported if SQLite is compiled with the ** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option. ** ** * No median(X) function exists in the SQL standard. App developers ** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)". ** ** * No percentile(Y,P) function exists in the SQL standard. Instead of ** percential(Y,P), developers must write this: ** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that ** the fraction parameter to percentile() goes from 0 to 100 whereas ** the fraction parameter in SQL standard percentile_cont() goes from ** 0 to 1. ** ** Implementation notes as of 2024-08-31: ** ** * The regular aggregate-function versions of these routines work ** by accumulating all values in an array of doubles, then sorting ** that array using quicksort before computing the answer. Thus ** the runtime is O(NlogN) where N is the number of rows of input. ** ** * For the window-function versions of these routines, the array of ** inputs is sorted as soon as the first value is computed. Thereafter, ** the array is kept in sorted order using an insert-sort. This ** results in O(N*K) performance where K is the size of the window. ** One can imagine alternative implementations that give O(N*logN*logK) ** performance, but they require more complex logic and data structures. ** The developers have elected to keep the asymptotically slower ** algorithm for now, for simplicity, under the theory that window ** functions are seldom used and when they are, the window size K is ** often small. The developers might revisit that decision later, ** should the need arise. */ #if defined(SQLITE3_H) /* no-op */ #elif defined(SQLITE_STATIC_PERCENTILE) /* # include "sqlite3.h" */ #else /* # include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #endif #include <assert.h> #include <string.h> #include <stdlib.h> /* The following object is the group context for a single percentile() ** aggregate. Remember all input Y values until the very end. ** Those values are accumulated in the Percentile.a[] array. */ typedef struct Percentile Percentile; struct Percentile { unsigned nAlloc; /* Number of slots allocated for a[] */ unsigned nUsed; /* Number of slots actually used in a[] */ char bSorted; /* True if a[] is already in sorted order */ char bKeepSorted; /* True if advantageous to keep a[] sorted */ char bPctValid; /* True if rPct is valid */ double rPct; /* Fraction. 0.0 to 1.0 */ double *a; /* Array of Y values */ }; /* Details of each function in the percentile family */ typedef struct PercentileFunc PercentileFunc; struct PercentileFunc { const char *zName; /* Function name */ char nArg; /* Number of arguments */ char mxFrac; /* Maximum value of the "fraction" input */ char bDiscrete; /* True for percentile_disc() */ }; static const PercentileFunc aPercentFunc[] = { { "median", 1, 1, 0 }, { "percentile", 2, 100, 0 }, { "percentile_cont", 2, 1, 0 }, { "percentile_disc", 2, 1, 1 }, }; /* ** Return TRUE if the input floating-point number is an infinity. */ static int percentIsInfinity(double r){ sqlite3_uint64 u; assert( sizeof(u)==sizeof(r) ); memcpy(&u, &r, sizeof(u)); return ((u>>52)&0x7ff)==0x7ff; } /* ** Return TRUE if two doubles differ by 0.001 or less. */ static int percentSameValue(double a, double b){ a -= b; return a>=-0.001 && a<=0.001; } /* ** Search p (which must have p->bSorted) looking for an entry with ** value y. Return the index of that entry. ** ** If bExact is true, return -1 if the entry is not found. ** ** If bExact is false, return the index at which a new entry with ** value y should be insert in order to keep the values in sorted ** order. The smallest return value in this case will be 0, and ** the largest return value will be p->nUsed. */ static int percentBinarySearch(Percentile *p, double y, int bExact){ int iFirst = 0; /* First element of search range */ int iLast = p->nUsed - 1; /* Last element of search range */ while( iLast>=iFirst ){ int iMid = (iFirst+iLast)/2; double x = p->a[iMid]; if( x<y ){ iFirst = iMid + 1; }else if( x>y ){ iLast = iMid - 1; }else{ return iMid; } } if( bExact ) return -1; return iFirst; } /* ** Generate an error for a percentile function. ** ** The error format string must have exactly one occurrance of "%%s()" ** (with two '%' characters). That substring will be replaced by the name ** of the function. */ static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){ PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); char *zMsg1; char *zMsg2; va_list ap; va_start(ap, zFormat); zMsg1 = sqlite3_vmprintf(zFormat, ap); va_end(ap); zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0; sqlite3_result_error(pCtx, zMsg2, -1); sqlite3_free(zMsg1); sqlite3_free(zMsg2); } /* ** The "step" function for percentile(Y,P) is called once for each ** input row. */ static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){ Percentile *p; double rPct; int eType; double y; assert( argc==2 || argc==1 ); if( argc==1 ){ /* Requirement 13: median(Y) is the same as percentile(Y,50). */ rPct = 0.5; }else{ /* Requirement 3: P must be a number between 0 and 100 */ PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); eType = sqlite3_value_numeric_type(argv[1]); rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac; if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) || rPct<0.0 || rPct>1.0 ){ percentError(pCtx, "the fraction argument to %%s()" " is not between 0.0 and %.1f", (double)pFunc->mxFrac); return; } } /* Allocate the session context. */ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p==0 ) return; /* Remember the P value. Throw an error if the P value is different ** from any prior row, per Requirement (2). */ if( !p->bPctValid ){ p->rPct = rPct; p->bPctValid = 1; }else if( !percentSameValue(p->rPct,rPct) ){ percentError(pCtx, "the fraction argument to %%s()" " is not the same for all input rows"); return; } /* Ignore rows for which Y is NULL */ eType = sqlite3_value_type(argv[0]); if( eType==SQLITE_NULL ) return; /* If not NULL, then Y must be numeric. Otherwise throw an error. ** Requirement 4 */ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){ percentError(pCtx, "input to %%s() is not numeric"); return; } /* Throw an error if the Y value is infinity or NaN */ y = sqlite3_value_double(argv[0]); if( percentIsInfinity(y) ){ percentError(pCtx, "Inf input to %%s()"); return; } /* Allocate and store the Y */ if( p->nUsed>=p->nAlloc ){ unsigned n = p->nAlloc*2 + 250; double *a = sqlite3_realloc64(p->a, sizeof(double)*n); if( a==0 ){ sqlite3_free(p->a); memset(p, 0, sizeof(*p)); sqlite3_result_error_nomem(pCtx); return; } p->nAlloc = n; p->a = a; } if( p->nUsed==0 ){ p->a[p->nUsed++] = y; p->bSorted = 1; }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){ p->a[p->nUsed++] = y; }else if( p->bKeepSorted ){ int i; i = percentBinarySearch(p, y, 0); if( i<p->nUsed ){ memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0])); } p->a[i] = y; p->nUsed++; }else{ p->a[p->nUsed++] = y; p->bSorted = 0; } } /* ** Interchange two doubles. */ #define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;} /* ** Sort an array of doubles. ** ** Algorithm: quicksort ** ** This is implemented separately rather than using the qsort() routine ** from the standard library because: ** ** (1) To avoid a dependency on qsort() ** (2) To avoid the function call to the comparison routine for each ** comparison. */ static void percentSort(double *a, unsigned int n){ int iLt; /* Entries before a[iLt] are less than rPivot */ int iGt; /* Entries at or after a[iGt] are greater than rPivot */ int i; /* Loop counter */ double rPivot; /* The pivot value */ assert( n>=2 ); if( a[0]>a[n-1] ){ SWAP_DOUBLE(a[0],a[n-1]) } if( n==2 ) return; iGt = n-1; i = n/2; if( a[0]>a[i] ){ SWAP_DOUBLE(a[0],a[i]) }else if( a[i]>a[iGt] ){ SWAP_DOUBLE(a[i],a[iGt]) } if( n==3 ) return; rPivot = a[i]; iLt = i = 1; do{ if( a[i]<rPivot ){ if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt]) iLt++; i++; }else if( a[i]>rPivot ){ do{ iGt--; }while( iGt>i && a[iGt]>rPivot ); SWAP_DOUBLE(a[i],a[iGt]) }else{ i++; } }while( i<iGt ); if( iLt>=2 ) percentSort(a, iLt); if( n-iGt>=2 ) percentSort(a+iGt, n-iGt); /* Uncomment for testing */ #if 0 for(i=0; i<n-1; i++){ assert( a[i]<=a[i+1] ); } #endif } /* ** The "inverse" function for percentile(Y,P) is called to remove a ** row that was previously inserted by "step". */ static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){ Percentile *p; int eType; double y; int i; assert( argc==2 || argc==1 ); /* Allocate the session context. */ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p)); assert( p!=0 ); /* Ignore rows for which Y is NULL */ eType = sqlite3_value_type(argv[0]); if( eType==SQLITE_NULL ) return; /* If not NULL, then Y must be numeric. Otherwise throw an error. ** Requirement 4 */ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){ return; } /* Ignore the Y value if it is infinity or NaN */ y = sqlite3_value_double(argv[0]); if( percentIsInfinity(y) ){ return; } if( p->bSorted==0 ){ assert( p->nUsed>1 ); percentSort(p->a, p->nUsed); p->bSorted = 1; } p->bKeepSorted = 1; /* Find and remove the row */ i = percentBinarySearch(p, y, 1); if( i>=0 ){ p->nUsed--; if( i<p->nUsed ){ memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0])); } } } /* ** Compute the final output of percentile(). Clean up all allocated ** memory if and only if bIsFinal is true. */ static void percentCompute(sqlite3_context *pCtx, int bIsFinal){ Percentile *p; PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); unsigned i1, i2; double v1, v2; double ix, vx; p = (Percentile*)sqlite3_aggregate_context(pCtx, 0); if( p==0 ) return; if( p->a==0 ) return; if( p->nUsed ){ if( p->bSorted==0 ){ assert( p->nUsed>1 ); percentSort(p->a, p->nUsed); p->bSorted = 1; } ix = p->rPct*(p->nUsed-1); i1 = (unsigned)ix; if( pFunc->bDiscrete ){ vx = p->a[i1]; }else{ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1; v1 = p->a[i1]; v2 = p->a[i2]; vx = v1 + (v2-v1)*(ix-i1); } sqlite3_result_double(pCtx, vx); } if( bIsFinal ){ sqlite3_free(p->a); memset(p, 0, sizeof(*p)); }else{ p->bKeepSorted = 1; } } static void percentFinal(sqlite3_context *pCtx){ percentCompute(pCtx, 1); } static void percentValue(sqlite3_context *pCtx){ percentCompute(pCtx, 0); } #if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE) #endif int sqlite3_percentile_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; int i; #if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE) (void)pApi; /* Unused parameter */ #else SQLITE_EXTENSION_INIT2(pApi); #endif (void)pzErrMsg; /* Unused parameter */ for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){ rc = sqlite3_create_window_function(db, aPercentFunc[i].zName, aPercentFunc[i].nArg, SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1, (void*)&aPercentFunc[i], percentStep, percentFinal, percentValue, percentInverse, 0); if( rc ) break; } return rc; } /************************* End ../ext/misc/percentile.c ********************/ #undef sqlite3_base_init #define sqlite3_base_init sqlite3_base64_init |
︙ | ︙ | |||
6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 | ** start, stop, and step columns, and if present, it uses those constraints ** to bound the sequence of generated values. If the equality constraints ** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step. ** xBestIndex returns a small cost when both start and stop are available, ** and a very large cost if either start or stop are unavailable. This ** 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> | > > > > > > > > > > > > > > > > > > > > | 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 | ** start, stop, and step columns, and if present, it uses those constraints ** to bound the sequence of generated values. If the equality constraints ** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step. ** xBestIndex returns a small cost when both start and stop are available, ** and a very large cost if either start or stop are unavailable. This ** encourages the query planner to order joins such that the bounds of the ** series are well-defined. ** ** Update on 2024-08-22: ** xBestIndex now also looks for equality and inequality constraints against ** the value column and uses those constraints as additional bounds against ** the sequence range. Thus, a query like this: ** ** SELECT value FROM generate_series($SA,$EA) ** WHERE value BETWEEN $SB AND $EB; ** ** Is logically the same as: ** ** SELECT value FROM generate_series(max($SA,$SB),min($EA,$EB)); ** ** Constraints on the value column can server as substitutes for constraints ** on the hidden start and stop columns. So, the following two queries ** are equivalent: ** ** SELECT value FROM generate_series($S,$E); ** SELECT value FROM generate_series WHERE value BETWEEN $S and $E; ** */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <limits.h> |
︙ | ︙ | |||
6171 6172 6173 6174 6175 6176 6177 | } return smBase + ((sqlite3_int64)ix)*smStep; } /* typedef unsigned char u8; */ typedef struct SequenceSpec { | > > | | | 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 | } return smBase + ((sqlite3_int64)ix)*smStep; } /* typedef unsigned char u8; */ typedef struct SequenceSpec { sqlite3_int64 iOBase; /* Original starting value ("start") */ sqlite3_int64 iOTerm; /* Original terminal value ("stop") */ sqlite3_int64 iBase; /* Starting value to actually use */ sqlite3_int64 iTerm; /* Terminal value to actually use */ 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; |
︙ | ︙ | |||
6365 6366 6367 6368 6369 6370 6371 | 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 ){ | | | | > > | 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 | 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.iOBase; break; case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break; case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break; default: x = pCur->ss.iValueNow; break; } sqlite3_result_int64(ctx, x); return SQLITE_OK; } #ifndef LARGEST_UINT64 #define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) #define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32)) #define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) #endif /* ** 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){ |
︙ | ︙ | |||
6416 6417 6418 6419 6420 6421 6422 | ** 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: ** | | | | | | | | > > > > > > > > > > > | 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 | ** 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: ** ** 0x0001: start=VALUE ** 0x0002: stop=VALUE ** 0x0004: step=VALUE ** 0x0008: descending order ** 0x0010: ascending order ** 0x0020: LIMIT VALUE ** 0x0040: OFFSET VALUE ** 0x0080: value=VALUE ** 0x0100: value>=VALUE ** 0x0200: value>VALUE ** 0x1000: value<=VALUE ** 0x2000: value<VALUE ** ** 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; int returnNoRows = 0; sqlite3_int64 iMin = SMALLEST_INT64; sqlite3_int64 iMax = LARGEST_INT64; sqlite3_int64 iLimit = 0; sqlite3_int64 iOffset = 0; (void)idxStrUnused; if( idxNum & 0x01 ){ pCur->ss.iBase = sqlite3_value_int64(argv[i++]); }else{ pCur->ss.iBase = 0; } if( idxNum & 0x02 ){ |
︙ | ︙ | |||
6456 6457 6458 6459 6460 6461 6462 6463 | pCur->ss.iStep = 1; }else if( pCur->ss.iStep<0 ){ if( (idxNum & 0x10)==0 ) idxNum |= 0x08; } }else{ pCur->ss.iStep = 1; } if( idxNum & 0x20 ){ | > > > > > > > > > > > > > > > > > > > | > > > > | | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < > > > | < < > > > > > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 | pCur->ss.iStep = 1; }else if( pCur->ss.iStep<0 ){ if( (idxNum & 0x10)==0 ) idxNum |= 0x08; } }else{ pCur->ss.iStep = 1; } /* If there are constraints on the value column but there are ** no constraints on the start, stop, and step columns, then ** initialize the default range to be the entire range of 64-bit signed ** integers. This range will contracted by the value column constraints ** further below. */ if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){ pCur->ss.iBase = SMALLEST_INT64; } if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){ pCur->ss.iTerm = LARGEST_INT64; } pCur->ss.iOBase = pCur->ss.iBase; pCur->ss.iOTerm = pCur->ss.iTerm; /* Extract the LIMIT and OFFSET values, but do not apply them yet. ** The range must first be constrained by the limits on value. */ if( idxNum & 0x20 ){ iLimit = sqlite3_value_int64(argv[i++]); if( idxNum & 0x40 ){ iOffset = sqlite3_value_int64(argv[i++]); } } if( idxNum & 0x3380 ){ /* Extract the maximum range of output values determined by ** constraints on the "value" column. */ if( idxNum & 0x0080 ){ iMin = iMax = sqlite3_value_int64(argv[i++]); }else{ if( idxNum & 0x0300 ){ iMin = sqlite3_value_int64(argv[i++]); if( idxNum & 0x0200 ){ if( iMin==LARGEST_INT64 ){ returnNoRows = 1; }else{ iMin++; } } } if( idxNum & 0x3000 ){ iMax = sqlite3_value_int64(argv[i++]); if( idxNum & 0x2000 ){ if( iMax==SMALLEST_INT64 ){ returnNoRows = 1; }else{ iMax--; } } } if( iMin>iMax ){ returnNoRows = 1; } } /* Try to reduce the range of values to be generated based on ** constraints on the "value" column. */ if( pCur->ss.iStep>0 ){ sqlite3_int64 szStep = pCur->ss.iStep; if( pCur->ss.iBase<iMin ){ sqlite3_uint64 d = iMin - pCur->ss.iBase; pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; } if( pCur->ss.iTerm>iMax ){ sqlite3_uint64 d = pCur->ss.iTerm - iMax; pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep; } }else{ sqlite3_int64 szStep = -pCur->ss.iStep; assert( szStep>0 ); if( pCur->ss.iBase>iMax ){ sqlite3_uint64 d = pCur->ss.iBase - iMax; pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; } if( pCur->ss.iTerm<iMin ){ sqlite3_uint64 d = iMin - pCur->ss.iTerm; pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep; } } } /* Apply LIMIT and OFFSET constraints, if any */ if( idxNum & 0x20 ){ if( iOffset>0 ){ pCur->ss.iBase += pCur->ss.iStep*iOffset; } if( iLimit>=0 ){ sqlite3_int64 iTerm; iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep; if( pCur->ss.iStep<0 ){ if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; }else{ if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm; } } } 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 */ returnNoRows = 1; break; } } if( returnNoRows ){ pCur->ss.iBase = 1; pCur->ss.iTerm = 0; pCur->ss.iStep = 1; } if( idxNum & 0x08 ){ 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 ** plan. ** ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** ** The query plan is represented by bits in idxNum: ** ** 0x0001 start = $num ** 0x0002 stop = $num ** 0x0004 step = $num ** 0x0008 output is in descending order ** 0x0010 output is in ascending order ** 0x0020 LIMIT $num ** 0x0040 OFFSET $num ** 0x0080 value = $num ** 0x0100 value >= $num ** 0x0200 value > $num ** 0x1000 value <= $num ** 0x2000 value < $num ** ** Only one of 0x0100 or 0x0200 will be returned. Similarly, only ** one of 0x1000 or 0x2000 will be returned. If the 0x0080 is set, then ** none of the 0xff00 bits will be set. ** ** The order of parameters passed to xFilter is as follows: ** ** * The argument to start= if bit 0x0001 is in the idxNum mask ** * The argument to stop= if bit 0x0002 is in the idxNum mask ** * The argument to step= if bit 0x0004 is in the idxNum mask ** * The argument to LIMIT if bit 0x0020 is in the idxNum mask ** * The argument to OFFSET if bit 0x0040 is in the idxNum mask ** * The argument to value=, or value>= or value> if any of ** bits 0x0380 are in the idxNum mask ** * The argument to value<= or value< if either of bits 0x3000 ** are in the mask ** */ static int seriesBestIndex( sqlite3_vtab *pVTab, sqlite3_index_info *pIdxInfo ){ int i, j; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ #ifndef ZERO_ARGUMENT_GENERATE_SERIES int bStartSeen = 0; /* EQ constraint seen on the START column */ #endif int unusableMask = 0; /* Mask of unusable constraints */ int nArg = 0; /* Number of arguments that seriesFilter() expects */ int aIdx[7]; /* Constraints on start, stop, step, LIMIT, OFFSET, ** and value. aIdx[5] covers value=, value>=, and ** value>, aIdx[6] covers value<= and value< */ const struct sqlite3_index_constraint *pConstraint; /* This implementation assumes that the start, stop, and step columns ** are the last three columns in the virtual table. */ assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = aIdx[5] = aIdx[6] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ int iCol; /* 0 for start, 1 for stop, 2 for step */ int iMask; /* bitmask for those column */ int op = pConstraint->op; if( op>=SQLITE_INDEX_CONSTRAINT_LIMIT && op<=SQLITE_INDEX_CONSTRAINT_OFFSET ){ if( pConstraint->usable==0 ){ /* do nothing */ }else if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){ aIdx[3] = i; idxNum |= 0x20; }else{ assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET ); aIdx[4] = i; idxNum |= 0x40; } continue; } if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){ switch( op ){ case SQLITE_INDEX_CONSTRAINT_EQ: case SQLITE_INDEX_CONSTRAINT_IS: { idxNum |= 0x0080; idxNum &= ~0x3300; aIdx[5] = i; aIdx[6] = -1; bStartSeen = 1; break; } case SQLITE_INDEX_CONSTRAINT_GE: { if( idxNum & 0x0080 ) break; idxNum |= 0x0100; idxNum &= ~0x0200; aIdx[5] = i; bStartSeen = 1; break; } case SQLITE_INDEX_CONSTRAINT_GT: { if( idxNum & 0x0080 ) break; idxNum |= 0x0200; idxNum &= ~0x0100; aIdx[5] = i; bStartSeen = 1; break; } case SQLITE_INDEX_CONSTRAINT_LE: { if( idxNum & 0x0080 ) break; idxNum |= 0x1000; idxNum &= ~0x2000; aIdx[6] = i; break; } case SQLITE_INDEX_CONSTRAINT_LT: { if( idxNum & 0x0080 ) break; idxNum |= 0x2000; idxNum &= ~0x1000; aIdx[6] = i; break; } } continue; } iCol = pConstraint->iColumn - SERIES_COLUMN_START; assert( iCol>=0 && iCol<=2 ); iMask = 1 << iCol; #ifndef ZERO_ARGUMENT_GENERATE_SERIES if( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){ bStartSeen = 1; } |
︙ | ︙ | |||
6574 6575 6576 6577 6578 6579 6580 | } } if( aIdx[3]==0 ){ /* Ignore OFFSET if LIMIT is omitted */ idxNum &= ~0x60; aIdx[4] = 0; } | | | 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 | } } if( aIdx[3]==0 ){ /* Ignore OFFSET if LIMIT is omitted */ idxNum &= ~0x60; aIdx[4] = 0; } for(i=0; i<7; i++){ if( (j = aIdx[i])>=0 ){ pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3; } } /* The current generate_column() implementation requires at least one |
︙ | ︙ | |||
6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 | }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ pIdxInfo->estimatedRows = 2147483647; } pIdxInfo->idxNum = idxNum; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** generate_series virtual table. */ | > > > | 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 | }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ pIdxInfo->estimatedRows = 2147483647; } pIdxInfo->idxNum = idxNum; #ifdef SQLITE_INDEX_SCAN_HEX pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_HEX; #endif return SQLITE_OK; } /* ** This following structure defines all the methods for the ** generate_series virtual table. */ |
︙ | ︙ | |||
22173 22174 22175 22176 22177 22178 22179 | #ifdef INFINITY }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ sqlite3_bind_double(pStmt, i, INFINITY); #endif }else if( strncmp(zVar, "$int_", 5)==0 ){ sqlite3_bind_int(pStmt, i, atoi(&zVar[5])); }else if( strncmp(zVar, "$text_", 6)==0 ){ | > | | | | 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 | #ifdef INFINITY }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ sqlite3_bind_double(pStmt, i, INFINITY); #endif }else if( strncmp(zVar, "$int_", 5)==0 ){ sqlite3_bind_int(pStmt, i, atoi(&zVar[5])); }else if( strncmp(zVar, "$text_", 6)==0 ){ size_t szVar = strlen(zVar); char *zBuf = sqlite3_malloc64( szVar-5 ); if( zBuf ){ memcpy(zBuf, &zVar[6], szVar-5); sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8); } }else{ sqlite3_bind_null(pStmt, i); } sqlite3_reset(pQ); } sqlite3_finalize(pQ); |
︙ | ︙ | |||
24968 24969 24970 24971 24972 24973 24974 24975 24976 24977 24978 | sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); oputf("%-20s %u\n", "data version", iDataVersion); return 0; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ /* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ | > > > > > > | < | 25473 25474 25475 25476 25477 25478 25479 25480 25481 25482 25483 25484 25485 25486 25487 25488 25489 25490 25491 25492 25493 25494 25495 25496 25497 | sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); oputf("%-20s %u\n", "data version", iDataVersion); return 0; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ /* ** Print the given string as an error message. */ static void shellEmitError(const char *zErr){ eputf("Error: %s\n", zErr); } /* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ shellEmitError(sqlite3_errmsg(db)); return 1; } /* ** Compare the pattern in zGlob[] against the text in z[]. Return TRUE ** if they match and FALSE (0) if they do not match. ** |
︙ | ︙ | |||
25518 25519 25520 25521 25522 25523 25524 | */ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_list ap; char *z; va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); | | | 26028 26029 26030 26031 26032 26033 26034 26035 26036 26037 26038 26039 26040 26041 26042 | */ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_list ap; char *z; va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); shellEmitError(z); if( pAr->fromCmdLine ){ eputz("Use \"-A\" for more help\n"); }else{ eputz("Use \".archive --help\" for more help\n"); } sqlite3_free(z); return SQLITE_ERROR; |
︙ | ︙ | |||
26749 26750 26751 26752 26753 26754 26755 | if( bAsync ){ sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", 0, 0, 0); } open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ | | | | 27259 27260 27261 27262 27263 27264 27265 27266 27267 27268 27269 27270 27271 27272 27273 27274 27275 27276 27277 27278 27279 27280 27281 27282 | if( bAsync ){ sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;", 0, 0, 0); } open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ shellDatabaseError(pDest); close_db(pDest); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else{ shellDatabaseError(pDest); rc = 1; } close_db(pDest); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ if( c=='b' && n>=3 && cli_strncmp(azArg[0], "bail", n)==0 ){ |
︙ | ︙ | |||
26934 26935 26936 26937 26938 26939 26940 | char **azName = 0; int nName = 0; sqlite3_stmt *pStmt; int i; open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ | | | 27444 27445 27446 27447 27448 27449 27450 27451 27452 27453 27454 27455 27456 27457 27458 | char **azName = 0; int nName = 0; sqlite3_stmt *pStmt; int i; open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ shellDatabaseError(p->db); rc = 1; }else{ while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zSchema = (const char *)sqlite3_column_text(pStmt,1); const char *zFile = (const char*)sqlite3_column_text(pStmt,2); if( zSchema==0 || zFile==0 ) continue; azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*)); |
︙ | ︙ | |||
27630 27631 27632 27633 27634 27635 27636 | shell_out_of_memory(); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ if (pStmt) sqlite3_finalize(pStmt); | | | 28140 28141 28142 28143 28144 28145 28146 28147 28148 28149 28150 28151 28152 28153 28154 | shell_out_of_memory(); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ if (pStmt) sqlite3_finalize(pStmt); shellDatabaseError(p->db); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } if( sqlite3_step(pStmt)==SQLITE_ROW ){ nCol = sqlite3_column_int(pStmt, 0); }else{ |
︙ | ︙ | |||
27674 27675 27676 27677 27678 27679 27680 | if( eVerbose>=2 ){ oputf("Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ | | | 28184 28185 28186 28187 28188 28189 28190 28191 28192 28193 28194 28195 28196 28197 28198 | if( eVerbose>=2 ){ oputf("Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ shellDatabaseError(p->db); if (pStmt) sqlite3_finalize(pStmt); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; } needCommit = sqlite3_get_autocommit(p->db); if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); |
︙ | ︙ | |||
27968 27969 27970 27971 27972 27973 27974 | 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 ){ | | | 28478 28479 28480 28481 28482 28483 28484 28485 28486 28487 28488 28489 28490 28491 28492 | 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 ){ shellEmitError(zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){ |
︙ | ︙ | |||
28590 28591 28592 28593 28594 28595 28596 | eputf("Error: cannot open \"%s\"\n", zSrcFile); close_db(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ | | | | 29100 29101 29102 29103 29104 29105 29106 29107 29108 29109 29110 29111 29112 29113 29114 29115 29116 29117 29118 29119 29120 29121 29122 29123 29124 29125 29126 29127 29128 29129 29130 29131 29132 | eputf("Error: cannot open \"%s\"\n", zSrcFile); close_db(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ shellDatabaseError(p->db); close_db(pSrc); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK || rc==SQLITE_BUSY ){ if( rc==SQLITE_BUSY ){ if( nTimeout++ >= 3 ) break; sqlite3_sleep(100); } } sqlite3_backup_finish(pBackup); if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ eputz("Error: source database is busy\n"); rc = 1; }else{ shellDatabaseError(p->db); rc = 1; } close_db(pSrc); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){ |
︙ | ︙ | |||
28705 28706 28707 28708 28709 28710 28711 | } } if( zDiv ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ | | | 29215 29216 29217 29218 29219 29220 29221 29222 29223 29224 29225 29226 29227 29228 29229 | } } if( zDiv ){ sqlite3_stmt *pStmt = 0; rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ shellDatabaseError(p->db); sqlite3_finalize(pStmt); rc = 1; goto meta_command_exit; } appendText(&sSelect, "SELECT sql FROM", 0); iSchema = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
︙ | ︙ | |||
28774 28775 28776 28777 28778 28779 28780 | oputf("SQL: %s;\n", sSelect.z); }else{ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); } freeText(&sSelect); } if( zErrMsg ){ | | | 29284 29285 29286 29287 29288 29289 29290 29291 29292 29293 29294 29295 29296 29297 29298 | oputf("SQL: %s;\n", sSelect.z); }else{ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); } freeText(&sSelect); } if( zErrMsg ){ shellEmitError(zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ eputz("Error: querying schema information\n"); rc = 1; }else{ rc = 0; |
︙ | ︙ | |||
29545 29546 29547 29548 29549 29550 29551 | {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." }, {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" }, {"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" }, {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, {"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, | | | 30055 30056 30057 30058 30059 30060 30061 30062 30063 30064 30065 30066 30067 30068 30069 | {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." }, {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" }, {"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" }, {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, {"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, {"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK ..."}, #ifdef YYCOVERAGE {"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, #endif {"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,1, "OFFSET " }, {"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, |
︙ | ︙ | |||
29608 29609 29610 29611 29612 29613 29614 29615 | } } if( testctrl<0 ){ eputf("Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); }else{ switch(testctrl){ | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30118 30119 30120 30121 30122 30123 30124 30125 30126 30127 30128 30129 30130 30131 30132 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 30145 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 30221 30222 30223 30224 30225 30226 30227 30228 30229 30230 30231 30232 30233 30234 30235 30236 30237 30238 30239 30240 30241 30242 30243 30244 | } } if( testctrl<0 ){ eputf("Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); }else{ switch(testctrl){ /* Special processing for .testctrl opt MASK ... ** Each MASK argument can be one of: ** ** +LABEL Enable the named optimization ** ** -LABEL Disable the named optimization ** ** INTEGER Mask of optimizations to disable */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { static const struct { unsigned int mask; /* Mask for this optimization */ unsigned int bDsply; /* Display this on output */ const char *zLabel; /* Name of optimization */ } aLabel[] = { { 0x00000001, 1, "QueryFlattener" }, { 0x00000001, 0, "Flatten" }, { 0x00000002, 1, "WindowFunc" }, { 0x00000004, 1, "GroupByOrder" }, { 0x00000008, 1, "FactorOutConst" }, { 0x00000010, 1, "DistinctOpt" }, { 0x00000020, 1, "CoverIdxScan" }, { 0x00000040, 1, "OrderByIdxJoin" }, { 0x00000080, 1, "Transitive" }, { 0x00000100, 1, "OmitNoopJoin" }, { 0x00000200, 1, "CountOfView" }, { 0x00000400, 1, "CurosrHints" }, { 0x00000800, 1, "Stat4" }, { 0x00001000, 1, "PushDown" }, { 0x00002000, 1, "SimplifyJoin" }, { 0x00004000, 1, "SkipScan" }, { 0x00008000, 1, "PropagateConst" }, { 0x00010000, 1, "MinMaxOpt" }, { 0x00020000, 1, "SeekScan" }, { 0x00040000, 1, "OmitOrderBy" }, { 0x00080000, 1, "BloomFilter" }, { 0x00100000, 1, "BloomPulldown" }, { 0x00200000, 1, "BalancedMerge" }, { 0x00400000, 1, "ReleaseReg" }, { 0x00800000, 1, "FlttnUnionAll" }, { 0x01000000, 1, "IndexedEXpr" }, { 0x02000000, 1, "Coroutines" }, { 0x04000000, 1, "NullUnusedCols" }, { 0x08000000, 1, "OnePass" }, { 0x10000000, 1, "OrderBySubq" }, { 0xffffffff, 0, "All" }, }; unsigned int curOpt; unsigned int newOpt; int ii; sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt); newOpt = curOpt; for(ii=2; ii<nArg; ii++){ const char *z = azArg[ii]; int useLabel = 0; const char *zLabel = 0; if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){ useLabel = z[0]; zLabel = &z[1]; }else if( !IsDigit(z[0]) && z[0]!=0 && !IsDigit(z[1]) ){ useLabel = '+'; zLabel = z; }else{ newOpt = (unsigned int)strtol(z,0,0); } if( useLabel ){ int jj; for(jj=0; jj<ArraySize(aLabel); jj++){ if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; } if( jj>=ArraySize(aLabel) ){ eputf("Error: no such optimization: \"%s\"\n", zLabel); eputz("Should be one of:"); for(jj=0; jj<ArraySize(aLabel); jj++){ eputf(" %s", aLabel[jj].zLabel); } eputz("\n"); rc = 1; goto meta_command_exit; } if( useLabel=='+' ){ newOpt &= ~aLabel[jj].mask; }else{ newOpt |= aLabel[jj].mask; } } } if( curOpt!=newOpt ){ sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt); }else if( nArg<3 ){ curOpt = ~newOpt; } if( newOpt==0 ){ oputz("+All\n"); }else if( newOpt==0xffffffff ){ oputz("-All\n"); }else{ int jj; for(jj=0; jj<ArraySize(aLabel); jj++){ unsigned int m = aLabel[jj].mask; if( !aLabel[jj].bDsply ) continue; if( (curOpt&m)!=(newOpt&m) ){ oputf("%c%s\n", (newOpt & m)==0 ? '+' : '-', aLabel[jj].zLabel); } } } rc2 = isOk = 3; break; } /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_FK_NO_ACTION: if( nArg==3 ){ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0); rc2 = sqlite3_test_control(testctrl, p->db, opt); isOk = 3; } break; |
︙ | ︙ | |||
31353 31354 31355 31356 31357 31358 31359 | if( z[0]=='.' ){ rc = do_meta_command(z, &data); if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ | | | 31973 31974 31975 31976 31977 31978 31979 31980 31981 31982 31983 31984 31985 31986 31987 | if( z[0]=='.' ){ rc = do_meta_command(z, &data); if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data, 0); rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ shellEmitError(zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; }else if( rc!=0 ){ eputf("Error: unable to process SQL \"%s\"\n", z); if( bail_on_error ) return rc; } } #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) |
︙ | ︙ | |||
31407 31408 31409 31410 31411 31412 31413 | } }else{ open_db(&data, 0); echo_group_input(&data, azCmd[i]); rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg || rc ){ if( zErrMsg!=0 ){ | | | 32027 32028 32029 32030 32031 32032 32033 32034 32035 32036 32037 32038 32039 32040 32041 | } }else{ open_db(&data, 0); echo_group_input(&data, azCmd[i]); rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg || rc ){ if( zErrMsg!=0 ){ shellEmitError(zErrMsg); }else{ eputf("Error: unable to process SQL: %s\n", azCmd[i]); } sqlite3_free(zErrMsg); if( rc==0 ) rc = 1; goto shell_main_exit; } |
︙ | ︙ |
Changes to extsrc/sqlite3.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in ** 7891a266c4425722ae8b9231397ef9e42e24. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif /************** Begin file sqliteInt.h ***************************************/ |
︙ | ︙ | |||
460 461 462 463 464 465 466 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.47.0" #define SQLITE_VERSION_NUMBER 3047000 | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.47.0" #define SQLITE_VERSION_NUMBER 3047000 #define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" /* ** 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 |
︙ | ︙ | |||
5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue | > > > > > > > > > > | 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** ** [[SQLITE_SELFORDER1]] <dt>SQLITE_SELFORDER1</dt><dd> ** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate ** that internally orders the values provided to the first argument. The ** ordered-set aggregate SQL notation with a single ORDER BY term can be ** used to invoke this function. If the ordered-set aggregate notation is ** used on a function that lacks this flag, then an error is raised. Note ** that the ordered-set aggregate syntax is only available if SQLite is ** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 #define SQLITE_SELFORDER1 0x002000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue |
︙ | ︙ | |||
7739 7740 7741 7742 7743 7744 7745 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a | | > > | | | 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a ** mask of SQLITE_INDEX_SCAN_* flags. One such flag is ** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN] ** output to show the idxNum has hex instead of as decimal. Another flag is ** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will ** return at most one row. ** ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then ** SQLite also assumes that if a call to the xUpdate() method is made as ** part of the same statement to delete or update a virtual table row and the ** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback ** any database changes. In other words, if the xUpdate() returns ** SQLITE_CONSTRAINT, the database contents must be exactly as they were |
︙ | ︙ | |||
7805 7806 7807 7808 7809 7810 7811 | /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ | | > > | 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 | /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ #define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */ #define SQLITE_INDEX_SCAN_HEX 0x00000002 /* Display idxNum as hex */ /* in EXPLAIN QUERY PLAN */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros define the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents ** an operator that is part of a constraint term in the WHERE clause of |
︙ | ︙ | |||
8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 | > | 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_GETOPT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 |
︙ | ︙ | |||
13416 13417 13418 13419 13420 13421 13422 13423 13424 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { | > > > > > > > > > > > > > > > > > > > > > > > | | 13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the locale associated ** with column iCol of the current row. Usually, there is no associated ** locale, and output parameters (*pzLocale) and (*pnLocale) are set ** to NULL and 0, respectively. However, if the fts5_locale() function ** was used to associate a locale with the value when it was inserted ** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ** is set to the size in bytes of the buffer, not including the ** nul-terminator. ** ** If successful, SQLITE_OK is returned. Or, if an error occurs, an ** SQLite error code is returned. The final value of the output parameters ** is undefined in this case. ** ** xTokenize_v2: ** Tokenize text using the tokenizer belonging to the FTS5 table. This ** API is the same as the xTokenize() API, except that it allows a tokenizer ** locale to be specified. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); |
︙ | ︙ | |||
13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) | > > > > > > > > > > | | 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); /* Below this point are iVersion>=4 only */ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); int (*xTokenize_v2)(Fts5Context*, const char *pText, int nText, /* Text to tokenize */ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ void *pCtx, /* Context passed to xToken() */ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) ** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the ** tokenizer name as part of the CREATE VIRTUAL TABLE statement used ** to create the FTS5 table. ** ** The final argument is an output variable. If successful, (*ppOut) |
︙ | ︙ | |||
13504 13505 13506 13507 13508 13509 13510 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** | | | 13552 13553 13554 13555 13556 13557 13558 13559 13560 13561 13562 13563 13564 13565 13566 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** ** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** ** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into ** or removed from the FTS table. The tokenizer is being invoked to ** determine the set of tokens to add to (or delete from) the ** FTS index. |
︙ | ︙ | |||
13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from | > > > > > > > | 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 13589 13590 13591 13592 13593 13594 13595 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** The sixth and seventh arguments passed to xTokenize() - pLocale and ** nLocale - are a pointer to a buffer containing the locale to use for ** tokenization (e.g. "en_US") and its size in bytes, respectively. The ** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ** which case nLocale is always 0) to indicate that the tokenizer should ** use its default locale. ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from |
︙ | ︙ | |||
13550 13551 13552 13553 13554 13555 13556 13557 13558 13559 13560 13561 13562 13563 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms | > > > > > > > > > > > > > > > > > > > > > > > > | 13605 13606 13607 13608 13609 13610 13611 13612 13613 13614 13615 13616 13617 13618 13619 13620 13621 13622 13623 13624 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** If the tokenizer is registered using an fts5_tokenizer_v2 object, ** then the xTokenize() method has two additional arguments - pLocale ** and nLocale. These specify the locale that the tokenizer should use ** for the current request. If pLocale and nLocale are both 0, then the ** tokenizer should use its default locale. Otherwise, pLocale points to ** an nLocale byte buffer containing the name of the locale to use as utf-8 ** text. pLocale is not nul-terminated. ** ** FTS5_TOKENIZER ** ** There is also an fts5_tokenizer object. This is an older, deprecated, ** version of fts5_tokenizer_v2. It is similar except that: ** ** <ul> ** <li> There is no "iVersion" field, and ** <li> The xTokenize() method does not take a locale argument. ** </ul> ** ** Legacy fts5_tokenizer tokenizers must be registered using the ** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ** ** Tokenizer implementations registered using either API may be retrieved ** using both xFindTokenizer() and xFindTokenizer_v2(). ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms |
︙ | ︙ | |||
13659 13660 13661 13662 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 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 13703 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 13738 13739 13740 13741 13742 13743 13744 13745 13746 13747 13748 13749 13750 13751 13752 13753 13754 13755 13756 13757 13758 13759 13760 13761 13762 13763 13764 13765 13766 13767 13768 13769 13770 13771 13772 13773 13774 13775 13776 13777 13778 13779 13780 13781 13782 13783 13784 13785 13786 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; struct fts5_tokenizer_v2 { int iVersion; /* Currently always 2 */ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, const char *pLocale, int nLocale, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* ** New code should use the fts5_tokenizer_v2 type to define tokenizer ** implementations. The following type is included for legacy applications ** that still use it. */ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer *pTokenizer, |
︙ | ︙ | |||
13724 13725 13726 13727 13728 13729 13730 13731 13732 13733 13734 13735 13736 13737 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); }; /* ** END OF REGISTRATION API *************************************************************************/ #if 0 | > > > > > > > > > > > > > > > > > > > | 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 13843 13844 13845 13846 13847 13848 13849 13850 13851 13852 13853 13854 13855 13856 13857 13858 13859 13860 13861 13862 13863 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); /* APIs below this point are only available if iVersion>=3 */ /* Create a new tokenizer */ int (*xCreateTokenizer_v2)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer_v2 *pTokenizer, void (*xDestroy)(void*) ); /* Find an existing tokenizer */ int (*xFindTokenizer_v2)( fts5_api *pApi, const char *zName, void **ppUserData, fts5_tokenizer_v2 **ppTokenizer ); }; /* ** END OF REGISTRATION API *************************************************************************/ #if 0 |
︙ | ︙ | |||
14701 14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 | /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT | > > | 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 | /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define float sqlite_int64 # define fabs(X) ((X)<0?-(X):(X)) # define sqlite3IsOverflow(X) 0 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT |
︙ | ︙ | |||
15378 15379 15380 15381 15382 15383 15384 15385 15386 15387 15388 15389 15390 15391 | typedef struct RenameToken RenameToken; typedef struct Returning Returning; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; typedef struct SQLiteThread SQLiteThread; typedef struct SelectDest SelectDest; typedef struct SrcItem SrcItem; typedef struct SrcList SrcList; typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; typedef struct TreeView TreeView; | > | 15506 15507 15508 15509 15510 15511 15512 15513 15514 15515 15516 15517 15518 15519 15520 | typedef struct RenameToken RenameToken; typedef struct Returning Returning; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; typedef struct SQLiteThread SQLiteThread; typedef struct SelectDest SelectDest; typedef struct Subquery Subquery; typedef struct SrcItem SrcItem; typedef struct SrcList SrcList; typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */ typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; typedef struct TreeView TreeView; |
︙ | ︙ | |||
19261 19262 19263 19264 19265 19266 19267 19268 19269 19270 19271 19272 19273 19274 19275 19276 19277 19278 19279 | ** Allowed values for IdList.eType, which determines which value of the a.u4 ** is valid. */ #define EU4_NONE 0 /* Does not use IdList.a.u4 */ #define EU4_IDX 1 /* Uses IdList.a.u4.idx */ #define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. ** ** The jointype starts out showing the join type between the current table ** and the next table on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the table and the previous table. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** | > > > > > > > > > > > > | > > > | | | | > > > > > > > > > > > < < | < < < < > > > < < < < > > > > > > > > > | 19390 19391 19392 19393 19394 19395 19396 19397 19398 19399 19400 19401 19402 19403 19404 19405 19406 19407 19408 19409 19410 19411 19412 19413 19414 19415 19416 19417 19418 19419 19420 19421 19422 19423 19424 19425 19426 19427 19428 19429 19430 19431 19432 19433 19434 19435 19436 19437 19438 19439 19440 19441 19442 19443 19444 19445 19446 19447 19448 19449 19450 19451 19452 19453 19454 19455 19456 19457 19458 19459 19460 19461 19462 19463 19464 19465 19466 19467 19468 19469 19470 19471 19472 19473 19474 19475 19476 19477 19478 19479 19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 19492 19493 19494 19495 | ** Allowed values for IdList.eType, which determines which value of the a.u4 ** is valid. */ #define EU4_NONE 0 /* Does not use IdList.a.u4 */ #define EU4_IDX 1 /* Uses IdList.a.u4.idx */ #define EU4_EXPR 2 /* Uses IdList.a.u4.pExpr -- NOT CURRENTLY USED */ /* ** Details of the implementation of a subquery. */ struct Subquery { Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to initialize a subquery */ int regReturn; /* Register holding return address of addrFillSub */ int regResult; /* Registers holding results of a co-routine */ }; /* ** The SrcItem object represents a single term in the FROM clause of a query. ** The SrcList object is mostly an array of SrcItems. ** ** The jointype starts out showing the join type between the current table ** and the next table on the list. The parser builds the list this way. ** But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each ** jointype expresses the join between the table and the previous table. ** ** In the colUsed field, the high-order bit (bit 63) is set if the table ** contains more than 63 columns and the 64-th or later column is used. ** ** Aggressive use of "union" helps keep the size of the object small. This ** has been shown to boost performance, in addition to saving memory. ** Access to union elements is gated by the following rules which should ** always be checked, either by an if-statement or by an assert(). ** ** Field Only access if this is true ** --------------- ----------------------------------- ** u1.zIndexedBy fg.isIndexedBy ** u1.pFuncArg fg.isTabFunc ** u1.nRow !fg.isTabFunc && !fg.isIndexedBy ** ** u2.pIBIndex fg.isIndexedBy ** u2.pCteUse fg.isCte ** ** u3.pOn !fg.isUsing ** u3.pUsing fg.isUsing ** ** u4.zDatabase !fg.fixedSchema && !fg.isSubquery ** u4.pSchema fg.fixedSchema ** u4.pSubq fg.isSubquery ** ** See also the sqlite3SrcListDelete() routine for assert() statements that ** check invariants on the fields of this object, especially the flags ** inside the fg struct. */ struct SrcItem { char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pSTab; /* Table object for zName. Mnemonic: Srcitem-TABle */ struct { u8 jointype; /* Type of join between this table and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isSubquery :1; /* True if this term is a subquery */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned isMaterialized:1; /* This is a materialized view */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ unsigned fromDDL :1; /* Comes from sqlite_schema */ unsigned isCte :1; /* This is a CTE */ unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ unsigned rowidUsed :1; /* The ROWID of this table is referenced */ unsigned fixedSchema :1; /* Uses u4.pSchema, not u4.zDatabase */ unsigned hadSchema :1; /* Had u4.zDatabase before u4.pSchema */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */ union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ u32 nRow; /* Number of rows in a VALUES clause */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */ } u2; union { Expr *pOn; /* fg.isUsing==0 => The ON clause of a join */ IdList *pUsing; /* fg.isUsing==1 => The USING clause of a join */ } u3; union { Schema *pSchema; /* Schema to which this item is fixed */ char *zDatabase; /* Name of database holding this table */ Subquery *pSubq; /* Description of a subquery */ } u4; }; /* ** The OnOrUsing object represents either an ON clause or a USING clause. ** It can never be both at the same time, but it can be neither. */ struct OnOrUsing { |
︙ | ︙ | |||
19584 19585 19586 19587 19588 19589 19590 | #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ #define SF_Correlated 0x20000000 /* True if references the outer context */ | | | > > | 19741 19742 19743 19744 19745 19746 19747 19748 19749 19750 19751 19752 19753 19754 19755 19756 19757 19758 | #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ #define SF_Correlated 0x20000000 /* True if references the outer context */ /* True if SrcItem X is a subquery that has SF_NestedFrom */ #define IsNestedFrom(X) \ ((X)->fg.isSubquery && \ ((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0) /* ** The results of a SELECT can be distributed in several ways, as defined ** by one of the following macros. The "SRT" prefix means "SELECT Result ** Type". ** ** SRT_Union Store results as a key in a temporary index |
︙ | ︙ | |||
20977 20978 20979 20980 20981 20982 20983 20984 20985 20986 20987 20988 20989 20990 | #endif SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, OnOrUsing*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *); SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); | > > > | 21136 21137 21138 21139 21140 21141 21142 21143 21144 21145 21146 21147 21148 21149 21150 21151 21152 | #endif SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendList(Parse *pParse, SrcList *p1, SrcList *p2); SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3*,Subquery*); SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3*,SrcItem*); SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery(Parse*, SrcItem*, Select*, int); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, OnOrUsing*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, SrcItem *); SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); |
︙ | ︙ | |||
22202 22203 22204 22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 | "ENABLE_NORMALIZE", #endif #ifdef SQLITE_ENABLE_NULL_TRIM "ENABLE_NULL_TRIM", #endif #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC "ENABLE_OFFSET_SQL_FUNC", #endif #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK "ENABLE_PREUPDATE_HOOK", #endif | > > > | 22364 22365 22366 22367 22368 22369 22370 22371 22372 22373 22374 22375 22376 22377 22378 22379 22380 | "ENABLE_NORMALIZE", #endif #ifdef SQLITE_ENABLE_NULL_TRIM "ENABLE_NULL_TRIM", #endif #ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC "ENABLE_OFFSET_SQL_FUNC", #endif #ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES "ENABLE_ORDERED_SET_AGGREGATES", #endif #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK "ENABLE_PREUPDATE_HOOK", #endif |
︙ | ︙ | |||
24519 24520 24521 24522 24523 24524 24525 | datetimeError(p); return; } if( M<=2 ){ Y--; M += 12; } | | | | 24684 24685 24686 24687 24688 24689 24690 24691 24692 24693 24694 24695 24696 24697 24698 24699 | datetimeError(p); return; } if( M<=2 ){ Y--; M += 12; } A = (Y+4800)/100; B = 38 - A + (A/4); X1 = 36525*(Y+4716)/100; X2 = 306001*(M+1)/10000; p->iJD = (sqlite3_int64)((X1 + X2 + D + B - 1524.5 ) * 86400000); p->validJD = 1; if( p->validHMS ){ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); if( p->tz ){ |
︙ | ︙ | |||
24704 24705 24706 24707 24708 24709 24710 | return iJD>=0 && iJD<=INT_464269060799999; } /* ** Compute the Year, Month, and Day from the julian day number. */ static void computeYMD(DateTime *p){ | | | | | 24869 24870 24871 24872 24873 24874 24875 24876 24877 24878 24879 24880 24881 24882 24883 24884 24885 24886 24887 24888 24889 24890 24891 24892 24893 24894 24895 | return iJD>=0 && iJD<=INT_464269060799999; } /* ** Compute the Year, Month, and Day from the julian day number. */ static void computeYMD(DateTime *p){ int Z, alpha, A, B, C, D, E, X1; if( p->validYMD ) return; if( !p->validJD ){ p->Y = 2000; p->M = 1; p->D = 1; }else if( !validJulianDay(p->iJD) ){ datetimeError(p); return; }else{ Z = (int)((p->iJD + 43200000)/86400000); alpha = (int)((Z + 32044.75)/36524.25) - 52; A = Z + 1 + alpha - ((alpha+100)/4) + 25; B = A + 1524; C = (int)((B - 122.1)/365.25); D = (36525*(C&32767))/100; E = (int)((B-D)/30.6001); X1 = (int)(30.6001*E); p->D = B - D - X1; p->M = E<14 ? E-1 : E-13; |
︙ | ︙ | |||
32015 32016 32017 32018 32019 32020 32021 | SrcItem *pItem; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; pItem = va_arg(ap, SrcItem*); assert( bArgList==0 ); if( pItem->zAlias && !flag_altform2 ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else if( pItem->zName ){ | | > > > | | | | | 32180 32181 32182 32183 32184 32185 32186 32187 32188 32189 32190 32191 32192 32193 32194 32195 32196 32197 32198 32199 32200 32201 32202 32203 32204 32205 32206 | SrcItem *pItem; if( (pAccum->printfFlags & SQLITE_PRINTF_INTERNAL)==0 ) return; pItem = va_arg(ap, SrcItem*); assert( bArgList==0 ); if( pItem->zAlias && !flag_altform2 ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else if( pItem->zName ){ if( pItem->fg.fixedSchema==0 && pItem->fg.isSubquery==0 && pItem->u4.zDatabase!=0 ){ sqlite3_str_appendall(pAccum, pItem->u4.zDatabase); sqlite3_str_append(pAccum, ".", 1); } sqlite3_str_appendall(pAccum, pItem->zName); }else if( pItem->zAlias ){ sqlite3_str_appendall(pAccum, pItem->zAlias); }else if( ALWAYS(pItem->fg.isSubquery) ){/* Because of tag-20240424-1 */ Select *pSel = pItem->u4.pSubq->pSelect; assert( pSel!=0 ); if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); }else if( pSel->selFlags & SF_MultiValue ){ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", pItem->u1.nRow); }else{ |
︙ | ︙ | |||
32806 32807 32808 32809 32810 32811 32812 | const SrcItem *pItem = &pSrc->a[i]; StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); | | | | 32974 32975 32976 32977 32978 32979 32980 32981 32982 32983 32984 32985 32986 32987 32988 32989 32990 | const SrcItem *pItem = &pSrc->a[i]; StrAccum x; int n = 0; char zLine[1000]; sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pSTab ){ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", pItem->pSTab->zName, pItem->pSTab->nCol, pItem->pSTab, pItem->colUsed, pItem->fg.rowidUsed ? "+rowid" : ""); } if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); }else if( pItem->fg.jointype & JT_LEFT ){ sqlite3_str_appendf(&x, " LEFT-JOIN"); |
︙ | ︙ | |||
32839 32840 32841 32842 32843 32844 32845 32846 32847 32848 32849 | } 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; | > > > | | > | | | > > > | | 33007 33008 33009 33010 33011 33012 33013 33014 33015 33016 33017 33018 33019 33020 33021 33022 33023 33024 33025 33026 33027 33028 33029 33030 33031 33032 33033 33034 33035 33036 33037 33038 33039 33040 33041 33042 33043 33044 | } 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"); if( pItem->fg.fixedSchema ) sqlite3_str_appendf(&x, " fixedSchema"); if( pItem->fg.hadSchema ) sqlite3_str_appendf(&x, " hadSchema"); if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery"); sqlite3StrAccumFinish(&x); sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1); n = 0; if( pItem->fg.isSubquery ) n++; if( pItem->fg.isTabFunc ) n++; if( pItem->fg.isUsing ) n++; if( pItem->fg.isUsing ){ sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->fg.isSubquery ){ assert( n==1 ); if( pItem->pSTab ){ Table *pTab = pItem->pSTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); sqlite3TreeViewPush(&pView, 0); sqlite3TreeViewLine(pView, "SUBQUERY"); sqlite3TreeViewPop(&pView); sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(&pView); } } |
︙ | ︙ | |||
38719 38720 38721 38722 38723 38724 38725 | /* ** Allowed values for the unixFile.ctrlFlags bitmask: */ #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ #define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ | | | 38894 38895 38896 38897 38898 38899 38900 38901 38902 38903 38904 38905 38906 38907 38908 | /* ** Allowed values for the unixFile.ctrlFlags bitmask: */ #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ #define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ #if !defined(SQLITE_DISABLE_DIRSYNC) && !defined(_AIX) # define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ #else # define UNIXFILE_DIRSYNC 0x00 #endif #define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ |
︙ | ︙ | |||
76571 76572 76573 76574 76575 76576 76577 | *pRes = c; return SQLITE_OK; /* Cursor already pointing at the correct spot */ } if( pCur->iPage>0 && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 && pIdxKey->errCode==SQLITE_OK ){ | | | 76746 76747 76748 76749 76750 76751 76752 76753 76754 76755 76756 76757 76758 76759 76760 | *pRes = c; return SQLITE_OK; /* Cursor already pointing at the correct spot */ } if( pCur->iPage>0 && indexCellCompare(pCur, 0, pIdxKey, xRecordCompare)<=0 && pIdxKey->errCode==SQLITE_OK ){ pCur->curFlags &= ~(BTCF_ValidOvfl|BTCF_AtLast); if( !pCur->pPage->isInit ){ return SQLITE_CORRUPT_BKPT; } goto bypass_moveto_root; /* Start search on the current page */ } pIdxKey->errCode = SQLITE_OK; } |
︙ | ︙ | |||
95265 95266 95267 95268 95269 95270 95271 | sqlite3VdbeMemRealify(pIn1); REGISTER_TRACE(pOp->p1, pIn1); } break; } #endif | | | 95440 95441 95442 95443 95444 95445 95446 95447 95448 95449 95450 95451 95452 95453 95454 | sqlite3VdbeMemRealify(pIn1); REGISTER_TRACE(pOp->p1, pIn1); } break; } #endif #if !defined(SQLITE_OMIT_CAST) || !defined(SQLITE_OMIT_ANALYZE) /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** ** Force the value in register P1 to be the type defined by P2. ** ** <ul> ** <li> P2=='A' → BLOB |
︙ | ︙ | |||
102545 102546 102547 102548 102549 102550 102551 102552 102553 102554 102555 102556 102557 102558 | pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable); } if( pTab && !HasRowid(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } #ifndef SQLITE_OMIT_VIEW if( pTab && IsView(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); } #endif if( !pTab ){ | > > > > > | 102720 102721 102722 102723 102724 102725 102726 102727 102728 102729 102730 102731 102732 102733 102734 102735 102736 102737 102738 | pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open virtual table: %s", zTable); } if( pTab && !HasRowid(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable); } if( pTab && (pTab->tabFlags&TF_HasGenerated)!=0 ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open table with generated columns: %s", zTable); } #ifndef SQLITE_OMIT_VIEW if( pTab && IsView(pTab) ){ pTab = 0; sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); } #endif if( !pTab ){ |
︙ | ︙ | |||
106729 106730 106731 106732 106733 106734 106735 | SrcList *pSrc; int i; SrcItem *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ | > | > | 106909 106910 106911 106912 106913 106914 106915 106916 106917 106918 106919 106920 106921 106922 106923 106924 106925 | SrcList *pSrc; int i; SrcItem *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->fg.isSubquery && sqlite3WalkSelect(pWalker, pItem->u4.pSubq->pSelect) ){ return WRC_Abort; } if( pItem->fg.isTabFunc && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg) ){ return WRC_Abort; } |
︙ | ︙ | |||
107035 107036 107037 107038 107039 107040 107041 | SrcItem *pMatch, /* Source table containing the column */ i16 iColumn /* The column number */ ){ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); if( pNew ){ pNew->iTable = pMatch->iCursor; pNew->iColumn = iColumn; | | | 107217 107218 107219 107220 107221 107222 107223 107224 107225 107226 107227 107228 107229 107230 107231 | SrcItem *pMatch, /* Source table containing the column */ i16 iColumn /* The column number */ ){ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0); if( pNew ){ pNew->iTable = pMatch->iCursor; pNew->iColumn = iColumn; pNew->y.pTab = pMatch->pSTab; assert( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ); ExprSetProperty(pNew, EP_CanBeNull); *ppList = sqlite3ExprListAppend(pParse, *ppList, pNew); } } /* |
︙ | ︙ | |||
107166 107167 107168 107169 107170 107171 107172 | do{ ExprList *pEList; SrcList *pSrcList = pNC->pSrcList; if( pSrcList ){ for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ u8 hCol; | | | > > | > > | | 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 | do{ ExprList *pEList; SrcList *pSrcList = pNC->pSrcList; if( pSrcList ){ for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){ u8 hCol; pTab = pItem->pSTab; assert( pTab!=0 && pTab->zName!=0 ); assert( pTab->nCol>0 || pParse->nErr ); assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem)); if( pItem->fg.isNestedFrom ){ /* In this case, pItem is a subquery that has been formed from a ** parenthesized subset of the FROM clause terms. Example: ** .... FROM t1 LEFT JOIN (t2 RIGHT JOIN t3 USING(x)) USING(y) ... ** \_________________________/ ** This pItem -------------^ */ int hit = 0; Select *pSel; assert( pItem->fg.isSubquery ); assert( pItem->u4.pSubq!=0 ); pSel = pItem->u4.pSubq->pSelect; assert( pSel!=0 ); pEList = pSel->pEList; assert( pEList!=0 ); assert( pEList->nExpr==pTab->nCol ); for(j=0; j<pEList->nExpr; j++){ int bRowid = 0; /* True if possible rowid match */ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){ continue; } |
︙ | ︙ | |||
107303 107304 107305 107306 107307 107308 107309 | ** if there is a single VIEW candidate or if there is a single ** non-VIEW candidate plus multiple VIEW candidates. In other ** words non-VIEW candidate terms take precedence over VIEWs. */ if( cntTab==0 || (cntTab==1 && ALWAYS(pMatch!=0) | | | | | 107489 107490 107491 107492 107493 107494 107495 107496 107497 107498 107499 107500 107501 107502 107503 107504 107505 107506 107507 107508 107509 107510 107511 107512 107513 107514 107515 107516 107517 107518 107519 107520 107521 107522 107523 107524 107525 | ** if there is a single VIEW candidate or if there is a single ** non-VIEW candidate plus multiple VIEW candidates. In other ** words non-VIEW candidate terms take precedence over VIEWs. */ if( cntTab==0 || (cntTab==1 && ALWAYS(pMatch!=0) && ALWAYS(pMatch->pSTab!=0) && (pMatch->pSTab->tabFlags & TF_Ephemeral)!=0 && (pTab->tabFlags & TF_Ephemeral)==0) ){ cntTab = 1; pMatch = pItem; }else{ cntTab++; } #else /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is ** simpler since we require exactly one candidate, which will ** always be a non-VIEW */ cntTab++; pMatch = pItem; #endif } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pMatch->pSTab; if( (pMatch->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } pSchema = pExpr->y.pTab->pSchema; } } /* if( pSrcList ) */ |
︙ | ︙ | |||
107367 107368 107369 107370 107371 107372 107373 | } } #endif /* SQLITE_OMIT_TRIGGER */ #ifndef SQLITE_OMIT_UPSERT if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ | | | 107553 107554 107555 107556 107557 107558 107559 107560 107561 107562 107563 107564 107565 107566 107567 | } } #endif /* SQLITE_OMIT_TRIGGER */ #ifndef SQLITE_OMIT_UPSERT if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){ Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ pTab = pUpsert->pUpsertSrc->a[0].pSTab; pExpr->iTable = EXCLUDED_TABLE_NUMBER; } } #endif /* SQLITE_OMIT_UPSERT */ if( pTab ){ int iCol; |
︙ | ︙ | |||
107450 107451 107452 107453 107454 107455 107456 | ** Perhaps the name is a reference to the ROWID */ if( cnt==0 && cntTab>=1 && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) | | | | 107636 107637 107638 107639 107640 107641 107642 107643 107644 107645 107646 107647 107648 107649 107650 107651 107652 107653 107654 | ** Perhaps the name is a reference to the ROWID */ if( cnt==0 && cntTab>=1 && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) && ALWAYS(VisibleRowid(pMatch->pSTab) || pMatch->fg.isNestedFrom) ){ cnt = cntTab; #if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 if( pMatch->pSTab!=0 && IsView(pMatch->pSTab) ){ eNewExprOp = TK_NULL; } #endif if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } |
︙ | ︙ | |||
107691 107692 107693 107694 107695 107696 107697 | */ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ SrcItem *pItem = &pSrc->a[iSrc]; Table *pTab; assert( ExprUseYTab(p) ); | | | 107877 107878 107879 107880 107881 107882 107883 107884 107885 107886 107887 107888 107889 107890 107891 | */ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); if( p ){ SrcItem *pItem = &pSrc->a[iSrc]; Table *pTab; assert( ExprUseYTab(p) ); pTab = p->y.pTab = pItem->pSTab; p->iTable = pItem->iCursor; if( p->y.pTab->iPKey==iCol ){ p->iColumn = -1; }else{ p->iColumn = (ynVar)iCol; if( (pTab->tabFlags & TF_HasGenerated)!=0 && (pTab->aCol[iCol].colFlags & COLFLAG_GENERATED)!=0 |
︙ | ︙ | |||
107810 107811 107812 107813 107814 107815 107816 | case TK_ROW: { SrcList *pSrcList = pNC->pSrcList; SrcItem *pItem; assert( pSrcList && pSrcList->nSrc>=1 ); pItem = pSrcList->a; pExpr->op = TK_COLUMN; assert( ExprUseYTab(pExpr) ); | | | 107996 107997 107998 107999 108000 108001 108002 108003 108004 108005 108006 108007 108008 108009 108010 | case TK_ROW: { SrcList *pSrcList = pNC->pSrcList; SrcItem *pItem; assert( pSrcList && pSrcList->nSrc>=1 ); pItem = pSrcList->a; pExpr->op = TK_COLUMN; assert( ExprUseYTab(pExpr) ); pExpr->y.pTab = pItem->pSTab; pExpr->iTable = pItem->iCursor; pExpr->iColumn--; pExpr->affExpr = SQLITE_AFF_INTEGER; break; } /* An optimization: Attempt to convert |
︙ | ︙ | |||
108116 108117 108118 108119 108120 108121 108122 | if( is_agg ){ if( pExpr->pLeft ){ assert( pExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pExpr->pLeft) ); sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); } #ifndef SQLITE_OMIT_WINDOWFUNC | | | | 108302 108303 108304 108305 108306 108307 108308 108309 108310 108311 108312 108313 108314 108315 108316 108317 108318 | if( is_agg ){ if( pExpr->pLeft ){ assert( pExpr->pLeft->op==TK_ORDER ); assert( ExprUseXList(pExpr->pLeft) ); sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); } #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin && pParse->nErr==0 ){ Select *pSel = pNC->pWinSelect; assert( ExprUseYWin(pExpr) && pWin==pExpr->y.pWin ); if( IN_RENAME_OBJECT==0 ){ sqlite3WindowUpdate(pParse, pSel ? pSel->pWinDefn : 0, pWin, pDef); if( pParse->db->mallocFailed ) break; } sqlite3WalkExprList(pWalker, pWin->pPartition); sqlite3WalkExprList(pWalker, pWin->pOrderBy); sqlite3WalkExpr(pWalker, pWin->pFilter); |
︙ | ︙ | |||
108700 108701 108702 108703 108704 108705 108706 | /* If the SF_Converted flags is set, then this Select object was ** was created by the convertCompoundSelectToSubquery() function. ** In this case the ORDER BY clause (p->pOrderBy) should be resolved ** as if it were part of the sub-query, not the parent. This block ** moves the pOrderBy down to the sub-query. It will be moved back ** after the names have been resolved. */ if( p->selFlags & SF_Converted ){ | | > > > > | > > | > | | 108886 108887 108888 108889 108890 108891 108892 108893 108894 108895 108896 108897 108898 108899 108900 108901 108902 108903 108904 108905 108906 108907 108908 108909 108910 108911 108912 108913 108914 108915 108916 108917 108918 108919 108920 108921 108922 108923 108924 108925 | /* If the SF_Converted flags is set, then this Select object was ** was created by the convertCompoundSelectToSubquery() function. ** In this case the ORDER BY clause (p->pOrderBy) should be resolved ** as if it were part of the sub-query, not the parent. This block ** moves the pOrderBy down to the sub-query. It will be moved back ** after the names have been resolved. */ if( p->selFlags & SF_Converted ){ Select *pSub; assert( p->pSrc->a[0].fg.isSubquery ); assert( p->pSrc->a[0].u4.pSubq!=0 ); pSub = p->pSrc->a[0].u4.pSubq->pSelect; assert( pSub!=0 ); assert( p->pSrc->nSrc==1 && p->pOrderBy ); assert( pSub->pPrior && pSub->pOrderBy==0 ); pSub->pOrderBy = p->pOrderBy; p->pOrderBy = 0; } /* Recursively resolve names in all subqueries in the FROM clause */ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; i<p->pSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; assert( pItem->zName!=0 || pItem->fg.isSubquery ); /* Test of tag-20240424-1*/ if( pItem->fg.isSubquery && (pItem->u4.pSubq->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->u4.pSubq->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; if( pParse->nErr ) return WRC_Abort; assert( db->mallocFailed==0 ); /* If the number of references to the outer context changed when ** expressions in the sub-select were resolved, the sub-select ** is correlated. It is not required to check the refcount on any |
︙ | ︙ | |||
108820 108821 108822 108823 108824 108825 108826 | /* If this is a converted compound query, move the ORDER BY clause from ** the sub-query back to the parent query. At this point each term ** within the ORDER BY clause has been transformed to an integer value. ** These integers will be replaced by copies of the corresponding result ** set expressions by the call to resolveOrderGroupBy() below. */ if( p->selFlags & SF_Converted ){ | | > > > | 109013 109014 109015 109016 109017 109018 109019 109020 109021 109022 109023 109024 109025 109026 109027 109028 109029 109030 | /* If this is a converted compound query, move the ORDER BY clause from ** the sub-query back to the parent query. At this point each term ** within the ORDER BY clause has been transformed to an integer value. ** These integers will be replaced by copies of the corresponding result ** set expressions by the call to resolveOrderGroupBy() below. */ if( p->selFlags & SF_Converted ){ Select *pSub; assert( p->pSrc->a[0].fg.isSubquery ); pSub = p->pSrc->a[0].u4.pSubq->pSelect; assert( pSub!=0 ); p->pOrderBy = pSub->pOrderBy; pSub->pOrderBy = 0; } /* Process the ORDER BY clause for singleton SELECT statements. ** The ORDER BY clause for compounds SELECT statements is handled ** below, after all of the result-sets for all of the elements of |
︙ | ︙ | |||
109087 109088 109089 109090 109091 109092 109093 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr || type==NC_GenCol || pTab==0 ); memset(&sNC, 0, sizeof(sNC)); memset(&sSrc, 0, sizeof(sSrc)); if( pTab ){ sSrc.nSrc = 1; sSrc.a[0].zName = pTab->zName; | | | 109283 109284 109285 109286 109287 109288 109289 109290 109291 109292 109293 109294 109295 109296 109297 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr || type==NC_GenCol || pTab==0 ); memset(&sNC, 0, sizeof(sNC)); memset(&sSrc, 0, sizeof(sSrc)); if( pTab ){ sSrc.nSrc = 1; sSrc.a[0].zName = pTab->zName; sSrc.a[0].pSTab = pTab; sSrc.a[0].iCursor = -1; if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP ** schema elements */ type |= NC_FromDDL; } } |
︙ | ︙ | |||
110984 110985 110986 110987 110988 110989 110990 | pNew = sqlite3DbMallocRawNN(db, nByte ); if( pNew==0 ) return 0; pNew->nSrc = pNew->nAlloc = p->nSrc; for(i=0; i<p->nSrc; i++){ SrcItem *pNewItem = &pNew->a[i]; const SrcItem *pOldItem = &p->a[i]; Table *pTab; | > > > > > > > > > > > > > > > > > | > | > < < < < | < | 111180 111181 111182 111183 111184 111185 111186 111187 111188 111189 111190 111191 111192 111193 111194 111195 111196 111197 111198 111199 111200 111201 111202 111203 111204 111205 111206 111207 111208 111209 111210 111211 111212 111213 111214 111215 111216 111217 111218 111219 111220 111221 111222 111223 111224 111225 111226 111227 111228 111229 111230 111231 111232 111233 | pNew = sqlite3DbMallocRawNN(db, nByte ); if( pNew==0 ) return 0; pNew->nSrc = pNew->nAlloc = p->nSrc; for(i=0; i<p->nSrc; i++){ SrcItem *pNewItem = &pNew->a[i]; const SrcItem *pOldItem = &p->a[i]; Table *pTab; pNewItem->fg = pOldItem->fg; if( pOldItem->fg.isSubquery ){ Subquery *pNewSubq = sqlite3DbMallocRaw(db, sizeof(Subquery)); if( pNewSubq==0 ){ assert( db->mallocFailed ); pNewItem->fg.isSubquery = 0; }else{ memcpy(pNewSubq, pOldItem->u4.pSubq, sizeof(*pNewSubq)); pNewSubq->pSelect = sqlite3SelectDup(db, pNewSubq->pSelect, flags); if( pNewSubq->pSelect==0 ){ sqlite3DbFree(db, pNewSubq); pNewSubq = 0; pNewItem->fg.isSubquery = 0; } } pNewItem->u4.pSubq = pNewSubq; }else if( pOldItem->fg.fixedSchema ){ pNewItem->u4.pSchema = pOldItem->u4.pSchema; }else{ pNewItem->u4.zDatabase = sqlite3DbStrDup(db, pOldItem->u4.zDatabase); } pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); pNewItem->iCursor = pOldItem->iCursor; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); }else if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); }else{ pNewItem->u1.nRow = pOldItem->u1.nRow; } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } pTab = pNewItem->pSTab = pOldItem->pSTab; if( pTab ){ pTab->nTabRef++; } if( pOldItem->fg.isUsing ){ assert( pNewItem->fg.isUsing ); pNewItem->u3.pUsing = sqlite3IdListDup(db, pOldItem->u3.pUsing); }else{ pNewItem->u3.pOn = sqlite3ExprDup(db, pOldItem->u3.pOn, flags); } pNewItem->colUsed = pOldItem->colUsed; |
︙ | ︙ | |||
111083 111084 111085 111086 111087 111088 111089 | sqlite3SelectDelete(db, pNew); break; } *pp = pNew; pp = &pNew->pPrior; pNext = pNew; } | < | 111293 111294 111295 111296 111297 111298 111299 111300 111301 111302 111303 111304 111305 111306 | sqlite3SelectDelete(db, pNew); break; } *pp = pNew; pp = &pNew->pPrior; pNext = pNew; } return pRet; } #else SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags){ assert( p==0 ); return 0; } |
︙ | ︙ | |||
112103 112104 112105 112106 112107 112108 112109 | } assert( p->pGroupBy==0 ); /* Has no GROUP BY clause */ if( p->pLimit ) return 0; /* Has no LIMIT clause */ if( p->pWhere ) return 0; /* Has no WHERE clause */ pSrc = p->pSrc; assert( pSrc!=0 ); if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ | | | | 112312 112313 112314 112315 112316 112317 112318 112319 112320 112321 112322 112323 112324 112325 112326 112327 | } assert( p->pGroupBy==0 ); /* Has no GROUP BY clause */ if( p->pLimit ) return 0; /* Has no LIMIT clause */ if( p->pWhere ) return 0; /* Has no WHERE clause */ pSrc = p->pSrc; assert( pSrc!=0 ); if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */ if( pSrc->a[0].fg.isSubquery) return 0;/* FROM is not a subquery or view */ pTab = pSrc->a[0].pSTab; assert( pTab!=0 ); assert( !IsView(pTab) ); /* FROM clause is not a view */ if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */ pEList = p->pEList; assert( pEList!=0 ); /* All SELECT results must be columns. */ for(i=0; i<pEList->nExpr; i++){ |
︙ | ︙ | |||
112287 112288 112289 112290 112291 112292 112293 | int iDb; /* Database idx for pTab */ ExprList *pEList = p->pEList; int nExpr = pEList->nExpr; assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ | | | 112496 112497 112498 112499 112500 112501 112502 112503 112504 112505 112506 112507 112508 112509 112510 | int iDb; /* Database idx for pTab */ ExprList *pEList = p->pEList; int nExpr = pEList->nExpr; assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */ pTab = p->pSrc->a[0].pSTab; /* Code an OP_Transaction and OP_TableLock for <table>. */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 && iDb<SQLITE_MAX_DB ); sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); |
︙ | ︙ | |||
117725 117726 117727 117728 117729 117730 117731 | pSel->pSrc = 0; sqlite3SelectDelete(db, pSel); } if( pStep->pFrom ){ int i; for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){ SrcItem *p = &pStep->pFrom->a[i]; | | > | | 117934 117935 117936 117937 117938 117939 117940 117941 117942 117943 117944 117945 117946 117947 117948 117949 117950 | pSel->pSrc = 0; sqlite3SelectDelete(db, pSel); } if( pStep->pFrom ){ int i; for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){ SrcItem *p = &pStep->pFrom->a[i]; if( p->fg.isSubquery ){ assert( p->u4.pSubq!=0 ); sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); } } } if( db->mallocFailed ){ rc = SQLITE_NOMEM; } |
︙ | ︙ | |||
117794 117795 117796 117797 117798 117799 117800 | sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget); sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet); sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); } if( pStep->pFrom ){ int i; | > | > > | > | 118004 118005 118006 118007 118008 118009 118010 118011 118012 118013 118014 118015 118016 118017 118018 118019 118020 118021 118022 118023 | sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget); sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet); sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); } if( pStep->pFrom ){ int i; SrcList *pFrom = pStep->pFrom; for(i=0; i<pFrom->nSrc; i++){ if( pFrom->a[i].fg.isSubquery ){ assert( pFrom->a[i].u4.pSubq!=0 ); sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect); } } } } } /* ** Free the contents of Parse object (*pParse). Do not free the memory |
︙ | ︙ | |||
118042 118043 118044 118045 118046 118047 118048 | } if( NEVER(pSrc==0) ){ assert( pWalker->pParse->db->mallocFailed ); return WRC_Abort; } for(i=0; i<pSrc->nSrc; i++){ SrcItem *pItem = &pSrc->a[i]; | | | 118256 118257 118258 118259 118260 118261 118262 118263 118264 118265 118266 118267 118268 118269 118270 | } if( NEVER(pSrc==0) ){ assert( pWalker->pParse->db->mallocFailed ); return WRC_Abort; } for(i=0; i<pSrc->nSrc; i++){ SrcItem *pItem = &pSrc->a[i]; if( pItem->pSTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } } renameWalkWith(pWalker, pSelect); return WRC_Continue; } |
︙ | ︙ | |||
121176 121177 121178 121179 121180 121181 121182 | SrcItem *pItem; sqlite3 *db = pFix->pParse->db; int iDb = sqlite3FindDbName(db, pFix->zDb); SrcList *pList = pSelect->pSrc; if( NEVER(pList==0) ) return WRC_Continue; for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){ | | | | | | < > | > | 121390 121391 121392 121393 121394 121395 121396 121397 121398 121399 121400 121401 121402 121403 121404 121405 121406 121407 121408 121409 121410 121411 121412 121413 121414 121415 121416 121417 121418 | SrcItem *pItem; sqlite3 *db = pFix->pParse->db; int iDb = sqlite3FindDbName(db, pFix->zDb); SrcList *pList = pSelect->pSrc; if( NEVER(pList==0) ) return WRC_Continue; for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){ if( pFix->bTemp==0 && pItem->fg.isSubquery==0 ){ if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ if( iDb!=sqlite3FindDbName(db, pItem->u4.zDatabase) ){ sqlite3ErrorMsg(pFix->pParse, "%s %T cannot reference objects in database %s", pFix->zType, pFix->pName, pItem->u4.zDatabase); return WRC_Abort; } sqlite3DbFree(db, pItem->u4.zDatabase); pItem->fg.notCte = 1; pItem->fg.hadSchema = 1; } pItem->u4.pSchema = pFix->pSchema; pItem->fg.fromDDL = 1; pItem->fg.fixedSchema = 1; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) if( pList->a[i].fg.isUsing==0 && sqlite3WalkExpr(&pFix->w, pList->a[i].u3.pOn) ){ return WRC_Abort; } |
︙ | ︙ | |||
121482 121483 121484 121485 121486 121487 121488 | if( pExpr->op==TK_TRIGGER ){ pTab = pParse->pTriggerTab; }else{ assert( pTabList ); for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){ if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ | | | 121697 121698 121699 121700 121701 121702 121703 121704 121705 121706 121707 121708 121709 121710 121711 | if( pExpr->op==TK_TRIGGER ){ pTab = pParse->pTriggerTab; }else{ assert( pTabList ); for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){ if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ pTab = pTabList->a[iSrc].pSTab; break; } } } iCol = pExpr->iColumn; if( pTab==0 ) return; |
︙ | ︙ | |||
122085 122086 122087 122088 122089 122090 122091 | */ SQLITE_PRIVATE Table *sqlite3LocateTableItem( Parse *pParse, u32 flags, SrcItem *p ){ const char *zDb; | < | | > | | 122300 122301 122302 122303 122304 122305 122306 122307 122308 122309 122310 122311 122312 122313 122314 122315 122316 122317 122318 122319 | */ SQLITE_PRIVATE Table *sqlite3LocateTableItem( Parse *pParse, u32 flags, SrcItem *p ){ const char *zDb; if( p->fg.fixedSchema ){ int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema); zDb = pParse->db->aDb[iDb].zDbSName; }else{ assert( !p->fg.isSubquery ); zDb = p->u4.zDatabase; } return sqlite3LocateTable(pParse, flags, p->zName, zDb); } /* ** Return the preferred table name for system tables. Translate legacy ** names into the new preferred names, as appropriate. |
︙ | ︙ | |||
125075 125076 125077 125078 125079 125080 125081 125082 125083 125084 125085 125086 125087 125088 125089 | int iDb; if( db->mallocFailed ){ goto exit_drop_table; } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; assert( isView==0 || isView==LOCATE_VIEW ); pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; if( pTab==0 ){ if( noErr ){ | > > | | 125290 125291 125292 125293 125294 125295 125296 125297 125298 125299 125300 125301 125302 125303 125304 125305 125306 125307 125308 125309 125310 125311 125312 125313 125314 | int iDb; if( db->mallocFailed ){ goto exit_drop_table; } assert( pParse->nErr==0 ); assert( pName->nSrc==1 ); assert( pName->a[0].fg.fixedSchema==0 ); assert( pName->a[0].fg.isSubquery==0 ); if( sqlite3ReadSchema(pParse) ) goto exit_drop_table; if( noErr ) db->suppressErr++; assert( isView==0 || isView==LOCATE_VIEW ); pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]); if( noErr ) db->suppressErr--; if( pTab==0 ){ if( noErr ){ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); sqlite3ForceNotReadOnly(pParse); } goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 && iDb<db->nDb ); |
︙ | ︙ | |||
126174 126175 126176 126177 126178 126179 126180 126181 126182 126183 | int iDb; if( db->mallocFailed ){ goto exit_drop_index; } assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto exit_drop_index; } | > > | | | 126391 126392 126393 126394 126395 126396 126397 126398 126399 126400 126401 126402 126403 126404 126405 126406 126407 126408 126409 126410 126411 126412 126413 126414 126415 | int iDb; if( db->mallocFailed ){ goto exit_drop_index; } assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); assert( pName->a[0].fg.fixedSchema==0 ); assert( pName->a[0].fg.isSubquery==0 ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto exit_drop_index; } pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].u4.zDatabase); if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName->a); }else{ sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].u4.zDatabase); sqlite3ForceNotReadOnly(pParse); } pParse->checkSchema = 1; goto exit_drop_index; } if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){ sqlite3ErrorMsg(pParse, "index associated with UNIQUE " |
︙ | ︙ | |||
126479 126480 126481 126482 126483 126484 126485 126486 126487 | pList = pNew; } } pItem = &pList->a[pList->nSrc-1]; if( pDatabase && pDatabase->z==0 ){ pDatabase = 0; } if( pDatabase ){ pItem->zName = sqlite3NameFromToken(db, pDatabase); | > > | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 126756 126757 126758 126759 126760 126761 126762 126763 126764 126765 126766 126767 126768 126769 126770 126771 126772 126773 126774 126775 126776 126777 126778 126779 126780 126781 126782 126783 126784 126785 126786 126787 126788 126789 126790 126791 126792 126793 126794 126795 126796 126797 126798 126799 126800 126801 126802 126803 126804 126805 126806 126807 126808 126809 126810 126811 126812 126813 126814 126815 126816 126817 126818 126819 126820 126821 126822 126823 126824 126825 126826 126827 126828 126829 126830 126831 126832 126833 126834 126835 126836 126837 126838 126839 126840 126841 126842 126843 126844 126845 126846 126847 126848 126849 126850 126851 | pList = pNew; } } pItem = &pList->a[pList->nSrc-1]; if( pDatabase && pDatabase->z==0 ){ pDatabase = 0; } assert( pItem->fg.fixedSchema==0 ); assert( pItem->fg.isSubquery==0 ); if( pDatabase ){ pItem->zName = sqlite3NameFromToken(db, pDatabase); pItem->u4.zDatabase = sqlite3NameFromToken(db, pTable); }else{ pItem->zName = sqlite3NameFromToken(db, pTable); pItem->u4.zDatabase = 0; } return pList; } /* ** Assign VdbeCursor index numbers to all tables in a SrcList */ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){ int i; SrcItem *pItem; assert( pList || pParse->db->mallocFailed ); if( ALWAYS(pList) ){ for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){ if( pItem->iCursor>=0 ) continue; pItem->iCursor = pParse->nTab++; if( pItem->fg.isSubquery ){ assert( pItem->u4.pSubq!=0 ); assert( pItem->u4.pSubq->pSelect!=0 ); assert( pItem->u4.pSubq->pSelect->pSrc!=0 ); sqlite3SrcListAssignCursors(pParse, pItem->u4.pSubq->pSelect->pSrc); } } } } /* ** Delete a Subquery object and its substructure. */ SQLITE_PRIVATE void sqlite3SubqueryDelete(sqlite3 *db, Subquery *pSubq){ assert( pSubq!=0 && pSubq->pSelect!=0 ); sqlite3SelectDelete(db, pSubq->pSelect); sqlite3DbFree(db, pSubq); } /* ** Remove a Subquery from a SrcItem. Return the associated Select object. ** The returned Select becomes the responsibility of the caller. */ SQLITE_PRIVATE Select *sqlite3SubqueryDetach(sqlite3 *db, SrcItem *pItem){ Select *pSel; assert( pItem!=0 ); assert( pItem->fg.isSubquery ); pSel = pItem->u4.pSubq->pSelect; sqlite3DbFree(db, pItem->u4.pSubq); pItem->u4.pSubq = 0; pItem->fg.isSubquery = 0; return pSel; } /* ** Delete an entire SrcList including all its substructure. */ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){ int i; SrcItem *pItem; assert( db!=0 ); if( pList==0 ) return; for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){ /* Check invariants on SrcItem */ assert( !pItem->fg.isIndexedBy || !pItem->fg.isTabFunc ); assert( !pItem->fg.isCte || !pItem->fg.isIndexedBy ); assert( !pItem->fg.fixedSchema || !pItem->fg.isSubquery ); assert( !pItem->fg.isSubquery || (pItem->u4.pSubq!=0 && pItem->u4.pSubq->pSelect!=0) ); if( pItem->zName ) sqlite3DbNNFreeNN(db, pItem->zName); if( pItem->zAlias ) sqlite3DbNNFreeNN(db, pItem->zAlias); if( pItem->fg.isSubquery ){ sqlite3SubqueryDelete(db, pItem->u4.pSubq); }else if( pItem->fg.fixedSchema==0 && pItem->u4.zDatabase!=0 ){ sqlite3DbNNFreeNN(db, pItem->u4.zDatabase); } if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlite3DeleteTable(db, pItem->pSTab); if( pItem->fg.isUsing ){ sqlite3IdListDelete(db, pItem->u3.pUsing); }else if( pItem->u3.pOn ){ sqlite3ExprDelete(db, pItem->u3.pOn); } } sqlite3DbNNFreeNN(db, pList); } /* ** Attach a Subquery object to pItem->uv.pSubq. Set the ** pSelect value but leave all the other values initialized ** to zero. ** ** A copy of the Select object is made if dupSelect is true, and the ** SrcItem takes responsibility for deleting the copy. If dupSelect is ** false, ownership of the Select passes to the SrcItem. Either way, ** the SrcItem will take responsibility for deleting the Select. ** ** When dupSelect is zero, that means the Select might get deleted right ** away if there is an OOM error. Beware. ** ** Return non-zero on success. Return zero on an OOM error. */ SQLITE_PRIVATE int sqlite3SrcItemAttachSubquery( Parse *pParse, /* Parsing context */ SrcItem *pItem, /* Item to which the subquery is to be attached */ Select *pSelect, /* The subquery SELECT. Must be non-NULL */ int dupSelect /* If true, attach a copy of pSelect, not pSelect itself.*/ ){ Subquery *p; assert( pSelect!=0 ); assert( pItem->fg.isSubquery==0 ); if( pItem->fg.fixedSchema ){ pItem->u4.pSchema = 0; pItem->fg.fixedSchema = 0; }else if( pItem->u4.zDatabase!=0 ){ sqlite3DbFree(pParse->db, pItem->u4.zDatabase); pItem->u4.zDatabase = 0; } if( dupSelect ){ pSelect = sqlite3SelectDup(pParse->db, pSelect, 0); if( pSelect==0 ) return 0; } p = pItem->u4.pSubq = sqlite3DbMallocRawNN(pParse->db, sizeof(Subquery)); if( p==0 ){ sqlite3SelectDelete(pParse->db, pSelect); return 0; } pItem->fg.isSubquery = 1; p->pSelect = pSelect; assert( offsetof(Subquery, pSelect)==0 ); memset(((char*)p)+sizeof(p->pSelect), 0, sizeof(*p)-sizeof(p->pSelect)); return 1; } /* ** This routine is called by the parser to add a new term to the ** end of a growing FROM clause. The "p" parameter is the part of ** the FROM clause that has already been constructed. "p" is NULL ** if this is the first term of the FROM clause. pTable and pDatabase ** are the name of the table and database named in the FROM clause term. |
︙ | ︙ | |||
126581 126582 126583 126584 126585 126586 126587 126588 | Token *pToken = (ALWAYS(pDatabase) && pDatabase->z) ? pDatabase : pTable; sqlite3RenameTokenMap(pParse, pItem->zName, pToken); } assert( pAlias!=0 ); if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); } if( pSubquery ){ | > | | | > | 126888 126889 126890 126891 126892 126893 126894 126895 126896 126897 126898 126899 126900 126901 126902 126903 126904 126905 126906 126907 | Token *pToken = (ALWAYS(pDatabase) && pDatabase->z) ? pDatabase : pTable; sqlite3RenameTokenMap(pParse, pItem->zName, pToken); } assert( pAlias!=0 ); if( pAlias->n ){ pItem->zAlias = sqlite3NameFromToken(db, pAlias); } assert( pSubquery==0 || pDatabase==0 ); if( pSubquery ){ if( sqlite3SrcItemAttachSubquery(pParse, pItem, pSubquery, 0) ){ if( pSubquery->selFlags & SF_NestedFrom ){ pItem->fg.isNestedFrom = 1; } } } assert( pOnUsing==0 || pOnUsing->pOn==0 || pOnUsing->pUsing==0 ); assert( pItem->fg.isUsing==0 ); if( pOnUsing==0 ){ pItem->u3.pOn = 0; }else if( pOnUsing->pUsing ){ |
︙ | ︙ | |||
127862 127863 127864 127865 127866 127867 127868 | ** the name of a single table, as one might find in an INSERT, DELETE, ** or UPDATE statement. Look up that table in the symbol table and ** return a pointer. Set an error message and return NULL if the table ** name is not found or if any other error occurs. ** ** The following fields are initialized appropriate in pSrc: ** | | | | | | 128171 128172 128173 128174 128175 128176 128177 128178 128179 128180 128181 128182 128183 128184 128185 128186 128187 128188 128189 128190 128191 128192 128193 128194 128195 | ** the name of a single table, as one might find in an INSERT, DELETE, ** or UPDATE statement. Look up that table in the symbol table and ** return a pointer. Set an error message and return NULL if the table ** name is not found or if any other error occurs. ** ** The following fields are initialized appropriate in pSrc: ** ** pSrc->a[0].spTab Pointer to the Table object ** pSrc->a[0].u2.pIBIndex Pointer to the INDEXED BY index, if there is one ** */ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ SrcItem *pItem = pSrc->a; Table *pTab; assert( pItem && pSrc->nSrc>=1 ); pTab = sqlite3LocateTableItem(pParse, 0, pItem); if( pItem->pSTab ) sqlite3DeleteTable(pParse->db, pItem->pSTab); pItem->pSTab = pTab; pItem->fg.notCte = 1; if( pTab ){ pTab->nTabRef++; if( pItem->fg.isIndexedBy && sqlite3IndexedByLookup(pParse, pItem) ){ pTab = 0; } } |
︙ | ︙ | |||
127994 127995 127996 127997 127998 127999 128000 | sqlite3 *db = pParse->db; int iDb = sqlite3SchemaToIndex(db, pView->pSchema); pWhere = sqlite3ExprDup(db, pWhere, 0); pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); | > | | 128303 128304 128305 128306 128307 128308 128309 128310 128311 128312 128313 128314 128315 128316 128317 128318 | sqlite3 *db = pParse->db; int iDb = sqlite3SchemaToIndex(db, pView->pSchema); pWhere = sqlite3ExprDup(db, pWhere, 0); pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); assert( pFrom->a[0].fg.fixedSchema==0 && pFrom->a[0].fg.isSubquery==0 ); pFrom->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); assert( pFrom->a[0].fg.isUsing==0 ); assert( pFrom->a[0].u3.pOn==0 ); } pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, pOrderBy, SF_IncludeHidden, pLimit); sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite3Select(pParse, pSel, &dest); |
︙ | ︙ | |||
128056 128057 128058 128059 128060 128061 128062 | ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ | | | 128366 128367 128368 128369 128370 128371 128372 128373 128374 128375 128376 128377 128378 128379 128380 | ** DELETE FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** becomes: ** DELETE FROM table_a WHERE rowid IN ( ** SELECT rowid FROM table_a WHERE col1=1 ORDER BY col2 LIMIT 1 OFFSET 1 ** ); */ pTab = pSrc->a[0].pSTab; if( HasRowid(pTab) ){ pLhs = sqlite3PExpr(pParse, TK_ROW, 0, 0); pEList = sqlite3ExprListAppend( pParse, 0, sqlite3PExpr(pParse, TK_ROW, 0, 0) ); }else{ Index *pPk = sqlite3PrimaryKeyIndex(pTab); |
︙ | ︙ | |||
128089 128090 128091 128092 128093 128094 128095 | pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0); } } } /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ | | | | 128399 128400 128401 128402 128403 128404 128405 128406 128407 128408 128409 128410 128411 128412 128413 128414 128415 | pLhs->x.pList = sqlite3ExprListDup(db, pEList, 0); } } } /* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree ** and the SELECT subtree. */ pSrc->a[0].pSTab = 0; pSelectSrc = sqlite3SrcListDup(db, pSrc, 0); pSrc->a[0].pSTab = pTab; if( pSrc->a[0].fg.isIndexedBy ){ assert( pSrc->a[0].fg.isCte==0 ); pSrc->a[0].u2.pIBIndex = 0; pSrc->a[0].fg.isIndexedBy = 0; sqlite3DbFree(db, pSrc->a[0].u1.zIndexedBy); }else if( pSrc->a[0].fg.isCte ){ pSrc->a[0].u2.pCteUse->nUse++; |
︙ | ︙ | |||
130918 130919 130920 130921 130922 130923 130924 | minMaxValueFinalize(context, 0); } /* ** group_concat(EXPR, ?SEPARATOR?) ** string_agg(EXPR, SEPARATOR) ** | > > > > | | 131228 131229 131230 131231 131232 131233 131234 131235 131236 131237 131238 131239 131240 131241 131242 131243 131244 131245 131246 | minMaxValueFinalize(context, 0); } /* ** group_concat(EXPR, ?SEPARATOR?) ** string_agg(EXPR, SEPARATOR) ** ** Content is accumulated in GroupConcatCtx.str with the SEPARATOR ** coming before the EXPR value, except for the first entry which ** omits the SEPARATOR. ** ** It is tragic that the SEPARATOR goes before the EXPR string. The ** groupConcatInverse() implementation would have been easier if the ** SEPARATOR were appended after EXPR. And the order is undocumented, ** so we could change it, in theory. But the old behavior has been ** around for so long that we dare not, for fear of breaking something. */ typedef struct { StrAccum str; /* The accumulated concatenation */ |
︙ | ︙ | |||
131022 131023 131024 131025 131026 131027 131028 | assert( argc==1 || argc==2 ); (void)argc; /* Suppress unused parameter warning */ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); /* pGCC is always non-NULL since groupConcatStep() will have always ** run first to initialize it */ if( ALWAYS(pGCC) ){ | | | 131336 131337 131338 131339 131340 131341 131342 131343 131344 131345 131346 131347 131348 131349 131350 | assert( argc==1 || argc==2 ); (void)argc; /* Suppress unused parameter warning */ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; pGCC = (GroupConcatCtx*)sqlite3_aggregate_context(context, sizeof(*pGCC)); /* pGCC is always non-NULL since groupConcatStep() will have always ** run first to initialize it */ if( ALWAYS(pGCC) ){ int nVS; /* Number of characters to remove */ /* Must call sqlite3_value_text() to convert the argument into text prior ** to invoking sqlite3_value_bytes(), in case the text encoding is UTF16 */ (void)sqlite3_value_text(argv[0]); nVS = sqlite3_value_bytes(argv[0]); pGCC->nAccum -= 1; if( pGCC->pnSepLengths!=0 ){ assert(pGCC->nAccum >= 0); |
︙ | ︙ | |||
132673 132674 132675 132676 132677 132678 132679 | assert( aiCol || pFKey->nCol==1 ); /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ SrcItem *pItem = pSrc->a; | | | | 132987 132988 132989 132990 132991 132992 132993 132994 132995 132996 132997 132998 132999 133000 133001 133002 133003 | assert( aiCol || pFKey->nCol==1 ); /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ SrcItem *pItem = pSrc->a; pItem->pSTab = pFKey->pFrom; pItem->zName = pFKey->pFrom->zName; pItem->pSTab->nTabRef++; pItem->iCursor = pParse->nTab++; if( regNew!=0 ){ fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); } if( regOld!=0 ){ int eAction = pFKey->aAction[aChange!=0]; |
︙ | ︙ | |||
132967 132968 132969 132970 132971 132972 132973 | 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); | > | | 133281 133282 133283 133284 133285 133286 133287 133288 133289 133290 133291 133292 133293 133294 133295 133296 | 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); assert( pSrc->a[0].fg.fixedSchema==0 && pSrc->a[0].fg.isSubquery==0 ); pSrc->a[0].u4.zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName); } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), pSrc, pWhere, 0, 0, 0, 0, 0 ); |
︙ | ︙ | |||
133701 133702 133703 133704 133705 133706 133707 | ** If argument pVal is a Select object returned by an sqlite3MultiValues() ** that was able to use the co-routine optimization, finish coding the ** co-routine. */ SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ SrcItem *pItem = &pVal->pSrc->a[0]; | > > | | > | 134016 134017 134018 134019 134020 134021 134022 134023 134024 134025 134026 134027 134028 134029 134030 134031 134032 134033 134034 | ** If argument pVal is a Select object returned by an sqlite3MultiValues() ** that was able to use the co-routine optimization, finish coding the ** co-routine. */ SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ SrcItem *pItem = &pVal->pSrc->a[0]; assert( (pItem->fg.isSubquery && pItem->u4.pSubq!=0) || pParse->nErr ); if( pItem->fg.isSubquery ){ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->u4.pSubq->regReturn); sqlite3VdbeJumpHere(pParse->pVdbe, pItem->u4.pSubq->addrFillSub - 1); } } } /* ** Return true if all expressions in the expression-list passed as the ** only argument are constant. */ |
︙ | ︙ | |||
133830 133831 133832 133833 133834 133835 133836 133837 133838 133839 133840 133841 133842 133843 133844 133845 | ** the correct text encoding. */ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ sqlite3ReadSchema(pParse); } if( pRet ){ SelectDest dest; pRet->pSrc->nSrc = 1; pRet->pPrior = pLeft->pPrior; pRet->op = pLeft->op; if( pRet->pPrior ) pRet->selFlags |= SF_Values; pLeft->pPrior = 0; pLeft->op = TK_SELECT; assert( pLeft->pNext==0 ); assert( pRet->pNext==0 ); p = &pRet->pSrc->a[0]; | > < < < > > > > > | > | | | | | | | | | | | | | > > > > > > > | | | | | 134148 134149 134150 134151 134152 134153 134154 134155 134156 134157 134158 134159 134160 134161 134162 134163 134164 134165 134166 134167 134168 134169 134170 134171 134172 134173 134174 134175 134176 134177 134178 134179 134180 134181 134182 134183 134184 134185 134186 134187 134188 134189 134190 134191 134192 134193 134194 134195 134196 134197 134198 134199 134200 134201 134202 134203 134204 134205 134206 134207 134208 134209 134210 134211 134212 134213 134214 134215 134216 134217 134218 | ** the correct text encoding. */ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ sqlite3ReadSchema(pParse); } if( pRet ){ SelectDest dest; Subquery *pSubq; pRet->pSrc->nSrc = 1; pRet->pPrior = pLeft->pPrior; pRet->op = pLeft->op; if( pRet->pPrior ) pRet->selFlags |= SF_Values; pLeft->pPrior = 0; pLeft->op = TK_SELECT; assert( pLeft->pNext==0 ); assert( pRet->pNext==0 ); p = &pRet->pSrc->a[0]; p->fg.viaCoroutine = 1; p->iCursor = -1; assert( !p->fg.isIndexedBy && !p->fg.isTabFunc ); p->u1.nRow = 2; if( sqlite3SrcItemAttachSubquery(pParse, p, pLeft, 0) ){ pSubq = p->u4.pSubq; pSubq->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; pSubq->regReturn = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, pSubq->addrFillSub); sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); /* Allocate registers for the output of the co-routine. Do so so ** that there are two unused registers immediately before those ** used by the co-routine. This allows the code in sqlite3Insert() ** to use these registers directly, instead of copying the output ** of the co-routine to a separate array for processing. */ dest.iSdst = pParse->nMem + 3; dest.nSdst = pLeft->pEList->nExpr; pParse->nMem += 2 + dest.nSdst; pLeft->selFlags |= SF_MultiValue; sqlite3Select(pParse, pLeft, &dest); pSubq->regResult = dest.iSdst; assert( pParse->nErr || dest.iSdst>0 ); } pLeft = pRet; } }else{ p = &pLeft->pSrc->a[0]; assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); p->u1.nRow++; } if( pParse->nErr==0 ){ Subquery *pSubq; assert( p!=0 ); assert( p->fg.isSubquery ); pSubq = p->u4.pSubq; assert( pSubq!=0 ); assert( pSubq->pSelect!=0 ); assert( pSubq->pSelect->pEList!=0 ); if( pSubq->pSelect->pEList->nExpr!=pRow->nExpr ){ sqlite3SelectWrongNumTermsError(pParse, pSubq->pSelect); }else{ sqlite3ExprCodeExprList(pParse, pRow, pSubq->regResult, 0, 0); sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, pSubq->regReturn); } } sqlite3ExprListDelete(pParse->db, pRow); } return pLeft; } |
︙ | ︙ | |||
134226 134227 134228 134229 134230 134231 134232 | int rc; /* Result code */ if( pSelect->pSrc->nSrc==1 && pSelect->pSrc->a[0].fg.viaCoroutine && pSelect->pPrior==0 ){ SrcItem *pItem = &pSelect->pSrc->a[0]; | > > > | | > > | | 134555 134556 134557 134558 134559 134560 134561 134562 134563 134564 134565 134566 134567 134568 134569 134570 134571 134572 134573 134574 134575 134576 | int rc; /* Result code */ if( pSelect->pSrc->nSrc==1 && pSelect->pSrc->a[0].fg.viaCoroutine && pSelect->pPrior==0 ){ SrcItem *pItem = &pSelect->pSrc->a[0]; Subquery *pSubq; assert( pItem->fg.isSubquery ); pSubq = pItem->u4.pSubq; dest.iSDParm = pSubq->regReturn; regFromSelect = pSubq->regResult; assert( pSubq->pSelect!=0 ); assert( pSubq->pSelect->pEList!=0 ); nColumn = pSubq->pSelect->pEList->nExpr; ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); if( bIdListInOrder && nColumn==pTab->nCol ){ regData = regFromSelect; regRowid = regData - 1; regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); } }else{ |
︙ | ︙ | |||
136148 136149 136150 136151 136152 136153 136154 | if( pDest->iPKey>=0 ) onError = pDest->keyConf; if( onError==OE_Default ) onError = OE_Abort; } assert(pSelect->pSrc); /* allocated even if there is no FROM clause */ if( pSelect->pSrc->nSrc!=1 ){ return 0; /* FROM clause must have exactly one term */ } | | | 136482 136483 136484 136485 136486 136487 136488 136489 136490 136491 136492 136493 136494 136495 136496 | if( pDest->iPKey>=0 ) onError = pDest->keyConf; if( onError==OE_Default ) onError = OE_Abort; } assert(pSelect->pSrc); /* allocated even if there is no FROM clause */ if( pSelect->pSrc->nSrc!=1 ){ return 0; /* FROM clause must have exactly one term */ } if( pSelect->pSrc->a[0].fg.isSubquery ){ return 0; /* FROM clause cannot contain a subquery */ } if( pSelect->pWhere ){ return 0; /* SELECT may not have a WHERE clause */ } if( pSelect->pOrderBy ){ return 0; /* SELECT may not have an ORDER BY clause */ |
︙ | ︙ | |||
143446 143447 143448 143449 143450 143451 143452 | } /* ** Mark a subquery result column as having been used. */ SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ assert( pItem!=0 ); | | > | > | | 143780 143781 143782 143783 143784 143785 143786 143787 143788 143789 143790 143791 143792 143793 143794 143795 143796 143797 143798 143799 143800 | } /* ** Mark a subquery result column as having been used. */ SQLITE_PRIVATE void sqlite3SrcItemColumnUsed(SrcItem *pItem, int iCol){ assert( pItem!=0 ); assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) ); if( pItem->fg.isNestedFrom ){ ExprList *pResults; assert( pItem->fg.isSubquery ); assert( pItem->u4.pSubq!=0 ); assert( pItem->u4.pSubq->pSelect!=0 ); pResults = pItem->u4.pSubq->pSelect->pEList; assert( pResults!=0 ); assert( iCol>=0 && iCol<pResults->nExpr ); pResults->a[iCol].fg.bUsed = 1; } } /* |
︙ | ︙ | |||
143484 143485 143486 143487 143488 143489 143490 | int iCol; /* Index of column matching zCol */ assert( iEnd<pSrc->nSrc ); assert( iStart>=0 ); assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ for(i=iStart; i<=iEnd; i++){ | | | | 143820 143821 143822 143823 143824 143825 143826 143827 143828 143829 143830 143831 143832 143833 143834 143835 143836 | int iCol; /* Index of column matching zCol */ assert( iEnd<pSrc->nSrc ); assert( iStart>=0 ); assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ for(i=iStart; i<=iEnd; i++){ iCol = sqlite3ColumnIndex(pSrc->a[i].pSTab, zCol); if( iCol>=0 && (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pSTab->aCol[iCol])==0) ){ if( piTab ){ sqlite3SrcItemColumnUsed(&pSrc->a[i], iCol); *piTab = i; *piCol = iCol; } return 1; |
︙ | ︙ | |||
143615 143616 143617 143618 143619 143620 143621 | SrcItem *pLeft; /* Left table being joined */ SrcItem *pRight; /* Right table being joined */ pSrc = p->pSrc; pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ | | | | 143951 143952 143953 143954 143955 143956 143957 143958 143959 143960 143961 143962 143963 143964 143965 143966 143967 143968 | SrcItem *pLeft; /* Left table being joined */ SrcItem *pRight; /* Right table being joined */ pSrc = p->pSrc; pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ Table *pRightTab = pRight->pSTab; u32 joinType; if( NEVER(pLeft->pSTab==0 || pRightTab==0) ) continue; joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; /* If this is a NATURAL join, synthesize an appropriate USING clause ** to specify which columns should be joined. */ if( pRight->fg.jointype & JT_NATURAL ){ IdList *pUsing = 0; |
︙ | ︙ | |||
145044 145045 145046 145047 145048 145049 145050 | Table *pTab = 0; /* Table structure column is extracted from */ Select *pS = 0; /* Select the column is extracted from */ int iCol = pExpr->iColumn; /* Index of column in pTab */ while( pNC && !pTab ){ SrcList *pTabList = pNC->pSrcList; for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); if( j<pTabList->nSrc ){ | | > | > > > | 145380 145381 145382 145383 145384 145385 145386 145387 145388 145389 145390 145391 145392 145393 145394 145395 145396 145397 145398 145399 | Table *pTab = 0; /* Table structure column is extracted from */ Select *pS = 0; /* Select the column is extracted from */ int iCol = pExpr->iColumn; /* Index of column in pTab */ while( pNC && !pTab ){ SrcList *pTabList = pNC->pSrcList; for(j=0;j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); if( j<pTabList->nSrc ){ pTab = pTabList->a[j].pSTab; if( pTabList->a[j].fg.isSubquery ){ pS = pTabList->a[j].u4.pSubq->pSelect; }else{ pS = 0; } }else{ pNC = pNC->pNext; } } if( pTab==0 ){ /* At one time, code such as "SELECT new.x" within a trigger would |
︙ | ︙ | |||
147097 147098 147099 147100 147101 147102 147103 | substExprList(pSubst, p->pGroupBy); substExprList(pSubst, p->pOrderBy); p->pHaving = substExpr(pSubst, p->pHaving); p->pWhere = substExpr(pSubst, p->pWhere); pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ | > | > | 147437 147438 147439 147440 147441 147442 147443 147444 147445 147446 147447 147448 147449 147450 147451 147452 147453 | substExprList(pSubst, p->pGroupBy); substExprList(pSubst, p->pOrderBy); p->pHaving = substExpr(pSubst, p->pHaving); p->pWhere = substExpr(pSubst, p->pWhere); pSrc = p->pSrc; assert( pSrc!=0 ); for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( pItem->fg.isSubquery ){ substSelect(pSubst, pItem->u4.pSubq->pSelect, 1); } if( pItem->fg.isTabFunc ){ substExprList(pSubst, pItem->u1.pFuncArg); } } }while( doPrior && (p = p->pPrior)!=0 ); } #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ |
︙ | ︙ | |||
147128 147129 147130 147131 147132 147133 147134 | return WRC_Continue; } static void recomputeColumnsUsed( Select *pSelect, /* The complete SELECT statement */ SrcItem *pSrcItem /* Which FROM clause item to recompute */ ){ Walker w; | | | 147470 147471 147472 147473 147474 147475 147476 147477 147478 147479 147480 147481 147482 147483 147484 | return WRC_Continue; } static void recomputeColumnsUsed( Select *pSelect, /* The complete SELECT statement */ SrcItem *pSrcItem /* Which FROM clause item to recompute */ ){ Walker w; if( NEVER(pSrcItem->pSTab==0) ) return; memset(&w, 0, sizeof(w)); w.xExprCallback = recomputeColumnsUsedExpr; w.xSelectCallback = sqlite3SelectWalkNoop; w.u.pSrcItem = pSrcItem; pSrcItem->colUsed = 0; sqlite3WalkSelect(&w, pSelect); } |
︙ | ︙ | |||
147168 147169 147170 147171 147172 147173 147174 | if( i!=iExcept ){ Select *p; assert( pItem->iCursor < aCsrMap[0] ); if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ aCsrMap[pItem->iCursor+1] = pParse->nTab++; } pItem->iCursor = aCsrMap[pItem->iCursor+1]; | > | | > | 147510 147511 147512 147513 147514 147515 147516 147517 147518 147519 147520 147521 147522 147523 147524 147525 147526 147527 | if( i!=iExcept ){ Select *p; assert( pItem->iCursor < aCsrMap[0] ); if( !pItem->fg.isRecursive || aCsrMap[pItem->iCursor+1]==0 ){ aCsrMap[pItem->iCursor+1] = pParse->nTab++; } pItem->iCursor = aCsrMap[pItem->iCursor+1]; if( pItem->fg.isSubquery ){ for(p=pItem->u4.pSubq->pSelect; p; p=p->pPrior){ srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1); } } } } } /* ** *piCursor is a cursor number. Change it if it needs to be mapped. |
︙ | ︙ | |||
147480 147481 147482 147483 147484 147485 147486 | assert( p!=0 ); assert( p->pPrior==0 ); if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0; pSrc = p->pSrc; assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc ); pSubitem = &pSrc->a[iFrom]; iParent = pSubitem->iCursor; | > | | 147824 147825 147826 147827 147828 147829 147830 147831 147832 147833 147834 147835 147836 147837 147838 147839 | assert( p!=0 ); assert( p->pPrior==0 ); if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0; pSrc = p->pSrc; assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc ); pSubitem = &pSrc->a[iFrom]; iParent = pSubitem->iCursor; assert( pSubitem->fg.isSubquery ); pSub = pSubitem->u4.pSubq->pSelect; assert( pSub!=0 ); #ifndef SQLITE_OMIT_WINDOWFUNC if( p->pWin || pSub->pWin ) return 0; /* Restriction (25) */ #endif pSubSrc = pSub->pSrc; |
︙ | ︙ | |||
147533 147534 147535 147536 147537 147538 147539 | ** ** which is not at all the same thing. ** ** See also tickets #306, #350, and #3300. */ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ if( pSubSrc->nSrc>1 /* (3a) */ | | | 147878 147879 147880 147881 147882 147883 147884 147885 147886 147887 147888 147889 147890 147891 147892 | ** ** which is not at all the same thing. ** ** See also tickets #306, #350, and #3300. */ if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){ if( pSubSrc->nSrc>1 /* (3a) */ || IsVirtual(pSubSrc->a[0].pSTab) /* (3b) */ || (p->selFlags & SF_Distinct)!=0 /* (3d) */ || (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */ ){ return 0; } isOuterJoin = 1; } |
︙ | ︙ | |||
147619 147620 147621 147622 147623 147624 147625 | /* Authorize the subquery */ pParse->zAuthContext = pSubitem->zName; TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0); testcase( i==SQLITE_DENY ); pParse->zAuthContext = zSavedAuthContext; /* Delete the transient structures associated with the subquery */ | > > > > | > | > < < | 147964 147965 147966 147967 147968 147969 147970 147971 147972 147973 147974 147975 147976 147977 147978 147979 147980 147981 147982 147983 147984 147985 147986 147987 147988 147989 | /* Authorize the subquery */ pParse->zAuthContext = pSubitem->zName; TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0); testcase( i==SQLITE_DENY ); pParse->zAuthContext = zSavedAuthContext; /* Delete the transient structures associated with the subquery */ if( ALWAYS(pSubitem->fg.isSubquery) ){ pSub1 = sqlite3SubqueryDetach(db, pSubitem); }else{ pSub1 = 0; } assert( pSubitem->fg.isSubquery==0 ); assert( pSubitem->fg.fixedSchema==0 ); sqlite3DbFree(db, pSubitem->zName); sqlite3DbFree(db, pSubitem->zAlias); pSubitem->zName = 0; pSubitem->zAlias = 0; assert( pSubitem->fg.isUsing!=0 || pSubitem->u3.pOn==0 ); /* If the sub-query is a compound SELECT statement, then (by restrictions ** 17 and 18 above) it must be a UNION ALL and the parent query must ** be of the form: ** ** SELECT <expr-list> FROM (<sub-query>) <where-clause> |
︙ | ︙ | |||
147667 147668 147669 147670 147671 147672 147673 | ** We call this the "compound-subquery flattening". */ for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){ Select *pNew; ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; Select *pPrior = p->pPrior; | | | | | > | > > | | | | 148016 148017 148018 148019 148020 148021 148022 148023 148024 148025 148026 148027 148028 148029 148030 148031 148032 148033 148034 148035 148036 148037 148038 148039 148040 148041 148042 148043 148044 148045 148046 148047 148048 148049 148050 148051 148052 148053 148054 148055 148056 148057 148058 148059 148060 148061 148062 148063 148064 148065 148066 148067 148068 148069 148070 148071 148072 148073 148074 148075 148076 148077 148078 148079 148080 148081 | ** We call this the "compound-subquery flattening". */ for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){ Select *pNew; ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; Select *pPrior = p->pPrior; Table *pItemTab = pSubitem->pSTab; pSubitem->pSTab = 0; p->pOrderBy = 0; p->pPrior = 0; p->pLimit = 0; pNew = sqlite3SelectDup(db, p, 0); p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->op = TK_ALL; pSubitem->pSTab = pItemTab; if( pNew==0 ){ p->pPrior = pPrior; }else{ pNew->selId = ++pParse->nSelect; if( aCsrMap && ALWAYS(db->mallocFailed==0) ){ renumberCursors(pParse, pNew, iFrom, aCsrMap); } pNew->pPrior = pPrior; if( pPrior ) pPrior->pNext = pNew; pNew->pNext = p; p->pPrior = pNew; TREETRACE(0x4,pParse,p,("compound-subquery flattener" " creates %u as peer\n",pNew->selId)); } assert( pSubitem->fg.isSubquery==0 ); } sqlite3DbFree(db, aCsrMap); if( db->mallocFailed ){ assert( pSubitem->fg.fixedSchema==0 ); assert( pSubitem->fg.isSubquery==0 ); assert( pSubitem->u4.zDatabase==0 ); sqlite3SrcItemAttachSubquery(pParse, pSubitem, pSub1, 0); return 1; } /* Defer deleting the Table object associated with the ** subquery until code generation is ** complete, since there may still exist Expr.pTab entries that ** refer to the subquery even after flattening. Ticket #3346. ** ** pSubitem->pTab is always non-NULL by test restrictions and tests above. */ if( ALWAYS(pSubitem->pSTab!=0) ){ Table *pTabToDel = pSubitem->pSTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; } pSubitem->pSTab = 0; } /* The following loop runs once for each term in a compound-subquery ** flattening (as described above). If we are doing a different kind ** of flattening - a flattening other than a compound-subquery flattening - ** then this loop only runs once. ** |
︙ | ︙ | |||
147771 147772 147773 147774 147775 147776 147777 | } /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ SrcItem *pItem = &pSrc->a[i+iFrom]; | | | > > > | 148123 148124 148125 148126 148127 148128 148129 148130 148131 148132 148133 148134 148135 148136 148137 148138 148139 148140 148141 | } /* Transfer the FROM clause terms from the subquery into the ** outer query. */ for(i=0; i<nSubSrc; i++){ SrcItem *pItem = &pSrc->a[i+iFrom]; assert( pItem->fg.isTabFunc==0 ); assert( pItem->fg.isSubquery || pItem->fg.fixedSchema || pItem->u4.zDatabase==0 ); if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing); *pItem = pSubSrc->a[i]; pItem->fg.jointype |= ltorj; iNewParent = pSubSrc->a[i].iCursor; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype &= JT_LTORJ; pSrc->a[iFrom].fg.jointype |= jointype | ltorj; |
︙ | ︙ | |||
148456 148457 148458 148459 148460 148461 148462 | 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; } | | | | | | 148811 148812 148813 148814 148815 148816 148817 148818 148819 148820 148821 148822 148823 148824 148825 148826 148827 148828 | 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->pSTab!=0 ); pTab = pItem->pSTab; assert( pItem->fg.isSubquery ); pSub = pItem->u4.pSubq->pSelect; assert( pSub->pEList->nExpr==pTab->nCol ); for(pX=pSub; pX; pX=pX->pPrior){ if( (pX->selFlags & (SF_Distinct|SF_Aggregate))!=0 ){ testcase( pX->selFlags & SF_Distinct ); testcase( pX->selFlags & SF_Aggregate ); return 0; } |
︙ | ︙ | |||
148588 148589 148590 148591 148592 148593 148594 | Expr *pExpr; assert( !p->pGroupBy ); if( p->pWhere || p->pEList->nExpr!=1 || p->pSrc->nSrc!=1 | | | | 148943 148944 148945 148946 148947 148948 148949 148950 148951 148952 148953 148954 148955 148956 148957 148958 148959 148960 148961 148962 148963 | Expr *pExpr; assert( !p->pGroupBy ); if( p->pWhere || p->pEList->nExpr!=1 || p->pSrc->nSrc!=1 || p->pSrc->a[0].fg.isSubquery || pAggInfo->nFunc!=1 || p->pHaving ){ return 0; } pTab = p->pSrc->a[0].pSTab; assert( pTab!=0 ); assert( !IsView(pTab) ); if( !IsOrdinaryTable(pTab) ) return 0; pExpr = p->pEList->a[0].pExpr; assert( pExpr!=0 ); if( pExpr->op!=TK_AGG_FUNCTION ) return 0; if( pExpr->pAggInfo!=pAggInfo ) return 0; |
︙ | ︙ | |||
148619 148620 148621 148622 148623 148624 148625 | ** If the source-list item passed as an argument was augmented with an ** INDEXED BY clause, then try to locate the specified index. If there ** was such a clause and the named index cannot be found, return ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate ** pFrom->pIndex and return SQLITE_OK. */ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ | | | 148974 148975 148976 148977 148978 148979 148980 148981 148982 148983 148984 148985 148986 148987 148988 | ** If the source-list item passed as an argument was augmented with an ** INDEXED BY clause, then try to locate the specified index. If there ** was such a clause and the named index cannot be found, return ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate ** pFrom->pIndex and return SQLITE_OK. */ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ Table *pTab = pFrom->pSTab; char *zIndexedBy = pFrom->u1.zIndexedBy; Index *pIdx; assert( pTab!=0 ); assert( pFrom->fg.isIndexedBy!=0 ); for(pIdx=pTab->pIndex; pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); |
︙ | ︙ | |||
148696 148697 148698 148699 148700 148701 148702 | pParse = pWalker->pParse; db = pParse->db; pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); | > > > | > | 149051 149052 149053 149054 149055 149056 149057 149058 149059 149060 149061 149062 149063 149064 149065 149066 149067 149068 149069 | pParse = pWalker->pParse; db = pParse->db; pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); if( pNew==0 ) return WRC_Abort; memset(&dummy, 0, sizeof(dummy)); pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0); assert( pNewSrc!=0 || pParse->nErr ); if( pParse->nErr ){ sqlite3SrcListDelete(db, pNewSrc); return WRC_Abort; } *pNew = *p; p->pSrc = pNewSrc; p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0)); p->op = TK_SELECT; p->pWhere = 0; pNew->pGroupBy = 0; pNew->pHaving = 0; |
︙ | ︙ | |||
148751 148752 148753 148754 148755 148756 148757 | static struct Cte *searchWith( With *pWith, /* Current innermost WITH clause */ SrcItem *pItem, /* FROM clause element to resolve */ With **ppContext /* OUT: WITH clause return value belongs to */ ){ const char *zName = pItem->zName; With *p; | | | 149110 149111 149112 149113 149114 149115 149116 149117 149118 149119 149120 149121 149122 149123 149124 | static struct Cte *searchWith( With *pWith, /* Current innermost WITH clause */ SrcItem *pItem, /* FROM clause element to resolve */ With **ppContext /* OUT: WITH clause return value belongs to */ ){ const char *zName = pItem->zName; With *p; assert( pItem->fg.fixedSchema || pItem->u4.zDatabase==0 ); assert( zName!=0 ); for(p=pWith; p; p=p->pOuter){ int i; for(i=0; i<p->nCte; i++){ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){ *ppContext = p; return &p->a[i]; |
︙ | ︙ | |||
148821 148822 148823 148824 148825 148826 148827 | Parse *pParse, /* The parsing context */ Walker *pWalker, /* Current tree walker */ SrcItem *pFrom /* The FROM clause term to check */ ){ Cte *pCte; /* Matched CTE (or NULL if no match) */ With *pWith; /* The matching WITH */ | | > | | 149180 149181 149182 149183 149184 149185 149186 149187 149188 149189 149190 149191 149192 149193 149194 149195 149196 149197 149198 149199 149200 149201 149202 149203 149204 149205 | Parse *pParse, /* The parsing context */ Walker *pWalker, /* Current tree walker */ SrcItem *pFrom /* The FROM clause term to check */ ){ Cte *pCte; /* Matched CTE (or NULL if no match) */ With *pWith; /* The matching WITH */ assert( pFrom->pSTab==0 ); if( pParse->pWith==0 ){ /* There are no WITH clauses in the stack. No match is possible */ return 0; } if( pParse->nErr ){ /* Prior errors might have left pParse->pWith in a goofy state, so ** go no further. */ return 0; } assert( pFrom->fg.hadSchema==0 || pFrom->fg.notCte!=0 ); if( pFrom->fg.fixedSchema==0 && pFrom->u4.zDatabase!=0 ){ /* The FROM term contains a schema qualifier (ex: main.t1) and so ** it cannot possibly be a CTE reference. */ return 0; } if( pFrom->fg.notCte ){ /* The FROM term is specifically excluded from matching a CTE. ** (1) It is part of a trigger that used to have zDatabase but had |
︙ | ︙ | |||
148867 148868 148869 148870 148871 148872 148873 | ** In this case, proceed. */ if( pCte->zCteErr ){ sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); return 2; } if( cannotBeFunction(pParse, pFrom) ) return 2; | | | | > > > | < > | | | > > | | 149227 149228 149229 149230 149231 149232 149233 149234 149235 149236 149237 149238 149239 149240 149241 149242 149243 149244 149245 149246 149247 149248 149249 149250 149251 149252 149253 149254 149255 149256 149257 149258 149259 149260 149261 149262 149263 149264 149265 149266 149267 149268 149269 149270 149271 149272 149273 149274 149275 149276 149277 149278 149279 149280 149281 149282 149283 149284 149285 149286 149287 149288 149289 149290 149291 | ** In this case, proceed. */ if( pCte->zCteErr ){ sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName); return 2; } if( cannotBeFunction(pParse, pFrom) ) return 2; assert( pFrom->pSTab==0 ); pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return 2; pCteUse = pCte->pUse; if( pCteUse==0 ){ pCte->pUse = pCteUse = sqlite3DbMallocZero(db, sizeof(pCteUse[0])); if( pCteUse==0 || sqlite3ParserAddCleanup(pParse,sqlite3DbFree,pCteUse)==0 ){ sqlite3DbFree(db, pTab); return 2; } pCteUse->eM10d = pCte->eM10d; } pFrom->pSTab = pTab; pTab->nTabRef = 1; pTab->zName = sqlite3DbStrDup(db, pCte->zName); pTab->iPKey = -1; pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid; sqlite3SrcItemAttachSubquery(pParse, pFrom, pCte->pSelect, 1); if( db->mallocFailed ) return 2; assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); pSel = pFrom->u4.pSubq->pSelect; assert( pSel!=0 ); pSel->selFlags |= SF_CopyCte; if( pFrom->fg.isIndexedBy ){ sqlite3ErrorMsg(pParse, "no such index: \"%s\"", pFrom->u1.zIndexedBy); return 2; } assert( !pFrom->fg.isIndexedBy ); pFrom->fg.isCte = 1; pFrom->u2.pCteUse = pCteUse; pCteUse->nUse++; /* Check if this is a recursive CTE. */ pRecTerm = pSel; bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION ); while( bMayRecursive && pRecTerm->op==pSel->op ){ int i; SrcList *pSrc = pRecTerm->pSrc; assert( pRecTerm->pPrior!=0 ); for(i=0; i<pSrc->nSrc; i++){ SrcItem *pItem = &pSrc->a[i]; if( pItem->zName!=0 && !pItem->fg.hadSchema && ALWAYS( !pItem->fg.isSubquery ) && (pItem->fg.fixedSchema || pItem->u4.zDatabase==0) && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ pItem->pSTab = pTab; pTab->nTabRef++; pItem->fg.isRecursive = 1; if( pRecTerm->selFlags & SF_Recursive ){ sqlite3ErrorMsg(pParse, "multiple references to recursive table: %s", pCte->zName ); return 2; |
︙ | ︙ | |||
149014 149015 149016 149017 149018 149019 149020 | ** The SrcItem structure passed as the second argument represents a ** sub-query in the FROM clause of a SELECT statement. This function ** allocates and populates the SrcItem.pTab object. If successful, ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, ** SQLITE_NOMEM. */ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ | | > > > | | 149379 149380 149381 149382 149383 149384 149385 149386 149387 149388 149389 149390 149391 149392 149393 149394 149395 149396 149397 149398 149399 149400 | ** The SrcItem structure passed as the second argument represents a ** sub-query in the FROM clause of a SELECT statement. This function ** allocates and populates the SrcItem.pTab object. If successful, ** SQLITE_OK is returned. Otherwise, if an OOM error is encountered, ** SQLITE_NOMEM. */ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){ Select *pSel; Table *pTab; assert( pFrom->fg.isSubquery ); assert( pFrom->u4.pSubq!=0 ); pSel = pFrom->u4.pSubq->pSelect; assert( pSel ); pFrom->pSTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table)); if( pTab==0 ) return SQLITE_NOMEM; pTab->nTabRef = 1; if( pFrom->zAlias ){ pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias); }else{ pTab->zName = sqlite3MPrintf(pParse->db, "%!S", pFrom); } |
︙ | ︙ | |||
149138 149139 149140 149141 149142 149143 149144 | /* Look up every table named in the FROM clause of the select. If ** an entry of the FROM clause is a subquery instead of a table or view, ** then create a transient table structure to describe the subquery. */ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab; | | | | > > | | | | | | | > | > | 149506 149507 149508 149509 149510 149511 149512 149513 149514 149515 149516 149517 149518 149519 149520 149521 149522 149523 149524 149525 149526 149527 149528 149529 149530 149531 149532 149533 149534 149535 149536 149537 149538 149539 149540 149541 149542 149543 149544 149545 149546 149547 149548 149549 149550 149551 149552 149553 149554 149555 149556 149557 149558 149559 149560 149561 149562 149563 149564 149565 149566 149567 149568 149569 149570 149571 149572 149573 149574 149575 149576 149577 149578 149579 149580 149581 149582 149583 149584 149585 149586 | /* Look up every table named in the FROM clause of the select. If ** an entry of the FROM clause is a subquery instead of a table or view, ** then create a transient table structure to describe the subquery. */ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab; assert( pFrom->fg.isRecursive==0 || pFrom->pSTab!=0 ); if( pFrom->pSTab ) continue; assert( pFrom->fg.isRecursive==0 ); if( pFrom->zName==0 ){ #ifndef SQLITE_OMIT_SUBQUERY Select *pSel; assert( pFrom->fg.isSubquery && pFrom->u4.pSubq!=0 ); pSel = pFrom->u4.pSubq->pSelect; /* A sub-query in the FROM clause of a SELECT */ assert( pSel!=0 ); assert( pFrom->pSTab==0 ); if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort; if( sqlite3ExpandSubquery(pParse, pFrom) ) return WRC_Abort; #endif #ifndef SQLITE_OMIT_CTE }else if( (rc = resolveFromTermToCte(pParse, pWalker, pFrom))!=0 ){ if( rc>1 ) return WRC_Abort; pTab = pFrom->pSTab; assert( pTab!=0 ); #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pSTab==0 ); pFrom->pSTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); if( pTab==0 ) return WRC_Abort; if( pTab->nTabRef>=0xffff ){ sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", pTab->zName); pFrom->pSTab = 0; return WRC_Abort; } pTab->nTabRef++; if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){ return WRC_Abort; } #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( !IsOrdinaryTable(pTab) ){ i16 nCol; u8 eCodeOrig = pWalker->eCode; if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort; assert( pFrom->fg.isSubquery==0 ); if( IsView(pTab) ){ if( (db->flags & SQLITE_EnableView)==0 && pTab->pSchema!=db->aDb[1].pSchema ){ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited", pTab->zName); } sqlite3SrcItemAttachSubquery(pParse, pFrom, pTab->u.view.pSelect, 1); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( ALWAYS(IsVirtual(pTab)) && pFrom->fg.fromDDL && ALWAYS(pTab->u.vtab.p!=0) && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) ){ sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"", pTab->zName); } assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 ); #endif nCol = pTab->nCol; pTab->nCol = -1; pWalker->eCode = 1; /* Turn on Select.selId renumbering */ if( pFrom->fg.isSubquery ){ sqlite3WalkSelect(pWalker, pFrom->u4.pSubq->pSelect); } pWalker->eCode = eCodeOrig; pTab->nCol = nCol; } #endif } /* Locate the index named by the INDEXED BY clause, if any. */ |
︙ | ︙ | |||
149287 149288 149289 149290 149291 149292 149293 | iErrOfst = pE->pRight->w.iOfst; }else{ assert( ExprUseWOfst(pE) ); iErrOfst = pE->w.iOfst; } for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ int nAdd; /* Number of cols including rowid */ | | | > | | | 149659 149660 149661 149662 149663 149664 149665 149666 149667 149668 149669 149670 149671 149672 149673 149674 149675 149676 149677 149678 149679 149680 149681 149682 149683 149684 149685 149686 149687 149688 | iErrOfst = pE->pRight->w.iOfst; }else{ assert( ExprUseWOfst(pE) ); iErrOfst = pE->w.iOfst; } for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ int nAdd; /* Number of cols including rowid */ Table *pTab = pFrom->pSTab; /* Table for this data source */ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ char *zTabName; /* AS name for this data source */ const char *zSchemaName = 0; /* Schema name for this data source */ int iDb; /* Schema index for this data src */ IdList *pUsing; /* USING clause for pFrom[1] */ if( (zTabName = pFrom->zAlias)==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; assert( (int)pFrom->fg.isNestedFrom == IsNestedFrom(pFrom) ); if( pFrom->fg.isNestedFrom ){ assert( pFrom->fg.isSubquery && pFrom->u4.pSubq ); assert( pFrom->u4.pSubq->pSelect!=0 ); pNestedFrom = pFrom->u4.pSubq->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid ); }else{ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; } |
︙ | ︙ | |||
149540 149541 149542 149543 149544 149545 149546 | if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ | | | | < | < | 149913 149914 149915 149916 149917 149918 149919 149920 149921 149922 149923 149924 149925 149926 149927 149928 149929 149930 149931 149932 | if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab = pFrom->pSTab; assert( pTab!=0 ); if( (pTab->tabFlags & TF_Ephemeral)!=0 && pFrom->fg.isSubquery ){ /* A sub-query in the FROM clause of a SELECT */ Select *pSel = pFrom->u4.pSubq->pSelect; sqlite3SubqueryColumnTypes(pParse, pTab, pSel, SQLITE_AFF_NONE); } } } #endif /* |
︙ | ︙ | |||
149861 149862 149863 149864 149865 149866 149867 149868 149869 149870 149871 149872 149873 149874 | static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); pList = pF->pFExpr->x.pList; if( pF->iOBTab>=0 ){ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs ** were stored in emphermal table pF->iOBTab. Here, we extract those ** inputs (in ORDER BY order) and make all calls to OP_AggStep ** before doing the OP_AggFinal call. */ int iTop; /* Start of loop for extracting columns */ | > | 150232 150233 150234 150235 150236 150237 150238 150239 150240 150241 150242 150243 150244 150245 150246 | static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ Vdbe *v = pParse->pVdbe; int i; struct AggInfo_func *pF; for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); if( pParse->nErr ) return; pList = pF->pFExpr->x.pList; if( pF->iOBTab>=0 ){ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs ** were stored in emphermal table pF->iOBTab. Here, we extract those ** inputs (in ORDER BY order) and make all calls to OP_AggStep ** before doing the OP_AggFinal call. */ int iTop; /* Start of loop for extracting columns */ |
︙ | ︙ | |||
150070 150071 150072 150073 150074 150075 150076 150077 150078 150079 150080 150081 150082 150083 150084 150085 150086 150087 150088 150089 150090 150091 150092 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); sqlite3ReleaseTempRange(pParse, regAgg, nArg); } if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); } } if( regHit==0 && pAggInfo->nAccumulator ){ regHit = regAcc; } if( regHit ){ addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); } for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); } pAggInfo->directMode = 0; if( addrHitTest ){ sqlite3VdbeJumpHereOrPopInst(v, addrHitTest); } } | > > | 150442 150443 150444 150445 150446 150447 150448 150449 150450 150451 150452 150453 150454 150455 150456 150457 150458 150459 150460 150461 150462 150463 150464 150465 150466 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); sqlite3ReleaseTempRange(pParse, regAgg, nArg); } if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); } if( pParse->nErr ) return; } if( regHit==0 && pAggInfo->nAccumulator ){ regHit = regAcc; } if( regHit ){ addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); } for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pCExpr, AggInfoColumnReg(pAggInfo,i)); if( pParse->nErr ) return; } pAggInfo->directMode = 0; if( addrHitTest ){ sqlite3VdbeJumpHereOrPopInst(v, addrHitTest); } } |
︙ | ︙ | |||
150194 150195 150196 150197 150198 150199 150200 | */ static SrcItem *isSelfJoinView( SrcList *pTabList, /* Search for self-joins in this FROM clause */ SrcItem *pThis, /* Search for prior reference to this subquery */ int iFirst, int iEnd /* Range of FROM-clause entries to search. */ ){ SrcItem *pItem; | > | > > | | | | | | | | | 150568 150569 150570 150571 150572 150573 150574 150575 150576 150577 150578 150579 150580 150581 150582 150583 150584 150585 150586 150587 150588 150589 150590 150591 150592 150593 150594 150595 150596 150597 150598 150599 150600 150601 150602 150603 | */ static SrcItem *isSelfJoinView( SrcList *pTabList, /* Search for self-joins in this FROM clause */ SrcItem *pThis, /* Search for prior reference to this subquery */ int iFirst, int iEnd /* Range of FROM-clause entries to search. */ ){ SrcItem *pItem; Select *pSel; assert( pThis->fg.isSubquery ); pSel = pThis->u4.pSubq->pSelect; assert( pSel!=0 ); if( pSel->selFlags & SF_PushDown ) return 0; while( iFirst<iEnd ){ Select *pS1; pItem = &pTabList->a[iFirst++]; if( !pItem->fg.isSubquery ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; assert( pItem->pSTab!=0 ); assert( pThis->pSTab!=0 ); if( pItem->pSTab->pSchema!=pThis->pSTab->pSchema ) continue; if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; pS1 = pItem->u4.pSubq->pSelect; if( pItem->pSTab->pSchema==0 && pSel->selId!=pS1->selId ){ /* The query flattener left two different CTE tables with identical ** names in the same FROM clause. */ continue; } if( pS1->selFlags & SF_PushDown ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; } return pItem; } return 0; |
︙ | ︙ | |||
150256 150257 150258 150259 150260 150261 150262 150263 150264 150265 150266 150267 150268 150269 150270 150271 150272 150273 150274 150275 150276 | ** Return TRUE if the optimization is undertaken. */ static int countOfViewOptimization(Parse *pParse, Select *p){ 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 */ | > | | > | | < | 150633 150634 150635 150636 150637 150638 150639 150640 150641 150642 150643 150644 150645 150646 150647 150648 150649 150650 150651 150652 150653 150654 150655 150656 150657 150658 150659 150660 150661 150662 150663 150664 150665 150666 150667 150668 150669 150670 150671 150672 150673 150674 150675 150676 150677 150678 150679 150680 150681 | ** Return TRUE if the optimization is undertaken. */ static int countOfViewOptimization(Parse *pParse, Select *p){ Select *pSub, *pPrior; Expr *pExpr; Expr *pCount; sqlite3 *db; SrcItem *pFrom; 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 */ pFrom = p->pSrc->a; if( pFrom->fg.isSubquery==0 ) return 0; /* FROM is a subquery */ pSub = pFrom->u4.pSubq->pSelect; 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; pSub = sqlite3SubqueryDetach(db, pFrom); sqlite3SrcListDelete(db, p->pSrc); p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); while( pSub ){ Expr *pTerm; pPrior = pSub->pPrior; pSub->pPrior = 0; pSub->pNext = 0; |
︙ | ︙ | |||
150334 150335 150336 150337 150338 150339 150340 | ** Otherwise return false. */ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ int i; for(i=0; i<pSrc->nSrc; i++){ SrcItem *p1 = &pSrc->a[i]; if( p1==p0 ) continue; | | | | | | 150712 150713 150714 150715 150716 150717 150718 150719 150720 150721 150722 150723 150724 150725 150726 150727 150728 150729 150730 150731 | ** Otherwise return false. */ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ int i; for(i=0; i<pSrc->nSrc; i++){ SrcItem *p1 = &pSrc->a[i]; if( p1==p0 ) continue; if( p0->pSTab==p1->pSTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ return 1; } if( p1->fg.isSubquery && (p1->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 && sameSrcAlias(p0, p1->u4.pSubq->pSelect->pSrc) ){ return 1; } } return 0; } |
︙ | ︙ | |||
150404 150405 150406 150407 150408 150409 150410 | } if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ while( 1 /*exit-by-break*/ ){ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */ if( i==0 ) break; i--; pItem--; | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 150782 150783 150784 150785 150786 150787 150788 150789 150790 150791 150792 150793 150794 150795 150796 150797 150798 150799 150800 150801 150802 150803 150804 150805 150806 150807 150808 150809 150810 150811 150812 150813 150814 150815 150816 150817 150818 150819 150820 150821 150822 150823 150824 150825 150826 150827 150828 150829 150830 150831 150832 150833 150834 150835 150836 150837 150838 150839 150840 150841 150842 150843 150844 150845 150846 | } if( selFlags & SF_UpdateFrom ) return 0; /* (1c-iii) */ while( 1 /*exit-by-break*/ ){ if( pItem->fg.jointype & (JT_OUTER|JT_CROSS) ) return 0; /* (1c-ii) */ if( i==0 ) break; i--; pItem--; if( pItem->fg.isSubquery ) return 0; /* (1c-i) */ } return 1; } /* ** Generate byte-code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** ** This routine returns the number of errors. If any errors are ** encountered, then an appropriate error message is left in ** pParse->zErrMsg. ** ** This routine does NOT free the Select structure passed in. The ** calling function needs to do that. ** ** This is a long function. The following is an outline of the processing ** steps, with tags referencing various milestones: ** ** * Resolve names and similar preparation tag-select-0100 ** * Scan of the FROM clause tag-select-0200 ** + OUTER JOIN strength reduction tag-select-0220 ** + Sub-query ORDER BY removal tag-select-0230 ** + Query flattening tag-select-0240 ** * Separate subroutine for compound-SELECT tag-select-0300 ** * WHERE-clause constant propagation tag-select-0330 ** * Count()-of-VIEW optimization tag-select-0350 ** * Scan of the FROM clause again tag-select-0400 ** + Authorize unreferenced tables tag-select-0410 ** + Predicate push-down optimization tag-select-0420 ** + Omit unused subquery columns optimization tag-select-0440 ** + Generate code to implement subqueries tag-select-0480 ** - Co-routines tag-select-0482 ** - Reuse previously computed CTE tag-select-0484 ** - REuse previously computed VIEW tag-select-0486 ** - Materialize a VIEW or CTE tag-select-0488 ** * DISTINCT ORDER BY -> GROUP BY optimization tag-select-0500 ** * Set up for ORDER BY tag-select-0600 ** * Create output table tag-select-0630 ** * Prepare registers for LIMIT tag-select-0650 ** * Setup for DISTINCT tag-select-0680 ** * Generate code for non-aggregate and non-GROUP BY tag-select-0700 ** * Generate code for aggregate and/or GROUP BY tag-select-0800 ** + GROUP BY queries tag-select-0810 ** + non-GROUP BY queries tag-select-0820 ** - Special case of count() w/o GROUP BY tag-select-0821 ** - General case of non-GROUP BY aggregates tag-select-0822 ** * Sort results, as needed tag-select-0900 ** * Internal self-checks tag-select-1000 */ SQLITE_PRIVATE int sqlite3Select( Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ SelectDest *pDest /* What to do with the query results */ ){ int i, j; /* Loop counters */ |
︙ | ︙ | |||
150464 150465 150466 150467 150468 150469 150470 150471 150472 150473 150474 150475 150476 150477 | sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d", __FILE__, __LINE__); } sqlite3ShowSelect(p); } #endif assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); if( IgnorableDistinct(pDest) ){ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || | > | 150876 150877 150878 150879 150880 150881 150882 150883 150884 150885 150886 150887 150888 150889 150890 | sqlite3TreeViewLine(0, "In sqlite3Select() at %s:%d", __FILE__, __LINE__); } sqlite3ShowSelect(p); } #endif /* tag-select-0100 */ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue ); assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue ); if( IgnorableDistinct(pDest) ){ assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union || pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard || |
︙ | ︙ | |||
150515 150516 150517 150518 150519 150520 150521 | ** which is just confusing. To avoid this, we follow PG's lead and ** disallow it altogether. */ if( p->selFlags & SF_UFSrcCheck ){ SrcItem *p0 = &p->pSrc->a[0]; if( sameSrcAlias(p0, p->pSrc) ){ sqlite3ErrorMsg(pParse, "target object/alias may not appear in FROM clause: %s", | | | 150928 150929 150930 150931 150932 150933 150934 150935 150936 150937 150938 150939 150940 150941 150942 | ** which is just confusing. To avoid this, we follow PG's lead and ** disallow it altogether. */ if( p->selFlags & SF_UFSrcCheck ){ SrcItem *p0 = &p->pSrc->a[0]; if( sameSrcAlias(p0, p->pSrc) ){ sqlite3ErrorMsg(pParse, "target object/alias may not appear in FROM clause: %s", p0->zAlias ? p0->zAlias : p0->pSTab->zName ); goto select_end; } /* Clear the SF_UFSrcCheck flag. The check has already been performed, ** and leaving this flag set can cause errors if a compound sub-query ** in p->pSrc is flattened into this query and this function called |
︙ | ︙ | |||
150550 150551 150552 150553 150554 150555 150556 150557 150558 150559 150560 | pTabList = p->pSrc; isAgg = (p->selFlags & SF_Aggregate)!=0; memset(&sSort, 0, sizeof(sSort)); sSort.pOrderBy = p->pOrderBy; /* Try to do various optimizations (flattening subqueries, and strength ** reduction of join operators) in the FROM clause up into the main query */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ SrcItem *pItem = &pTabList->a[i]; | > | | > | 150963 150964 150965 150966 150967 150968 150969 150970 150971 150972 150973 150974 150975 150976 150977 150978 150979 150980 150981 150982 150983 150984 150985 150986 150987 150988 150989 150990 150991 150992 150993 150994 150995 150996 150997 150998 150999 151000 | pTabList = p->pSrc; isAgg = (p->selFlags & SF_Aggregate)!=0; memset(&sSort, 0, sizeof(sSort)); sSort.pOrderBy = p->pOrderBy; /* Try to do various optimizations (flattening subqueries, and strength ** reduction of join operators) in the FROM clause up into the main query ** tag-select-0200 */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ SrcItem *pItem = &pTabList->a[i]; Select *pSub = pItem->fg.isSubquery ? pItem->u4.pSubq->pSelect : 0; Table *pTab = pItem->pSTab; /* The expander should have already created transient Table objects ** even for FROM clause elements such as subqueries that do not correspond ** to a real table */ assert( pTab!=0 ); /* Try to simplify joins: ** ** LEFT JOIN -> JOIN ** RIGHT JOIN -> JOIN ** FULL JOIN -> RIGHT JOIN ** ** If terms of the i-th table are used in the WHERE clause in such a ** way that the i-th table cannot be the NULL row of a join, then ** perform the appropriate simplification. This is called ** "OUTER JOIN strength reduction" in the SQLite documentation. ** tag-select-0220 */ if( (pItem->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 && sqlite3ExprImpliesNonNullRow(p->pWhere, pItem->iCursor, pItem->fg.jointype & JT_LTORJ) && OptimizationEnabled(db, SQLITE_SimplifyJoin) ){ if( pItem->fg.jointype & JT_LEFT ){ |
︙ | ︙ | |||
150642 150643 150644 150645 150646 150647 150648 | ** is not a join. But if the outer query is not a join, then the subquery ** will be implemented as a co-routine and there is no advantage to ** flattening in that case. */ if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); | > | | 151057 151058 151059 151060 151061 151062 151063 151064 151065 151066 151067 151068 151069 151070 151071 151072 | ** is not a join. But if the outer query is not a join, then the subquery ** will be implemented as a co-routine and there is no advantage to ** flattening in that case. */ if( (pSub->selFlags & SF_Aggregate)!=0 ) continue; assert( pSub->pGroupBy==0 ); /* tag-select-0230: ** If a FROM-clause subquery has an ORDER BY clause that is not ** really doing anything, then delete it now so that it does not ** interfere with query flattening. See the discussion at ** https://sqlite.org/forum/forumpost/2d76f2bcf65d256a ** ** Beware of these cases where the ORDER BY clause may not be safely ** omitted: ** |
︙ | ︙ | |||
150708 150709 150710 150711 150712 150713 150714 150715 150716 150717 150718 150719 150720 150721 150722 150723 150724 150725 150726 150727 150728 150729 | && (p->selFlags & SF_ComplexResult)!=0 && (pTabList->nSrc==1 || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) ){ continue; } if( flattenSubquery(pParse, p, i, isAgg) ){ if( pParse->nErr ) goto select_end; /* This subquery can be absorbed into its parent. */ i = -1; } pTabList = p->pSrc; if( db->mallocFailed ) goto select_end; if( !IgnorableOrderby(pDest) ){ sSort.pOrderBy = p->pOrderBy; } } #endif #ifndef SQLITE_OMIT_COMPOUND_SELECT /* Handle compound SELECT statements using the separate multiSelect() | > | | | > | > | | > > > > > | > > > > > > > > > | > | | > | > | > | | | | | | | | | > | | | > > > > | | > | | | | | | | | | 151124 151125 151126 151127 151128 151129 151130 151131 151132 151133 151134 151135 151136 151137 151138 151139 151140 151141 151142 151143 151144 151145 151146 151147 151148 151149 151150 151151 151152 151153 151154 151155 151156 151157 151158 151159 151160 151161 151162 151163 151164 151165 151166 151167 151168 151169 151170 151171 151172 151173 151174 151175 151176 151177 151178 151179 151180 151181 151182 151183 151184 151185 151186 151187 151188 151189 151190 151191 151192 151193 151194 151195 151196 151197 151198 151199 151200 151201 151202 151203 151204 151205 151206 151207 151208 151209 151210 151211 151212 151213 151214 151215 151216 151217 151218 151219 151220 151221 151222 151223 151224 151225 151226 151227 151228 151229 151230 151231 151232 151233 151234 151235 151236 151237 151238 151239 151240 151241 151242 151243 151244 151245 151246 151247 151248 151249 151250 151251 151252 151253 151254 151255 151256 151257 151258 151259 151260 151261 151262 151263 151264 151265 151266 151267 151268 151269 151270 151271 151272 151273 151274 151275 151276 151277 151278 151279 151280 151281 151282 151283 151284 151285 151286 151287 151288 151289 151290 151291 151292 151293 151294 151295 151296 151297 151298 151299 151300 151301 151302 151303 151304 151305 151306 151307 151308 151309 151310 151311 151312 151313 151314 151315 151316 151317 151318 151319 151320 151321 151322 151323 151324 151325 151326 151327 151328 151329 151330 151331 151332 151333 151334 151335 151336 151337 151338 151339 151340 151341 151342 151343 151344 151345 151346 151347 151348 151349 151350 151351 151352 151353 151354 151355 151356 151357 151358 151359 151360 151361 151362 151363 151364 151365 151366 151367 151368 151369 151370 151371 151372 151373 151374 151375 151376 151377 151378 151379 151380 151381 151382 151383 151384 151385 151386 151387 151388 151389 151390 | && (p->selFlags & SF_ComplexResult)!=0 && (pTabList->nSrc==1 || (pTabList->a[1].fg.jointype&(JT_OUTER|JT_CROSS))!=0) ){ continue; } /* tag-select-0240 */ if( flattenSubquery(pParse, p, i, isAgg) ){ if( pParse->nErr ) goto select_end; /* This subquery can be absorbed into its parent. */ i = -1; } pTabList = p->pSrc; if( db->mallocFailed ) goto select_end; if( !IgnorableOrderby(pDest) ){ sSort.pOrderBy = p->pOrderBy; } } #endif #ifndef SQLITE_OMIT_COMPOUND_SELECT /* Handle compound SELECT statements using the separate multiSelect() ** procedure. tag-select-0300 */ if( p->pPrior ){ rc = multiSelect(pParse, p, pDest); #if TREETRACE_ENABLED TREETRACE(0x400,pParse,p,("end compound-select processing\n")); if( (sqlite3TreeTrace & 0x400)!=0 && ExplainQueryPlanParent(pParse)==0 ){ sqlite3TreeViewSelect(0, p, 0); } #endif if( p->pNext==0 ) ExplainQueryPlanPop(pParse); return rc; } #endif /* Do the WHERE-clause constant propagation optimization if this is ** a join. No need to spend time on this operation for non-join queries ** as the equivalent optimization will be handled by query planner in ** sqlite3WhereBegin(). tag-select-0330 */ if( p->pWhere!=0 && p->pWhere->op==TK_AND && OptimizationEnabled(db, SQLITE_PropagateConst) && propagateConstants(pParse, p) ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x2000 ){ TREETRACE(0x2000,pParse,p,("After constant propagation:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif }else{ TREETRACE(0x2000,pParse,p,("Constant propagation not helpful\n")); } /* tag-select-0350 */ if( OptimizationEnabled(db, SQLITE_QueryFlattener|SQLITE_CountOfView) && countOfViewOptimization(pParse, p) ){ if( db->mallocFailed ) goto select_end; pTabList = p->pSrc; } /* Loop over all terms in the FROM clause and do two things for each term: ** ** (1) Authorize unreferenced tables ** (2) Generate code for all sub-queries ** ** tag-select-0400 */ for(i=0; i<pTabList->nSrc; i++){ SrcItem *pItem = &pTabList->a[i]; SrcItem *pPrior; SelectDest dest; Subquery *pSubq; Select *pSub; #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) const char *zSavedAuthContext; #endif /* Authorized unreferenced tables. tag-select-0410 ** ** Issue SQLITE_READ authorizations with a fake column name for any ** tables that are referenced but from which no values are extracted. ** Examples of where these kinds of null SQLITE_READ authorizations ** would occur: ** ** SELECT count(*) FROM t1; -- SQLITE_READ t1."" ** SELECT t1.* FROM t1, t2; -- SQLITE_READ t2."" ** ** The fake column name is an empty string. It is possible for a table to ** have a column named by the empty string, in which case there is no way to ** distinguish between an unreferenced table and an actual reference to the ** "" column. The original design was for the fake column name to be a NULL, ** which would be unambiguous. But legacy authorization callbacks might ** assume the column name is non-NULL and segfault. The use of an empty ** string for the fake column name seems safer. */ if( pItem->colUsed==0 && pItem->zName!=0 ){ const char *zDb; if( pItem->fg.fixedSchema ){ int iDb = sqlite3SchemaToIndex(pParse->db, pItem->u4.pSchema); zDb = db->aDb[iDb].zDbSName; }else if( pItem->fg.isSubquery ){ zDb = 0; }else{ zDb = pItem->u4.zDatabase; } sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", zDb); } #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Generate code for all sub-queries in the FROM clause */ if( pItem->fg.isSubquery==0 ) continue; pSubq = pItem->u4.pSubq; assert( pSubq!=0 ); pSub = pSubq->pSelect; /* The code for a subquery should only be generated once. */ if( pSubq->addrFillSub!=0 ) continue; /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select ** may contain expression trees of at most ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit ** more conservative than necessary, but much easier than enforcing ** an exact limit. */ pParse->nHeight += sqlite3SelectExprHeight(p); /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. ** This is the "predicate push-down optimization". tag-select-0420 */ 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( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 ); }else{ TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); } /* Convert unused result columns of the subquery into simple NULL ** expressions, to avoid unneeded searching and computation. ** tag-select-0440 */ 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 byte-code to implement the subquery tag-select-0480 */ if( fromClauseTermCanBeCoroutine(pParse, pTabList, i, p->selFlags) ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. tag-select-0482 */ int addrTop = sqlite3VdbeCurrentAddr(v)+1; pSubq->regReturn = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_InitCoroutine, pSubq->regReturn, 0, addrTop); VdbeComment((v, "%!S", pItem)); pSubq->addrFillSub = addrTop; sqlite3SelectDestInit(&dest, SRT_Coroutine, pSubq->regReturn); ExplainQueryPlan((pParse, 1, "CO-ROUTINE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pSTab->nRowLogEst = pSub->nSelectRow; pItem->fg.viaCoroutine = 1; pSubq->regResult = dest.iSdst; sqlite3VdbeEndCoroutine(v, pSubq->regReturn); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeJumpHere(v, addrTop-1); sqlite3ClearTempRegCache(pParse); }else if( pItem->fg.isCte && pItem->u2.pCteUse->addrM9e>0 ){ /* This is a CTE for which materialization code has already been ** generated. Invoke the subroutine to compute the materialization, ** then make the pItem->iCursor be a copy of the ephemeral table that ** holds the result of the materialization. tag-select-0484 */ CteUse *pCteUse = pItem->u2.pCteUse; sqlite3VdbeAddOp2(v, OP_Gosub, pCteUse->regRtn, pCteUse->addrM9e); if( pItem->iCursor!=pCteUse->iCur ){ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pCteUse->iCur); VdbeComment((v, "%!S", pItem)); } pSub->nSelectRow = pCteUse->nRowEst; }else if( (pPrior = isSelfJoinView(pTabList, pItem, 0, i))!=0 ){ /* This view has already been materialized by a prior entry in ** this same FROM clause. Reuse it. tag-select-0486 */ Subquery *pPriorSubq; assert( pPrior->fg.isSubquery ); pPriorSubq = pPrior->u4.pSubq; assert( pPriorSubq!=0 ); if( pPriorSubq->addrFillSub ){ sqlite3VdbeAddOp2(v, OP_Gosub, pPriorSubq->regReturn, pPriorSubq->addrFillSub); } sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); pSub->nSelectRow = pPriorSubq->pSelect->nSelectRow; }else{ /* Materialize the view. If the view is not correlated, generate a ** subroutine to do the materialization so that subsequent uses of ** the same view can reuse the materialization. tag-select-0488 */ int topAddr; int onceAddr = 0; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int addrExplain; #endif pSubq->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp0(v, OP_Goto); pSubq->addrFillSub = topAddr+1; pItem->fg.isMaterialized = 1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); VdbeComment((v, "materialize %!S", pItem)); }else{ VdbeNoopComment((v, "materialize %!S", pItem)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); ExplainQueryPlan2(addrExplain, (pParse, 1, "MATERIALIZE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pSTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); sqlite3VdbeAddOp2(v, OP_Return, pSubq->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeScanStatusRange(v, addrExplain, addrExplain, -1); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ CteUse *pCteUse = pItem->u2.pCteUse; pCteUse->addrM9e = pSubq->addrFillSub; pCteUse->regRtn = pSubq->regReturn; pCteUse->iCur = pItem->iCursor; pCteUse->nRowEst = pSub->nSelectRow; } } if( db->mallocFailed ) goto select_end; pParse->nHeight -= sqlite3SelectExprHeight(p); pParse->zAuthContext = zSavedAuthContext; |
︙ | ︙ | |||
150959 150960 150961 150962 150963 150964 150965 | #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x8000 ){ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif | > > | | 151402 151403 151404 151405 151406 151407 151408 151409 151410 151411 151412 151413 151414 151415 151416 151417 151418 | #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x8000 ){ TREETRACE(0x8000,pParse,p,("After all FROM-clause analysis:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif /* tag-select-0500 ** ** If the query is DISTINCT with an ORDER BY but is not an aggregate, and ** if the select-list is the same as the ORDER BY list, then this query ** can be rewritten as a GROUP BY. In other words, this: ** ** SELECT DISTINCT xyz FROM ... ORDER BY xyz ** ** is transformed to: ** |
︙ | ︙ | |||
151009 151010 151011 151012 151013 151014 151015 | /* If there is an ORDER BY clause, then create an ephemeral index to ** do the sorting. But this sorting ephemeral index might end up ** being unused if the data can be extracted in pre-sorted order. ** If that is the case, then the OP_OpenEphemeral instruction will be ** changed to an OP_Noop once we figure out that the sorting index is ** not needed. The sSort.addrSortIndex variable is used to facilitate | | > | | | | 151454 151455 151456 151457 151458 151459 151460 151461 151462 151463 151464 151465 151466 151467 151468 151469 151470 151471 151472 151473 151474 151475 151476 151477 151478 151479 151480 151481 151482 151483 151484 151485 151486 151487 151488 151489 151490 151491 151492 151493 151494 151495 151496 151497 151498 151499 151500 151501 151502 151503 151504 151505 151506 151507 151508 151509 151510 151511 151512 151513 151514 151515 151516 151517 151518 151519 151520 151521 151522 151523 151524 151525 151526 151527 151528 151529 151530 | /* If there is an ORDER BY clause, then create an ephemeral index to ** do the sorting. But this sorting ephemeral index might end up ** being unused if the data can be extracted in pre-sorted order. ** If that is the case, then the OP_OpenEphemeral instruction will be ** changed to an OP_Noop once we figure out that the sorting index is ** not needed. The sSort.addrSortIndex variable is used to facilitate ** that change. tag-select-0600 */ if( sSort.pOrderBy ){ KeyInfo *pKeyInfo; pKeyInfo = sqlite3KeyInfoFromExprList( pParse, sSort.pOrderBy, 0, pEList->nExpr); sSort.iECursor = pParse->nTab++; sSort.addrSortIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0, (char*)pKeyInfo, P4_KEYINFO ); }else{ sSort.addrSortIndex = -1; } /* If the output is destined for a temporary table, open that table. ** tag-select-0630 */ if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr); if( p->selFlags & SF_NestedFrom ){ /* Delete or NULL-out result columns that will never be used */ int ii; for(ii=pEList->nExpr-1; ii>0 && pEList->a[ii].fg.bUsed==0; ii--){ sqlite3ExprDelete(db, pEList->a[ii].pExpr); sqlite3DbFree(db, pEList->a[ii].zEName); pEList->nExpr--; } for(ii=0; ii<pEList->nExpr; ii++){ if( pEList->a[ii].fg.bUsed==0 ) pEList->a[ii].pExpr->op = TK_NULL; } } } /* Set the limiter. tag-select-0650 */ iEnd = sqlite3VdbeMakeLabel(pParse); if( (p->selFlags & SF_FixedLimit)==0 ){ p->nSelectRow = 320; /* 4 billion rows */ } if( p->pLimit ) computeLimitRegisters(pParse, p, iEnd); if( p->iLimit==0 && sSort.addrSortIndex>=0 ){ sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen); sSort.sortFlags |= SORTFLAG_UseSorter; } /* Open an ephemeral index to use for the distinct set. tag-select-0680 */ if( p->selFlags & SF_Distinct ){ sDistinct.tabTnct = pParse->nTab++; sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, sDistinct.tabTnct, 0, 0, (char*)sqlite3KeyInfoFromExprList(pParse, p->pEList,0,0), P4_KEYINFO); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED; }else{ sDistinct.eTnctType = WHERE_DISTINCT_NOOP; } if( !isAgg && pGroupBy==0 ){ /* No aggregate functions and no GROUP BY clause. tag-select-0700 */ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0) | (p->selFlags & SF_FixedLimit); #ifndef SQLITE_OMIT_WINDOWFUNC Window *pWin = p->pWin; /* Main window object (or NULL) */ if( pWin ){ sqlite3WindowCodeInit(pParse, p); } |
︙ | ︙ | |||
151143 151144 151145 151146 151147 151148 151149 | /* End the database scan loop. */ TREETRACE(0x2,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); } }else{ | | | | 151589 151590 151591 151592 151593 151594 151595 151596 151597 151598 151599 151600 151601 151602 151603 151604 | /* End the database scan loop. */ TREETRACE(0x2,pParse,p,("WhereEnd\n")); sqlite3WhereEnd(pWInfo); } }else{ /* This case is for when there exist aggregate functions or a GROUP BY ** clause or both. tag-select-0800 */ NameContext sNC; /* Name context for processing aggregate information */ int iAMem; /* First Mem address for storing current GROUP BY */ int iBMem; /* First Mem address for previous GROUP BY */ int iUseFlag; /* Mem address holding flag indicating that at least ** one row of the input to the aggregator has been ** processed */ int iAbortFlag; /* Mem address which causes query abort if positive */ |
︙ | ︙ | |||
151263 151264 151265 151266 151267 151268 151269 | } printAggInfo(pAggInfo); } #endif /* Processing for aggregates with GROUP BY is very different and | | | 151709 151710 151711 151712 151713 151714 151715 151716 151717 151718 151719 151720 151721 151722 151723 | } printAggInfo(pAggInfo); } #endif /* Processing for aggregates with GROUP BY is very different and ** much more complex than aggregates without a GROUP BY. tag-select-0810 */ if( pGroupBy ){ KeyInfo *pKeyInfo; /* Keying information for the group by clause */ int addr1; /* A-vs-B comparison jump */ int addrOutputRow; /* Start of subroutine that outputs a result row */ int regOutputRow; /* Return address register for output subroutine */ int addrSetAbort; /* Set the abort flag and return */ |
︙ | ︙ | |||
151560 151561 151562 151563 151564 151565 151566 151567 151568 | if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ struct AggInfo_func *pF = &pAggInfo->aFunc[0]; fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); } } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { Table *pTab; if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ | > > > | | 152006 152007 152008 152009 152010 152011 152012 152013 152014 152015 152016 152017 152018 152019 152020 152021 152022 152023 152024 152025 | if( distFlag!=0 && eDist!=WHERE_DISTINCT_NOOP ){ struct AggInfo_func *pF = &pAggInfo->aFunc[0]; fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); } } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ else { /* Aggregate functions without GROUP BY. tag-select-0820 */ Table *pTab; if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ /* tag-select-0821 ** ** If isSimpleCount() returns a pointer to a Table structure, then ** the SQL statement is of the form: ** ** SELECT count(*) FROM <tbl> ** ** where the Table structure returned represents table <tbl>. ** ** This statement is so common that it is optimized specially. The |
︙ | ︙ | |||
151621 151622 151623 151624 151625 151626 151627 151628 151629 151630 151631 151632 151633 151634 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } assignAggregateRegisters(pParse, pAggInfo); sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0)); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else{ int regAcc = 0; /* "populate accumulators" flag */ ExprList *pDistinct = 0; u16 distFlag = 0; int eDist; /* If there are accumulator registers but no min() or max() functions ** without FILTER clauses, allocate register regAcc. Register regAcc | > > | 152070 152071 152072 152073 152074 152075 152076 152077 152078 152079 152080 152081 152082 152083 152084 152085 | sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); } assignAggregateRegisters(pParse, pAggInfo); sqlite3VdbeAddOp2(v, OP_Count, iCsr, AggInfoFuncReg(pAggInfo,0)); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else{ /* The general case of an aggregate query without GROUP BY ** tag-select-0822 */ int regAcc = 0; /* "populate accumulators" flag */ ExprList *pDistinct = 0; u16 distFlag = 0; int eDist; /* If there are accumulator registers but no min() or max() functions ** without FILTER clauses, allocate register regAcc. Register regAcc |
︙ | ︙ | |||
151709 151710 151711 151712 151713 151714 151715 | } /* endif aggregate query */ if( sDistinct.eTnctType==WHERE_DISTINCT_UNORDERED ){ explainTempTable(pParse, "DISTINCT"); } /* If there is an ORDER BY clause, then we need to sort the results | | | 152160 152161 152162 152163 152164 152165 152166 152167 152168 152169 152170 152171 152172 152173 152174 | } /* endif aggregate query */ if( sDistinct.eTnctType==WHERE_DISTINCT_UNORDERED ){ explainTempTable(pParse, "DISTINCT"); } /* If there is an ORDER BY clause, then we need to sort the results ** and send them to the callback one by one. tag-select-0900 */ if( sSort.pOrderBy ){ assert( p->pEList==pEList ); generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest); } /* Jump here to skip this query |
︙ | ︙ | |||
151732 151733 151734 151735 151736 151737 151738 151739 151740 151741 151742 151743 151744 151745 | ** successful coding of the SELECT. */ select_end: assert( db->mallocFailed==0 || db->mallocFailed==1 ); assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x20 ){ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); printAggInfo(pAggInfo); } #endif | > | 152183 152184 152185 152186 152187 152188 152189 152190 152191 152192 152193 152194 152195 152196 152197 | ** successful coding of the SELECT. */ select_end: assert( db->mallocFailed==0 || db->mallocFailed==1 ); assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG /* Internal self-checks. tag-select-1000 */ if( pAggInfo && !db->mallocFailed ){ #if TREETRACE_ENABLED if( sqlite3TreeTrace & 0x20 ){ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); printAggInfo(pAggInfo); } #endif |
︙ | ︙ | |||
152121 152122 152123 152124 152125 152126 152127 | ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... ** ^^^^^^^^ ** ** To maintain backwards compatibility, ignore the database ** name on pTableName if we are reparsing out of the schema table */ if( db->init.busy && iDb!=1 ){ | > > | | | 152573 152574 152575 152576 152577 152578 152579 152580 152581 152582 152583 152584 152585 152586 152587 152588 152589 152590 | ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... ** ^^^^^^^^ ** ** To maintain backwards compatibility, ignore the database ** name on pTableName if we are reparsing out of the schema table */ if( db->init.busy && iDb!=1 ){ assert( pTableName->a[0].fg.fixedSchema==0 ); assert( pTableName->a[0].fg.isSubquery==0 ); sqlite3DbFree(db, pTableName->a[0].u4.zDatabase); pTableName->a[0].u4.zDatabase = 0; } /* If the trigger name was unqualified, and the table is a temp table, ** then set iDb to 1 to create the trigger in the temporary database. ** If sqlite3SrcListLookup() returns 0, indicating the table does not ** exist, the error is caught by the block below. */ |
︙ | ︙ | |||
152600 152601 152602 152603 152604 152605 152606 | if( db->mallocFailed ) goto drop_trigger_cleanup; if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto drop_trigger_cleanup; } assert( pName->nSrc==1 ); | > | | 153054 153055 153056 153057 153058 153059 153060 153061 153062 153063 153064 153065 153066 153067 153068 153069 | if( db->mallocFailed ) goto drop_trigger_cleanup; if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto drop_trigger_cleanup; } assert( pName->nSrc==1 ); assert( pName->a[0].fg.fixedSchema==0 && pName->a[0].fg.isSubquery==0 ); zDb = pName->a[0].u4.zDatabase; zName = pName->a[0].zName; assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite3DbIsNamed(db, j, zDb)==0 ) continue; assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName); |
︙ | ︙ | |||
152837 152838 152839 152840 152841 152842 152843 | pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); assert( pSrc==0 || pSrc->nSrc==1 ); assert( zName || pSrc==0 ); if( pSrc ){ Schema *pSchema = pStep->pTrig->pSchema; pSrc->a[0].zName = zName; if( pSchema!=db->aDb[1].pSchema ){ | > | > | 153292 153293 153294 153295 153296 153297 153298 153299 153300 153301 153302 153303 153304 153305 153306 153307 153308 | pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); assert( pSrc==0 || pSrc->nSrc==1 ); assert( zName || pSrc==0 ); if( pSrc ){ Schema *pSchema = pStep->pTrig->pSchema; pSrc->a[0].zName = zName; if( pSchema!=db->aDb[1].pSchema ){ assert( pSrc->a[0].fg.fixedSchema || pSrc->a[0].u4.zDatabase==0 ); pSrc->a[0].u4.pSchema = pSchema; pSrc->a[0].fg.fixedSchema = 1; } if( pStep->pFrom ){ SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ Select *pSubquery; Token as; pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); |
︙ | ︙ | |||
152950 152951 152952 152953 152954 152955 152956 | static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ int i; SrcList *pSrc; assert( pSelect!=0 ); pSrc = pSelect->pSrc; assert( pSrc!=0 ); for(i=0; i<pSrc->nSrc; i++){ | | | 153407 153408 153409 153410 153411 153412 153413 153414 153415 153416 153417 153418 153419 153420 153421 | static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ int i; SrcList *pSrc; assert( pSelect!=0 ); pSrc = pSelect->pSrc; assert( pSrc!=0 ); for(i=0; i<pSrc->nSrc; i++){ if( pSrc->a[i].pSTab==pWalker->u.pTab ){ testcase( pSelect->selFlags & SF_Correlated ); pSelect->selFlags |= SF_Correlated; pWalker->eCode = 1; break; } } return WRC_Continue; |
︙ | ︙ | |||
153021 153022 153023 153024 153025 153026 153027 | return; } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; | | | 153478 153479 153480 153481 153482 153483 153484 153485 153486 153487 153488 153489 153490 153491 153492 | return; } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pSTab = pTab; sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } |
︙ | ︙ | |||
153732 153733 153734 153735 153736 153737 153738 | SelectDest dest; Select *pSelect = 0; ExprList *pList = 0; ExprList *pGrp = 0; Expr *pLimit2 = 0; ExprList *pOrderBy2 = 0; sqlite3 *db = pParse->db; | | | 154189 154190 154191 154192 154193 154194 154195 154196 154197 154198 154199 154200 154201 154202 154203 | SelectDest dest; Select *pSelect = 0; ExprList *pList = 0; ExprList *pGrp = 0; Expr *pLimit2 = 0; ExprList *pOrderBy2 = 0; sqlite3 *db = pParse->db; Table *pTab = pTabList->a[0].pSTab; SrcList *pSrc; Expr *pWhere2; int eDest; #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT if( pOrderBy && pLimit==0 ) { sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on UPDATE"); |
︙ | ︙ | |||
153756 153757 153758 153759 153760 153761 153762 | pSrc = sqlite3SrcListDup(db, pTabList, 0); pWhere2 = sqlite3ExprDup(db, pWhere, 0); assert( pTabList->nSrc>1 ); if( pSrc ){ assert( pSrc->a[0].fg.notCte ); pSrc->a[0].iCursor = -1; | | | | 154213 154214 154215 154216 154217 154218 154219 154220 154221 154222 154223 154224 154225 154226 154227 154228 | pSrc = sqlite3SrcListDup(db, pTabList, 0); pWhere2 = sqlite3ExprDup(db, pWhere, 0); assert( pTabList->nSrc>1 ); if( pSrc ){ assert( pSrc->a[0].fg.notCte ); pSrc->a[0].iCursor = -1; pSrc->a[0].pSTab->nTabRef--; pSrc->a[0].pSTab = 0; } if( pPk ){ for(i=0; i<pPk->nKeyCol; i++){ Expr *pNew = exprRowColumn(pParse, pPk->aiColumn[i]); #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT if( pLimit ){ pGrp = sqlite3ExprListAppend(pParse, pGrp, sqlite3ExprDup(db, pNew, 0)); |
︙ | ︙ | |||
155005 155006 155007 155008 155009 155010 155011 | ExprList *pTarget; /* The conflict-target clause */ Expr *pTerm; /* One term of the conflict-target clause */ NameContext sNC; /* Context for resolving symbolic names */ Expr sCol[2]; /* Index column converted into an Expr */ int nClause = 0; /* Counter of ON CONFLICT clauses */ assert( pTabList->nSrc==1 ); | | | | 155462 155463 155464 155465 155466 155467 155468 155469 155470 155471 155472 155473 155474 155475 155476 155477 155478 155479 155480 155481 155482 155483 155484 155485 155486 155487 155488 155489 155490 155491 155492 155493 155494 155495 | ExprList *pTarget; /* The conflict-target clause */ Expr *pTerm; /* One term of the conflict-target clause */ NameContext sNC; /* Context for resolving symbolic names */ Expr sCol[2]; /* Index column converted into an Expr */ int nClause = 0; /* Counter of ON CONFLICT clauses */ assert( pTabList->nSrc==1 ); assert( pTabList->a[0].pSTab!=0 ); assert( pUpsert!=0 ); assert( pUpsert->pUpsertTarget!=0 ); /* Resolve all symbolic names in the conflict-target clause, which ** includes both the list of columns and the optional partial-index ** WHERE clause. */ memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pParse; sNC.pSrcList = pTabList; for(; pUpsert && pUpsert->pUpsertTarget; pUpsert=pUpsert->pNextUpsert, nClause++){ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); if( rc ) return rc; rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere); if( rc ) return rc; /* Check to see if the conflict target matches the rowid. */ pTab = pTabList->a[0].pSTab; pTarget = pUpsert->pUpsertTarget; iCursor = pTabList->a[0].iCursor; if( HasRowid(pTab) && pTarget->nExpr==1 && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN && pTerm->iColumn==XN_ROWID ){ |
︙ | ︙ | |||
155395 155396 155397 155398 155399 155400 155401 155402 155403 155404 155405 155406 155407 155408 | Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); | > > > | 155852 155853 155854 155855 155856 155857 155858 155859 155860 155861 155862 155863 155864 155865 155866 155867 155868 | Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */ u64 iRandom; /* Random value used for zDbVacuum[] */ char zDbVacuum[42]; /* Name of the ATTACH-ed database used for vacuum */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); |
︙ | ︙ | |||
155435 155436 155437 155438 155439 155440 155441 | | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); | | | > > | | | 155895 155896 155897 155898 155899 155900 155901 155902 155903 155904 155905 155906 155907 155908 155909 155910 155911 155912 155913 155914 155915 155916 155917 155918 155919 155920 155921 155922 155923 155924 155925 155926 155927 155928 155929 155930 155931 | | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; zDbMain = db->aDb[iDb].zDbSName; pMain = db->aDb[iDb].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); /* Attach the temporary database as 'vacuum_XXXXXX'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before ** sqlite3BtreeCopyFile() is called. ** ** An optimization would be to use a non-journaled pager. ** (Later:) I tried setting "PRAGMA vacuum_XXXXXX.journal_mode=OFF" but ** that actually made the VACUUM run slower. Very little journalling ** actually occurs when doing a vacuum since the vacuum_db is initially ** empty. Only the journal header is written. Apparently it takes more ** time to parse and run the PRAGMA to turn journalling off than it does ** to write the journal header file. */ sqlite3_randomness(sizeof(iRandom),&iRandom); sqlite3_snprintf(sizeof(zDbVacuum), zDbVacuum, "vacuum_%016llx", iRandom); nDb = db->nDb; rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS %s", zOut, zDbVacuum); db->openFlags = saved_openFlags; if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); pTemp = pDb->pBt; if( pOut ){ sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); i64 sz = 0; if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ rc = SQLITE_ERROR; sqlite3SetString(pzErrMsg, db, "output file already exists"); |
︙ | ︙ | |||
155532 155533 155534 155535 155536 155537 155538 | db->init.iDb = 0; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execSqlF(db, pzErrMsg, | | | | | | | 155994 155995 155996 155997 155998 155999 156000 156001 156002 156003 156004 156005 156006 156007 156008 156009 156010 156011 156012 156013 156014 156015 156016 156017 156018 156019 156020 156021 156022 156023 156024 156025 156026 156027 156028 | db->init.iDb = 0; /* Loop through the tables in the main database. For each, do ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execSqlF(db, pzErrMsg, "SELECT'INSERT INTO %s.'||quote(name)" "||' SELECT*FROM\"%w\".'||quote(name)" "FROM %s.sqlite_schema " "WHERE type='table'AND coalesce(rootpage,1)>0", zDbVacuum, zDbMain, zDbVacuum ); assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); db->mDbFlags &= ~DBFLAG_Vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Copy the triggers, views, and virtual tables from the main database ** over to the temporary database. None of these objects has any ** associated storage, so all we have to do is copy their entries ** from the schema table. */ rc = execSqlF(db, pzErrMsg, "INSERT INTO %s.sqlite_schema" " SELECT*FROM \"%w\".sqlite_schema" " WHERE type IN('view','trigger')" " OR(type='table'AND rootpage=0)", zDbVacuum, zDbMain ); if( rc ) goto end_of_vacuum; /* At this point, there is a write transaction open on both the ** vacuum database and the main database. Assuming no error occurs, ** both transactions are closed by this block - the main database ** transaction by sqlite3BtreeCopyFile() and the other by an explicit |
︙ | ︙ | |||
157196 157197 157198 157199 157200 157201 157202 157203 157204 157205 157206 157207 157208 157209 | Index *pIndex; /* Index used, or NULL */ ExprList *pOrderBy; /* ORDER BY clause if this is really a subquery */ } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ u32 bOmitOffset : 1; /* True to let virtual table handle offset */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ } vtab; } u; u32 wsFlags; /* WHERE_* flags describing the plan */ | > | 157658 157659 157660 157661 157662 157663 157664 157665 157666 157667 157668 157669 157670 157671 157672 | Index *pIndex; /* Index used, or NULL */ ExprList *pOrderBy; /* ORDER BY clause if this is really a subquery */ } btree; struct { /* Information for virtual tables */ int idxNum; /* Index number */ u32 needFree : 1; /* True if sqlite3_free(idxStr) is needed */ u32 bOmitOffset : 1; /* True to let virtual table handle offset */ u32 bIdxNumHex : 1; /* Show idxNum as hex in EXPLAIN QUERY PLAN */ i8 isOrdered; /* True if satisfies ORDER BY */ u16 omitMask; /* Terms that may be omitted */ char *idxStr; /* Index identifier string */ u32 mHandleIn; /* Terms to handle as IN(...) instead of == */ } vtab; } u; u32 wsFlags; /* WHERE_* flags describing the plan */ |
︙ | ︙ | |||
157830 157831 157832 157833 157834 157835 157836 | if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ const char *zFmt = 0; Index *pIdx; assert( pLoop->u.btree.pIndex!=0 ); pIdx = pLoop->u.btree.pIndex; assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); | | | 158293 158294 158295 158296 158297 158298 158299 158300 158301 158302 158303 158304 158305 158306 158307 | if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ const char *zFmt = 0; Index *pIdx; assert( pLoop->u.btree.pIndex!=0 ); pIdx = pLoop->u.btree.pIndex; assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); if( !HasRowid(pItem->pSTab) && IsPrimaryKeyIndex(pIdx) ){ if( isSearch ){ zFmt = "PRIMARY KEY"; } }else if( flags & WHERE_PARTIALIDX ){ zFmt = "AUTOMATIC PARTIAL COVERING INDEX"; }else if( flags & WHERE_AUTO_INDEX ){ zFmt = "AUTOMATIC COVERING INDEX"; |
︙ | ︙ | |||
157873 157874 157875 157876 157877 157878 157879 | assert( flags&WHERE_TOP_LIMIT); cRangeOp = '<'; } sqlite3_str_appendf(&str, "%c?)", cRangeOp); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ | > | > | 158336 158337 158338 158339 158340 158341 158342 158343 158344 158345 158346 158347 158348 158349 158350 158351 158352 | assert( flags&WHERE_TOP_LIMIT); cRangeOp = '<'; } sqlite3_str_appendf(&str, "%c?)", cRangeOp); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ sqlite3_str_appendall(&str, " VIRTUAL TABLE INDEX "); sqlite3_str_appendf(&str, pLoop->u.vtab.bIdxNumHex ? "0x%x:%s" : "%d:%s", pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif if( pItem->fg.jointype & JT_LEFT ){ sqlite3_str_appendf(&str, " LEFT-JOIN"); } #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS |
︙ | ︙ | |||
157927 157928 157929 157930 157931 157932 157933 | char zBuf[100]; /* Initial space for EQP output string */ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); str.printfFlags = SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); pLoop = pLevel->pWLoop; if( pLoop->wsFlags & WHERE_IPK ){ | | | 158392 158393 158394 158395 158396 158397 158398 158399 158400 158401 158402 158403 158404 158405 158406 | char zBuf[100]; /* Initial space for EQP output string */ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); str.printfFlags = SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&str, "BLOOM FILTER ON %S (", pItem); pLoop = pLevel->pWLoop; if( pLoop->wsFlags & WHERE_IPK ){ const Table *pTab = pItem->pSTab; if( pTab->iPKey>=0 ){ sqlite3_str_appendf(&str, "%s=?", pTab->aCol[pTab->iPKey].zCnName); }else{ sqlite3_str_appendf(&str, "rowid=?"); } }else{ for(i=pLoop->nSkip; i<pLoop->u.btree.nEq; i++){ |
︙ | ︙ | |||
157990 157991 157992 157993 157994 157995 157996 | 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); } }else{ | > > | | 158455 158456 158457 158458 158459 158460 158461 158462 158463 158464 158465 158466 158467 158468 158469 158470 158471 | 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); } }else{ int addr; assert( pSrclist->a[pLvl->iFrom].fg.isSubquery ); addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub; VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine ); assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr ); sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1); } } } |
︙ | ︙ | |||
159127 159128 159129 159130 159131 159132 159133 | pWC = &pWInfo->sWC; db = pParse->db; pLoop = pLevel->pWLoop; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; | | > | 159594 159595 159596 159597 159598 159599 159600 159601 159602 159603 159604 159605 159606 159607 159608 159609 | pWC = &pWInfo->sWC; db = pParse->db; pLoop = pLevel->pWLoop; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; VdbeModuleComment((v, "Begin WHERE-loop%d: %s", iLevel, pTabItem->pSTab->zName)); #if WHERETRACE_ENABLED /* 0x4001 */ if( sqlite3WhereTrace & 0x1 ){ sqlite3DebugPrintf("Coding level %d of %d: notReady=%llx iFrom=%d\n", iLevel, pWInfo->nLevel, (u64)notReady, pLevel->iFrom); if( sqlite3WhereTrace & 0x1000 ){ sqlite3WhereLoopPrint(pLoop, pWC); } |
︙ | ︙ | |||
159182 159183 159184 159185 159186 159187 159188 | if( pWInfo->a[j].iLeftJoin ) break; if( pWInfo->a[j].pRJ ) break; } addrHalt = pWInfo->a[j].addrBrk; /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ | | > > > > | | | 159650 159651 159652 159653 159654 159655 159656 159657 159658 159659 159660 159661 159662 159663 159664 159665 159666 159667 159668 159669 159670 159671 159672 | if( pWInfo->a[j].iLeftJoin ) break; if( pWInfo->a[j].pRJ ) break; } addrHalt = pWInfo->a[j].addrBrk; /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ int regYield; Subquery *pSubq; assert( pTabItem->fg.isSubquery && pTabItem->u4.pSubq!=0 ); pSubq = pTabItem->u4.pSubq; regYield = pSubq->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); VdbeComment((v, "next row of %s", pTabItem->pSTab->zName)); pLevel->op = OP_Goto; }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){ /* Case 1: The table is a virtual-table. Use the VFilter and VNext ** to access the data. |
︙ | ︙ | |||
159915 159916 159917 159918 159919 159920 159921 | int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(pParse);/* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ | | | 160387 160388 160389 160390 160391 160392 160393 160394 160395 160396 160397 160398 160399 160400 160401 | int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(pParse);/* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ Table *pTab = pTabItem->pSTab; pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->eOperator & WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; pLevel->op = OP_Return; |
︙ | ︙ | |||
160374 160375 160376 160377 160378 160379 160380 | WhereRightJoin *pRJ = pLevel->pRJ; /* pTab is the right-hand table of the RIGHT JOIN. Generate code that ** will record that the current row of that table has been matched at ** least once. This is accomplished by storing the PK for the row in ** both the iMatch index and the regBloom Bloom filter. */ | | | 160846 160847 160848 160849 160850 160851 160852 160853 160854 160855 160856 160857 160858 160859 160860 | WhereRightJoin *pRJ = pLevel->pRJ; /* pTab is the right-hand table of the RIGHT JOIN. Generate code that ** will record that the current row of that table has been matched at ** least once. This is accomplished by storing the PK for the row in ** both the iMatch index and the regBloom Bloom filter. */ pTab = pWInfo->pTabList->a[pLevel->iFrom].pSTab; if( HasRowid(pTab) ){ r = sqlite3GetTempRange(pParse, 2); sqlite3ExprCodeGetColumnOfTable(v, pTab, pLevel->iTabCur, -1, r+1); nPk = 1; }else{ int iPk; Index *pPk = sqlite3PrimaryKeyIndex(pTab); |
︙ | ︙ | |||
160481 160482 160483 160484 160485 160486 160487 | WhereInfo *pSubWInfo; WhereLoop *pLoop = pLevel->pWLoop; SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; SrcList sFrom; Bitmask mAll = 0; int k; | | > > > > | | | 160953 160954 160955 160956 160957 160958 160959 160960 160961 160962 160963 160964 160965 160966 160967 160968 160969 160970 160971 160972 160973 160974 160975 160976 160977 160978 160979 160980 160981 160982 160983 | WhereInfo *pSubWInfo; WhereLoop *pLoop = pLevel->pWLoop; SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; SrcList sFrom; Bitmask mAll = 0; int k; ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, pRJ->regReturn); for(k=0; k<iLevel; k++){ int iIdxCur; SrcItem *pRight; assert( pWInfo->a[k].pWLoop->iTab == pWInfo->a[k].iFrom ); pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; if( pRight->fg.viaCoroutine ){ Subquery *pSubq; assert( pRight->fg.isSubquery && pRight->u4.pSubq!=0 ); pSubq = pRight->u4.pSubq; assert( pSubq->pSelect!=0 && pSubq->pSelect->pEList!=0 ); sqlite3VdbeAddOp3( v, OP_Null, 0, pSubq->regResult, pSubq->regResult + pSubq->pSelect->pEList->nExpr-1 ); } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur); } |
︙ | ︙ | |||
160531 160532 160533 160534 160535 160536 160537 | WHERE_RIGHT_JOIN, 0); if( pSubWInfo ){ int iCur = pLevel->iTabCur; int r = ++pParse->nMem; int nPk; int jmp; int addrCont = sqlite3WhereContinueLabel(pSubWInfo); | | | 161007 161008 161009 161010 161011 161012 161013 161014 161015 161016 161017 161018 161019 161020 161021 | WHERE_RIGHT_JOIN, 0); if( pSubWInfo ){ int iCur = pLevel->iTabCur; int r = ++pParse->nMem; int nPk; int jmp; int addrCont = sqlite3WhereContinueLabel(pSubWInfo); Table *pTab = pTabItem->pSTab; if( HasRowid(pTab) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, -1, r); nPk = 1; }else{ int iPk; Index *pPk = sqlite3PrimaryKeyIndex(pTab); nPk = pPk->nKeyCol; |
︙ | ︙ | |||
160783 160784 160785 160786 160787 160788 160789 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ assert( !ExprHasProperty(pRight, EP_IntValue) ); z = (u8*)pRight->u.zToken; } if( z ){ | | > > > > | > > > > > | | 161259 161260 161261 161262 161263 161264 161265 161266 161267 161268 161269 161270 161271 161272 161273 161274 161275 161276 161277 161278 161279 161280 161281 161282 161283 161284 161285 161286 161287 161288 161289 161290 161291 161292 161293 161294 161295 161296 161297 161298 161299 161300 161301 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ assert( !ExprHasProperty(pRight, EP_IntValue) ); z = (u8*)pRight->u.zToken; } if( z ){ /* Count the number of prefix characters prior to the first wildcard. ** If the underlying database has a UTF16LE encoding, then only consider ** ASCII characters. Note that the encoding of z[] is UTF8 - we are ** dealing with only UTF8 here in this code, but the database engine ** itself might be processing content using a different encoding. */ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; if( c==wc[3] && z[cnt]!=0 ){ cnt++; }else if( c>=0x80 && ENC(db)==SQLITE_UTF16LE ){ cnt--; break; } } /* The optimization is possible only if (1) the pattern does not begin ** with a wildcard and if (2) the non-wildcard prefix does not end with ** an (illegal 0xff) character, or (3) the pattern does not consist of ** a single escape character. The second condition is necessary so ** that we can increment the prefix key to find an upper bound for the ** range search. The third is because the caller assumes that the pattern ** consists of at least one character after all escapes have been ** removed. */ if( (cnt>1 || (cnt>0 && z[0]!=wc[3])) && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; /* A "complete" match if the pattern ends with "*" or "%" */ *pisComplete = c==wc[0] && z[cnt+1]==0 && ENC(db)!=SQLITE_UTF16LE; /* Get the pattern prefix. Remove all escapes from the prefix. */ pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); if( pPrefix ){ int iFrom, iTo; char *zNew; assert( !ExprHasProperty(pPrefix, EP_IntValue) ); |
︙ | ︙ | |||
161521 161522 161523 161524 161525 161526 161527 | mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pGroupBy); mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pOrderBy); mask |= sqlite3WhereExprUsage(pMaskSet, pS->pWhere); mask |= sqlite3WhereExprUsage(pMaskSet, pS->pHaving); if( ALWAYS(pSrc!=0) ){ int i; for(i=0; i<pSrc->nSrc; i++){ | > | > | 162006 162007 162008 162009 162010 162011 162012 162013 162014 162015 162016 162017 162018 162019 162020 162021 162022 | mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pGroupBy); mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pOrderBy); mask |= sqlite3WhereExprUsage(pMaskSet, pS->pWhere); mask |= sqlite3WhereExprUsage(pMaskSet, pS->pHaving); if( ALWAYS(pSrc!=0) ){ int i; for(i=0; i<pSrc->nSrc; i++){ if( pSrc->a[i].fg.isSubquery ){ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].u4.pSubq->pSelect); } if( pSrc->a[i].fg.isUsing==0 ){ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].u3.pOn); } if( pSrc->a[i].fg.isTabFunc ){ mask |= sqlite3WhereExprListUsage(pMaskSet, pSrc->a[i].u1.pFuncArg); } } |
︙ | ︙ | |||
161559 161560 161561 161562 161563 161564 161565 | int j /* Start looking with the j-th pFrom entry */ ){ Index *pIdx; int i; int iCur; do{ iCur = pFrom->a[j].iCursor; | | | 162046 162047 162048 162049 162050 162051 162052 162053 162054 162055 162056 162057 162058 162059 162060 | 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].pSTab->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 && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) ){ |
︙ | ︙ | |||
161603 161604 161605 161606 161607 161608 161609 | aiCurCol[0] = pExpr->iTable; aiCurCol[1] = pExpr->iColumn; return 1; } for(i=0; i<pFrom->nSrc; i++){ Index *pIdx; | | | 162090 162091 162092 162093 162094 162095 162096 162097 162098 162099 162100 162101 162102 162103 162104 | aiCurCol[0] = pExpr->iTable; aiCurCol[1] = pExpr->iColumn; return 1; } for(i=0; i<pFrom->nSrc; i++){ Index *pIdx; for(pIdx=pFrom->a[i].pSTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->aColExpr ){ return exprMightBeIndexed2(pFrom,aiCurCol,pExpr,i); } } } return 0; } |
︙ | ︙ | |||
162191 162192 162193 162194 162195 162196 162197 | ** exist only so that they may be passed to the xBestIndex method of the ** single virtual table in the FROM clause of the SELECT. */ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ if( p->pGroupBy==0 && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ | | | 162678 162679 162680 162681 162682 162683 162684 162685 162686 162687 162688 162689 162690 162691 162692 | ** exist only so that they may be passed to the xBestIndex method of the ** single virtual table in the FROM clause of the SELECT. */ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ assert( p!=0 && p->pLimit!=0 ); /* 1 -- checked by caller */ if( p->pGroupBy==0 && (p->selFlags & (SF_Distinct|SF_Aggregate))==0 /* 2 */ && (p->pSrc->nSrc==1 && IsVirtual(p->pSrc->a[0].pSTab)) /* 3 */ ){ ExprList *pOrderBy = p->pOrderBy; int iCsr = p->pSrc->a[0].iCursor; int ii; /* Check condition (4). Return early if it is not met. */ for(ii=0; ii<pWC->nTerm; ii++){ |
︙ | ︙ | |||
162412 162413 162414 162415 162416 162417 162418 | ){ Table *pTab; int j, k; ExprList *pArgs; Expr *pColRef; Expr *pTerm; if( pItem->fg.isTabFunc==0 ) return; | | | 162899 162900 162901 162902 162903 162904 162905 162906 162907 162908 162909 162910 162911 162912 162913 | ){ Table *pTab; int j, k; ExprList *pArgs; Expr *pColRef; Expr *pTerm; if( pItem->fg.isTabFunc==0 ) return; pTab = pItem->pSTab; assert( pTab!=0 ); pArgs = pItem->u1.pFuncArg; if( pArgs==0 ) return; for(j=k=0; j<pArgs->nExpr; j++){ Expr *pRhs; u32 joinType; while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;} |
︙ | ︙ | |||
163096 163097 163098 163099 163100 163101 163102 | int iBase; /* If there is more than one table or sub-select in the FROM clause of ** this query, then it will not be possible to show that the DISTINCT ** clause is redundant. */ if( pTabList->nSrc!=1 ) return 0; iBase = pTabList->a[0].iCursor; | | | 163583 163584 163585 163586 163587 163588 163589 163590 163591 163592 163593 163594 163595 163596 163597 | int iBase; /* If there is more than one table or sub-select in the FROM clause of ** this query, then it will not be possible to show that the DISTINCT ** clause is redundant. */ if( pTabList->nSrc!=1 ) return 0; iBase = pTabList->a[0].iCursor; pTab = pTabList->a[0].pSTab; /* If any of the expressions is an IPK column on table iBase, then return ** true. Note: The (p->iTable==iBase) part of this test may be false if the ** current SELECT is a correlated sub-query. */ for(i=0; i<pDistinct->nExpr; i++){ Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr); |
︙ | ︙ | |||
163360 163361 163362 163363 163364 163365 163366 | ){ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */ } if( (pTerm->prereqRight & notReady)!=0 ) return 0; assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); leftCol = pTerm->u.x.leftColumn; if( leftCol<0 ) return 0; | | | | 163847 163848 163849 163850 163851 163852 163853 163854 163855 163856 163857 163858 163859 163860 163861 163862 163863 163864 | ){ return 0; /* See https://sqlite.org/forum/forumpost/51e6959f61 */ } if( (pTerm->prereqRight & notReady)!=0 ) return 0; assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); leftCol = pTerm->u.x.leftColumn; if( leftCol<0 ) return 0; aff = pSrc->pSTab->aCol[leftCol].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; testcase( pTerm->pExpr->op==TK_IS ); return columnIsGoodIndexCandidate(pSrc->pSTab, leftCol); } #endif #ifndef SQLITE_OMIT_AUTOMATIC_INDEX #ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
︙ | ︙ | |||
163471 163472 163473 163474 163475 163476 163477 | 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]; | | | 163958 163959 163960 163961 163962 163963 163964 163965 163966 163967 163968 163969 163970 163971 163972 | 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->pSTab; 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 |
︙ | ︙ | |||
163613 163614 163615 163616 163617 163618 163619 | 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 ){ | | > > > > > | | | 164100 164101 164102 164103 164104 164105 164106 164107 164108 164109 164110 164111 164112 164113 164114 164115 164116 164117 164118 164119 164120 164121 164122 164123 164124 | 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; Subquery *pSubq; assert( pSrc->fg.isSubquery ); pSubq = pSrc->u4.pSubq; assert( pSubq!=0 ); regYield = pSubq->regReturn; addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pSubq->addrFillSub); addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); VdbeCoverage(v); VdbeComment((v, "next row of %s", pSrc->pSTab->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; |
︙ | ︙ | |||
163640 163641 163642 163643 163644 163645 163646 163647 163648 163649 163650 | 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, | > | | 164132 164133 164134 164135 164136 164137 164138 164139 164140 164141 164142 164143 164144 164145 164146 164147 164148 164149 164150 164151 | 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 ){ assert( pSrc->fg.isSubquery && pSrc->u4.pSubq!=0 ); sqlite3VdbeChangeP2(v, addrCounter, regBase+n); testcase( pParse->db->mallocFailed ); assert( pLevel->iIdxCur>0 ); translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pSrc->u4.pSubq->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); |
︙ | ︙ | |||
163735 163736 163737 163738 163739 163740 163741 | ** 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 ); | | | 164228 164229 164230 164231 164232 164233 164234 164235 164236 164237 164238 164239 164240 164241 164242 | ** 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->pSTab; assert( pTab!=0 ); sz = sqlite3LogEstToInt(pTab->nRowLogEst); if( sz<10000 ){ sz = 10000; }else if( sz>10000000 ){ sz = 10000000; } |
︙ | ︙ | |||
163766 163767 163768 163769 163770 163771 163772 | 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++){ | | | 164259 164260 164261 164262 164263 164264 164265 164266 164267 164268 164269 164270 164271 164272 164273 | 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->pSTab ); 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); |
︙ | ︙ | |||
163847 163848 163849 163850 163851 163852 163853 | u16 mNoOmit = 0; const Table *pTab; int eDistinct = 0; ExprList *pOrderBy = pWInfo->pOrderBy; WhereClause *p; assert( pSrc!=0 ); | | | 164340 164341 164342 164343 164344 164345 164346 164347 164348 164349 164350 164351 164352 164353 164354 | u16 mNoOmit = 0; const Table *pTab; int eDistinct = 0; ExprList *pOrderBy = pWInfo->pOrderBy; WhereClause *p; assert( pSrc!=0 ); pTab = pSrc->pSTab; assert( pTab!=0 ); assert( IsVirtual(pTab) ); /* Find all WHERE clause constraints referring to this virtual table. ** Mark each term with the TERM_OK flag. Set nTerm to the number of ** terms found. */ |
︙ | ︙ | |||
164855 164856 164857 164858 164859 164860 164861 | ** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ if( pWC ){ WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; | | | 165348 165349 165350 165351 165352 165353 165354 165355 165356 165357 165358 165359 165360 165361 165362 | ** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ if( pWC ){ WhereInfo *pWInfo = pWC->pWInfo; int nb = 1+(pWInfo->pTabList->nSrc+3)/4; SrcItem *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pSTab; Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->zName); }else{ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", |
︙ | ︙ | |||
165843 165844 165845 165846 165847 165848 165849 | /* Set rCostIdx to the estimated cost of visiting selected rows in the ** index. The estimate is the sum of two values: ** 1. The cost of doing one search-by-key to find the first matching ** entry ** 2. Stepping forward in the index pNew->nOut times to find all ** additional matching entries. */ | | | | 166336 166337 166338 166339 166340 166341 166342 166343 166344 166345 166346 166347 166348 166349 166350 166351 166352 166353 166354 166355 166356 166357 166358 | /* Set rCostIdx to the estimated cost of visiting selected rows in the ** index. The estimate is the sum of two values: ** 1. The cost of doing one search-by-key to find the first matching ** entry ** 2. Stepping forward in the index pNew->nOut times to find all ** additional matching entries. */ assert( pSrc->pSTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior ** pages are small. Thus szIdxRow gives a good estimate of seek cost. ** But the leaf pages are full-size, so pProbe->szIdxRow would badly ** under-estimate the scanning cost. */ rCostIdx = pNew->nOut + 16; }else{ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pSTab->szTabRow; } rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); /* Estimate the cost of running the loop. If all data is coming ** from the index, then this is just the cost of doing the index ** lookup and scan. But if some data is coming out of the main table, ** we also have to add in the cost of doing pNew->nOut searches to |
︙ | ︙ | |||
166316 166317 166318 166319 166320 166321 166322 | WhereClause *pWC; /* The parsed WHERE clause */ Table *pTab; /* Table being queried */ pNew = pBuilder->pNew; pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; | | | | 166809 166810 166811 166812 166813 166814 166815 166816 166817 166818 166819 166820 166821 166822 166823 166824 166825 | WhereClause *pWC; /* The parsed WHERE clause */ Table *pTab; /* Table being queried */ pNew = pBuilder->pNew; pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; pTab = pSrc->pSTab; pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pSTab) ); if( pSrc->fg.isIndexedBy ){ assert( pSrc->fg.isCte==0 ); /* An INDEXED BY clause specifies a particular index to use */ pProbe = pSrc->u2.pIBIndex; }else if( !HasRowid(pTab) ){ pProbe = pTab->pIndex; |
︙ | ︙ | |||
166343 166344 166345 166346 166347 166348 166349 | sPk.aiRowLogEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */ sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; | | | 166836 166837 166838 166839 166840 166841 166842 166843 166844 166845 166846 166847 166848 166849 166850 | sPk.aiRowLogEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = 3; /* TUNING: Interior rows of IPK table are very small */ sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; pFirst = pSrc->pSTab->pIndex; if( pSrc->fg.notIndexed==0 ){ /* The real indices of the table are only considered if the ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; } |
︙ | ︙ | |||
166433 166434 166435 166436 166437 166438 166439 166440 166441 166442 166443 166444 166445 166446 | pNew->nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; pNew->rSetup = 0; pNew->prereq = mPrereq; pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* Integer primary key index */ pNew->wsFlags = WHERE_IPK; | > | 166926 166927 166928 166929 166930 166931 166932 166933 166934 166935 166936 166937 166938 166939 166940 | pNew->nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; pNew->rSetup = 0; pNew->prereq = mPrereq; pNew->nOut = rSize; pNew->u.btree.pIndex = pProbe; pNew->u.btree.pOrderBy = 0; b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* Integer primary key index */ pNew->wsFlags = WHERE_IPK; |
︙ | ︙ | |||
166462 166463 166464 166465 166466 166467 166468 | #ifdef SQLITE_ENABLE_STAT4 pNew->rRun = rSize + 16 - 2*((pTab->tabFlags & TF_HasStat4)!=0); #else pNew->rRun = rSize + 16; #endif ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); | | | | 166956 166957 166958 166959 166960 166961 166962 166963 166964 166965 166966 166967 166968 166969 166970 166971 166972 | #ifdef SQLITE_ENABLE_STAT4 pNew->rRun = rSize + 16 - 2*((pTab->tabFlags & TF_HasStat4)!=0); #else pNew->rRun = rSize + 16; #endif ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); if( pSrc->fg.isSubquery ){ if( pSrc->fg.viaCoroutine ) pNew->wsFlags |= WHERE_COROUTINE; pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; } rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; if( rc ) break; }else{ Bitmask m; if( pProbe->isCovering ){ |
︙ | ︙ | |||
166690 166691 166692 166693 166694 166695 166696 | pIdxInfo->orderByConsumed = 0; pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ | | | 167184 167185 167186 167187 167188 167189 167190 167191 167192 167193 167194 167195 167196 167197 167198 | pIdxInfo->orderByConsumed = 0; pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2; pIdxInfo->estimatedRows = 25; pIdxInfo->idxFlags = 0; pHidden->mHandleIn = 0; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pSTab, pIdxInfo); if( rc ){ if( rc==SQLITE_CONSTRAINT ){ /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means ** that the particular combination of parameters provided is unusable. ** Make no entries in the loop table. */ WHERETRACE(0xffffffff, (" ^^^^--- non-viable plan rejected!\n")); |
︙ | ︙ | |||
166720 166721 166722 166723 166724 166725 166726 | int j = pIdxCons->iTermOffset; if( iTerm>=nConstraint || j<0 || (pTerm = termFromWhereClause(pWC, j))==0 || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ){ | | | 167214 167215 167216 167217 167218 167219 167220 167221 167222 167223 167224 167225 167226 167227 167228 | int j = pIdxCons->iTermOffset; if( iTerm>=nConstraint || j<0 || (pTerm = termFromWhereClause(pWC, j))==0 || pNew->aLTerm[iTerm]!=0 || pIdxCons->usable==0 ){ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); freeIdxStr(pIdxInfo); return SQLITE_ERROR; } testcase( iTerm==nConstraint-1 ); testcase( j==0 ); testcase( j==pWC->nTerm-1 ); pNew->prereq |= pTerm->prereqRight; |
︙ | ︙ | |||
166783 166784 166785 166786 166787 166788 166789 | } pNew->nLTerm = mxTerm+1; for(i=0; i<=mxTerm; i++){ if( pNew->aLTerm[i]==0 ){ /* The non-zero argvIdx values must be contiguous. Raise an ** error if they are not */ | | > | 167277 167278 167279 167280 167281 167282 167283 167284 167285 167286 167287 167288 167289 167290 167291 167292 167293 167294 167295 167296 167297 167298 167299 167300 167301 167302 167303 | } pNew->nLTerm = mxTerm+1; for(i=0; i<=mxTerm; i++){ if( pNew->aLTerm[i]==0 ){ /* The non-zero argvIdx values must be contiguous. Raise an ** error if they are not */ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pSTab->zName); freeIdxStr(pIdxInfo); return SQLITE_ERROR; } } assert( pNew->nLTerm<=pNew->nLSlot ); pNew->u.vtab.idxNum = pIdxInfo->idxNum; pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr; pIdxInfo->needToFreeIdxStr = 0; pNew->u.vtab.idxStr = pIdxInfo->idxStr; pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ? pIdxInfo->nOrderBy : 0); pNew->u.vtab.bIdxNumHex = (pIdxInfo->idxFlags&SQLITE_INDEX_SCAN_HEX)!=0; pNew->rSetup = 0; pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost); pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows); /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated ** that the scan will visit at most one row. Clear it otherwise. */ if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){ |
︙ | ︙ | |||
166985 166986 166987 166988 166989 166990 166991 | assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; pParse = pWInfo->pParse; pWC = pBuilder->pWC; pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; | | | | 167480 167481 167482 167483 167484 167485 167486 167487 167488 167489 167490 167491 167492 167493 167494 167495 167496 167497 167498 167499 167500 167501 167502 167503 167504 167505 167506 167507 167508 | assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; pParse = pWInfo->pParse; pWC = pBuilder->pWC; pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pSTab) ); p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; pNew->nLTerm = 0; pNew->u.vtab.needFree = 0; nConstraint = p->nConstraint; if( whereLoopResize(pParse->db, pNew, nConstraint) ){ freeIndexInfo(pParse->db, p); return SQLITE_NOMEM_BKPT; } /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pSTab->zName)); WHERETRACE(0x800, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn, &bRetry ); if( bRetry ){ assert( rc==SQLITE_OK ); rc = whereLoopAddVirtualOne( |
︙ | ︙ | |||
167081 167082 167083 167084 167085 167086 167087 | WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } freeIndexInfo(pParse->db, p); | | | 167576 167577 167578 167579 167580 167581 167582 167583 167584 167585 167586 167587 167588 167589 167590 | WHERETRACE(0x800, (" VirtualOne: all disabled and w/o IN\n")); rc = whereLoopAddVirtualOne( pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn, 0); } } freeIndexInfo(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pSTab->zName, rc)); return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Add WhereLoop entries to handle OR terms. This works for either ** btrees or virtual tables. |
︙ | ︙ | |||
167153 167154 167155 167156 167157 167158 167159 | WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n", (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); if( sqlite3WhereTrace & 0x20000 ){ sqlite3WhereClausePrint(sSubBuild.pWC); } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE | | | 167648 167649 167650 167651 167652 167653 167654 167655 167656 167657 167658 167659 167660 167661 167662 | WHERETRACE(0x400, ("OR-term %d of %p has %d subterms:\n", (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm)); if( sqlite3WhereTrace & 0x20000 ){ sqlite3WhereClausePrint(sSubBuild.pWC); } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pSTab) ){ rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable); }else #endif { rc = whereLoopAddBtree(&sSubBuild, mPrereq); } if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
167267 167268 167269 167270 167271 167272 167273 | if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; mPrereq |= mPrior; bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; }else if( !hasRightJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE | | | 167762 167763 167764 167765 167766 167767 167768 167769 167770 167771 167772 167773 167774 167775 167776 | if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; mPrereq |= mPrior; bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; }else if( !hasRightJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pSTab) ){ SrcItem *p; for(p=&pItem[1]; p<pEnd; p++){ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable); |
︙ | ︙ | |||
167903 167904 167905 167906 167907 167908 167909 | } if( nDep<=3 ) continue; rDelta = 15*(nDep-3); #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ SrcItem *pItem = pWInfo->pTabList->a + iLoop; sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n", | | | 168398 168399 168400 168401 168402 168403 168404 168405 168406 168407 168408 168409 168410 168411 168412 | } if( nDep<=3 ) continue; rDelta = 15*(nDep-3); #ifdef WHERETRACE_ENABLED /* 0x4 */ if( sqlite3WhereTrace&0x4 ){ SrcItem *pItem = pWInfo->pTabList->a + iLoop; sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n", pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName, nDep, rDelta); } #endif if( pWInfo->nOutStarDelta==0 ){ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){ pWLoop->rStarDelta = 0; } |
︙ | ︙ | |||
168453 168454 168455 168456 168457 168458 168459 | Index *pIdx; WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; | | | 168948 168949 168950 168951 168952 168953 168954 168955 168956 168957 168958 168959 168960 168961 168962 | Index *pIdx; WhereScan scan; pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pSTab; if( IsVirtual(pTab) ) return 0; if( pItem->fg.isIndexedBy || pItem->fg.notIndexed ){ testcase( pItem->fg.isIndexedBy ); testcase( pItem->fg.notIndexed ); return 0; } iCur = pItem->iCursor; |
︙ | ︙ | |||
168716 168717 168718 168719 168720 168721 168722 | 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]; | | | 169211 169212 169213 169214 169215 169216 169217 169218 169219 169220 169221 169222 169223 169224 169225 | 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->pSTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; pTab->tabFlags |= TF_MaybeReanalyze; 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) ){ |
︙ | ︙ | |||
168873 168874 168875 168876 168877 168878 168879 | */ static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ int ii; for(ii=0; ii<pWInfo->pTabList->nSrc; ii++){ SrcItem *pItem = &pWInfo->pTabList->a[ii]; if( !pItem->fg.isCte || pItem->u2.pCteUse->eM10d!=M10d_Yes | | | | 169368 169369 169370 169371 169372 169373 169374 169375 169376 169377 169378 169379 169380 169381 169382 169383 | */ static SQLITE_NOINLINE void whereReverseScanOrder(WhereInfo *pWInfo){ int ii; for(ii=0; ii<pWInfo->pTabList->nSrc; ii++){ SrcItem *pItem = &pWInfo->pTabList->a[ii]; if( !pItem->fg.isCte || pItem->u2.pCteUse->eM10d!=M10d_Yes || NEVER(pItem->fg.isSubquery==0) || pItem->u4.pSubq->pSelect->pOrderBy==0 ){ pWInfo->revMask |= MASKBIT(ii); } } } /* |
︙ | ︙ | |||
169364 169365 169366 169367 169368 169369 169370 | ** use a one-pass approach, and this is not set accurately for scans ** that use the OR optimization. */ assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; | | | | | | 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 | ** use a one-pass approach, and this is not set accurately for scans ** that use the OR optimization. */ assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 ); if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; assert( !(wsFlags&WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pSTab) ); if( bOnerow || ( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) && !IsVirtual(pTabList->a[0].pSTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) && OptimizationEnabled(db, SQLITE_OnePass) )){ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; if( HasRowid(pTabList->a[0].pSTab) && (wsFlags & WHERE_IDX_ONLY) ){ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){ bFordelete = OPFLAG_FORDELETE; } pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY); } } } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ SrcItem *pTabItem; pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pSTab; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); pLoop = pLevel->pWLoop; if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){ |
︙ | ︙ | |||
169461 169462 169463 169464 169465 169466 169467 | && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ /* This is one term of an OR-optimization using the PRIMARY KEY of a ** WITHOUT ROWID table. No need for a separate index */ iIndexCur = pLevel->iTabCur; op = 0; }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ | | | 169956 169957 169958 169959 169960 169961 169962 169963 169964 169965 169966 169967 169968 169969 169970 | && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ /* This is one term of an OR-optimization using the PRIMARY KEY of a ** WITHOUT ROWID table. No need for a separate index */ iIndexCur = pLevel->iTabCur; op = 0; }else if( pWInfo->eOnePass!=ONEPASS_OFF ){ Index *pJ = pTabItem->pSTab->pIndex; iIndexCur = iAuxArg; assert( wctrlFlags & WHERE_ONEPASS_DESIRED ); while( ALWAYS(pJ) && pJ!=pIx ){ iIndexCur++; pJ = pJ->pNext; } op = OP_OpenWrite; |
︙ | ︙ | |||
169528 169529 169530 169531 169532 169533 169534 | ){ WhereRightJoin *pRJ = pLevel->pRJ; pRJ->iMatch = pParse->nTab++; pRJ->regBloom = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); pRJ->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); | | | 170023 170024 170025 170026 170027 170028 170029 170030 170031 170032 170033 170034 170035 170036 170037 | ){ WhereRightJoin *pRJ = pLevel->pRJ; pRJ->iMatch = pParse->nTab++; pRJ->regBloom = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Blob, 65536, pRJ->regBloom); pRJ->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, pRJ->regReturn); assert( pTab==pTabItem->pSTab ); if( HasRowid(pTab) ){ KeyInfo *pInfo; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRJ->iMatch, 1); pInfo = sqlite3KeyInfoAlloc(pParse->db, 1, 0); if( pInfo ){ pInfo->aColl[0] = 0; pInfo->aSortFlags[0] = 0; |
︙ | ︙ | |||
169567 169568 169569 169570 169571 169572 169573 | int wsFlags; SrcItem *pSrc; if( pParse->nErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; pSrc = &pTabList->a[pLevel->iFrom]; if( pSrc->fg.isMaterialized ){ | > > > > | | | > | > | < | 170062 170063 170064 170065 170066 170067 170068 170069 170070 170071 170072 170073 170074 170075 170076 170077 170078 170079 170080 170081 170082 170083 170084 170085 170086 170087 | int wsFlags; SrcItem *pSrc; if( pParse->nErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; pSrc = &pTabList->a[pLevel->iFrom]; if( pSrc->fg.isMaterialized ){ Subquery *pSubq; int iOnce = 0; assert( pSrc->fg.isSubquery ); pSubq = pSrc->u4.pSubq; if( pSrc->fg.isCorrelated==0 ){ iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); }else{ iOnce = 0; } sqlite3VdbeAddOp2(v, OP_Gosub, pSubq->regReturn, pSubq->addrFillSub); VdbeComment((v, "materialize %!S", pSrc)); if( iOnce ) 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 |
︙ | ︙ | |||
169786 169787 169788 169789 169790 169791 169792 | addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; | > | | | | 170286 170287 170288 170289 170290 170291 170292 170293 170294 170295 170296 170297 170298 170299 170300 170301 170302 170303 | addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; assert( pLevel->iTabCur==pSrc->iCursor ); if( pSrc->fg.viaCoroutine ){ int m, n; assert( pSrc->fg.isSubquery ); n = pSrc->u4.pSubq->regResult; assert( pSrc->pSTab!=0 ); m = pSrc->pSTab->nCol; sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) || ((ws & WHERE_MULTI_OR) && pLevel->u.pCoveringIdx) ){ |
︙ | ︙ | |||
169812 169813 169814 169815 169816 169817 169818 | sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); }else{ sqlite3VdbeGoto(v, pLevel->addrFirst); } sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, | | | | > | | 170313 170314 170315 170316 170317 170318 170319 170320 170321 170322 170323 170324 170325 170326 170327 170328 170329 170330 170331 170332 170333 170334 170335 170336 170337 170338 170339 170340 170341 170342 170343 170344 170345 170346 170347 170348 170349 170350 170351 170352 170353 170354 170355 170356 170357 170358 | sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst); }else{ sqlite3VdbeGoto(v, pLevel->addrFirst); } sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, pWInfo->pTabList->a[pLevel->iFrom].pSTab->zName)); } assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ int k, last; VdbeOp *pOp, *pLastOp; Index *pIdx = 0; SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pSTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* Do RIGHT JOIN processing. Generate code that will output the ** unmatched rows of the right operand of the RIGHT JOIN with ** all of the columns of the left operand set to NULL. */ if( pLevel->pRJ ){ sqlite3WhereRightJoinLoop(pWInfo, i, pLevel); continue; } /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); assert( pTabItem->fg.isSubquery ); assert( pTabItem->u4.pSubq->regResult>=0 ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->u4.pSubq->regResult, 0); continue; } /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can ** yield a significant performance boost. |
︙ | ︙ | |||
171052 171053 171054 171055 171056 171057 171058 | TREETRACE(0x40,pParse,pSub, ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside ** of sqlite3DbMallocRawNN() called from ** sqlite3SrcListAppend() */ | | > > < | < < | 171554 171555 171556 171557 171558 171559 171560 171561 171562 171563 171564 171565 171566 171567 171568 171569 171570 171571 171572 171573 171574 171575 171576 171577 171578 171579 171580 171581 171582 171583 171584 171585 171586 171587 171588 171589 171590 171591 171592 | TREETRACE(0x40,pParse,pSub, ("New window-function subquery in FROM clause of (%u/%p)\n", p->selId, p)); p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); assert( pSub!=0 || p->pSrc==0 ); /* Due to db->mallocFailed test inside ** of sqlite3DbMallocRawNN() called from ** sqlite3SrcListAppend() */ if( p->pSrc==0 ){ sqlite3SelectDelete(db, pSub); }else if( sqlite3SrcItemAttachSubquery(pParse, &p->pSrc->a[0], pSub, 0) ){ Table *pTab2; 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 ** the correct error message regardless. */ rc = SQLITE_NOMEM; }else{ memcpy(pTab, pTab2, sizeof(Table)); pTab->tabFlags |= TF_Ephemeral; p->pSrc->a[0].pSTab = pTab; pTab = pTab2; memset(&w, 0, sizeof(w)); w.xExprCallback = sqlite3WindowExtraAggFuncDepth; w.xSelectCallback = sqlite3WalkerDepthIncrease; w.xSelectCallback2 = sqlite3WalkerDepthDecrease; sqlite3WalkSelect(&w, pSub); } } if( db->mallocFailed ) rc = SQLITE_NOMEM; /* Defer deleting the temporary table pTab because if an error occurred, ** there could still be references to that table embedded in the ** result-set or ORDER BY clause of the SELECT statement p. */ sqlite3ParserAddCleanup(pParse, sqlite3DbFree, pTab); |
︙ | ︙ | |||
171364 171365 171366 171367 171368 171369 171370 | /* ** This is called by code in select.c before it calls sqlite3WhereBegin() ** to begin iterating through the sub-query results. It is used to allocate ** and initialize registers and cursors used by sqlite3WindowCodeStep(). */ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ | > | | > | > > > | | 171865 171866 171867 171868 171869 171870 171871 171872 171873 171874 171875 171876 171877 171878 171879 171880 171881 171882 171883 171884 171885 171886 171887 | /* ** This is called by code in select.c before it calls sqlite3WhereBegin() ** to begin iterating through the sub-query results. It is used to allocate ** and initialize registers and cursors used by sqlite3WindowCodeStep(). */ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Select *pSelect){ Window *pWin; int nEphExpr; Window *pMWin; Vdbe *v; assert( pSelect->pSrc->a[0].fg.isSubquery ); nEphExpr = pSelect->pSrc->a[0].u4.pSubq->pSelect->pEList->nExpr; pMWin = pSelect->pWin; v = sqlite3GetVdbe(pParse); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, nEphExpr); sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr); sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr); /* Allocate registers to use for PARTITION BY values, if any. Initialize |
︙ | ︙ | |||
172764 172765 172766 172767 172768 172769 172770 | int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; ExprList *pOrderBy = pMWin->pOrderBy; Vdbe *v = sqlite3GetVdbe(pParse); int csrWrite; /* Cursor used to write to eph. table */ int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ | | | 173270 173271 173272 173273 173274 173275 173276 173277 173278 173279 173280 173281 173282 173283 173284 | int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; ExprList *pOrderBy = pMWin->pOrderBy; Vdbe *v = sqlite3GetVdbe(pParse); int csrWrite; /* Cursor used to write to eph. table */ int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ int nInput = p->pSrc->a[0].pSTab->nCol; /* Number of cols returned by sub */ int iInput; /* To iterate through sub cols */ int addrNe; /* Address of OP_Ne */ int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ int addrInteger = 0; /* Address of OP_Integer */ int addrEmpty; /* Address of OP_Rewind in flush: */ int regNew; /* Array of registers holding new input row */ int regRecord; /* regNew array in record form */ |
︙ | ︙ | |||
177215 177216 177217 177218 177219 177220 177221 177222 | if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){ yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203; }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); if( yymsp[-5].minor.yy203 ){ SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; SrcItem *pOld = yymsp[-3].minor.yy203->a; pNew->zName = pOld->zName; | > > | > | > > > | | > > > > | < | 177721 177722 177723 177724 177725 177726 177727 177728 177729 177730 177731 177732 177733 177734 177735 177736 177737 177738 177739 177740 177741 177742 177743 177744 177745 177746 177747 177748 177749 177750 177751 177752 177753 177754 177755 177756 177757 | if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){ yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203; }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); if( yymsp[-5].minor.yy203 ){ SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; SrcItem *pOld = yymsp[-3].minor.yy203->a; assert( pOld->fg.fixedSchema==0 ); pNew->zName = pOld->zName; assert( pOld->fg.fixedSchema==0 ); if( pOld->fg.isSubquery ){ pNew->fg.isSubquery = 1; pNew->u4.pSubq = pOld->u4.pSubq; pOld->u4.pSubq = 0; pOld->fg.isSubquery = 0; assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 ); if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){ pNew->fg.isNestedFrom = 1; } }else{ pNew->u4.zDatabase = pOld->u4.zDatabase; pOld->u4.zDatabase = 0; } if( pOld->fg.isTabFunc ){ pNew->u1.pFuncArg = pOld->u1.pFuncArg; pOld->u1.pFuncArg = 0; pOld->fg.isTabFunc = 0; pNew->fg.isTabFunc = 1; } pOld->zName = 0; } sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); }else{ Select *pSubquery; sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203); pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0); yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269); |
︙ | ︙ | |||
182322 182323 182324 182325 182326 182327 182328 | ){ return SQLITE_MISUSE_BKPT; } assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY ); extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY| | | > | 182837 182838 182839 182840 182841 182842 182843 182844 182845 182846 182847 182848 182849 182850 182851 182852 | ){ return SQLITE_MISUSE_BKPT; } assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY ); extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY| SQLITE_SUBTYPE|SQLITE_INNOCUOUS| SQLITE_RESULT_SUBTYPE|SQLITE_SELFORDER1); enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY); /* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But ** the meaning is inverted. So flip the bit. */ assert( SQLITE_FUNC_UNSAFE==SQLITE_INNOCUOUS ); extraFlags ^= SQLITE_FUNC_UNSAFE; /* tag-20230109-1 */ |
︙ | ︙ | |||
184788 184789 184790 184791 184792 184793 184794 184795 184796 184797 184798 184799 184800 184801 | ** is obtained in every case. */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlite3 *db = va_arg(ap, sqlite3*); db->dbOptFlags = va_arg(ap, u32); break; } /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); ** ** If parameter onoff is 1, subsequent calls to localtime() fail. ** If 2, then invoke xAlt() instead of localtime(). If 0, normal ** processing. ** | > > > > > > > > > > > > | 185304 185305 185306 185307 185308 185309 185310 185311 185312 185313 185314 185315 185316 185317 185318 185319 185320 185321 185322 185323 185324 185325 185326 185327 185328 185329 | ** is obtained in every case. */ case SQLITE_TESTCTRL_OPTIMIZATIONS: { sqlite3 *db = va_arg(ap, sqlite3*); db->dbOptFlags = va_arg(ap, u32); break; } /* sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, sqlite3 *db, int *N) ** ** Write the current optimization settings into *N. A zero bit means that ** the optimization is on, and a 1 bit means that the optimization is off. */ case SQLITE_TESTCTRL_GETOPT: { sqlite3 *db = va_arg(ap, sqlite3*); int *pN = va_arg(ap, int*); *pN = db->dbOptFlags; break; } /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, onoff, xAlt); ** ** If parameter onoff is 1, subsequent calls to localtime() fail. ** If 2, then invoke xAlt() instead of localtime(). If 0, normal ** processing. ** |
︙ | ︙ | |||
224462 224463 224464 224465 224466 224467 224468 224469 224470 224471 224472 224473 224474 224475 | && pIdxInfo->aOrderBy[1].iColumn==1 && pIdxInfo->aOrderBy[1].desc==0 ) ){ pIdxInfo->orderByConsumed = 1; pIdxInfo->idxNum |= 0x08; } return SQLITE_OK; } /* ** Open a new DBSTAT cursor. */ | > | 224990 224991 224992 224993 224994 224995 224996 224997 224998 224999 225000 225001 225002 225003 225004 | && pIdxInfo->aOrderBy[1].iColumn==1 && pIdxInfo->aOrderBy[1].desc==0 ) ){ pIdxInfo->orderByConsumed = 1; pIdxInfo->idxNum |= 0x08; } pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_HEX; return SQLITE_OK; } /* ** Open a new DBSTAT cursor. */ |
︙ | ︙ | |||
232372 232373 232374 232375 232376 232377 232378 232379 232380 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { | > > > > > > > > > > > > > > > > > > > > > > > | | 232901 232902 232903 232904 232905 232906 232907 232908 232909 232910 232911 232912 232913 232914 232915 232916 232917 232918 232919 232920 232921 232922 232923 232924 232925 232926 232927 232928 232929 232930 232931 232932 232933 232934 232935 232936 232937 232938 232939 232940 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the locale associated ** with column iCol of the current row. Usually, there is no associated ** locale, and output parameters (*pzLocale) and (*pnLocale) are set ** to NULL and 0, respectively. However, if the fts5_locale() function ** was used to associate a locale with the value when it was inserted ** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ** is set to the size in bytes of the buffer, not including the ** nul-terminator. ** ** If successful, SQLITE_OK is returned. Or, if an error occurs, an ** SQLite error code is returned. The final value of the output parameters ** is undefined in this case. ** ** xTokenize_v2: ** Tokenize text using the tokenizer belonging to the FTS5 table. This ** API is the same as the xTokenize() API, except that it allows a tokenizer ** locale to be specified. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); |
︙ | ︙ | |||
232416 232417 232418 232419 232420 232421 232422 232423 232424 232425 232426 232427 232428 232429 232430 232431 232432 232433 232434 232435 232436 232437 232438 232439 232440 232441 232442 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) | > > > > > > > > > > | | 232968 232969 232970 232971 232972 232973 232974 232975 232976 232977 232978 232979 232980 232981 232982 232983 232984 232985 232986 232987 232988 232989 232990 232991 232992 232993 232994 232995 232996 232997 232998 232999 233000 233001 233002 233003 233004 233005 233006 233007 233008 233009 233010 233011 233012 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); /* Below this point are iVersion>=4 only */ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); int (*xTokenize_v2)(Fts5Context*, const char *pText, int nText, /* Text to tokenize */ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ void *pCtx, /* Context passed to xToken() */ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) ** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the ** tokenizer name as part of the CREATE VIRTUAL TABLE statement used ** to create the FTS5 table. ** ** The final argument is an output variable. If successful, (*ppOut) |
︙ | ︙ | |||
232460 232461 232462 232463 232464 232465 232466 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** | | | 233022 233023 233024 233025 233026 233027 233028 233029 233030 233031 233032 233033 233034 233035 233036 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** ** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** ** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into ** or removed from the FTS table. The tokenizer is being invoked to ** determine the set of tokens to add to (or delete from) the ** FTS index. |
︙ | ︙ | |||
232483 232484 232485 232486 232487 232488 232489 232490 232491 232492 232493 232494 232495 232496 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from | > > > > > > > | 233045 233046 233047 233048 233049 233050 233051 233052 233053 233054 233055 233056 233057 233058 233059 233060 233061 233062 233063 233064 233065 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** The sixth and seventh arguments passed to xTokenize() - pLocale and ** nLocale - are a pointer to a buffer containing the locale to use for ** tokenization (e.g. "en_US") and its size in bytes, respectively. The ** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ** which case nLocale is always 0) to indicate that the tokenizer should ** use its default locale. ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from |
︙ | ︙ | |||
232506 232507 232508 232509 232510 232511 232512 232513 232514 232515 232516 232517 232518 232519 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms | > > > > > > > > > > > > > > > > > > > > > > > > | 233075 233076 233077 233078 233079 233080 233081 233082 233083 233084 233085 233086 233087 233088 233089 233090 233091 233092 233093 233094 233095 233096 233097 233098 233099 233100 233101 233102 233103 233104 233105 233106 233107 233108 233109 233110 233111 233112 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** If the tokenizer is registered using an fts5_tokenizer_v2 object, ** then the xTokenize() method has two additional arguments - pLocale ** and nLocale. These specify the locale that the tokenizer should use ** for the current request. If pLocale and nLocale are both 0, then the ** tokenizer should use its default locale. Otherwise, pLocale points to ** an nLocale byte buffer containing the name of the locale to use as utf-8 ** text. pLocale is not nul-terminated. ** ** FTS5_TOKENIZER ** ** There is also an fts5_tokenizer object. This is an older, deprecated, ** version of fts5_tokenizer_v2. It is similar except that: ** ** <ul> ** <li> There is no "iVersion" field, and ** <li> The xTokenize() method does not take a locale argument. ** </ul> ** ** Legacy fts5_tokenizer tokenizers must be registered using the ** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ** ** Tokenizer implementations registered using either API may be retrieved ** using both xFindTokenizer() and xFindTokenizer_v2(). ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms |
︙ | ︙ | |||
232615 232616 232617 232618 232619 232620 232621 232622 232623 232624 232625 232626 232627 232628 232629 232630 232631 232632 232633 232634 232635 232636 232637 232638 232639 232640 232641 232642 232643 232644 232645 232646 232647 232648 232649 232650 232651 232652 232653 232654 232655 232656 232657 232658 232659 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 233208 233209 233210 233211 233212 233213 233214 233215 233216 233217 233218 233219 233220 233221 233222 233223 233224 233225 233226 233227 233228 233229 233230 233231 233232 233233 233234 233235 233236 233237 233238 233239 233240 233241 233242 233243 233244 233245 233246 233247 233248 233249 233250 233251 233252 233253 233254 233255 233256 233257 233258 233259 233260 233261 233262 233263 233264 233265 233266 233267 233268 233269 233270 233271 233272 233273 233274 233275 233276 233277 233278 233279 233280 233281 233282 233283 233284 233285 233286 233287 233288 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; struct fts5_tokenizer_v2 { int iVersion; /* Currently always 2 */ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, const char *pLocale, int nLocale, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* ** New code should use the fts5_tokenizer_v2 type to define tokenizer ** implementations. The following type is included for legacy applications ** that still use it. */ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer *pTokenizer, |
︙ | ︙ | |||
232680 232681 232682 232683 232684 232685 232686 232687 232688 232689 232690 232691 232692 232693 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); }; /* ** END OF REGISTRATION API *************************************************************************/ #if 0 | > > > > > > > > > > > > > > > > > > > | 233301 233302 233303 233304 233305 233306 233307 233308 233309 233310 233311 233312 233313 233314 233315 233316 233317 233318 233319 233320 233321 233322 233323 233324 233325 233326 233327 233328 233329 233330 233331 233332 233333 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); /* APIs below this point are only available if iVersion>=3 */ /* Create a new tokenizer */ int (*xCreateTokenizer_v2)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer_v2 *pTokenizer, void (*xDestroy)(void*) ); /* Find an existing tokenizer */ int (*xFindTokenizer_v2)( fts5_api *pApi, const char *zName, void **ppUserData, fts5_tokenizer_v2 **ppTokenizer ); }; /* ** END OF REGISTRATION API *************************************************************************/ #if 0 |
︙ | ︙ | |||
232856 232857 232858 232859 232860 232861 232862 | */ typedef struct Fts5Config Fts5Config; typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; struct Fts5TokenizerConfig { Fts5Tokenizer *pTok; | > | > > | 233496 233497 233498 233499 233500 233501 233502 233503 233504 233505 233506 233507 233508 233509 233510 233511 233512 233513 233514 233515 233516 | */ typedef struct Fts5Config Fts5Config; typedef struct Fts5TokenizerConfig Fts5TokenizerConfig; struct Fts5TokenizerConfig { Fts5Tokenizer *pTok; fts5_tokenizer_v2 *pApi2; fts5_tokenizer *pApi1; const char **azArg; int nArg; int ePattern; /* FTS_PATTERN_XXX constant */ const char *pLocale; /* Current locale to use */ int nLocale; /* Size of pLocale in bytes */ }; /* ** An instance of the following structure encodes all information that can ** be gleaned from the CREATE VIRTUAL TABLE statement. ** ** And all information loaded from the %_config table. |
︙ | ︙ | |||
232900 232901 232902 232903 232904 232905 232906 232907 232908 232909 232910 232911 232912 232913 232914 232915 232916 232917 232918 232919 232920 232921 232922 232923 232924 232925 232926 232927 232928 232929 232930 232931 232932 232933 232934 | ** ** bPrefixIndex: ** This is only used for debugging. If set to false, any prefix indexes ** are ignored. This value is configured using: ** ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); ** */ struct Fts5Config { sqlite3 *db; /* Database handle */ Fts5Global *pGlobal; /* Global fts5 object for handle db */ char *zDb; /* Database holding FTS index (e.g. "main") */ char *zName; /* Name of FTS index */ int nCol; /* Number of columns */ char **azCol; /* Column names */ u8 *abUnindexed; /* True for unindexed columns */ int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5TokenizerConfig t; int bLock; /* True when table is preparing statement */ /* 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 */ | > > > > | 233543 233544 233545 233546 233547 233548 233549 233550 233551 233552 233553 233554 233555 233556 233557 233558 233559 233560 233561 233562 233563 233564 233565 233566 233567 233568 233569 233570 233571 233572 233573 233574 233575 233576 233577 233578 233579 233580 233581 | ** ** bPrefixIndex: ** This is only used for debugging. If set to false, any prefix indexes ** are ignored. This value is configured using: ** ** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex); ** ** bLocale: ** Set to true if locale=1 was specified when the table was created. */ struct Fts5Config { sqlite3 *db; /* Database handle */ Fts5Global *pGlobal; /* Global fts5 object for handle db */ char *zDb; /* Database holding FTS index (e.g. "main") */ char *zName; /* Name of FTS index */ int nCol; /* Number of columns */ char **azCol; /* Column names */ u8 *abUnindexed; /* True for unindexed columns */ int nPrefix; /* Number of prefix indexes */ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ int eContent; /* An FTS5_CONTENT value */ int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */ char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ int bTokendata; /* "tokendata=" option value (dflt==0) */ int bLocale; /* "locale=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5TokenizerConfig t; int bLock; /* True when table is preparing statement */ /* 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 */ |
︙ | ︙ | |||
232986 232987 232988 232989 232990 232991 232992 232993 232994 232995 232996 232997 232998 232999 | static int sqlite3Fts5ConfigLoad(Fts5Config*, int); /* Set the value of a single config attribute */ static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*); static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); /* ** End of interface to code in fts5_config.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_buffer.c. */ | > > | 233633 233634 233635 233636 233637 233638 233639 233640 233641 233642 233643 233644 233645 233646 233647 233648 | static int sqlite3Fts5ConfigLoad(Fts5Config*, int); /* Set the value of a single config attribute */ static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*); static int sqlite3Fts5ConfigParseRank(const char*, char**, char**); static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...); /* ** End of interface to code in fts5_config.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_buffer.c. */ |
︙ | ︙ | |||
233030 233031 233032 233033 233034 233035 233036 | sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \ ) /* Write and decode big-endian 32-bit integer values */ static void sqlite3Fts5Put32(u8*, int); static int sqlite3Fts5Get32(const u8*); | | | 233679 233680 233681 233682 233683 233684 233685 233686 233687 233688 233689 233690 233691 233692 233693 | sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \ ) /* Write and decode big-endian 32-bit integer values */ static void sqlite3Fts5Put32(u8*, int); static int sqlite3Fts5Get32(const u8*); #define FTS5_POS2COLUMN(iPos) (int)((iPos >> 32) & 0x7FFFFFFF) #define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) typedef struct Fts5PoslistReader Fts5PoslistReader; struct Fts5PoslistReader { /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */ const u8 *a; /* Position list to iterate through */ int n; /* Size of buffer at a[] in bytes */ |
︙ | ︙ | |||
233321 233322 233323 233324 233325 233326 233327 233328 233329 233330 233331 233332 233333 233334 | static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); static int sqlite3Fts5FlushToDisk(Fts5Table*); /* ** End of interface to code in fts5.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_hash.c. */ | > > > > > > > > > > > | 233970 233971 233972 233973 233974 233975 233976 233977 233978 233979 233980 233981 233982 233983 233984 233985 233986 233987 233988 233989 233990 233991 233992 233993 233994 | static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig); static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); static int sqlite3Fts5FlushToDisk(Fts5Table*); static int sqlite3Fts5ExtractText( Fts5Config *pConfig, sqlite3_value *pVal, /* Value to extract text from */ int bContent, /* Loaded from content table */ int *pbResetTokenizer, /* OUT: True if ClearLocale() required */ const char **ppText, /* OUT: Pointer to text buffer */ int *pnText /* OUT: Size of (*ppText) in bytes */ ); static void sqlite3Fts5ClearLocale(Fts5Config *pConfig); /* ** End of interface to code in fts5.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_hash.c. */ |
︙ | ︙ | |||
233400 233401 233402 233403 233404 233405 233406 | static int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**); static int sqlite3Fts5StorageClose(Fts5Storage *p); static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); static int sqlite3Fts5DropAll(Fts5Config*); static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); | | | 234060 234061 234062 234063 234064 234065 234066 234067 234068 234069 234070 234071 234072 234073 234074 | static int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**); static int sqlite3Fts5StorageClose(Fts5Storage *p); static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName); static int sqlite3Fts5DropAll(Fts5Config*); static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**, int); static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*); static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64); static int sqlite3Fts5StorageIntegrity(Fts5Storage *p, int iArg); static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); |
︙ | ︙ | |||
233426 233427 233428 233429 233430 233431 233432 233433 233434 233435 233436 233437 233438 233439 | static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p); static int sqlite3Fts5StorageRebuild(Fts5Storage *p); static int sqlite3Fts5StorageOptimize(Fts5Storage *p); static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); static int sqlite3Fts5StorageReset(Fts5Storage *p); /* ** End of interface to code in fts5_storage.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_expr.c. | > > > | 234086 234087 234088 234089 234090 234091 234092 234093 234094 234095 234096 234097 234098 234099 234100 234101 234102 | static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p); static int sqlite3Fts5StorageRebuild(Fts5Storage *p); static int sqlite3Fts5StorageOptimize(Fts5Storage *p); static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge); static int sqlite3Fts5StorageReset(Fts5Storage *p); static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage*); static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel); /* ** End of interface to code in fts5_storage.c. **************************************************************************/ /************************************************************************** ** Interface to code in fts5_expr.c. |
︙ | ︙ | |||
235355 235356 235357 235358 235359 235360 235361 235362 235363 235364 235365 235366 235367 235368 | } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } return rc; } /* ** Implementation of highlight() function. */ static void fts5HighlightFunction( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ | > | 236018 236019 236020 236021 236022 236023 236024 236025 236026 236027 236028 236029 236030 236031 236032 | } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; } return rc; } /* ** Implementation of highlight() function. */ static void fts5HighlightFunction( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ |
︙ | ︙ | |||
235386 235387 235388 235389 235390 235391 235392 235393 235394 235395 235396 235397 | ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); if( rc==SQLITE_RANGE ){ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = SQLITE_OK; }else if( ctx.zIn ){ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); } if( rc==SQLITE_OK ){ | > > > > > | > > | 236050 236051 236052 236053 236054 236055 236056 236057 236058 236059 236060 236061 236062 236063 236064 236065 236066 236067 236068 236069 236070 236071 236072 236073 236074 236075 236076 | ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); if( rc==SQLITE_RANGE ){ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); rc = SQLITE_OK; }else if( ctx.zIn ){ const char *pLoc = 0; /* Locale of column iCol */ int nLoc = 0; /* Size of pLoc in bytes */ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); } if( rc==SQLITE_OK ){ rc = pApi->xColumnLocale(pFts, iCol, &pLoc, &nLoc); } if( rc==SQLITE_OK ){ rc = pApi->xTokenize_v2( pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx, fts5HighlightCb ); } if( ctx.bOpen ){ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); } fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
235588 235589 235590 235591 235592 235593 235594 235595 235596 235597 235598 235599 235600 235601 | if( rc==SQLITE_OK ){ rc = pApi->xInstCount(pFts, &nInst); } memset(&sFinder, 0, sizeof(Fts5SFinder)); for(i=0; i<nCol; i++){ if( iCol<0 || iCol==i ){ int nDoc; int nDocsize; int ii; sFinder.iPos = 0; sFinder.nFirst = 0; rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc); if( rc!=SQLITE_OK ) break; | > > > > | | | 236259 236260 236261 236262 236263 236264 236265 236266 236267 236268 236269 236270 236271 236272 236273 236274 236275 236276 236277 236278 236279 236280 236281 236282 236283 236284 236285 | if( rc==SQLITE_OK ){ rc = pApi->xInstCount(pFts, &nInst); } memset(&sFinder, 0, sizeof(Fts5SFinder)); for(i=0; i<nCol; i++){ if( iCol<0 || iCol==i ){ const char *pLoc = 0; /* Locale of column iCol */ int nLoc = 0; /* Size of pLoc in bytes */ int nDoc; int nDocsize; int ii; sFinder.iPos = 0; sFinder.nFirst = 0; rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc); if( rc!=SQLITE_OK ) break; rc = pApi->xColumnLocale(pFts, i, &pLoc, &nLoc); if( rc!=SQLITE_OK ) break; rc = pApi->xTokenize_v2(pFts, sFinder.zDoc, nDoc, pLoc, nLoc, (void*)&sFinder, fts5SentenceFinderCb ); if( rc!=SQLITE_OK ) break; rc = pApi->xColumnSize(pFts, i, &nDocsize); if( rc!=SQLITE_OK ) break; for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){ int ip, ic, io; |
︙ | ︙ | |||
235654 235655 235656 235657 235658 235659 235660 235661 235662 235663 235664 235665 235666 235667 235668 235669 235670 235671 235672 235673 235674 235675 235676 235677 235678 | if( rc==SQLITE_OK ){ rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); } if( rc==SQLITE_OK && nColSize==0 ){ rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); } if( ctx.zIn ){ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); } ctx.iRangeStart = iBestStart; ctx.iRangeEnd = iBestStart + nToken - 1; if( iBestStart>0 ){ fts5HighlightAppend(&rc, &ctx, zEllips, -1); } /* Advance iterator ctx.iter so that it points to the first coalesced ** phrase instance at or following position iBestStart. */ while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){ rc = fts5CInstIterNext(&ctx.iter); } if( rc==SQLITE_OK ){ | > > > > > > | > > | 236329 236330 236331 236332 236333 236334 236335 236336 236337 236338 236339 236340 236341 236342 236343 236344 236345 236346 236347 236348 236349 236350 236351 236352 236353 236354 236355 236356 236357 236358 236359 236360 236361 236362 236363 236364 236365 236366 236367 236368 236369 | if( rc==SQLITE_OK ){ rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); } if( rc==SQLITE_OK && nColSize==0 ){ rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); } if( ctx.zIn ){ const char *pLoc = 0; /* Locale of column iBestCol */ int nLoc = 0; /* Bytes in pLoc */ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); } ctx.iRangeStart = iBestStart; ctx.iRangeEnd = iBestStart + nToken - 1; if( iBestStart>0 ){ fts5HighlightAppend(&rc, &ctx, zEllips, -1); } /* Advance iterator ctx.iter so that it points to the first coalesced ** phrase instance at or following position iBestStart. */ while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){ rc = fts5CInstIterNext(&ctx.iter); } if( rc==SQLITE_OK ){ rc = pApi->xColumnLocale(pFts, iBestCol, &pLoc, &nLoc); } if( rc==SQLITE_OK ){ rc = pApi->xTokenize_v2( pFts, ctx.zIn, ctx.nIn, pLoc, nLoc, (void*)&ctx,fts5HighlightCb ); } if( ctx.bOpen ){ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); } if( ctx.iRangeEnd>=(nColSize-1) ){ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); }else{ |
︙ | ︙ | |||
235855 235856 235857 235858 235859 235860 235861 235862 235863 235864 235865 235866 235867 235868 235869 | ); } sqlite3_result_double(pCtx, -1.0 * score); }else{ sqlite3_result_error_code(pCtx, rc); } } static int sqlite3Fts5AuxInit(fts5_api *pApi){ struct Builtin { const char *zFunc; /* Function name (nul-terminated) */ void *pUserData; /* User-data pointer */ fts5_extension_function xFunc;/* Callback function */ void (*xDestroy)(void*); /* Destructor function */ } aBuiltin [] = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | 236538 236539 236540 236541 236542 236543 236544 236545 236546 236547 236548 236549 236550 236551 236552 236553 236554 236555 236556 236557 236558 236559 236560 236561 236562 236563 236564 236565 236566 236567 236568 236569 236570 236571 236572 236573 236574 236575 236576 236577 236578 236579 236580 236581 236582 236583 236584 236585 236586 236587 236588 236589 236590 236591 236592 236593 236594 236595 236596 236597 236598 236599 236600 236601 236602 236603 236604 236605 236606 236607 236608 236609 236610 | ); } sqlite3_result_double(pCtx, -1.0 * score); }else{ sqlite3_result_error_code(pCtx, rc); } } /* ** Implementation of fts5_get_locale() function. */ static void fts5GetLocaleFunction( 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 */ ){ int iCol = 0; int eType = 0; int rc = SQLITE_OK; const char *zLocale = 0; int nLocale = 0; /* xColumnLocale() must be available */ assert( pApi->iVersion>=4 ); if( nVal!=1 ){ const char *z = "wrong number of arguments to function fts5_get_locale()"; sqlite3_result_error(pCtx, z, -1); return; } eType = sqlite3_value_numeric_type(apVal[0]); if( eType!=SQLITE_INTEGER ){ const char *z = "non-integer argument passed to function fts5_get_locale()"; sqlite3_result_error(pCtx, z, -1); return; } iCol = sqlite3_value_int(apVal[0]); if( iCol<0 || iCol>=pApi->xColumnCount(pFts) ){ sqlite3_result_error_code(pCtx, SQLITE_RANGE); return; } rc = pApi->xColumnLocale(pFts, iCol, &zLocale, &nLocale); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); return; } sqlite3_result_text(pCtx, zLocale, nLocale, SQLITE_TRANSIENT); } static int sqlite3Fts5AuxInit(fts5_api *pApi){ struct Builtin { const char *zFunc; /* Function name (nul-terminated) */ void *pUserData; /* User-data pointer */ fts5_extension_function xFunc;/* Callback function */ void (*xDestroy)(void*); /* Destructor function */ } aBuiltin [] = { { "snippet", 0, fts5SnippetFunction, 0 }, { "highlight", 0, fts5HighlightFunction, 0 }, { "bm25", 0, fts5Bm25Function, 0 }, { "fts5_get_locale", 0, fts5GetLocaleFunction, 0 }, }; int rc = SQLITE_OK; /* Return code */ int i; /* To iterate through builtin functions */ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){ rc = pApi->xCreateFunction(pApi, aBuiltin[i].zFunc, |
︙ | ︙ | |||
236675 236676 236677 236678 236679 236680 236681 236682 236683 236684 236685 236686 236687 236688 | *pzErr = sqlite3_mprintf("malformed columnsize=... directive"); rc = SQLITE_ERROR; }else{ pConfig->bColumnsize = (zArg[0]=='1'); } return rc; } if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ const Fts5Enum aDetail[] = { { "none", FTS5_DETAIL_NONE }, { "full", FTS5_DETAIL_FULL }, { "columns", FTS5_DETAIL_COLUMNS }, { 0, 0 } | > > > > > > > > > > | 237406 237407 237408 237409 237410 237411 237412 237413 237414 237415 237416 237417 237418 237419 237420 237421 237422 237423 237424 237425 237426 237427 237428 237429 | *pzErr = sqlite3_mprintf("malformed columnsize=... directive"); rc = SQLITE_ERROR; }else{ pConfig->bColumnsize = (zArg[0]=='1'); } return rc; } if( sqlite3_strnicmp("locale", zCmd, nCmd)==0 ){ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ *pzErr = sqlite3_mprintf("malformed locale=... directive"); rc = SQLITE_ERROR; }else{ pConfig->bLocale = (zArg[0]=='1'); } return rc; } if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ const Fts5Enum aDetail[] = { { "none", FTS5_DETAIL_NONE }, { "full", FTS5_DETAIL_FULL }, { "columns", FTS5_DETAIL_COLUMNS }, { 0, 0 } |
︙ | ︙ | |||
236965 236966 236967 236968 236969 236970 236971 | /* ** Free the configuration object passed as the only argument. */ static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ if( pConfig ){ int i; if( pConfig->t.pTok ){ | > | > > > | 237706 237707 237708 237709 237710 237711 237712 237713 237714 237715 237716 237717 237718 237719 237720 237721 237722 237723 237724 | /* ** Free the configuration object passed as the only argument. */ static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ if( pConfig ){ int i; if( pConfig->t.pTok ){ if( pConfig->t.pApi1 ){ pConfig->t.pApi1->xDelete(pConfig->t.pTok); }else{ pConfig->t.pApi2->xDelete(pConfig->t.pTok); } } sqlite3_free((char*)pConfig->t.azArg); sqlite3_free(pConfig->zDb); sqlite3_free(pConfig->zName); for(i=0; i<pConfig->nCol; i++){ sqlite3_free(pConfig->azCol[i]); } |
︙ | ︙ | |||
237048 237049 237050 237051 237052 237053 237054 | ){ int rc = SQLITE_OK; if( pText ){ if( pConfig->t.pTok==0 ){ rc = sqlite3Fts5LoadTokenizer(pConfig); } if( rc==SQLITE_OK ){ | > | | | > > > > > | 237793 237794 237795 237796 237797 237798 237799 237800 237801 237802 237803 237804 237805 237806 237807 237808 237809 237810 237811 237812 237813 237814 237815 | ){ int rc = SQLITE_OK; if( pText ){ if( pConfig->t.pTok==0 ){ rc = sqlite3Fts5LoadTokenizer(pConfig); } if( rc==SQLITE_OK ){ if( pConfig->t.pApi1 ){ rc = pConfig->t.pApi1->xTokenize( pConfig->t.pTok, pCtx, flags, pText, nText, xToken ); }else{ rc = pConfig->t.pApi2->xTokenize(pConfig->t.pTok, pCtx, flags, pText, nText, pConfig->t.pLocale, pConfig->t.nLocale, xToken ); } } } return rc; } /* ** Argument pIn points to the first character in what is expected to be |
︙ | ︙ | |||
237307 237308 237309 237310 237311 237312 237313 | } if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ rc = SQLITE_ERROR; | < < | | | | < > > > > > > > > > > > > > > > > > > > > > > > | 238058 238059 238060 238061 238062 238063 238064 238065 238066 238067 238068 238069 238070 238071 238072 238073 238074 238075 238076 238077 238078 238079 238080 238081 238082 238083 238084 238085 238086 238087 238088 238089 238090 238091 238092 238093 238094 238095 238096 238097 238098 238099 238100 238101 238102 238103 238104 238105 238106 238107 | } if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION && iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){ rc = SQLITE_ERROR; sqlite3Fts5ConfigErrmsg(pConfig, "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; } /* ** Set (*pConfig->pzErrmsg) to point to an sqlite3_malloc()ed buffer ** containing the error message created using printf() style formatting ** string zFmt and its trailing arguments. */ static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ va_list ap; /* ... printf arguments */ char *zMsg = 0; va_start(ap, zFmt); zMsg = sqlite3_vmprintf(zFmt, ap); if( pConfig->pzErrmsg ){ assert( *pConfig->pzErrmsg==0 ); *pConfig->pzErrmsg = zMsg; }else{ sqlite3_free(zMsg); } va_end(ap); } /* ** 2014 May 31 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** |
︙ | ︙ | |||
237612 237613 237614 237615 237616 237617 237618 237619 237620 237621 237622 | 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. */ | > | < < < < < < < | < | 238383 238384 238385 238386 238387 238388 238389 238390 238391 238392 238393 238394 238395 238396 238397 238398 238399 238400 238401 238402 238403 238404 238405 238406 238407 238408 238409 238410 238411 238412 238413 238414 238415 238416 238417 238418 238419 | do { t = fts5ExprGetToken(&sParse, &z, &token); sqlite3Fts5Parser(pEngine, t, token, &sParse); }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); sqlite3Fts5ParserFree(pEngine, fts5ParseFree); assert( sParse.pExpr || sParse.rc!=SQLITE_OK ); 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( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ int n = sizeof(Fts5Colset); Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); if( pColset ){ pColset->nCol = 1; pColset->aiCol[0] = iCol; sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); } } assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 ); if( sParse.rc==SQLITE_OK ){ *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr)); if( pNew==0 ){ sParse.rc = SQLITE_NOMEM; sqlite3Fts5ParseNodeFree(sParse.pExpr); }else{ pNew->pRoot = sParse.pExpr; pNew->pIndex = 0; pNew->pConfig = pConfig; pNew->apExprPhrase = sParse.apPhrase; pNew->nPhrase = sParse.nPhrase; pNew->bDesc = 0; sParse.apPhrase = 0; } |
︙ | ︙ | |||
238459 238460 238461 238462 238463 238464 238465 | if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){ pNode->bNomatch = 0; pNode->bEof = 1; return rc; } }else{ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; | | | 239223 239224 239225 239226 239227 239228 239229 239230 239231 239232 239233 239234 239235 239236 239237 | if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){ pNode->bNomatch = 0; pNode->bEof = 1; return rc; } }else{ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; if( pIter->iRowid==iLast ) continue; bMatch = 0; if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){ return rc; } } } } |
︙ | ︙ | |||
238981 238982 238983 238984 238985 238986 238987 | Fts5ExprNearset *pNear, /* Existing nearset, or NULL */ Fts5ExprPhrase *pPhrase /* Recently parsed phrase */ ){ const int SZALLOC = 8; Fts5ExprNearset *pRet = 0; if( pParse->rc==SQLITE_OK ){ | < < < | 239745 239746 239747 239748 239749 239750 239751 239752 239753 239754 239755 239756 239757 239758 | Fts5ExprNearset *pNear, /* Existing nearset, or NULL */ Fts5ExprPhrase *pPhrase /* Recently parsed phrase */ ){ const int SZALLOC = 8; Fts5ExprNearset *pRet = 0; if( pParse->rc==SQLITE_OK ){ if( pNear==0 ){ sqlite3_int64 nByte; nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; }else{ |
︙ | ︙ | |||
243380 243381 243382 243383 243384 243385 243386 | if( p->rc || pIter->pLeaf==0 ) return; pIter->iRowid = 0; iOff = 4; } if( iOff<pIter->iEndofDoclist ){ /* Next entry is on the current page */ | | | 244141 244142 244143 244144 244145 244146 244147 244148 244149 244150 244151 244152 244153 244154 244155 | if( p->rc || pIter->pLeaf==0 ) return; pIter->iRowid = 0; iOff = 4; } if( iOff<pIter->iEndofDoclist ){ /* Next entry is on the current page */ u64 iDelta; iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); pIter->iLeafOffset = iOff; pIter->iRowid += iDelta; }else if( (pIter->flags & FTS5_SEGITER_ONETERM)==0 ){ if( pIter->pSeg ){ int nKeep = 0; if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){ |
︙ | ︙ | |||
250370 250371 250372 250373 250374 250375 250376 250377 250378 250379 250380 | Fts5Auxiliary *pNext; /* Next registered auxiliary function */ }; /* ** Each tokenizer module registered with the FTS5 module is represented ** by an object of the following type. All such objects are stored as part ** of the Fts5Global.pTok list. */ struct Fts5TokenizerModule { char *zName; /* Name of tokenizer */ void *pUserData; /* User pointer passed to xCreate() */ | > > > > > > > > > > > > > > > > | > | 251131 251132 251133 251134 251135 251136 251137 251138 251139 251140 251141 251142 251143 251144 251145 251146 251147 251148 251149 251150 251151 251152 251153 251154 251155 251156 251157 251158 251159 251160 251161 251162 251163 251164 251165 251166 | Fts5Auxiliary *pNext; /* Next registered auxiliary function */ }; /* ** Each tokenizer module registered with the FTS5 module is represented ** by an object of the following type. All such objects are stored as part ** of the Fts5Global.pTok list. ** ** bV2Native: ** True if the tokenizer was registered using xCreateTokenizer_v2(), false ** for xCreateTokenizer(). If this variable is true, then x2 is populated ** with the routines as supplied by the caller and x1 contains synthesized ** wrapper routines. In this case the user-data pointer passed to ** x1.xCreate should be a pointer to the Fts5TokenizerModule structure, ** not a copy of pUserData. ** ** Of course, if bV2Native is false, then x1 contains the real routines and ** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule ** object should be passed to x2.xCreate. ** ** The synthesized wrapper routines are necessary for xFindTokenizer(_v2) ** calls. */ struct Fts5TokenizerModule { char *zName; /* Name of tokenizer */ void *pUserData; /* User pointer passed to xCreate() */ int bV2Native; /* True if v2 native tokenizer */ fts5_tokenizer x1; /* Tokenizer functions */ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ void (*xDestroy)(void*); /* Destructor function */ Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ }; struct Fts5FullTable { Fts5Table p; /* Public class members from fts5Int.h */ Fts5Storage *pStorage; /* Document store */ |
︙ | ︙ | |||
250462 250463 250464 250465 250466 250467 250468 | sqlite3_value **apRankArg; /* Array of trailing arguments */ sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ /* Auxiliary data storage */ Fts5Auxiliary *pAux; /* Currently executing extension function */ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ | | | 251240 251241 251242 251243 251244 251245 251246 251247 251248 251249 251250 251251 251252 251253 251254 | sqlite3_value **apRankArg; /* Array of trailing arguments */ sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ /* Auxiliary data storage */ Fts5Auxiliary *pAux; /* Currently executing extension function */ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ /* Cache used by auxiliary API functions xInst() and xInstCount() */ Fts5PoslistReader *aInstIter; /* One for each phrase */ int nInstAlloc; /* Size of aInst[] array (entries / 3) */ int nInstCount; /* Number of phrase instances */ int *aInst; /* 3 integers per phrase instance */ }; /* |
︙ | ︙ | |||
250497 250498 250499 250500 250501 250502 250503 250504 250505 250506 250507 250508 250509 250510 | #define FTS5CSR_FREE_ZRANK 0x10 #define FTS5CSR_REQUIRE_RESEEK 0x20 #define FTS5CSR_REQUIRE_POSLIST 0x40 #define BitFlagAllTest(x,y) (((x) & (y))==(y)) #define BitFlagTest(x,y) (((x) & (y))!=0) /* ** Macros to Set(), Clear() and Test() cursor flags. */ #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) | > > > > > > | 251275 251276 251277 251278 251279 251280 251281 251282 251283 251284 251285 251286 251287 251288 251289 251290 251291 251292 251293 251294 | #define FTS5CSR_FREE_ZRANK 0x10 #define FTS5CSR_REQUIRE_RESEEK 0x20 #define FTS5CSR_REQUIRE_POSLIST 0x40 #define BitFlagAllTest(x,y) (((x) & (y))==(y)) #define BitFlagTest(x,y) (((x) & (y))!=0) /* ** The subtype value and header bytes used by fts5_locale(). */ #define FTS5_LOCALE_SUBTYPE ((unsigned int)'L') #define FTS5_LOCALE_HEADER "\x00\xE0\xB2\xEB" /* ** Macros to Set(), Clear() and Test() cursor flags. */ #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) |
︙ | ︙ | |||
250671 250672 250673 250674 250675 250676 250677 | /* Call sqlite3_declare_vtab() */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigDeclareVtab(pConfig); } /* Load the initial configuration */ if( rc==SQLITE_OK ){ | | < | 251455 251456 251457 251458 251459 251460 251461 251462 251463 251464 251465 251466 251467 251468 251469 | /* Call sqlite3_declare_vtab() */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigDeclareVtab(pConfig); } /* Load the initial configuration */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1); } if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1); } if( rc==SQLITE_OK ){ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); |
︙ | ︙ | |||
250874 250875 250876 250877 250878 250879 250880 | ** unusable plan. Return SQLITE_CONSTRAINT. */ return SQLITE_CONSTRAINT; }else{ if( iCol==nCol+1 ){ if( bSeenRank ) continue; idxStr[iIdxStr++] = 'r'; bSeenRank = 1; | | | 251657 251658 251659 251660 251661 251662 251663 251664 251665 251666 251667 251668 251669 251670 251671 | ** unusable plan. Return SQLITE_CONSTRAINT. */ return SQLITE_CONSTRAINT; }else{ if( iCol==nCol+1 ){ if( bSeenRank ) continue; idxStr[iIdxStr++] = 'r'; bSeenRank = 1; }else{ nSeenMatch++; idxStr[iIdxStr++] = 'M'; sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol); idxStr += strlen(&idxStr[iIdxStr]); assert( idxStr[iIdxStr]=='\0' ); } pInfo->aConstraintUsage[i].argvIndex = ++iCons; |
︙ | ︙ | |||
251260 251261 251262 251263 251264 251265 251266 | zSql = sqlite3_vmprintf(zFmt, ap); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pRet, 0); if( rc!=SQLITE_OK ){ | | | 252043 252044 252045 252046 252047 252048 252049 252050 252051 252052 252053 252054 252055 252056 252057 | zSql = sqlite3_vmprintf(zFmt, ap); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, &pRet, 0); if( rc!=SQLITE_OK ){ sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsg(pConfig->db)); } sqlite3_free(zSql); } va_end(ap); *ppStmt = pRet; return rc; |
︙ | ︙ | |||
251495 251496 251497 251498 251499 251500 251501 251502 251503 251504 251505 251506 251507 251508 | va_list ap; /* ... printf arguments */ va_start(ap, zFormat); sqlite3_free(p->p.base.zErrMsg); p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** There are three possible query strategies: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 252278 252279 252280 252281 252282 252283 252284 252285 252286 252287 252288 252289 252290 252291 252292 252293 252294 252295 252296 252297 252298 252299 252300 252301 252302 252303 252304 252305 252306 252307 252308 252309 252310 252311 252312 252313 252314 252315 252316 252317 252318 252319 252320 252321 252322 252323 252324 252325 252326 252327 252328 252329 252330 252331 252332 252333 252334 252335 252336 252337 252338 252339 252340 252341 252342 252343 252344 252345 252346 252347 252348 252349 252350 252351 252352 252353 252354 252355 252356 252357 252358 252359 252360 252361 252362 252363 252364 252365 252366 252367 252368 252369 252370 252371 252372 252373 252374 252375 252376 252377 252378 252379 252380 252381 252382 252383 252384 252385 252386 252387 252388 252389 252390 252391 252392 252393 252394 252395 252396 252397 252398 252399 252400 252401 252402 252403 252404 252405 252406 252407 252408 252409 252410 252411 252412 252413 252414 252415 252416 252417 252418 252419 252420 252421 252422 252423 252424 252425 252426 252427 252428 252429 252430 252431 252432 252433 252434 252435 252436 252437 252438 252439 252440 252441 252442 252443 252444 252445 252446 252447 252448 252449 252450 252451 252452 252453 252454 252455 252456 252457 252458 252459 252460 252461 252462 252463 252464 252465 252466 252467 252468 252469 252470 252471 252472 252473 | va_list ap; /* ... printf arguments */ va_start(ap, zFormat); sqlite3_free(p->p.base.zErrMsg); p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); } /* ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain ** valid until after the final call to sqlite3Fts5Tokenize() that will use ** the locale. */ static void fts5SetLocale( Fts5Config *pConfig, const char *zLocale, int nLocale ){ Fts5TokenizerConfig *pT = &pConfig->t; pT->pLocale = zLocale; pT->nLocale = nLocale; } /* ** Clear any locale configured by an earlier call to fts5SetLocale() or ** sqlite3Fts5ExtractText(). */ static void sqlite3Fts5ClearLocale(Fts5Config *pConfig){ fts5SetLocale(pConfig, 0, 0); } /* ** This function is used to extract utf-8 text from an sqlite3_value. This ** is usually done in order to tokenize it. For example, when: ** ** * a value is written to an fts5 table, ** * a value is deleted from an FTS5_CONTENT_NORMAL table, ** * a value containing a query expression is passed to xFilter() ** ** and so on. ** ** This function handles 2 cases: ** ** 1) Ordinary values. The text can be extracted from these using ** sqlite3_value_text(). ** ** 2) Combination text/locale blobs created by fts5_locale(). There ** are several cases for these: ** ** * Blobs tagged with FTS5_LOCALE_SUBTYPE. ** * Blobs read from the content table of a locale=1 external-content ** table, and ** * Blobs read from the content table of a locale=1 regular ** content table. ** ** The first two cases above should have the 4 byte FTS5_LOCALE_HEADER ** header. It is an error if a blob with the subtype or a blob read ** from the content table of an external content table does not have ** the required header. A blob read from the content table of a regular ** locale=1 table does not have the header. This is to save space. ** ** If successful, SQLITE_OK is returned and output parameters (*ppText) ** and (*pnText) are set to point to a buffer containing the extracted utf-8 ** text and its length in bytes, respectively. The buffer is not ** nul-terminated. It has the same lifetime as the sqlite3_value object ** from which it is extracted. ** ** Parameter bContent must be true if the value was read from an indexed ** column (i.e. not UNINDEXED) of the on disk content. ** ** If pbResetTokenizer is not NULL and if case (2) is used, then ** fts5SetLocale() is called to ensure subsequent sqlite3Fts5Tokenize() calls ** use the locale. In this case (*pbResetTokenizer) is set to true before ** returning, to indicate that the caller must call sqlite3Fts5ClearLocale() ** to clear the locale after tokenizing the text. */ static int sqlite3Fts5ExtractText( Fts5Config *pConfig, sqlite3_value *pVal, /* Value to extract text from */ int bContent, /* True if indexed table content */ int *pbResetTokenizer, /* OUT: True if xSetLocale(NULL) required */ const char **ppText, /* OUT: Pointer to text buffer */ int *pnText /* OUT: Size of (*ppText) in bytes */ ){ const char *pText = 0; int nText = 0; int rc = SQLITE_OK; int bDecodeBlob = 0; assert( pbResetTokenizer==0 || *pbResetTokenizer==0 ); assert( bContent==0 || pConfig->eContent!=FTS5_CONTENT_NONE ); assert( bContent==0 || sqlite3_value_subtype(pVal)==0 ); if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE || (bContent && pConfig->bLocale) ){ bDecodeBlob = 1; } } if( bDecodeBlob ){ const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; const u8 *pBlob = sqlite3_value_blob(pVal); int nBlob = sqlite3_value_bytes(pVal); /* Unless this blob was read from the %_content table of an ** FTS5_CONTENT_NORMAL table, it should have the 4 byte fts5_locale() ** header. Check for this. If it is not found, return an error. */ if( (!bContent || pConfig->eContent!=FTS5_CONTENT_NORMAL) ){ if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ rc = SQLITE_ERROR; }else{ pBlob += 4; nBlob -= 4; } } if( rc==SQLITE_OK ){ int nLocale = 0; for(nLocale=0; nLocale<nBlob; nLocale++){ if( pBlob[nLocale]==0x00 ) break; } if( nLocale==nBlob || nLocale==0 ){ rc = SQLITE_ERROR; }else{ pText = (const char*)&pBlob[nLocale+1]; nText = nBlob-nLocale-1; if( pbResetTokenizer ){ fts5SetLocale(pConfig, (const char*)pBlob, nLocale); *pbResetTokenizer = 1; } } } }else{ pText = (const char*)sqlite3_value_text(pVal); nText = sqlite3_value_bytes(pVal); } *ppText = pText; *pnText = nText; return rc; } /* ** Argument pVal is the text of a full-text search expression. It may or ** may not have been wrapped by fts5_locale(). This function extracts ** the text of the expression, and sets output variable (*pzText) to ** point to a nul-terminated buffer containing the expression. ** ** If pVal was an fts5_locale() value, then fts5SetLocale() is called to ** set the tokenizer to use the specified locale. ** ** If output variable (*pbFreeAndReset) is set to true, then the caller ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer ** locale, and (b) call sqlite3_free() to free (*pzText). */ static int fts5ExtractExprText( Fts5Config *pConfig, /* Fts5 configuration */ sqlite3_value *pVal, /* Value to extract expression text from */ char **pzText, /* OUT: nul-terminated buffer of text */ int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */ ){ const char *zText = 0; int nText = 0; int rc = SQLITE_OK; int bReset = 0; *pbFreeAndReset = 0; rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, &bReset, &zText, &nText); if( rc==SQLITE_OK ){ if( bReset ){ *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, zText); if( rc!=SQLITE_OK ){ sqlite3Fts5ClearLocale(pConfig); }else{ *pbFreeAndReset = 1; } }else{ *pzText = (char*)zText; } } return rc; } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** There are three possible query strategies: |
︙ | ︙ | |||
251530 251531 251532 251533 251534 251535 251536 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ int iCol; /* Column on LHS of MATCH operator */ char **pzErrmsg = pConfig->pzErrmsg; int i; int iIdxStr = 0; Fts5Expr *pExpr = 0; | | < < < < < < | 252495 252496 252497 252498 252499 252500 252501 252502 252503 252504 252505 252506 252507 252508 252509 | sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ int iCol; /* Column on LHS of MATCH operator */ char **pzErrmsg = pConfig->pzErrmsg; int i; int iIdxStr = 0; Fts5Expr *pExpr = 0; assert( pConfig->bLock==0 ); if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); } assert( pCsr->pStmt==0 ); assert( pCsr->pExpr==0 ); |
︙ | ︙ | |||
251560 251561 251562 251563 251564 251565 251566 | /* Decode the arguments passed through to this function. */ for(i=0; i<nVal; i++){ switch( idxStr[iIdxStr++] ){ case 'r': pRank = apVal[i]; break; case 'M': { | | > > > > > > | > | > > > > > | 252519 252520 252521 252522 252523 252524 252525 252526 252527 252528 252529 252530 252531 252532 252533 252534 252535 252536 252537 252538 252539 252540 252541 252542 252543 252544 252545 252546 252547 252548 252549 252550 252551 252552 252553 252554 252555 252556 252557 252558 252559 252560 252561 252562 252563 252564 252565 252566 252567 252568 | /* Decode the arguments passed through to this function. */ for(i=0; i<nVal; i++){ switch( idxStr[iIdxStr++] ){ case 'r': pRank = apVal[i]; break; case 'M': { char *zText = 0; int bFreeAndReset = 0; int bInternal = 0; rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset); if( rc!=SQLITE_OK ) goto filter_out; if( zText==0 ) zText = ""; iCol = 0; do{ iCol = iCol*10 + (idxStr[iIdxStr]-'0'); iIdxStr++; }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ); if( zText[0]=='*' ){ /* The user has issued a query of the form "MATCH '*...'". This ** indicates that the MATCH expression is not a full text query, ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zText[1]); bInternal = 1; }else{ char **pzErr = &pTab->p.base.zErrMsg; rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr); pExpr = 0; } } if( bFreeAndReset ){ sqlite3_free(zText); sqlite3Fts5ClearLocale(pConfig); } if( bInternal || rc!=SQLITE_OK ) goto filter_out; break; } case 'L': case 'G': { int bGlob = (idxStr[iIdxStr-1]=='G'); const char *zText = (const char*)sqlite3_value_text(apVal[i]); iCol = 0; |
︙ | ︙ | |||
251891 251892 251893 251894 251895 251896 251897 | Fts5FullTable *pTab, sqlite3_value **apVal ){ int rc = SQLITE_OK; int eType1 = sqlite3_value_type(apVal[1]); if( eType1==SQLITE_INTEGER ){ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); | | | 252862 252863 252864 252865 252866 252867 252868 252869 252870 252871 252872 252873 252874 252875 252876 | Fts5FullTable *pTab, sqlite3_value **apVal ){ int rc = SQLITE_OK; int eType1 = sqlite3_value_type(apVal[1]); if( eType1==SQLITE_INTEGER ){ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0); } return rc; } static void fts5StorageInsert( int *pRc, Fts5FullTable *pTab, |
︙ | ︙ | |||
252015 252016 252017 252018 252019 252020 252021 | ); rc = SQLITE_ERROR; } /* DELETE */ else if( nArg==1 ){ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ | | > > > > > | > > > > > > > | > | | > > | | | > > | | > > | > | | > > > | 252986 252987 252988 252989 252990 252991 252992 252993 252994 252995 252996 252997 252998 252999 253000 253001 253002 253003 253004 253005 253006 253007 253008 253009 253010 253011 253012 253013 253014 253015 253016 253017 253018 253019 253020 253021 253022 253023 253024 253025 253026 253027 253028 253029 253030 253031 253032 253033 253034 253035 253036 253037 253038 253039 253040 253041 253042 253043 253044 253045 253046 253047 253048 253049 253050 253051 253052 253053 253054 253055 253056 253057 253058 253059 253060 253061 253062 253063 253064 253065 253066 253067 253068 253069 253070 253071 253072 253073 253074 253075 253076 253077 253078 253079 253080 253081 253082 253083 253084 253085 253086 253087 | ); rc = SQLITE_ERROR; } /* DELETE */ else if( nArg==1 ){ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0); bUpdateOrDelete = 1; } /* INSERT or UPDATE */ else{ int eType1 = sqlite3_value_numeric_type(apVal[1]); /* Ensure that no fts5_locale() values are written to locale=0 tables. ** And that no blobs except fts5_locale() blobs are written to indexed ** (i.e. not UNINDEXED) columns of locale=1 tables. */ int ii; for(ii=0; ii<pConfig->nCol; ii++){ if( sqlite3_value_type(apVal[ii+2])==SQLITE_BLOB ){ int bSub = (sqlite3_value_subtype(apVal[ii+2])==FTS5_LOCALE_SUBTYPE); if( (pConfig->bLocale && !bSub && pConfig->abUnindexed[ii]==0) || (pConfig->bLocale==0 && bSub) ){ if( pConfig->bLocale==0 ){ fts5SetVtabError(pTab, "fts5_locale() requires locale=1"); } rc = SQLITE_MISMATCH; goto update_out; } } } if( eType0!=SQLITE_INTEGER ){ /* An INSERT statement. If the conflict-mode is 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, 0); bUpdateOrDelete = 1; } fts5StorageInsert(&rc, pTab, apVal, pRowid); } /* UPDATE */ else{ i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ if( eType1!=SQLITE_INTEGER ){ rc = SQLITE_MISMATCH; }else if( iOld!=iNew ){ if( eConflict==SQLITE_REPLACE ){ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0); } fts5StorageInsert(&rc, pTab, apVal, pRowid); }else{ rc = sqlite3Fts5StorageFindDeleteRow(pTab->pStorage, iOld); if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage,apVal,pRowid); } if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); } if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); } } }else{ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0, 1); fts5StorageInsert(&rc, pTab, apVal, pRowid); } bUpdateOrDelete = 1; sqlite3Fts5StorageReleaseDeleteRow(pTab->pStorage); } } } 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; } } update_out: pTab->p.pConfig->pzErrmsg = 0; return rc; } /* ** Implementation of xSync() method. */ |
︙ | ︙ | |||
252101 252102 252103 252104 252105 252106 252107 | return rc; } /* ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ | > > | < > | | 253095 253096 253097 253098 253099 253100 253101 253102 253103 253104 253105 253106 253107 253108 253109 253110 253111 253112 253113 | return rc; } /* ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ int rc = fts5NewTransaction((Fts5FullTable*)pVtab); if( rc==SQLITE_OK ){ fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); } return rc; } /* ** Implementation of xCommit() method. This is a no-op. The contents of ** the pending-terms hash-table have already been flushed into the database ** by fts5SyncMethod(). */ |
︙ | ︙ | |||
252157 252158 252159 252160 252161 252162 252163 252164 252165 252166 252167 252168 252169 | static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); } static int fts5ApiTokenize( Fts5Context *pCtx, const char *pText, int nText, void *pUserData, int (*xToken)(void*, int, const char*, int, int, int) ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | < | 253153 253154 253155 253156 253157 253158 253159 253160 253161 253162 253163 253164 253165 253166 253167 253168 253169 253170 253171 253172 253173 253174 253175 253176 253177 253178 253179 253180 253181 253182 253183 253184 253185 253186 253187 253188 253189 253190 253191 253192 253193 253194 253195 253196 253197 253198 253199 253200 | static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); } /* ** Implementation of xTokenize_v2() API. */ static int fts5ApiTokenize_v2( Fts5Context *pCtx, const char *pText, int nText, const char *pLoc, int nLoc, void *pUserData, int (*xToken)(void*, int, const char*, int, int, int) ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); int rc = SQLITE_OK; fts5SetLocale(pTab->pConfig, pLoc, nLoc); rc = sqlite3Fts5Tokenize(pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken ); fts5SetLocale(pTab->pConfig, 0, 0); return rc; } /* ** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0 ** passed as the locale. */ static int fts5ApiTokenize( Fts5Context *pCtx, const char *pText, int nText, void *pUserData, int (*xToken)(void*, int, const char*, int, int, int) ){ return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken); } static int fts5ApiPhraseCount(Fts5Context *pCtx){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; return sqlite3Fts5ExprPhraseCount(pCsr->pExpr); } |
︙ | ︙ | |||
252189 252190 252191 252192 252193 252194 252195 252196 252197 | int iCol, const char **pz, int *pn ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); if( iCol<0 || iCol>=pTab->pConfig->nCol ){ rc = SQLITE_RANGE; | > > | < < > > | | > > > > > > > | | | | > > > > > > > > > > > | > > | > | 253208 253209 253210 253211 253212 253213 253214 253215 253216 253217 253218 253219 253220 253221 253222 253223 253224 253225 253226 253227 253228 253229 253230 253231 253232 253233 253234 253235 253236 253237 253238 253239 253240 253241 253242 253243 253244 253245 253246 253247 253248 253249 253250 253251 253252 253253 253254 253255 253256 253257 253258 253259 253260 253261 253262 253263 253264 253265 253266 253267 253268 253269 253270 253271 253272 253273 253274 253275 253276 253277 253278 253279 253280 253281 253282 253283 253284 253285 253286 253287 | int iCol, const char **pz, int *pn ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); if( iCol<0 || iCol>=pTab->pConfig->nCol ){ rc = SQLITE_RANGE; }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) ){ *pz = 0; *pn = 0; }else{ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ Fts5Config *pConfig = pTab->pConfig; int bContent = (pConfig->abUnindexed[iCol]==0); sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); sqlite3Fts5ExtractText(pConfig, pVal, bContent, 0, pz, pn); } } return rc; } /* ** This is called by various API functions - xInst, xPhraseFirst, ** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase ** of the current row. This function works for both detail=full tables (in ** which case the position-list was read from the fts index) or for other ** detail= modes if the row content is available. */ static int fts5CsrPoslist( Fts5Cursor *pCsr, /* Fts5 cursor object */ int iPhrase, /* Phrase to find position list for */ const u8 **pa, /* OUT: Pointer to position list buffer */ int *pn /* OUT: Size of (*pa) in bytes */ ){ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; int rc = SQLITE_OK; int bLive = (pCsr->pSorter==0); if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ rc = SQLITE_RANGE; }else if( pConfig->eDetail!=FTS5_DETAIL_FULL && pConfig->eContent==FTS5_CONTENT_NONE ){ *pa = 0; *pn = 0; return SQLITE_OK; }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ Fts5PoslistPopulator *aPopulator; int i; aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); if( aPopulator==0 ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK ){ rc = fts5SeekCursor(pCsr, 0); } for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); const char *z = 0; int n = 0; int bReset = 0; rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ExprPopulatePoslists( pConfig, pCsr->pExpr, aPopulator, i, z, n ); } if( bReset ) sqlite3Fts5ClearLocale(pConfig); } sqlite3_free(aPopulator); if( pCsr->pSorter ){ sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); } } |
︙ | ︙ | |||
252255 252256 252257 252258 252259 252260 252261 | }else{ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); } }else{ *pa = 0; *pn = 0; } | < | 253297 253298 253299 253300 253301 253302 253303 253304 253305 253306 253307 253308 253309 253310 | }else{ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); } }else{ *pa = 0; *pn = 0; } return rc; } /* ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated ** correctly for the current view. Return SQLITE_OK if successful, or an |
︙ | ︙ | |||
252325 252326 252327 252328 252329 252330 252331 | } } aInst = &pCsr->aInst[3 * (nInst-1)]; aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); | > | | 253366 253367 253368 253369 253370 253371 253372 253373 253374 253375 253376 253377 253378 253379 253380 253381 | } } aInst = &pCsr->aInst[3 * (nInst-1)]; aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); assert( aInst[1]>=0 ); if( aInst[1]>=nCol ){ rc = FTS5_CORRUPT; break; } sqlite3Fts5PoslistReaderNext(&aIter[iBest]); } } |
︙ | ︙ | |||
252412 252413 252414 252415 252416 252417 252418 252419 252420 | for(i=0; i<pConfig->nCol; i++){ if( pConfig->abUnindexed[i]==0 ){ pCsr->aColumnSize[i] = -1; } } }else{ int i; for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ if( pConfig->abUnindexed[i]==0 ){ | > | > > > | | | | > | 253454 253455 253456 253457 253458 253459 253460 253461 253462 253463 253464 253465 253466 253467 253468 253469 253470 253471 253472 253473 253474 253475 253476 253477 253478 253479 253480 253481 253482 | for(i=0; i<pConfig->nCol; i++){ if( pConfig->abUnindexed[i]==0 ){ pCsr->aColumnSize[i] = -1; } } }else{ int i; rc = fts5SeekCursor(pCsr, 0); for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ if( pConfig->abUnindexed[i]==0 ){ const char *z = 0; int n = 0; int bReset = 0; sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, i+1); pCsr->aColumnSize[i] = 0; rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &z, &n); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX, z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb ); if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } } } CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE); } if( iCol<0 ){ |
︙ | ︙ | |||
252667 252668 252669 252670 252671 252672 252673 252674 252675 | return rc; } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | 253714 253715 253716 253717 253718 253719 253720 253721 253722 253723 253724 253725 253726 253727 253728 253729 253730 253731 253732 253733 253734 253735 253736 253737 253738 253739 253740 253741 253742 253743 253744 253745 253746 253747 253748 253749 253750 253751 253752 253753 253754 253755 253756 253757 253758 253759 253760 253761 253762 253763 253764 253765 253766 253767 253768 253769 253770 253771 253772 253773 253774 253775 253776 253777 253778 253779 253780 253781 253782 253783 253784 253785 253786 253787 253788 253789 253790 253791 253792 253793 253794 253795 253796 253797 253798 253799 253800 253801 253802 253803 253804 253805 253806 253807 253808 253809 253810 253811 253812 253813 253814 253815 253816 | return rc; } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); /* ** The xColumnLocale() API. */ static int fts5ApiColumnLocale( Fts5Context *pCtx, int iCol, const char **pzLocale, int *pnLocale ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; *pzLocale = 0; *pnLocale = 0; assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); if( iCol<0 || iCol>=pConfig->nCol ){ rc = SQLITE_RANGE; }else if( pConfig->abUnindexed[iCol]==0 && pConfig->eContent!=FTS5_CONTENT_NONE && pConfig->bLocale ){ rc = fts5SeekCursor(pCsr, 0); if( rc==SQLITE_OK ){ /* Load the value into pVal. pVal is a locale/text pair iff: ** ** 1) It is an SQLITE_BLOB, and ** 2) Either the subtype is FTS5_LOCALE_SUBTYPE, or else the ** value was loaded from an FTS5_CONTENT_NORMAL table, and ** 3) It does not begin with an 0x00 byte. */ sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); if( sqlite3_value_type(pVal)==SQLITE_BLOB ){ const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); int nBlob = sqlite3_value_bytes(pVal); if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; if( nBlob<SZHDR || memcmp(FTS5_LOCALE_HEADER, pBlob, SZHDR) ){ rc = SQLITE_ERROR; } pBlob += 4; nBlob -= 4; } if( rc==SQLITE_OK ){ int nLocale = 0; for(nLocale=0; nLocale<nBlob && pBlob[nLocale]!=0x00; nLocale++); if( nLocale==nBlob || nLocale==0 ){ rc = SQLITE_ERROR; }else{ /* A locale/text pair */ *pzLocale = (const char*)pBlob; *pnLocale = nLocale; } } } } } return rc; } static const Fts5ExtensionApi sFts5Api = { 4, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, fts5ApiColumnTotalSize, fts5ApiTokenize, fts5ApiPhraseCount, fts5ApiPhraseSize, fts5ApiInstCount, fts5ApiInst, fts5ApiRowid, fts5ApiColumnText, fts5ApiColumnSize, fts5ApiQueryPhrase, fts5ApiSetAuxdata, fts5ApiGetAuxdata, fts5ApiPhraseFirst, fts5ApiPhraseNext, fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, fts5ApiQueryToken, fts5ApiInstToken, fts5ApiColumnLocale, fts5ApiTokenize_v2 }; /* ** Implementation of API function xQueryPhrase(). */ static int fts5ApiQueryPhrase( Fts5Context *pCtx, |
︙ | ︙ | |||
252741 252742 252743 252744 252745 252746 252747 252748 252749 252750 252751 252752 252753 252754 252755 252756 252757 252758 252759 252760 252761 252762 252763 252764 252765 252766 252767 252768 252769 252770 252771 252772 252773 252774 252775 | Fts5Auxiliary *pAux, Fts5Cursor *pCsr, sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( pCsr->pAux==0 ); pCsr->pAux = pAux; pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); pCsr->pAux = 0; } static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ Fts5Cursor *pCsr; for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->iCsrId==iCsrId ) break; } return pCsr; } static void fts5ApiCallback( sqlite3_context *context, int argc, sqlite3_value **argv ){ Fts5Auxiliary *pAux; Fts5Cursor *pCsr; i64 iCsrId; assert( argc>=1 ); pAux = (Fts5Auxiliary*)sqlite3_user_data(context); iCsrId = sqlite3_value_int64(argv[0]); pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); | > > > > > > > > > > > > > > > > | | < < | 253853 253854 253855 253856 253857 253858 253859 253860 253861 253862 253863 253864 253865 253866 253867 253868 253869 253870 253871 253872 253873 253874 253875 253876 253877 253878 253879 253880 253881 253882 253883 253884 253885 253886 253887 253888 253889 253890 253891 253892 253893 253894 253895 253896 253897 253898 253899 253900 253901 253902 253903 253904 253905 253906 253907 253908 253909 253910 253911 253912 | Fts5Auxiliary *pAux, Fts5Cursor *pCsr, sqlite3_context *context, int argc, sqlite3_value **argv ){ assert( pCsr->pAux==0 ); assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL ); pCsr->pAux = pAux; pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); pCsr->pAux = 0; } static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ Fts5Cursor *pCsr; for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->iCsrId==iCsrId ) break; } return pCsr; } /* ** Parameter zFmt is a printf() style formatting string. This function ** formats it using the trailing arguments and returns the result as ** an error message to the context passed as the first argument. */ static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){ char *zErr = 0; va_list ap; va_start(ap, zFmt); zErr = sqlite3_vmprintf(zFmt, ap); sqlite3_result_error(pCtx, zErr, -1); sqlite3_free(zErr); va_end(ap); } static void fts5ApiCallback( sqlite3_context *context, int argc, sqlite3_value **argv ){ Fts5Auxiliary *pAux; Fts5Cursor *pCsr; i64 iCsrId; assert( argc>=1 ); pAux = (Fts5Auxiliary*)sqlite3_user_data(context); iCsrId = sqlite3_value_int64(argv[0]); pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL) ){ fts5ResultError(context, "no such cursor: %lld", iCsrId); }else{ sqlite3_vtab *pTab = pCsr->base.pVtab; fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); sqlite3_free(pTab->zErrMsg); pTab->zErrMsg = 0; } } |
︙ | ︙ | |||
252865 252866 252867 252868 252869 252870 252871 252872 252873 252874 252875 252876 252877 252878 | default: break; } sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); return rc; } /* ** This is the xColumn method, called by SQLite to request a value from ** the row that the supplied cursor currently points to. */ static int fts5ColumnMethod( sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 253991 253992 253993 253994 253995 253996 253997 253998 253999 254000 254001 254002 254003 254004 254005 254006 254007 254008 254009 254010 254011 254012 254013 254014 254015 254016 254017 254018 254019 254020 254021 254022 254023 254024 254025 254026 254027 254028 254029 254030 254031 254032 254033 254034 254035 254036 254037 254038 254039 254040 254041 254042 254043 254044 254045 254046 254047 254048 254049 254050 254051 254052 254053 254054 254055 | default: break; } sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); return rc; } /* ** Value pVal was read from column iCol of the FTS5 table. This function ** returns it to the owner of pCtx via a call to an sqlite3_result_xxx() ** function. This function deals with the same cases as ** sqlite3Fts5ExtractText(): ** ** 1) Ordinary values. These can be returned using sqlite3_result_value(). ** ** 2) Blobs from fts5_locale(). The text is extracted from these and ** returned via sqlite3_result_text(). The locale is discarded. */ static void fts5ExtractValueFromColumn( sqlite3_context *pCtx, Fts5Config *pConfig, int iCol, sqlite3_value *pVal ){ assert( pConfig->eContent!=FTS5_CONTENT_NONE ); if( pConfig->bLocale && sqlite3_value_type(pVal)==SQLITE_BLOB && pConfig->abUnindexed[iCol]==0 ){ const int SZHDR = sizeof(FTS5_LOCALE_HEADER)-1; const u8 *pBlob = sqlite3_value_blob(pVal); int nBlob = sqlite3_value_bytes(pVal); int ii; if( pConfig->eContent==FTS5_CONTENT_EXTERNAL ){ if( nBlob<SZHDR || memcmp(pBlob, FTS5_LOCALE_HEADER, SZHDR) ){ sqlite3_result_error_code(pCtx, SQLITE_ERROR); return; }else{ pBlob += 4; nBlob -= 4; } } for(ii=0; ii<nBlob && pBlob[ii]; ii++); if( ii==0 || ii==nBlob ){ sqlite3_result_error_code(pCtx, SQLITE_ERROR); }else{ const char *pText = (const char*)&pBlob[ii+1]; sqlite3_result_text(pCtx, pText, nBlob-ii-1, SQLITE_TRANSIENT); } return; } sqlite3_result_value(pCtx, pVal); } /* ** This is the xColumn method, called by SQLite to request a value from ** the row that the supplied cursor currently points to. */ static int fts5ColumnMethod( sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ |
︙ | ︙ | |||
252895 252896 252897 252898 252899 252900 252901 | if( iCol==pConfig->nCol ){ /* User is requesting the value of the special column with the same name ** as the table. Return the cursor integer id number. This value is only ** useful in that it may be passed as the first argument to an FTS5 ** auxiliary function. */ sqlite3_result_int64(pCtx, pCsr->iCsrId); }else if( iCol==pConfig->nCol+1 ){ | < > > > > > | > > > > > > | | | | > | | < < < < < < | > > | 254072 254073 254074 254075 254076 254077 254078 254079 254080 254081 254082 254083 254084 254085 254086 254087 254088 254089 254090 254091 254092 254093 254094 254095 254096 254097 254098 254099 254100 254101 254102 254103 254104 254105 254106 254107 254108 254109 254110 254111 254112 254113 254114 254115 254116 254117 254118 | if( iCol==pConfig->nCol ){ /* User is requesting the value of the special column with the same name ** as the table. Return the cursor integer id number. This value is only ** useful in that it may be passed as the first argument to an FTS5 ** auxiliary function. */ sqlite3_result_int64(pCtx, pCsr->iCsrId); }else if( iCol==pConfig->nCol+1 ){ /* The value of the "rank" column. */ if( pCsr->ePlan==FTS5_PLAN_SOURCE ){ fts5PoslistBlob(pCtx, pCsr); }else if( pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH ){ if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); } } }else{ /* A column created by the user containing values. */ int bNochange = sqlite3_vtab_nochange(pCtx); if( fts5IsContentless(pTab) ){ if( bNochange && pConfig->bContentlessDelete ){ fts5ResultError(pCtx, "cannot UPDATE a subset of " "columns on fts5 contentless-delete table: %s", pConfig->zName ); } }else if( bNochange==0 || pConfig->eContent!=FTS5_CONTENT_NORMAL ){ pConfig->pzErrmsg = &pTab->p.base.zErrMsg; rc = fts5SeekCursor(pCsr, 1); if( rc==SQLITE_OK ){ sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1); fts5ExtractValueFromColumn(pCtx, pConfig, iCol, pVal); } pConfig->pzErrmsg = 0; } } return rc; } /* ** This routine implements the xFindFunction method for the FTS3 ** virtual table. |
︙ | ︙ | |||
253058 253059 253060 253061 253062 253063 253064 253065 253066 253067 | }else{ rc = SQLITE_NOMEM; } } return rc; } /* ** Register a new tokenizer. This is the implementation of the | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < < | | > < < < < | | | < | < | < < < < > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < | < | < < < < < < < < < < < < < < < < < | < < < < < < < | | < < < < | | < < > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > | 254242 254243 254244 254245 254246 254247 254248 254249 254250 254251 254252 254253 254254 254255 254256 254257 254258 254259 254260 254261 254262 254263 254264 254265 254266 254267 254268 254269 254270 254271 254272 254273 254274 254275 254276 254277 254278 254279 254280 254281 254282 254283 254284 254285 254286 254287 254288 254289 254290 254291 254292 254293 254294 254295 254296 254297 254298 254299 254300 254301 254302 254303 254304 254305 254306 254307 254308 254309 254310 254311 254312 254313 254314 254315 254316 254317 254318 254319 254320 254321 254322 254323 254324 254325 254326 254327 254328 254329 254330 254331 254332 254333 254334 254335 254336 254337 254338 254339 254340 254341 254342 254343 254344 254345 254346 254347 254348 254349 254350 254351 254352 254353 254354 254355 254356 254357 254358 254359 254360 254361 254362 254363 254364 254365 254366 254367 254368 254369 254370 254371 254372 254373 254374 254375 254376 254377 254378 254379 254380 254381 254382 254383 254384 254385 254386 254387 254388 254389 254390 254391 254392 254393 254394 254395 254396 254397 254398 254399 254400 254401 254402 254403 254404 254405 254406 254407 254408 254409 254410 254411 254412 254413 254414 254415 254416 254417 254418 254419 254420 254421 254422 254423 254424 254425 254426 254427 254428 254429 254430 254431 254432 254433 254434 254435 254436 254437 254438 254439 254440 254441 254442 254443 254444 254445 254446 254447 254448 254449 254450 254451 254452 254453 254454 254455 254456 254457 254458 254459 254460 254461 254462 254463 254464 254465 254466 254467 254468 254469 254470 254471 254472 254473 254474 254475 254476 254477 254478 254479 254480 254481 254482 254483 254484 254485 254486 254487 254488 254489 254490 254491 254492 254493 254494 254495 254496 254497 254498 254499 254500 254501 254502 254503 254504 254505 254506 254507 254508 254509 254510 254511 254512 254513 254514 254515 254516 254517 254518 254519 254520 254521 254522 254523 254524 254525 254526 254527 254528 254529 254530 254531 254532 254533 254534 254535 254536 254537 254538 254539 254540 254541 254542 254543 254544 254545 254546 254547 254548 254549 254550 254551 254552 254553 254554 254555 254556 254557 254558 254559 254560 254561 254562 254563 254564 254565 254566 254567 254568 254569 254570 254571 254572 254573 254574 254575 254576 254577 254578 254579 254580 254581 254582 254583 254584 254585 254586 254587 254588 254589 254590 254591 254592 254593 254594 254595 254596 254597 254598 254599 254600 254601 254602 254603 254604 254605 254606 254607 254608 254609 254610 254611 254612 254613 | }else{ rc = SQLITE_NOMEM; } } return rc; } /* ** This function is used by xCreateTokenizer_v2() and xCreateTokenizer(). ** It allocates and partially populates a new Fts5TokenizerModule object. ** The new object is already linked into the Fts5Global context before ** returning. ** ** If successful, SQLITE_OK is returned and a pointer to the new ** Fts5TokenizerModule object returned via output parameter (*ppNew). All ** that is required is for the caller to fill in the methods in ** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native ** as appropriate. ** ** If an error occurs, an SQLite error code is returned and the final value ** of (*ppNew) undefined. */ static int fts5NewTokenizerModule( Fts5Global *pGlobal, /* Global context (one per db handle) */ const char *zName, /* Name of new function */ void *pUserData, /* User data for aux. function */ void(*xDestroy)(void*), /* Destructor for pUserData */ Fts5TokenizerModule **ppNew ){ int rc = SQLITE_OK; Fts5TokenizerModule *pNew; sqlite3_int64 nName; /* Size of zName and its \0 terminator */ sqlite3_int64 nByte; /* Bytes of space to allocate */ nName = strlen(zName) + 1; nByte = sizeof(Fts5TokenizerModule) + nName; *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte); if( pNew ){ pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName); pNew->pUserData = pUserData; pNew->xDestroy = xDestroy; pNew->pNext = pGlobal->pTok; pGlobal->pTok = pNew; if( pNew->pNext==0 ){ pGlobal->pDfltTok = pNew; } } return rc; } /* ** An instance of this type is used as the Fts5Tokenizer object for ** wrapper tokenizers - those that provide access to a v1 tokenizer via ** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer ** via the fts5_tokenizer API. */ typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer; struct Fts5VtoVTokenizer { int bV2Native; /* True if v2 native tokenizer */ fts5_tokenizer x1; /* Tokenizer functions */ fts5_tokenizer_v2 x2; /* V2 tokenizer functions */ Fts5Tokenizer *pReal; }; /* ** Create a wrapper tokenizer. The context argument pCtx points to the ** Fts5TokenizerModule object. */ static int fts5VtoVCreate( void *pCtx, const char **azArg, int nArg, Fts5Tokenizer **ppOut ){ Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx; Fts5VtoVTokenizer *pNew = 0; int rc = SQLITE_OK; pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew)); if( rc==SQLITE_OK ){ pNew->x1 = pMod->x1; pNew->x2 = pMod->x2; pNew->bV2Native = pMod->bV2Native; if( pMod->bV2Native ){ rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); }else{ rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal); } if( rc!=SQLITE_OK ){ sqlite3_free(pNew); pNew = 0; } } *ppOut = (Fts5Tokenizer*)pNew; return rc; } /* ** Delete an Fts5VtoVTokenizer wrapper tokenizer. */ static void fts5VtoVDelete(Fts5Tokenizer *pTok){ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; if( p ){ if( p->bV2Native ){ p->x2.xDelete(p->pReal); }else{ p->x1.xDelete(p->pReal); } sqlite3_free(p); } } /* ** xTokenizer method for a wrapper tokenizer that offers the v1 interface ** (no support for locales). */ static int fts5V1toV2Tokenize( Fts5Tokenizer *pTok, void *pCtx, int flags, const char *pText, int nText, int (*xToken)(void*, int, const char*, int, int, int) ){ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; assert( p->bV2Native ); return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken); } /* ** xTokenizer method for a wrapper tokenizer that offers the v2 interface ** (with locale support). */ static int fts5V2toV1Tokenize( Fts5Tokenizer *pTok, void *pCtx, int flags, const char *pText, int nText, const char *pLocale, int nLocale, int (*xToken)(void*, int, const char*, int, int, int) ){ Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok; assert( p->bV2Native==0 ); UNUSED_PARAM2(pLocale,nLocale); return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken); } /* ** Register a new tokenizer. This is the implementation of the ** fts5_api.xCreateTokenizer_v2() method. */ static int fts5CreateTokenizer_v2( fts5_api *pApi, /* Global context (one per db handle) */ const char *zName, /* Name of new function */ void *pUserData, /* User data for aux. function */ fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */ void(*xDestroy)(void*) /* Destructor for pUserData */ ){ Fts5Global *pGlobal = (Fts5Global*)pApi; int rc = SQLITE_OK; if( pTokenizer->iVersion>2 ){ rc = SQLITE_ERROR; }else{ Fts5TokenizerModule *pNew = 0; rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew); if( pNew ){ pNew->x2 = *pTokenizer; pNew->bV2Native = 1; pNew->x1.xCreate = fts5VtoVCreate; pNew->x1.xTokenize = fts5V1toV2Tokenize; pNew->x1.xDelete = fts5VtoVDelete; } } return rc; } /* ** The fts5_api.xCreateTokenizer() method. */ static int fts5CreateTokenizer( fts5_api *pApi, /* Global context (one per db handle) */ const char *zName, /* Name of new function */ void *pUserData, /* User data for aux. function */ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ void(*xDestroy)(void*) /* Destructor for pUserData */ ){ Fts5TokenizerModule *pNew = 0; int rc = SQLITE_OK; rc = fts5NewTokenizerModule( (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew ); if( pNew ){ pNew->x1 = *pTokenizer; pNew->x2.xCreate = fts5VtoVCreate; pNew->x2.xTokenize = fts5V2toV1Tokenize; pNew->x2.xDelete = fts5VtoVDelete; } return rc; } /* ** Search the global context passed as the first argument for a tokenizer ** module named zName. If found, return a pointer to the Fts5TokenizerModule ** object. Otherwise, return NULL. */ static Fts5TokenizerModule *fts5LocateTokenizer( Fts5Global *pGlobal, /* Global (one per db handle) object */ const char *zName /* Name of tokenizer module to find */ ){ Fts5TokenizerModule *pMod = 0; if( zName==0 ){ pMod = pGlobal->pDfltTok; }else{ for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){ if( sqlite3_stricmp(zName, pMod->zName)==0 ) break; } } return pMod; } /* ** Find a tokenizer. This is the implementation of the ** fts5_api.xFindTokenizer_v2() method. */ static int fts5FindTokenizer_v2( fts5_api *pApi, /* Global context (one per db handle) */ const char *zName, /* Name of tokenizer */ void **ppUserData, fts5_tokenizer_v2 **ppTokenizer /* Populate this object */ ){ int rc = SQLITE_OK; Fts5TokenizerModule *pMod; pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); if( pMod ){ if( pMod->bV2Native ){ *ppUserData = pMod->pUserData; }else{ *ppUserData = (void*)pMod; } *ppTokenizer = &pMod->x2; }else{ *ppTokenizer = 0; *ppUserData = 0; rc = SQLITE_ERROR; } return rc; } /* ** Find a tokenizer. This is the implementation of the ** fts5_api.xFindTokenizer() method. */ static int fts5FindTokenizer( fts5_api *pApi, /* Global context (one per db handle) */ const char *zName, /* Name of new function */ void **ppUserData, fts5_tokenizer *pTokenizer /* Populate this object */ ){ int rc = SQLITE_OK; Fts5TokenizerModule *pMod; pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); if( pMod ){ if( pMod->bV2Native==0 ){ *ppUserData = pMod->pUserData; }else{ *ppUserData = (void*)pMod; } *pTokenizer = pMod->x1; }else{ memset(pTokenizer, 0, sizeof(*pTokenizer)); *ppUserData = 0; rc = SQLITE_ERROR; } return rc; } /* ** Attempt to instantiate the tokenizer. */ static int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){ const char **azArg = pConfig->t.azArg; const int nArg = pConfig->t.nArg; Fts5TokenizerModule *pMod = 0; int rc = SQLITE_OK; pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]); if( pMod==0 ){ assert( nArg>0 ); rc = SQLITE_ERROR; sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]); }else{ int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0; if( pMod->bV2Native ){ xCreate = pMod->x2.xCreate; pConfig->t.pApi2 = &pMod->x2; }else{ pConfig->t.pApi1 = &pMod->x1; xCreate = pMod->x1.xCreate; } rc = xCreate(pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok ); if( rc!=SQLITE_OK ){ if( rc!=SQLITE_NOMEM ){ sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor"); } }else if( pMod->bV2Native==0 ){ pConfig->t.ePattern = sqlite3Fts5TokenizerPattern( pMod->x1.xCreate, pConfig->t.pTok ); } } if( rc!=SQLITE_OK ){ pConfig->t.pApi1 = 0; pConfig->t.pApi2 = 0; pConfig->t.pTok = 0; } return rc; } /* ** xDestroy callback passed to sqlite3_create_module(). This is invoked ** when the db handle is being closed. Free memory associated with ** tokenizers and aux functions registered with this db handle. */ static void fts5ModuleDestroy(void *pCtx){ Fts5TokenizerModule *pTok, *pNextTok; Fts5Auxiliary *pAux, *pNextAux; Fts5Global *pGlobal = (Fts5Global*)pCtx; for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){ pNextAux = pAux->pNext; if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData); sqlite3_free(pAux); } for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){ pNextTok = pTok->pNext; if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData); sqlite3_free(pTok); } sqlite3_free(pGlobal); } /* ** Implementation of the fts5() function used by clients to obtain the ** API pointer. */ static void fts5Fts5Func( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ sqlite3_value **apArg /* Function arguments */ ){ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); fts5_api **ppApi; |
︙ | ︙ | |||
253233 253234 253235 253236 253237 253238 253239 | 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); | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 254623 254624 254625 254626 254627 254628 254629 254630 254631 254632 254633 254634 254635 254636 254637 254638 254639 254640 254641 254642 254643 254644 254645 254646 254647 254648 254649 254650 254651 254652 254653 254654 254655 254656 254657 254658 254659 254660 254661 254662 254663 254664 254665 254666 254667 254668 254669 254670 254671 254672 254673 254674 254675 254676 254677 254678 254679 254680 254681 254682 254683 254684 254685 254686 254687 254688 254689 254690 254691 254692 254693 254694 254695 254696 254697 254698 254699 254700 | 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: 2024-09-02 18:41:59 e6bec37ea1ca51e1d048941ce4c5211d8fc5c5e3556a1441f9c79b036843f9e3", -1, SQLITE_TRANSIENT); } /* ** Implementation of fts5_locale(LOCALE, TEXT) function. ** ** If parameter LOCALE is NULL, or a zero-length string, then a copy of ** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as ** text, and the value returned is a blob consisting of: ** ** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER). ** * The LOCALE, as utf-8 text, followed by ** * 0x00, followed by ** * The TEXT, as utf-8 text. ** ** There is no final nul-terminator following the TEXT value. */ static void fts5LocaleFunc( sqlite3_context *pCtx, /* Function call context */ int nArg, /* Number of args */ sqlite3_value **apArg /* Function arguments */ ){ const char *zLocale = 0; int nLocale = 0; const char *zText = 0; int nText = 0; assert( nArg==2 ); UNUSED_PARAM(nArg); zLocale = (const char*)sqlite3_value_text(apArg[0]); nLocale = sqlite3_value_bytes(apArg[0]); zText = (const char*)sqlite3_value_text(apArg[1]); nText = sqlite3_value_bytes(apArg[1]); if( zLocale==0 || zLocale[0]=='\0' ){ sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT); }else{ u8 *pBlob = 0; u8 *pCsr = 0; int nBlob = 0; const int nHdr = 4; assert( sizeof(FTS5_LOCALE_HEADER)==nHdr+1 ); nBlob = nHdr + nLocale + 1 + nText; pBlob = (u8*)sqlite3_malloc(nBlob); if( pBlob==0 ){ sqlite3_result_error_nomem(pCtx); return; } pCsr = pBlob; memcpy(pCsr, FTS5_LOCALE_HEADER, nHdr); pCsr += nHdr; memcpy(pCsr, zLocale, nLocale); pCsr += nLocale; (*pCsr++) = 0x00; if( zText ) memcpy(pCsr, zText, nText); assert( &pCsr[nText]==&pBlob[nBlob] ); sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free); sqlite3_result_subtype(pCtx, FTS5_LOCALE_SUBTYPE); } } /* ** Return true if zName is the extension on one of the shadow tables used ** by this module. */ static int fts5ShadowName(const char *zName){ |
︙ | ︙ | |||
253328 253329 253330 253331 253332 253333 253334 | pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); if( pGlobal==0 ){ rc = SQLITE_NOMEM; }else{ void *p = (void*)pGlobal; memset(pGlobal, 0, sizeof(Fts5Global)); pGlobal->db = db; | | > > > > > > > > > | 254781 254782 254783 254784 254785 254786 254787 254788 254789 254790 254791 254792 254793 254794 254795 254796 254797 254798 254799 254800 254801 254802 254803 254804 254805 254806 254807 254808 254809 254810 254811 254812 254813 254814 254815 254816 254817 254818 254819 254820 254821 254822 254823 254824 | pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); if( pGlobal==0 ){ rc = SQLITE_NOMEM; }else{ void *p = (void*)pGlobal; memset(pGlobal, 0, sizeof(Fts5Global)); pGlobal->db = db; pGlobal->api.iVersion = 3; pGlobal->api.xCreateFunction = fts5CreateAux; pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; pGlobal->api.xFindTokenizer = fts5FindTokenizer; pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2; pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2; rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db); 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( rc==SQLITE_OK ){ rc = sqlite3_create_function( db, "fts5_locale", 2, SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE, p, fts5LocaleFunc, 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. */ #ifdef SQLITE_FTS5_ENABLE_TEST_MI |
︙ | ︙ | |||
253424 253425 253426 253427 253428 253429 253430 253431 253432 253433 253434 253435 253436 | ** */ /* #include "fts5Int.h" */ struct Fts5Storage { Fts5Config *pConfig; Fts5Index *pIndex; int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ i64 nTotalRow; /* Total number of rows in FTS table */ i64 *aTotalSize; /* Total sizes of each column */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | | | | | | 254886 254887 254888 254889 254890 254891 254892 254893 254894 254895 254896 254897 254898 254899 254900 254901 254902 254903 254904 254905 254906 254907 254908 254909 254910 254911 254912 254913 254914 254915 254916 254917 254918 254919 254920 254921 254922 254923 254924 254925 254926 254927 254928 254929 254930 254931 254932 254933 254934 254935 254936 254937 254938 254939 254940 254941 254942 254943 254944 254945 254946 254947 254948 254949 254950 254951 254952 254953 254954 254955 | ** */ /* #include "fts5Int.h" */ /* ** pSavedRow: ** SQL statement FTS5_STMT_LOOKUP2 is a copy of FTS5_STMT_LOOKUP, it ** does a by-rowid lookup to retrieve a single row from the %_content ** table or equivalent external-content table/view. ** ** However, FTS5_STMT_LOOKUP2 is only used when retrieving the original ** values for a row being UPDATEd. In that case, the SQL statement is ** not reset and pSavedRow is set to point at it. This is so that the ** insert operation that follows the delete may access the original ** row values for any new values for which sqlite3_value_nochange() returns ** true. i.e. if the user executes: ** ** CREATE VIRTUAL TABLE ft USING fts5(a, b, c, locale=1); ** ... ** UPDATE fts SET a=?, b=? WHERE rowid=?; ** ** then the value passed to the xUpdate() method of this table as the ** new.c value is an sqlite3_value_nochange() value. So in this case it ** must be read from the saved row stored in Fts5Storage.pSavedRow. ** ** This is necessary - using sqlite3_value_nochange() instead of just having ** SQLite pass the original value back via xUpdate() - so as not to discard ** any locale information associated with such values. ** */ struct Fts5Storage { Fts5Config *pConfig; Fts5Index *pIndex; int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */ i64 nTotalRow; /* Total number of rows in FTS table */ i64 *aTotalSize; /* Total sizes of each column */ sqlite3_stmt *pSavedRow; sqlite3_stmt *aStmt[12]; }; #if FTS5_STMT_SCAN_ASC!=0 # error "FTS5_STMT_SCAN_ASC mismatch" #endif #if FTS5_STMT_SCAN_DESC!=1 # error "FTS5_STMT_SCAN_DESC mismatch" #endif #if FTS5_STMT_LOOKUP!=2 # error "FTS5_STMT_LOOKUP mismatch" #endif #define FTS5_STMT_LOOKUP2 3 #define FTS5_STMT_INSERT_CONTENT 4 #define FTS5_STMT_REPLACE_CONTENT 5 #define FTS5_STMT_DELETE_CONTENT 6 #define FTS5_STMT_REPLACE_DOCSIZE 7 #define FTS5_STMT_DELETE_DOCSIZE 8 #define FTS5_STMT_LOOKUP_DOCSIZE 9 #define FTS5_STMT_REPLACE_CONFIG 10 #define FTS5_STMT_SCAN 11 /* ** Prepare the two insert statements - Fts5Storage.pInsertContent and ** Fts5Storage.pInsertDocsize - if they have not already been prepared. ** Return SQLITE_OK if successful, or an SQLite error code if an error ** occurs. */ |
︙ | ︙ | |||
253481 253482 253483 253484 253485 253486 253487 253488 253489 253490 253491 253492 253493 253494 253495 253496 253497 253498 253499 253500 253501 253502 253503 253504 253505 253506 253507 253508 253509 253510 253511 253512 253513 253514 253515 253516 253517 253518 253519 253520 253521 253522 253523 253524 253525 | assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) ); if( p->aStmt[eStmt]==0 ){ const char *azStmt[] = { "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ "SELECT %s FROM %s AS T", /* SCAN */ }; Fts5Config *pC = p->pConfig; char *zSql = 0; switch( eStmt ){ case FTS5_STMT_SCAN: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent ); break; case FTS5_STMT_SCAN_ASC: case FTS5_STMT_SCAN_DESC: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent, pC->zContentRowid, pC->zContentRowid, pC->zContentRowid ); break; case FTS5_STMT_LOOKUP: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent, pC->zContentRowid ); break; case FTS5_STMT_INSERT_CONTENT: case FTS5_STMT_REPLACE_CONTENT: { | > > > > | 254971 254972 254973 254974 254975 254976 254977 254978 254979 254980 254981 254982 254983 254984 254985 254986 254987 254988 254989 254990 254991 254992 254993 254994 254995 254996 254997 254998 254999 255000 255001 255002 255003 255004 255005 255006 255007 255008 255009 255010 255011 255012 255013 255014 255015 255016 255017 255018 255019 | assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) ); if( p->aStmt[eStmt]==0 ){ const char *azStmt[] = { "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC", "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC", "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP2 */ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ "SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ "SELECT %s FROM %s AS T", /* SCAN */ }; Fts5Config *pC = p->pConfig; char *zSql = 0; assert( ArraySize(azStmt)==ArraySize(p->aStmt) ); switch( eStmt ){ case FTS5_STMT_SCAN: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent ); break; case FTS5_STMT_SCAN_ASC: case FTS5_STMT_SCAN_DESC: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent, pC->zContentRowid, pC->zContentRowid, pC->zContentRowid ); break; case FTS5_STMT_LOOKUP: case FTS5_STMT_LOOKUP2: zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist, pC->zContent, pC->zContentRowid ); break; case FTS5_STMT_INSERT_CONTENT: case FTS5_STMT_REPLACE_CONTENT: { |
︙ | ︙ | |||
253558 253559 253560 253561 253562 253563 253564 | break; } if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ int f = SQLITE_PREPARE_PERSISTENT; | | | 255052 255053 255054 255055 255056 255057 255058 255059 255060 255061 255062 255063 255064 255065 255066 | break; } if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ int f = SQLITE_PREPARE_PERSISTENT; if( eStmt>FTS5_STMT_LOOKUP2 ) f |= SQLITE_PREPARE_NO_VTAB; p->pConfig->bLock++; rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); p->pConfig->bLock--; sqlite3_free(zSql); if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); } |
︙ | ︙ | |||
253806 253807 253808 253809 253810 253811 253812 253813 253814 253815 253816 253817 253818 253819 253820 253821 | UNUSED_PARAM2(iUnused1, iUnused2); if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); } /* ** If a row with rowid iDel is present in the %_content table, add the ** delete-markers to the FTS index necessary to delete it. Do not actually ** remove the %_content row at this time though. */ static int fts5StorageDeleteFromIndex( Fts5Storage *p, i64 iDel, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > | | | | | > > | | > > < | < < < < > > > > > > | | | | | | | > > > > > > | | > > > > > > > > > > > > > > | 255300 255301 255302 255303 255304 255305 255306 255307 255308 255309 255310 255311 255312 255313 255314 255315 255316 255317 255318 255319 255320 255321 255322 255323 255324 255325 255326 255327 255328 255329 255330 255331 255332 255333 255334 255335 255336 255337 255338 255339 255340 255341 255342 255343 255344 255345 255346 255347 255348 255349 255350 255351 255352 255353 255354 255355 255356 255357 255358 255359 255360 255361 255362 255363 255364 255365 255366 255367 255368 255369 255370 255371 255372 255373 255374 255375 255376 255377 255378 255379 255380 255381 255382 255383 255384 255385 255386 255387 255388 255389 255390 255391 255392 255393 255394 255395 255396 255397 255398 255399 255400 255401 255402 255403 255404 255405 255406 255407 255408 255409 255410 255411 255412 255413 255414 255415 255416 255417 255418 255419 255420 255421 255422 255423 255424 255425 255426 255427 255428 255429 255430 255431 255432 255433 255434 255435 255436 255437 255438 255439 255440 255441 255442 255443 255444 | UNUSED_PARAM2(iUnused1, iUnused2); if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ pCtx->szCol++; } return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken); } /* ** This function is used as part of an UPDATE statement that modifies the ** rowid of a row. In that case, this function is called first to set ** Fts5Storage.pSavedRow to point to a statement that may be used to ** access the original values of the row being deleted - iDel. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** It is not considered an error if row iDel does not exist. In this case ** pSavedRow is not set and SQLITE_OK returned. */ static int sqlite3Fts5StorageFindDeleteRow(Fts5Storage *p, i64 iDel){ int rc = SQLITE_OK; sqlite3_stmt *pSeek = 0; assert( p->pSavedRow==0 ); rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+1, &pSeek, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int64(pSeek, 1, iDel); if( sqlite3_step(pSeek)!=SQLITE_ROW ){ rc = sqlite3_reset(pSeek); }else{ p->pSavedRow = pSeek; } } return rc; } /* ** If a row with rowid iDel is present in the %_content table, add the ** delete-markers to the FTS index necessary to delete it. Do not actually ** remove the %_content row at this time though. ** ** If parameter bSaveRow is true, then Fts5Storage.pSavedRow is left ** pointing to a statement (FTS5_STMT_LOOKUP2) that may be used to access ** the original values of the row being deleted. This is used by UPDATE ** statements. */ static int fts5StorageDeleteFromIndex( Fts5Storage *p, i64 iDel, sqlite3_value **apVal, int bSaveRow /* True to set pSavedRow */ ){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */ int rc = SQLITE_OK; /* Return code */ int rc2; /* sqlite3_reset() return code */ int iCol; Fts5InsertCtx ctx; assert( bSaveRow==0 || apVal==0 ); assert( bSaveRow==0 || bSaveRow==1 ); assert( FTS5_STMT_LOOKUP2==FTS5_STMT_LOOKUP+1 ); if( apVal==0 ){ if( p->pSavedRow && bSaveRow ){ pSeek = p->pSavedRow; p->pSavedRow = 0; }else{ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP+bSaveRow, &pSeek, 0); if( rc!=SQLITE_OK ) return rc; sqlite3_bind_int64(pSeek, 1, iDel); if( sqlite3_step(pSeek)!=SQLITE_ROW ){ return sqlite3_reset(pSeek); } } } ctx.pStorage = p; ctx.iCol = -1; for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ if( pConfig->abUnindexed[iCol-1]==0 ){ sqlite3_value *pVal = 0; const char *pText = 0; int nText = 0; int bReset = 0; assert( pSeek==0 || apVal==0 ); assert( pSeek!=0 || apVal!=0 ); if( pSeek ){ pVal = sqlite3_column_value(pSeek, iCol); }else{ pVal = apVal[iCol-1]; } rc = sqlite3Fts5ExtractText( pConfig, pVal, pSeek!=0, &bReset, &pText, &nText ); if( rc==SQLITE_OK ){ ctx.szCol = 0; rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); p->aTotalSize[iCol-1] -= (i64)ctx.szCol; if( rc==SQLITE_OK && p->aTotalSize[iCol-1]<0 ){ rc = FTS5_CORRUPT; } if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } } if( rc==SQLITE_OK && p->nTotalRow<1 ){ rc = FTS5_CORRUPT; }else{ p->nTotalRow--; } if( rc==SQLITE_OK && bSaveRow ){ assert( p->pSavedRow==0 ); p->pSavedRow = pSeek; }else{ rc2 = sqlite3_reset(pSeek); if( rc==SQLITE_OK ) rc = rc2; } return rc; } /* ** Reset any saved statement pSavedRow. Zero pSavedRow as well. This ** should be called by the xUpdate() method of the fts5 table before ** returning from any operation that may have set Fts5Storage.pSavedRow. */ static void sqlite3Fts5StorageReleaseDeleteRow(Fts5Storage *pStorage){ assert( pStorage->pSavedRow==0 || pStorage->pSavedRow==pStorage->aStmt[FTS5_STMT_LOOKUP2] ); sqlite3_reset(pStorage->pSavedRow); pStorage->pSavedRow = 0; } /* ** This function is called to process a DELETE on a contentless_delete=1 ** table. It adds the tombstone required to delete the entry with rowid ** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs, ** an SQLite error code. */ |
︙ | ︙ | |||
253927 253928 253929 253930 253931 253932 253933 | if( rc==SQLITE_OK ){ sqlite3_bind_int64(pReplace, 1, iRowid); if( p->pConfig->bContentlessDelete ){ i64 iOrigin = 0; rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); sqlite3_bind_int64(pReplace, 3, iOrigin); } | > | | | | | < | 255488 255489 255490 255491 255492 255493 255494 255495 255496 255497 255498 255499 255500 255501 255502 255503 255504 255505 255506 255507 | if( rc==SQLITE_OK ){ sqlite3_bind_int64(pReplace, 1, iRowid); if( p->pConfig->bContentlessDelete ){ i64 iOrigin = 0; rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin); sqlite3_bind_int64(pReplace, 3, iOrigin); } } if( rc==SQLITE_OK ){ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); sqlite3_step(pReplace); rc = sqlite3_reset(pReplace); sqlite3_bind_null(pReplace, 2); } } return rc; } /* ** Load the contents of the "averages" record from disk into the |
︙ | ︙ | |||
253986 253987 253988 253989 253990 253991 253992 | return rc; } /* ** Remove a row from the FTS table. */ | | > > > > > | | 255547 255548 255549 255550 255551 255552 255553 255554 255555 255556 255557 255558 255559 255560 255561 255562 255563 255564 255565 255566 255567 255568 255569 255570 255571 255572 255573 255574 255575 255576 255577 255578 255579 255580 255581 255582 255583 | return rc; } /* ** Remove a row from the FTS table. */ static int sqlite3Fts5StorageDelete( Fts5Storage *p, /* Storage object */ i64 iDel, /* Rowid to delete from table */ sqlite3_value **apVal, /* Optional - values to remove from index */ int bSaveRow /* If true, set pSavedRow for deleted row */ ){ Fts5Config *pConfig = p->pConfig; int rc; sqlite3_stmt *pDel = 0; assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 ); rc = fts5StorageLoadTotals(p, 1); /* Delete the index records */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel); } if( rc==SQLITE_OK ){ if( p->pConfig->bContentlessDelete ){ rc = fts5StorageContentlessDelete(p, iDel); }else{ rc = fts5StorageDeleteFromIndex(p, iDel, apVal, bSaveRow); } } /* Delete the %_docsize record */ if( rc==SQLITE_OK && pConfig->bColumnsize ){ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
254092 254093 254094 254095 254096 254097 254098 | i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ | > > > | | > > | | | | | | > > | 255658 255659 255660 255661 255662 255663 255664 255665 255666 255667 255668 255669 255670 255671 255672 255673 255674 255675 255676 255677 255678 255679 255680 255681 255682 255683 255684 255685 255686 | i64 iRowid = sqlite3_column_int64(pScan, 0); sqlite3Fts5BufferZero(&buf); rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ sqlite3_value *pVal = sqlite3_column_value(pScan, ctx.iCol+1); rc = sqlite3Fts5ExtractText(pConfig, pVal, 1, &bReset, &pText, &nText); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; } p->nTotalRow++; if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
254183 254184 254185 254186 254187 254188 254189 | rc = fts5StorageNewRowid(p, piRowid); } }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ | > > > > > > > > > > > > > > > > > > > > > > > > | | 255756 255757 255758 255759 255760 255761 255762 255763 255764 255765 255766 255767 255768 255769 255770 255771 255772 255773 255774 255775 255776 255777 255778 255779 255780 255781 255782 255783 255784 255785 255786 255787 255788 255789 255790 255791 255792 255793 255794 | rc = fts5StorageNewRowid(p, piRowid); } }else{ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */ int i; /* Counter variable */ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0); for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ sqlite3_value *pVal = apVal[i]; if( sqlite3_value_nochange(pVal) && p->pSavedRow ){ /* This is an UPDATE statement, and column (i-2) was not modified. ** Retrieve the value from Fts5Storage.pSavedRow instead. */ pVal = sqlite3_column_value(p->pSavedRow, i-1); }else if( sqlite3_value_subtype(pVal)==FTS5_LOCALE_SUBTYPE ){ assert( pConfig->bLocale ); assert( i>1 ); if( pConfig->abUnindexed[i-2] ){ /* At attempt to insert an fts5_locale() value into an UNINDEXED ** column. Strip the locale away and just bind the text. */ const char *pText = 0; int nText = 0; rc = sqlite3Fts5ExtractText(pConfig, pVal, 0, 0, &pText, &nText); sqlite3_bind_text(pInsert, i, pText, nText, SQLITE_TRANSIENT); }else{ const u8 *pBlob = (const u8*)sqlite3_value_blob(pVal); int nBlob = sqlite3_value_bytes(pVal); assert( nBlob>4 ); sqlite3_bind_blob(pInsert, i, pBlob+4, nBlob-4, SQLITE_TRANSIENT); } continue; } rc = sqlite3_bind_value(pInsert, i, pVal); } if( rc==SQLITE_OK ){ sqlite3_step(pInsert); rc = sqlite3_reset(pInsert); } *piRowid = sqlite3_last_insert_rowid(pConfig->db); } |
︙ | ︙ | |||
254218 254219 254220 254221 254222 254223 254224 | if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); } for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ | > > > | > | > > > > > > | | < < | | > > | 255815 255816 255817 255818 255819 255820 255821 255822 255823 255824 255825 255826 255827 255828 255829 255830 255831 255832 255833 255834 255835 255836 255837 255838 255839 255840 255841 255842 255843 255844 255845 255846 | if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid); } for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){ ctx.szCol = 0; if( pConfig->abUnindexed[ctx.iCol]==0 ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ sqlite3_value *pVal = apVal[ctx.iCol+2]; int bDisk = 0; if( p->pSavedRow && sqlite3_value_nochange(pVal) ){ pVal = sqlite3_column_value(p->pSavedRow, ctx.iCol+1); bDisk = 1; } rc = sqlite3Fts5ExtractText(pConfig, pVal, bDisk, &bReset, &pText,&nText); if( rc==SQLITE_OK ){ assert( bReset==0 || pConfig->bLocale ); rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageInsertCallback ); if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; } p->nTotalRow++; /* Write the %_docsize record */ |
︙ | ︙ | |||
254396 254397 254398 254399 254400 254401 254402 | if( pConfig->abUnindexed[i] ) continue; ctx.iCol = i; ctx.szCol = 0; if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } if( rc==SQLITE_OK ){ | > > > | > | > > | | | | | | > > | 256003 256004 256005 256006 256007 256008 256009 256010 256011 256012 256013 256014 256015 256016 256017 256018 256019 256020 256021 256022 256023 256024 256025 256026 256027 256028 256029 256030 256031 256032 | if( pConfig->abUnindexed[i] ) continue; ctx.iCol = i; ctx.szCol = 0; if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ rc = sqlite3Fts5TermsetNew(&ctx.pTermset); } if( rc==SQLITE_OK ){ int bReset = 0; /* True if tokenizer locale must be reset */ int nText = 0; /* Size of pText in bytes */ const char *pText = 0; /* Pointer to buffer containing text value */ rc = sqlite3Fts5ExtractText(pConfig, sqlite3_column_value(pScan, i+1), 1, &bReset, &pText, &nText ); if( rc==SQLITE_OK ){ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT, pText, nText, (void*)&ctx, fts5StorageIntegrityCallback ); if( bReset ) sqlite3Fts5ClearLocale(pConfig); } } if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ rc = FTS5_CORRUPT; } aTotalSize[i] += ctx.szCol; if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ sqlite3Fts5TermsetFree(ctx.pTermset); |
︙ | ︙ | |||
254718 254719 254720 254721 254722 254723 254724 | p = sqlite3_malloc(sizeof(AsciiTokenizer)); if( p==0 ){ rc = SQLITE_NOMEM; }else{ int i; memset(p, 0, sizeof(AsciiTokenizer)); memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); | | < | 256333 256334 256335 256336 256337 256338 256339 256340 256341 256342 256343 256344 256345 256346 256347 256348 256349 256350 256351 256352 256353 256354 256355 256356 256357 | p = sqlite3_malloc(sizeof(AsciiTokenizer)); if( p==0 ){ rc = SQLITE_NOMEM; }else{ int i; memset(p, 0, sizeof(AsciiTokenizer)); memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ const char *zArg = azArg[i+1]; if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){ fts5AsciiAddExceptions(p, zArg, 1); }else if( 0==sqlite3_stricmp(azArg[i], "separators") ){ fts5AsciiAddExceptions(p, zArg, 0); }else{ rc = SQLITE_ERROR; } } if( rc!=SQLITE_OK ){ fts5AsciiDelete((Fts5Tokenizer*)p); p = 0; } } } |
︙ | ︙ | |||
255021 255022 255023 255024 255025 255026 255027 | p->nFold = 64; p->aFold = sqlite3_malloc64(p->nFold * sizeof(char)); if( p->aFold==0 ){ rc = SQLITE_NOMEM; } /* Search for a "categories" argument */ | | | | 256635 256636 256637 256638 256639 256640 256641 256642 256643 256644 256645 256646 256647 256648 256649 256650 256651 256652 256653 256654 256655 256656 256657 256658 | p->nFold = 64; p->aFold = sqlite3_malloc64(p->nFold * sizeof(char)); if( p->aFold==0 ){ rc = SQLITE_NOMEM; } /* Search for a "categories" argument */ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ if( 0==sqlite3_stricmp(azArg[i], "categories") ){ zCat = azArg[i+1]; } } if( rc==SQLITE_OK ){ rc = unicodeSetCategories(p, zCat); } for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ const char *zArg = azArg[i+1]; if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ rc = SQLITE_ERROR; }else{ p->eRemoveDiacritic = (zArg[0] - '0'); assert( p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_NONE |
︙ | ︙ | |||
255055 255056 255057 255058 255059 255060 255061 | }else if( 0==sqlite3_stricmp(azArg[i], "categories") ){ /* no-op */ }else{ rc = SQLITE_ERROR; } } | < < | 256669 256670 256671 256672 256673 256674 256675 256676 256677 256678 256679 256680 256681 256682 | }else if( 0==sqlite3_stricmp(azArg[i], "categories") ){ /* no-op */ }else{ rc = SQLITE_ERROR; } } }else{ rc = SQLITE_NOMEM; } if( rc!=SQLITE_OK ){ fts5UnicodeDelete((Fts5Tokenizer*)p); p = 0; } |
︙ | ︙ | |||
255195 255196 255197 255198 255199 255200 255201 | /* Any tokens larger than this (in bytes) are passed through without ** stemming. */ #define FTS5_PORTER_MAX_TOKEN 64 typedef struct PorterTokenizer PorterTokenizer; struct PorterTokenizer { | | | > | | > | | 256807 256808 256809 256810 256811 256812 256813 256814 256815 256816 256817 256818 256819 256820 256821 256822 256823 256824 256825 256826 256827 256828 256829 256830 256831 256832 256833 256834 256835 256836 256837 256838 256839 256840 256841 256842 256843 256844 256845 256846 256847 256848 256849 256850 256851 256852 256853 256854 256855 256856 256857 256858 256859 256860 256861 256862 256863 256864 256865 256866 256867 256868 256869 | /* Any tokens larger than this (in bytes) are passed through without ** stemming. */ #define FTS5_PORTER_MAX_TOKEN 64 typedef struct PorterTokenizer PorterTokenizer; struct PorterTokenizer { fts5_tokenizer_v2 tokenizer_v2; /* Parent tokenizer module */ Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */ char aBuf[FTS5_PORTER_MAX_TOKEN + 64]; }; /* ** Delete a "porter" tokenizer. */ static void fts5PorterDelete(Fts5Tokenizer *pTok){ if( pTok ){ PorterTokenizer *p = (PorterTokenizer*)pTok; if( p->pTokenizer ){ p->tokenizer_v2.xDelete(p->pTokenizer); } sqlite3_free(p); } } /* ** Create a "porter" tokenizer. */ static int fts5PorterCreate( void *pCtx, const char **azArg, int nArg, Fts5Tokenizer **ppOut ){ fts5_api *pApi = (fts5_api*)pCtx; int rc = SQLITE_OK; PorterTokenizer *pRet; void *pUserdata = 0; const char *zBase = "unicode61"; fts5_tokenizer_v2 *pV2 = 0; if( nArg>0 ){ zBase = azArg[0]; } pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); if( pRet ){ memset(pRet, 0, sizeof(PorterTokenizer)); rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); }else{ rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ int nArg2 = (nArg>0 ? nArg-1 : 0); const char **az2 = (nArg2 ? &azArg[1] : 0); memcpy(&pRet->tokenizer_v2, pV2, sizeof(fts5_tokenizer_v2)); rc = pRet->tokenizer_v2.xCreate(pUserdata, az2, nArg2, &pRet->pTokenizer); } if( rc!=SQLITE_OK ){ fts5PorterDelete((Fts5Tokenizer*)pRet); pRet = 0; } *ppOut = (Fts5Tokenizer*)pRet; |
︙ | ︙ | |||
255892 255893 255894 255895 255896 255897 255898 255899 255900 255901 255902 255903 255904 255905 | ** Tokenize using the porter tokenizer. */ static int fts5PorterTokenize( Fts5Tokenizer *pTokenizer, void *pCtx, int flags, const char *pText, int nText, int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) ){ PorterTokenizer *p = (PorterTokenizer*)pTokenizer; PorterContext sCtx; sCtx.xToken = xToken; sCtx.pCtx = pCtx; sCtx.aBuf = p->aBuf; | > | | | 257506 257507 257508 257509 257510 257511 257512 257513 257514 257515 257516 257517 257518 257519 257520 257521 257522 257523 257524 257525 257526 257527 257528 257529 | ** Tokenize using the porter tokenizer. */ static int fts5PorterTokenize( Fts5Tokenizer *pTokenizer, void *pCtx, int flags, const char *pText, int nText, const char *pLoc, int nLoc, int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd) ){ PorterTokenizer *p = (PorterTokenizer*)pTokenizer; PorterContext sCtx; sCtx.xToken = xToken; sCtx.pCtx = pCtx; sCtx.aBuf = p->aBuf; return p->tokenizer_v2.xTokenize( p->pTokenizer, (void*)&sCtx, flags, pText, nText, pLoc, nLoc, fts5PorterCb ); } /************************************************************************** ** Start of trigram implementation. */ typedef struct TrigramTokenizer TrigramTokenizer; |
︙ | ︙ | |||
255930 255931 255932 255933 255934 255935 255936 | static int fts5TriCreate( void *pUnused, const char **azArg, int nArg, Fts5Tokenizer **ppOut ){ int rc = SQLITE_OK; | | | | > > > > | | > | | | | | | | | | | | | | | | | | | < | | | | | | > | 257545 257546 257547 257548 257549 257550 257551 257552 257553 257554 257555 257556 257557 257558 257559 257560 257561 257562 257563 257564 257565 257566 257567 257568 257569 257570 257571 257572 257573 257574 257575 257576 257577 257578 257579 257580 257581 257582 257583 257584 257585 257586 257587 257588 257589 257590 257591 257592 257593 257594 257595 257596 257597 257598 | static int fts5TriCreate( void *pUnused, const char **azArg, int nArg, Fts5Tokenizer **ppOut ){ int rc = SQLITE_OK; TrigramTokenizer *pNew = 0; UNUSED_PARAM(pUnused); if( nArg%2 ){ rc = SQLITE_ERROR; }else{ int i; pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ pNew->bFold = 1; pNew->iFoldParam = 0; for(i=0; rc==SQLITE_OK && i<nArg; i+=2){ const char *zArg = azArg[i+1]; if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){ rc = SQLITE_ERROR; }else{ pNew->bFold = (zArg[0]=='0'); } }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ rc = SQLITE_ERROR; }else{ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; } }else{ rc = SQLITE_ERROR; } } if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ rc = SQLITE_ERROR; } if( rc!=SQLITE_OK ){ fts5TriDelete((Fts5Tokenizer*)pNew); pNew = 0; } } } *ppOut = (Fts5Tokenizer*)pNew; return rc; } /* |
︙ | ︙ | |||
256089 256090 256091 256092 256093 256094 256095 | static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ struct BuiltinTokenizer { const char *zName; fts5_tokenizer x; } aBuiltin[] = { { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, | < > > > > > > > > > > > | > > | 257709 257710 257711 257712 257713 257714 257715 257716 257717 257718 257719 257720 257721 257722 257723 257724 257725 257726 257727 257728 257729 257730 257731 257732 257733 257734 257735 257736 257737 257738 257739 257740 257741 257742 257743 257744 257745 257746 257747 257748 257749 257750 | static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ struct BuiltinTokenizer { const char *zName; fts5_tokenizer x; } aBuiltin[] = { { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, { "trigram", {fts5TriCreate, fts5TriDelete, fts5TriTokenize}}, }; int rc = SQLITE_OK; /* Return code */ int i; /* To iterate through builtin functions */ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){ rc = pApi->xCreateTokenizer(pApi, aBuiltin[i].zName, (void*)pApi, &aBuiltin[i].x, 0 ); } if( rc==SQLITE_OK ){ fts5_tokenizer_v2 sPorter = { 2, fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }; rc = pApi->xCreateTokenizer_v2(pApi, "porter", (void*)pApi, &sPorter, 0 ); } return rc; } /* ** 2012-05-25 ** ** The author disclaims copyright to this source code. In place of |
︙ | ︙ | |||
256474 256475 256476 256477 256478 256479 256480 256481 256482 256483 256484 256485 256486 256487 | aArray[27] = 1; aArray[28] = 1; aArray[29] = 1; break; default: return 1; } break; } return 0; } static u16 aFts5UnicodeBlock[] = { 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1763, 1765, | > > > | 258106 258107 258108 258109 258110 258111 258112 258113 258114 258115 258116 258117 258118 258119 258120 258121 258122 | aArray[27] = 1; aArray[28] = 1; aArray[29] = 1; break; default: return 1; } break; default: return 1; } return 0; } static u16 aFts5UnicodeBlock[] = { 0, 1471, 1753, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1763, 1765, |
︙ | ︙ |
Changes to extsrc/sqlite3.h.
︙ | ︙ | |||
144 145 146 147 148 149 150 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.47.0" #define SQLITE_VERSION_NUMBER 3047000 | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.47.0" #define SQLITE_VERSION_NUMBER 3047000 #define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" /* ** 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 |
︙ | ︙ | |||
5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue | > > > > > > > > > > | 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** ** [[SQLITE_SELFORDER1]] <dt>SQLITE_SELFORDER1</dt><dd> ** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate ** that internally orders the values provided to the first argument. The ** ordered-set aggregate SQL notation with a single ORDER BY term can be ** used to invoke this function. If the ordered-set aggregate notation is ** used on a function that lacks this flag, then an error is raised. Note ** that the ordered-set aggregate syntax is only available if SQLite is ** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 #define SQLITE_SELFORDER1 0x002000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue |
︙ | ︙ | |||
7423 7424 7425 7426 7427 7428 7429 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a | | > > | | | 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a ** mask of SQLITE_INDEX_SCAN_* flags. One such flag is ** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN] ** output to show the idxNum has hex instead of as decimal. Another flag is ** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will ** return at most one row. ** ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then ** SQLite also assumes that if a call to the xUpdate() method is made as ** part of the same statement to delete or update a virtual table row and the ** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback ** any database changes. In other words, if the xUpdate() returns ** SQLITE_CONSTRAINT, the database contents must be exactly as they were |
︙ | ︙ | |||
7489 7490 7491 7492 7493 7494 7495 | /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ | | > > | 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 | /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ #define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */ #define SQLITE_INDEX_SCAN_HEX 0x00000002 /* Display idxNum as hex */ /* in EXPLAIN QUERY PLAN */ /* ** CAPI3REF: Virtual Table Constraint Operator Codes ** ** These macros define the allowed values for the ** [sqlite3_index_info].aConstraint[].op field. Each value represents ** an operator that is part of a constraint term in the WHERE clause of |
︙ | ︙ | |||
8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 | > | 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_GETOPT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 |
︙ | ︙ | |||
13100 13101 13102 13103 13104 13105 13106 13107 13108 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { | > > > > > > > > > > > > > > > > > > > > > > > | | 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 | ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. ** ** Otherwise, this function attempts to retrieve the locale associated ** with column iCol of the current row. Usually, there is no associated ** locale, and output parameters (*pzLocale) and (*pnLocale) are set ** to NULL and 0, respectively. However, if the fts5_locale() function ** was used to associate a locale with the value when it was inserted ** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated ** buffer containing the name of the locale in utf-8 encoding. (*pnLocale) ** is set to the size in bytes of the buffer, not including the ** nul-terminator. ** ** If successful, SQLITE_OK is returned. Or, if an error occurs, an ** SQLite error code is returned. The final value of the output parameters ** is undefined in this case. ** ** xTokenize_v2: ** Tokenize text using the tokenizer belonging to the FTS5 table. This ** API is the same as the xTokenize() API, except that it allows a tokenizer ** locale to be specified. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 4 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); |
︙ | ︙ | |||
13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) | > > > > > > > > > > | | 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 | /* Below this point are iVersion>=3 only */ int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken ); int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); /* Below this point are iVersion>=4 only */ int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn); int (*xTokenize_v2)(Fts5Context*, const char *pText, int nText, /* Text to tokenize */ const char *pLocale, int nLocale, /* Locale to pass to tokenizer */ void *pCtx, /* Context passed to xToken() */ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */ ); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* ** CUSTOM TOKENIZERS ** ** Applications may also register custom tokenizer types. A tokenizer ** is registered by providing fts5 with a populated instance of the ** following structure. All structure methods must be defined, setting ** ** any member of the fts5_tokenizer struct to NULL leads to undefined ** behaviour. The structure methods are expected to function as follows: ** ** xCreate: ** This function is used to allocate and initialize a tokenizer instance. ** A tokenizer instance is required to actually tokenize text. ** ** The first argument passed to this function is a copy of the (void*) ** pointer provided by the application when the fts5_tokenizer_v2 object ** was registered with FTS5 (the third argument to xCreateTokenizer()). ** The second and third arguments are an array of nul-terminated strings ** containing the tokenizer arguments, if any, specified following the ** tokenizer name as part of the CREATE VIRTUAL TABLE statement used ** to create the FTS5 table. ** ** The final argument is an output variable. If successful, (*ppOut) |
︙ | ︙ | |||
13188 13189 13190 13191 13192 13193 13194 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** | | | 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** ** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** ** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into ** or removed from the FTS table. The tokenizer is being invoked to ** determine the set of tokens to add to (or delete from) the ** FTS index. |
︙ | ︙ | |||
13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from | > > > > > > > | 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** The sixth and seventh arguments passed to xTokenize() - pLocale and ** nLocale - are a pointer to a buffer containing the locale to use for ** tokenization (e.g. "en_US") and its size in bytes, respectively. The ** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ** which case nLocale is always 0) to indicate that the tokenizer should ** use its default locale. ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from |
︙ | ︙ | |||
13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms | > > > > > > > > > > > > > > > > > > > > > > > > | 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** If the tokenizer is registered using an fts5_tokenizer_v2 object, ** then the xTokenize() method has two additional arguments - pLocale ** and nLocale. These specify the locale that the tokenizer should use ** for the current request. If pLocale and nLocale are both 0, then the ** tokenizer should use its default locale. Otherwise, pLocale points to ** an nLocale byte buffer containing the name of the locale to use as utf-8 ** text. pLocale is not nul-terminated. ** ** FTS5_TOKENIZER ** ** There is also an fts5_tokenizer object. This is an older, deprecated, ** version of fts5_tokenizer_v2. It is similar except that: ** ** <ul> ** <li> There is no "iVersion" field, and ** <li> The xTokenize() method does not take a locale argument. ** </ul> ** ** Legacy fts5_tokenizer tokenizers must be registered using the ** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ** ** Tokenizer implementations registered using either API may be retrieved ** using both xFindTokenizer() and xFindTokenizer_v2(). ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms |
︙ | ︙ | |||
13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 13363 13364 13365 13366 13367 13368 13369 13370 13371 13372 13373 13374 13375 13376 13377 13378 13379 13380 13381 13382 13383 13384 13385 13386 13387 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 13422 13423 13424 13425 13426 13427 13428 13429 13430 13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 | ** ** When using methods (2) or (3), it is important that the tokenizer only ** provide synonyms when tokenizing document text (method (3)) or query ** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2; struct fts5_tokenizer_v2 { int iVersion; /* Currently always 2 */ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, const char *pLocale, int nLocale, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* ** New code should use the fts5_tokenizer_v2 type to define tokenizer ** implementations. The following type is included for legacy applications ** that still use it. */ typedef struct fts5_tokenizer fts5_tokenizer; struct fts5_tokenizer { int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); void (*xDelete)(Fts5Tokenizer*); int (*xTokenize)(Fts5Tokenizer*, void *pCtx, int flags, /* Mask of FTS5_TOKENIZE_* flags */ const char *pText, int nText, int (*xToken)( void *pCtx, /* Copy of 2nd argument to xTokenize() */ int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Pointer to buffer containing token */ int nToken, /* Size of token in bytes */ int iStart, /* Byte offset of token within input text */ int iEnd /* Byte offset of end of token within input text */ ) ); }; /* Flags that may be passed as the third argument to xTokenize() */ #define FTS5_TOKENIZE_QUERY 0x0001 #define FTS5_TOKENIZE_PREFIX 0x0002 #define FTS5_TOKENIZE_DOCUMENT 0x0004 #define FTS5_TOKENIZE_AUX 0x0008 /* Flags that may be passed by the tokenizer implementation back to FTS5 ** as the third argument to the supplied xToken callback. */ #define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */ /* ** END OF CUSTOM TOKENIZERS *************************************************************************/ /************************************************************************* ** FTS5 EXTENSION REGISTRATION API */ typedef struct fts5_api fts5_api; struct fts5_api { int iVersion; /* Currently always set to 3 */ /* Create a new tokenizer */ int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer *pTokenizer, |
︙ | ︙ | |||
13408 13409 13410 13411 13412 13413 13414 13415 13416 13417 13418 13419 13420 13421 13422 13423 13424 13425 13426 13427 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); }; /* ** END OF REGISTRATION API *************************************************************************/ #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ | > > > > > > > > > > > > > > > > > > > | 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 13553 | int (*xCreateFunction)( fts5_api *pApi, const char *zName, void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); /* APIs below this point are only available if iVersion>=3 */ /* Create a new tokenizer */ int (*xCreateTokenizer_v2)( fts5_api *pApi, const char *zName, void *pUserData, fts5_tokenizer_v2 *pTokenizer, void (*xDestroy)(void*) ); /* Find an existing tokenizer */ int (*xFindTokenizer_v2)( fts5_api *pApi, const char *zName, void **ppUserData, fts5_tokenizer_v2 **ppTokenizer ); }; /* ** END OF REGISTRATION API *************************************************************************/ #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ |
Changes to skins/darkmode/css.txt.
︙ | ︙ | |||
597 598 599 600 601 602 603 | 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; } | > > > > | 597 598 599 600 601 602 603 604 605 606 607 | 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; } p.noMoreShun { color: #e5e500; } |
Changes to skins/default/css.txt.
︙ | ︙ | |||
379 380 381 382 383 384 385 | .artifact > .content th, .dir > .content th, .doc > .content th, .wiki > .content th { border-bottom: 1px solid #dee8f2; padding-bottom: 4px; padding-right: 6px; | < > > > > > > | 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 | .artifact > .content th, .dir > .content th, .doc > .content th, .wiki > .content th { border-bottom: 1px solid #dee8f2; padding-bottom: 4px; padding-right: 6px; } .artifact > .content tr > th, .dir > .content tr > th, .doc > .content tr > th, .wiki > .content tr > th { background-color: #dee8f0; } .artifact > .content tr:nth-child(odd), .dir > .content tr:nth-child(odd), .doc > .content tr:nth-child(odd), .wiki > .content tr:nth-child(odd) { background-color: #e0e8ee; } .artifact > .content td, .dir > .content td, .doc > .content td, .wiki > .content td { padding-bottom: 4px; padding-right: 6px; } th { /* Special rule at high level to override default centering of table header cell text. If it isn't at this level, it can't be overridden in the HTML, as by the MD table generator's handling of `:` alignment markers. */ text-align: left; } /* Wiki adjustments */ pre.verbatim { /* keep code examples from crashing into sidebars, etc. */ white-space: pre-wrap; |
︙ | ︙ |
Changes to skins/eagle/css.txt.
︙ | ︙ | |||
450 451 452 453 454 455 456 | background-color: #e5e500; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #7EA2D9; } | > > > > | 450 451 452 453 454 455 456 457 458 459 460 | background-color: #e5e500; } body.branch .brlist > table > tbody > tr:hover:not(.selected), body.branch .brlist > table > tbody > tr.selected { background-color: #7EA2D9; } p.noMoreShun { color: #e5e500; } |
Changes to src/alerts.c.
︙ | ︙ | |||
1647 1648 1649 1650 1651 1652 1653 | const char *zInit = ""; if( P("captchaseed")!=0 && eErr!=2 ){ uSeed = strtoul(P("captchaseed"),0,10); zInit = P("captcha"); }else{ uSeed = captcha_seed(); } | | | 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 | const char *zInit = ""; if( P("captchaseed")!=0 && eErr!=2 ){ uSeed = strtoul(P("captchaseed"),0,10); zInit = P("captcha"); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed, 0); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="%h(zInit)" size="30"> captcha_speakit_button(uSeed, "Speak the code"); @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> @ </tr> |
︙ | ︙ | |||
2351 2352 2353 2354 2355 2356 2357 | @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td> if( eErr==1 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> uSeed = captcha_seed(); | | | 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 | @ <td class="form_label">Email Address:</td> @ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td> if( eErr==1 ){ @ <td><span class="loginError">← %h(zErr)</span></td> } @ </tr> uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed, 0); zCaptcha = captcha_render(zDecoded); @ <tr> @ <td class="form_label">Security Code:</td> @ <td><input type="text" name="captcha" value="" size="30"> captcha_speakit_button(uSeed, "Speak the code"); @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td> if( eErr==2 ){ |
︙ | ︙ | |||
3354 3355 3356 3357 3358 3359 3360 | } alert_sender_free(pSender); style_finish_page(); return; } if( captcha_needed() ){ uSeed = captcha_seed(); | | | 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 | } alert_sender_free(pSender); style_finish_page(); return; } if( captcha_needed() ){ uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed, 0); zCaptcha = captcha_render(zDecoded); } style_set_current_feature("alerts"); style_header("Message To Administrator"); form_begin(0, "%R/contact_admin"); @ <p>Enter a message to the repository administrator below:</p> @ <table class="subscribe"> |
︙ | ︙ |
Changes to src/blob.c.
︙ | ︙ | |||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /* ** Other replacements for ctype.h functions. */ int fossil_islower(char c){ return c>='a' && c<='z'; } int fossil_isupper(char c){ return c>='A' && c<='Z'; } int fossil_isdigit(char c){ return c>='0' && c<='9'; } int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); } int fossil_tolower(char c){ return fossil_isupper(c) ? c - 'A' + 'a' : c; } int fossil_toupper(char c){ return fossil_islower(c) ? c - 'a' + 'A' : c; } int fossil_isalpha(char c){ | > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | /* ** Other replacements for ctype.h functions. */ int fossil_islower(char c){ return c>='a' && c<='z'; } int fossil_isupper(char c){ return c>='A' && c<='Z'; } int fossil_isdigit(char c){ return c>='0' && c<='9'; } int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); } int fossil_isXdigit(char c){ return (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f'); } int fossil_tolower(char c){ return fossil_isupper(c) ? c - 'A' + 'a' : c; } int fossil_toupper(char c){ return fossil_islower(c) ? c - 'a' + 'A' : c; } int fossil_isalpha(char c){ |
︙ | ︙ |
Changes to src/captcha.c.
︙ | ︙ | |||
506 507 508 509 510 511 512 513 514 515 516 517 518 | */ unsigned int captcha_seed(void){ unsigned int x; sqlite3_randomness(sizeof(x), &x); x &= 0x7fffffff; return x; } /* ** Translate a captcha seed value into the captcha password string. ** The returned string is static and overwritten on each call to ** this function. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > | 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 | */ unsigned int captcha_seed(void){ unsigned int x; sqlite3_randomness(sizeof(x), &x); x &= 0x7fffffff; return x; } /* The SQL that will rotate the the captcha-secret. */ static const char captchaSecretRotationSql[] = @ SAVEPOINT rotate; @ DELETE FROM config @ WHERE name GLOB 'captcha-secret-*' @ AND mtime<unixepoch('now','-6 hours'); @ UPDATE config @ SET name=format('captcha-secret-%%d',substr(name,16)+1) @ WHERE name GLOB 'captcha-secret-*'; @ UPDATE config @ SET name='captcha-secret-1', mtime=unixepoch() @ WHERE name='captcha-secret'; @ REPLACE INTO config(name,value,mtime) @ VALUES('captcha-secret',%Q,unixepoch()); @ RELEASE rotate; ; /* ** Create a new random captcha-secret. Rotate the old one into ** the captcha-secret-N backups. Purge captch-secret-N backups ** older than 6 hours. ** ** Do this on the current database and in all other databases of ** the same login group. */ void captcha_secret_rotate(void){ char *zNew = db_text(0, "SELECT lower(hex(randomblob(20)))"); char *zSql = mprintf(captchaSecretRotationSql/*works-like:"%Q"*/, zNew); char *zErrs = 0; fossil_free(zNew); db_unprotect(PROTECT_CONFIG); db_begin_transaction(); sqlite3_exec(g.db, zSql, 0, 0, &zErrs); db_protect_pop(); if( zErrs && zErrs[0] ){ db_rollback_transaction(); fossil_fatal("Unable to rotate captcha-secret\n%s\nERROR: %s\n", zSql, zErrs); } db_end_transaction(0); login_group_sql(zSql, "", "", &zErrs); if( zErrs ){ sqlite3_free(zErrs); /* Silently ignore errors on other repos */ } fossil_free(zSql); } /* ** Return the value of the N-th more recent captcha-secret. The ** most recent captch-secret is 0. Others are prior captcha-secrets ** that have expired, but are retained for a limited period of time ** so that pending anonymous login cookies and/or captcha dialogs ** don't malfunction when the captcha-secret changes. ** ** Clients should start by using the 0-th captcha-secret. Only if ** that one does not work should they advance to 1 and 2 and so forth, ** until this routine returns a NULL pointer. ** ** The value returned is a string obtained from fossil_malloc() and ** should be freed by the caller. ** ** The 0-th captcha secret is the value of Config.Name='captcha-secret'. ** For N>0, the value is in Config.Name='captcha-secret-$N'. */ char *captcha_secret(int N){ if( N==0 ){ return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'"); }else{ return db_text(0, "SELECT value FROM config" " WHERE name='captcha-secret-%d'" " AND mtime>unixepoch('now','-6 hours')", N); } } /* ** Translate a captcha seed value into the captcha password string. ** The returned string is static and overwritten on each call to ** this function. ** ** Use the N-th captcha secret to compute the password. When N==0, ** a valid password is always returned. A new captcha-secret will ** be created if necessary. But for N>0, the return value might ** be NULL to indicate that there is no N-th captcha-secret. */ const char *captcha_decode(unsigned int seed, int N){ char *zSecret; const char *z; Blob b; static char zRes[20]; zSecret = captcha_secret(N); if( zSecret==0 ){ if( N>0 ) return 0; db_unprotect(PROTECT_CONFIG); db_multi_exec( "REPLACE INTO config(name,value)" " VALUES('captcha-secret', lower(hex(randomblob(20))));" ); db_protect_pop(); zSecret = captcha_secret(0); assert( zSecret!=0 ); } blob_init(&b, 0, 0); blob_appendf(&b, "%s-%x", zSecret, seed); sha1sum_blob(&b, &b); z = blob_buffer(&b); memcpy(zRes, z, 8); zRes[8] = 0; fossil_free(zSecret); return zRes; } /* ** Return true if a CAPTCHA is required for editing wiki or tickets or for ** adding attachments. ** |
︙ | ︙ | |||
567 568 569 570 571 572 573 574 575 576 577 578 579 580 | */ int captcha_is_correct(int bAlwaysNeeded){ const char *zSeed; const char *zEntered; const char *zDecode; char z[30]; int i; if( !bAlwaysNeeded && !captcha_needed() ){ return 1; /* No captcha needed */ } zSeed = P("captchaseed"); if( zSeed==0 ) return 0; zEntered = P("captcha"); if( zEntered==0 || strlen(zEntered)!=8 ) return 0; | > > | > | | | | | | | | | 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 | */ int captcha_is_correct(int bAlwaysNeeded){ const char *zSeed; const char *zEntered; const char *zDecode; char z[30]; int i; int n = 0; if( !bAlwaysNeeded && !captcha_needed() ){ return 1; /* No captcha needed */ } zSeed = P("captchaseed"); if( zSeed==0 ) return 0; zEntered = P("captcha"); if( zEntered==0 || strlen(zEntered)!=8 ) return 0; do{ zDecode = captcha_decode((unsigned int)atoi(zSeed), n++); if( zDecode==0 ) return 0; assert( strlen(zDecode)==8 ); for(i=0; i<8; i++){ char c = zEntered[i]; if( c>='A' && c<='F' ) c += 'a' - 'A'; if( c=='O' ) c = '0'; z[i] = c; } }while( strncmp(zDecode,z,8)!=0 ); return 1; } /* ** Generate a captcha display together with the necessary hidden parameter ** for the seed and the entry box into which the user will type the text of ** the captcha. This is typically done at the very bottom of a form. |
︙ | ︙ | |||
605 606 607 608 609 610 611 | void captcha_generate(int mFlags){ unsigned int uSeed; const char *zDecoded; char *zCaptcha; if( !captcha_needed() && (mFlags & 0x02)==0 ) return; uSeed = captcha_seed(); | | | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | void captcha_generate(int mFlags){ unsigned int uSeed; const char *zDecoded; char *zCaptcha; if( !captcha_needed() && (mFlags & 0x02)==0 ) return; uSeed = captcha_seed(); zDecoded = captcha_decode(uSeed, 0); 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" autofocus> |
︙ | ︙ | |||
654 655 656 657 658 659 660 | */ void captcha_test(void){ const char *zPw = P("name"); if( zPw==0 || zPw[0]==0 ){ (void)exclude_spiders(1); @ <hr><p>The captcha is shown above. Add a name=HEX query parameter @ to see how HEX would be rendered in the current captcha font. | > > > > > | > | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 | */ void captcha_test(void){ const char *zPw = P("name"); if( zPw==0 || zPw[0]==0 ){ (void)exclude_spiders(1); @ <hr><p>The captcha is shown above. Add a name=HEX query parameter @ to see how HEX would be rendered in the current captcha font. @ <h2>Debug/Testing Values:</h2> @ <ul> @ <li> g.isHuman = %d(g.isHuman) @ <li> g.zLogin = %h(g.zLogin) @ <li> login_cookie_welformed() = %d(login_cookie_wellformed()) @ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)). @ </ul> style_finish_page(); }else{ style_set_current_feature("test"); style_header("Captcha Test"); @ <pre class="captcha"> @ %s(captcha_render(zPw)) @ </pre> |
︙ | ︙ | |||
681 682 683 684 685 686 687 | ** no login, offer a captcha challenge to allow the user agent to prove ** that he is human and return non-zero. ** ** If the bTest argument is non-zero, then show the captcha regardless of ** how the agent identifies. This is used for testing only. */ int exclude_spiders(int bTest){ | | > > > > > > > | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 | ** no login, offer a captcha challenge to allow the user agent to prove ** that he is human and return non-zero. ** ** If the bTest argument is non-zero, then show the captcha regardless of ** how the agent identifies. This is used for testing only. */ int exclude_spiders(int bTest){ if( !bTest ){ if( g.isHuman ) return 0; /* This user has already proven human */ if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */ if( login_cookie_wellformed() ){ /* Logged into another member of the login group */ return 0; } } /* This appears to be a spider. Offer the captcha */ style_set_current_feature("captcha"); style_header("I think you are a robot"); style_submenu_enable(0); @ <form method='POST' action='%R/ityaar'> @ <p>You seem like a robot. |
︙ | ︙ | |||
721 722 723 724 725 726 727 | ** is set. Regardless of whether or not the captcha was solved, this ** page always redirects to the fossil-goto cookie. */ void captcha_callback(void){ int bTest = atoi(PD("istest","0")); if( captcha_is_correct(1) ){ if( bTest==0 ){ | > > | > | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 | ** is set. Regardless of whether or not the captcha was solved, this ** page always redirects to the fossil-goto cookie. */ void captcha_callback(void){ int bTest = atoi(PD("istest","0")); if( captcha_is_correct(1) ){ if( bTest==0 ){ if( !login_cookie_wellformed() ){ /* ^^^^--- Don't overwrite a valid login on another repo! */ login_set_anon_cookie(0, 0); } cgi_append_header("X-Robot: 0\r\n"); } login_redirect_to_g(); }else{ g.isHuman = 0; (void)exclude_spiders(bTest); if( bTest ){ |
︙ | ︙ | |||
790 791 792 793 794 795 796 | ** 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"); | | | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | ** 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), 0); Blob audio; captcha_wav(zDecode, &audio); cgi_set_content_type("audio/wav"); cgi_set_content(&audio); } /* |
︙ | ︙ |
Changes to src/cgi.c.
︙ | ︙ | |||
2120 2121 2122 2123 2124 2125 2126 | cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 ){ | | | 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 | cgi_setenv("REQUEST_URI", zToken); cgi_setenv("SCRIPT_NAME", ""); for(i=0; zToken[i] && zToken[i]!='?'; i++){} if( zToken[i] ) zToken[i++] = 0; cgi_setenv("PATH_INFO", zToken); cgi_setenv("QUERY_STRING", &zToken[i]); if( zIpAddr==0 ){ zIpAddr = cgi_remote_ip(fossil_fileno(g.httpIn)); } if( zIpAddr ){ cgi_setenv("REMOTE_ADDR", zIpAddr); g.zIpAddr = fossil_strdup(zIpAddr); } |
︙ | ︙ | |||
2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 | if( child>0 ){ nchildren++; nRequest++; } close(connection); }else{ int nErr = 0, fd; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; if( 0 && !g.fAnyTrace ){ | > | 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 | if( child>0 ){ nchildren++; nRequest++; } close(connection); }else{ int nErr = 0, fd; g.zSockName = 0 /* avoid deleting the socket via atexit() */; close(0); fd = dup(connection); if( fd!=0 ) nErr++; close(1); fd = dup(connection); if( fd!=1 ) nErr++; if( 0 && !g.fAnyTrace ){ |
︙ | ︙ |
Changes to src/cookies.c.
︙ | ︙ | |||
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 | const char *cookie_value(const char *zPName, const char *zDefault){ int i; assert( zPName!=0 ); cookie_parse(); for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){} return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault; } /* ** WEBPAGE: cookies ** ** Show all cookies associated with Fossil. This shows the text of the ** login cookie and is hence dangerous if an adversary is looking over ** your shoulder and is able to read and reproduce that cookie. ** ** WEBPAGE: fdscookie ** ** Show the current display settings contained in the ** "fossil_display_settings" cookie. */ void cookie_page(void){ int i; int nCookie = 0; const char *zName = 0; const char *zValue = 0; int isQP = 0; int bFDSonly = strstr(g.zPath, "fdscookie")!=0; cookie_parse(); if( bFDSonly ){ style_header("Display Preferences Cookie"); }else{ style_header("All Cookies"); | > > > > > > > > > > | 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 | const char *cookie_value(const char *zPName, const char *zDefault){ int i; assert( zPName!=0 ); cookie_parse(); for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){} return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault; } /* Return the number of characters of hex in the prefix to the ** given string. */ static int hex_prefix_length(const char *z){ int i; for(i=0; fossil_isXdigit(z[i]); i++){} return i; } /* ** WEBPAGE: cookies ** ** Show all cookies associated with Fossil. This shows the text of the ** login cookie and is hence dangerous if an adversary is looking over ** your shoulder and is able to read and reproduce that cookie. ** ** WEBPAGE: fdscookie ** ** Show the current display settings contained in the ** "fossil_display_settings" cookie. */ void cookie_page(void){ int i; int nCookie = 0; const char *zName = 0; const char *zValue = 0; const char *zLoginCookie = login_cookie_name(); int isQP = 0; int bFDSonly = strstr(g.zPath, "fdscookie")!=0; cookie_parse(); if( bFDSonly ){ style_header("Display Preferences Cookie"); }else{ style_header("All Cookies"); |
︙ | ︙ | |||
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | cgi_redirect(g.zPath); } nCookie++; @ <li><p><b>%h(zName)</b>: %h(zValue) @ <input type="submit" name="%h(zDel)" value="Delete"> if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){ int j; @ <ul> for(j=0; j<cookies.nParam; j++){ @ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)" } @ </ul> } fossil_free(zDel); } @ </ol> @ </form> if( nCookie==0 ){ if( bFDSonly ){ | > > > > > > > > > > > > > > > > > | 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 | cgi_redirect(g.zPath); } nCookie++; @ <li><p><b>%h(zName)</b>: %h(zValue) @ <input type="submit" name="%h(zDel)" value="Delete"> if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){ int j; @ <p>This cookie remembers your Fossil display preferences. @ <ul> for(j=0; j<cookies.nParam; j++){ @ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)" } @ </ul> }else if( fossil_strcmp(zName, zLoginCookie)==0 ){ @ <p>This is your login cookie. If you delete this cookie, you will @ be logged out. }else if( fossil_strncmp(zName, "fossil-", 7)==0 && strlen(zName)==23 && hex_prefix_length(&zName[7])==16 && hex_prefix_length(zValue)>24 ){ @ <p>This appears to be a login cookie for another Fossil repository @ in the same website. } else { @ <p>This cookie was not generated by Fossil. It might be something @ from another program on the same website. } fossil_free(zDel); } @ </ol> @ </form> if( nCookie==0 ){ if( bFDSonly ){ |
︙ | ︙ |
Changes to src/db.c.
︙ | ︙ | |||
894 895 896 897 898 899 900 | const char *db_column_name(Stmt *pStmt, int N){ return (char*)sqlite3_column_name(pStmt->pStmt, N); } int db_column_count(Stmt *pStmt){ return sqlite3_column_count(pStmt->pStmt); } char *db_column_malloc(Stmt *pStmt, int N){ | | | 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 | const char *db_column_name(Stmt *pStmt, int N){ return (char*)sqlite3_column_name(pStmt->pStmt, N); } int db_column_count(Stmt *pStmt){ return sqlite3_column_count(pStmt->pStmt); } char *db_column_malloc(Stmt *pStmt, int N){ return fossil_strdup_nn(db_column_text(pStmt, N)); } void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){ blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N), sqlite3_column_bytes(pStmt->pStmt, N)); } Blob db_column_text_as_blob(Stmt *pStmt, int N){ Blob x; |
︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 | va_list ap; Stmt s; char *z; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)==SQLITE_ROW ){ | | < < | | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 | va_list ap; Stmt s; char *z; va_start(ap, zSql); db_vprepare(&s, 0, zSql, ap); va_end(ap); if( db_step(&s)==SQLITE_ROW ){ z = fossil_strdup_nn((const char*)sqlite3_column_text(s.pStmt, 0)); }else{ z = fossil_strdup(zDefault); } db_finalize(&s); return z; } /* ** Initialize a new database file with the given schema. If anything |
︙ | ︙ | |||
1435 1436 1437 1438 1439 1440 1441 | return; } if( sqlite3_user_data(context)==0 ){ zTemp = obscure((char*)zIn); }else{ zTemp = unobscure((char*)zIn); } | | | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 | return; } if( sqlite3_user_data(context)==0 ){ zTemp = obscure((char*)zIn); }else{ zTemp = unobscure((char*)zIn); } fossil_strcpy(zOut, zTemp); fossil_free(zTemp); sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free); } /* ** Return True if zName is a protected (a.k.a. "sensitive") setting. */ |
︙ | ︙ | |||
2532 2533 2534 2535 2536 2537 2538 | for(i=0; i<count(aDbName); i++){ sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]); if( isValidLocalDb(zPwd) ){ if( db_open_config(0, 1)==0 ){ return 0; /* Configuration could not be opened */ } /* Found a valid check-out database file */ | | | 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 | for(i=0; i<count(aDbName); i++){ sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]); if( isValidLocalDb(zPwd) ){ if( db_open_config(0, 1)==0 ){ return 0; /* Configuration could not be opened */ } /* Found a valid check-out database file */ g.zLocalDbName = fossil_strdup(zPwd); zPwd[n] = 0; while( n>0 && zPwd[n-1]=='/' ){ n--; zPwd[n] = 0; } g.zLocalRoot = mprintf("%s/", zPwd); g.localOpen = 1; |
︙ | ︙ | |||
2668 2669 2670 2671 2672 2673 2674 | }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; #endif fossil_fatal("not a valid repository: %s", zDbName); } } | | | 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 | }else{ #ifdef FOSSIL_ENABLE_JSON g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; #endif fossil_fatal("not a valid repository: %s", zDbName); } } g.zRepositoryName = fossil_strdup(zDbName); db_open_or_attach(g.zRepositoryName, "repository"); g.repositoryOpen = 1; sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &g.iRepoDataVers); /* Cache "allow-symlinks" option, because we'll need it on every stat call */ g.allowSymlinks = db_get_boolean("allow-symlinks",0); |
︙ | ︙ | |||
3508 3509 3510 3511 3512 3513 3514 | char *zOut; if( g.perm.RdAddr ){ zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey); }else{ zOut = 0; } if( zOut==0 ){ | | | 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 | char *zOut; if( g.perm.RdAddr ){ zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey); }else{ zOut = 0; } if( zOut==0 ){ zOut = fossil_strdup_nn(zKey); } return zOut; } /* ** Return true if the string zVal represents "true" (or "false"). */ |
︙ | ︙ |
Changes to src/default.css.
︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 | (e.g. DIV) so that certain nesting constructs are legal. */ .input-with-label { border: 1px inset rgba(128, 128, 128, 0.5); border-radius: 0.25em; padding: 0.1em; margin: 0 0.5em; | | > > > > > > > > | 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 | (e.g. DIV) so that certain nesting constructs are legal. */ .input-with-label { border: 1px inset rgba(128, 128, 128, 0.5); border-radius: 0.25em; padding: 0.1em; margin: 0 0.5em; display: inline-block /* We would really like flex layout but changing that currently introduces a good deal of UI breakage to chase down. The advantage would be better alignment of the contained elements. */; cursor: default; white-space: nowrap; } .submenu .input-with-label { border: none; } .input-with-label > * { vertical-align: middle; } .input-with-label > label { display: inline; /* some skins set label display to block! */ cursor: pointer; white-space: nowrap; } .input-with-label > input { margin: 0; } .input-with-label > button { margin: 0; } |
︙ | ︙ | |||
1762 1763 1764 1765 1766 1767 1768 | * to avoid repeating this long list of fonts. */ code, kbd, pre, samp, tt, var, div.markdown ol.footnotes > li.fn-joined > sup.fn-joined, table.numbered-lines > tbody > tr, tr.diffskip > td.chunkctrl, #fossil-status-bar, .monospace { | | | | | | 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 | * to avoid repeating this long list of fonts. */ code, kbd, pre, samp, tt, var, div.markdown ol.footnotes > li.fn-joined > sup.fn-joined, table.numbered-lines > tbody > tr, tr.diffskip > td.chunkctrl, #fossil-status-bar, .monospace { font-family: "Source Code Pro", "Menlo", "Monaco", "Consolas", "Andale Mono", "Ubuntu Mono", "Deja Vu Sans Mono", "Letter Gothic", "Letter Gothic Std", "Prestige Elite Std", "Courier", "Courier New", monospace; } div.markdown > ol.footnotes { font-size: 90%; } div.markdown > ol.footnotes > li { |
︙ | ︙ |
Changes to src/descendants.c.
︙ | ︙ | |||
245 246 247 248 249 250 251 | " FROM ancestor, plink, event" " WHERE plink.cid=ancestor.rid" " AND event.objid=plink.pid" " ORDER BY mtime DESC" " )" " SELECT ancestor.rid FROM ancestor" " WHERE EXISTS(SELECT 1 FROM tagxref" | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | " FROM ancestor, plink, event" " WHERE plink.cid=ancestor.rid" " AND event.objid=plink.pid" " ORDER BY mtime DESC" " )" " SELECT ancestor.rid FROM ancestor" " WHERE EXISTS(SELECT 1 FROM tagxref" " WHERE tagid=%d AND tagxref.rid=ancestor.rid" " AND value=%Q AND tagtype>0)" " ORDER BY mtime DESC" " LIMIT 1", rid, rid, TAG_BRANCH, zBranch ); } |
︙ | ︙ |
Changes to src/fossil.bootstrap.js.
︙ | ︙ | |||
135 136 137 138 139 140 141 | }; /** repoUrl( repoRelativePath [,urlParams] ) Creates a URL by prepending this.rootPath to the given path (which must be relative from the top of the site, without a leading slash). If urlParams is a string, it must be | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | }; /** repoUrl( repoRelativePath [,urlParams] ) Creates a URL by prepending this.rootPath to the given path (which must be relative from the top of the site, without a leading slash). If urlParams is a string, it must be parameters encoded in the form "key=val&key2=val2..." WITHOUT a leading '?'. If it's an object, all of its properties get appended to the URL in that form. */ F.repoUrl = function(path,urlParams){ if(!urlParams) return this.rootPath+path; const url=[this.rootPath,path]; url.push('?'); |
︙ | ︙ | |||
298 299 300 301 302 303 304 | the callback immediately and hinder future invocations until at least the given time has passed. If passed only 1 argument, or passed a falsy 2nd argument, the default wait time set in this function's $defaultDelay property is used. | | | | | | | | | | 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 | the callback immediately and hinder future invocations until at least the given time has passed. If passed only 1 argument, or passed a falsy 2nd argument, the default wait time set in this function's $defaultDelay property is used. Inspiration: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function */ F.debounce = function f(func, waitMs, immediate) { var timeoutId; if(!waitMs) waitMs = f.$defaultDelay; return function() { const context = this, args = Array.prototype.slice.call(arguments); const later = function() { timeoutId = undefined; if(!immediate) func.apply(context, args); }; const callNow = immediate && !timeoutId; clearTimeout(timeoutId); timeoutId = setTimeout(later, waitMs); if(callNow) func.apply(context, args); }; }; F.debounce.$defaultDelay = 500 /*arbitrary*/; })(window); |
Changes to src/fossil.page.fileedit.js.
︙ | ︙ | |||
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 | D.parseHtml(D.clearElement(target),[ "<div>Diff <code>[", self.finfo.checkin, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; | > | 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 | D.parseHtml(D.clearElement(target),[ "<div>Diff <code>[", self.finfo.checkin, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); if(sbs) P.tweakSbsDiffs(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; |
︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
︙ | ︙ | |||
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 | D.parseHtml(D.clearElement(target), [ "<div>Diff <code>[", self.winfo.name, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; | > | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 | D.parseHtml(D.clearElement(target), [ "<div>Diff <code>[", self.winfo.name, "]</code> → Local Edits</div>", c||'No changes.' ].join('')); F.diff.setupDiffContextLoad(); if(sbs) P.tweakSbsDiffs(); F.message('Updated diff.'); self.tabs.switchToTab(self.e.tabs.diff); } }); return this; }; |
︙ | ︙ |
Changes to src/http.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "http.h" #include <assert.h> #ifdef _WIN32 #include <io.h> | < < < < < < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | */ #include "config.h" #include "http.h" #include <assert.h> #ifdef _WIN32 #include <io.h> #endif #if INTERFACE /* ** Bits of the mHttpFlags parameter to http_exchange() */ |
︙ | ︙ | |||
204 205 206 207 208 209 210 | */ char *prompt_for_httpauth_creds(void){ Blob x; char *zUser; char *zPw; char *zPrompt; char *zHttpAuth = 0; | | | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | */ char *prompt_for_httpauth_creds(void){ Blob x; char *zUser; char *zPw; char *zPrompt; char *zHttpAuth = 0; if( !fossil_isatty(fossil_fileno(stdin)) ) return 0; zPrompt = mprintf("\n%s authorization required by\n%s\n", g.url.isHttps==1 ? "Encrypted HTTPS" : "Unencrypted HTTP", g.url.canonical); fossil_print("%s", zPrompt); free(zPrompt); if ( g.url.user && g.url.passwd && use_fossil_creds_for_httpauth_prompt() ){ zHttpAuth = mprintf("%s:%s", g.url.user, g.url.passwd); }else{ |
︙ | ︙ |
Changes to src/import.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | int nFileAlloc; /* Number of slots in aFile[] */ ImportFile *aFile; /* Information about files in a commit */ ImportFile *pInlineFile; /* File marked "inline" */ int fromLoaded; /* True zFrom content loaded into aFile[] */ int tagCommit; /* True if the commit adds a tag */ } gg; | < < < < < < < < < < < < < < < < < < < < < < | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | int nFileAlloc; /* Number of slots in aFile[] */ ImportFile *aFile; /* Information about files in a commit */ ImportFile *pInlineFile; /* File marked "inline" */ int fromLoaded; /* True zFrom content loaded into aFile[] */ int tagCommit; /* True if the commit adds a tag */ } gg; /* ** A no-op "xFinish" method */ static void finish_noop(void){} /* ** Deallocate the state information. |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
889 890 891 892 893 894 895 | } 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"> | | > | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 | } 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 info-changes-menu"> /* ^^^ .info-changes-menu is used by diff scroll sync */ pCfg = construct_diff_flags(diffType, &DCfg); DCfg.pRe = pRe; zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; if( diffType!=0 ){ @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\ @ Hide Diffs</a> } |
︙ | ︙ |
Changes to src/json.c.
︙ | ︙ | |||
219 220 221 222 223 224 225 | ** ** In practice we will only ever call this one time per app execution ** when constructing the JSON response envelope, so the static buffer ** "shouldn't" be a problem. ** */ char const * json_rc_cstr( int code ){ | | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | ** ** In practice we will only ever call this one time per app execution ** when constructing the JSON response envelope, so the static buffer ** "shouldn't" be a problem. ** */ char const * json_rc_cstr( int code ){ enum { BufSize = 13 }; static char buf[BufSize] = {'F','O','S','S','I','L','-',0}; assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code."); sqlite3_snprintf((int)BufSize, buf+7,"%04d", code); return buf; } /* ** Adds v to the API-internal cleanup mechanism. key is ignored ** (legacy) but might be re-introduced and "should" be a unique ** (app-wide) value. Failure to insert an item may be caused by any |
︙ | ︙ |
Changes to src/json_login.c.
︙ | ︙ | |||
103 104 105 106 107 108 109 | jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed); if( !jseed ){ jseed = json_getenv("cs") /* name used by HTML interface */; } } if(jseed){ if( cson_value_is_number(jseed) ){ | > | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed); if( !jseed ){ jseed = json_getenv("cs") /* name used by HTML interface */; } } if(jseed){ if( cson_value_is_number(jseed) ){ sqlite3_snprintf((int)SeedBufLen, seedBuffer, "%"CSON_INT_T_PFMT, cson_value_get_integer(jseed)); anonSeed = seedBuffer; }else if( cson_value_is_string(jseed) ){ anonSeed = cson_string_cstr(cson_value_get_string(jseed)); } } if(!anonSeed){ g.json.resultCode = preciseErrors |
︙ | ︙ | |||
211 212 213 214 215 216 217 | /* ** 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(); | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | /* ** 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, 0); 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; |
︙ | ︙ |
Changes to src/login.c.
︙ | ︙ | |||
152 153 154 155 156 157 158 159 160 161 162 163 | int login_is_valid_anonymous( const char *zUsername, /* The username. Must be "anonymous" */ const char *zPassword, /* The supplied password */ const char *zCS /* The captcha seed value */ ){ const char *zPw; /* The correct password shown in the captcha */ int uid; /* The user ID of anonymous */ if( zUsername==0 ) return 0; else if( zPassword==0 ) return 0; else if( zCS==0 ) return 0; else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; | > > | > | > > | 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 | int login_is_valid_anonymous( const char *zUsername, /* The username. Must be "anonymous" */ const char *zPassword, /* The supplied password */ const char *zCS /* The captcha seed value */ ){ const char *zPw; /* The correct password shown in the captcha */ int uid; /* The user ID of anonymous */ int n = 0; /* Counter of captcha-secrets */ if( zUsername==0 ) return 0; else if( zPassword==0 ) return 0; else if( zCS==0 ) return 0; else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0; while( 1/*exit-by-break*/ ){ zPw = captcha_decode((unsigned int)atoi(zCS), n); if( zPw==0 ) return 0; if( fossil_stricmp(zPw, zPassword)==0 ) break; n++; } uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND octet_length(pw)>0 AND octet_length(cap)>0"); return uid; } /* ** Make sure the accesslog table exists. Create it if it does not |
︙ | ︙ | |||
344 345 346 347 348 349 350 | ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. ** ** If bSessionCookie is true, the cookie will be a session cookie. */ void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ | | | > | 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 | ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. ** ** If bSessionCookie is true, the cookie will be a session cookie. */ void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){ char *zNow; /* Current time (julian day number) */ char *zCookie; /* The login cookie */ const char *zCookieName; /* Name of the login cookie */ Blob b; /* Blob used during cookie construction */ int expires = bSessionCookie ? 0 : 6*3600; zCookieName = login_cookie_name(); zNow = db_text("0", "SELECT julianday('now')"); assert( zCookieName && zNow ); blob_init(&b, zNow, -1); blob_appendf(&b, "/%z", captcha_secret(0)); sha1sum_blob(&b, &b); zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow); blob_reset(&b); cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires); if( zCookieDest ){ *zCookieDest = zCookie; }else{ free(zCookie); } fossil_free(zNow); } /* ** "Unsets" the login cookie (insofar as cookies can be unset) and ** clears the current user's (g.userUid) login information from the ** user table. Sets: user.cookie, user.ipaddr, user.cexpire. ** |
︙ | ︙ | |||
804 805 806 807 808 809 810 | @ <tr> @ <td></td> @ <td><input type="submit" name="pwreset" value="Reset My Password"> @ </tr> } @ </table> if( zAnonPw && !noAnon ){ | | | 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | @ <tr> @ <td></td> @ <td><input type="submit" name="pwreset" value="Reset My Password"> @ </tr> } @ </table> if( zAnonPw && !noAnon ){ const char *zDecoded = captcha_decode(uSeed, 0); 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>\ |
︙ | ︙ | |||
836 837 838 839 840 841 842 | @ for user <b>%h(g.zLogin)</b></p> } if( db_table_exists("repository","forumpost") ){ @ <hr><p> @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum @ post timeline</a> for user <b>%h(g.zLogin)</b></p> } | > | | | > > > | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 | @ for user <b>%h(g.zLogin)</b></p> } if( db_table_exists("repository","forumpost") ){ @ <hr><p> @ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum @ post timeline</a> for user <b>%h(g.zLogin)</b></p> } } @ <hr><p> @ Select your preferred <a href="%R/skins">site skin</a>. @ </p> @ <hr><p> @ Manage your <a href="%R/cookies">cookies</a>.</p> if( login_is_individual() ){ if( g.perm.Password ){ char *zRPW = fossil_random_password(12); @ <hr> @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p> form_begin(0, "%R/login"); @ <table> @ <tr><td class="form_label" id="oldpw">Old Password:</td> |
︙ | ︙ | |||
1308 1309 1310 1311 1312 1313 1314 | /* If we reach this point, it means we have a situation where we ** want to restrict the activity of a robot. */ g.isHuman = 0; (void)exclude_spiders(0); cgi_reply(); fossil_exit(0); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 | /* If we reach this point, it means we have a situation where we ** want to restrict the activity of a robot. */ g.isHuman = 0; (void)exclude_spiders(0); cgi_reply(); fossil_exit(0); } /* ** When this routine is called, we know that the request does not ** have a login on the present repository. This routine checks to ** see if their login cookie might be for another member of the ** login-group. ** ** If this repository is not a part of any login group, then this ** routine always returns false. ** ** If this repository is part of a login group, and the login cookie ** appears to be well-formed, then return true. That might be a ** false-positive, as we don't actually check to see if the login ** cookie is valid for some other repository. But false-positives ** are ok. This routine is used for robot defense only. */ int login_cookie_wellformed(void){ const char *zCookie; int n; zCookie = P(login_cookie_name()); if( zCookie==0 ){ return 0; } if( !db_exists("SELECT 1 FROM config WHERE name='login-group-code'") ){ return 0; } for(n=0; fossil_isXdigit(zCookie[n]); n++){} return n>48 && zCookie[n]=='/' && zCookie[n+1]!=0; } /* ** This routine examines the login cookie to see if it exists and ** is valid. If the login cookie checks out, it then sets global ** variables appropriately. ** ** g.userUid Database USER.UID value. Might be -1 for "nobody" |
︙ | ︙ | |||
1395 1396 1397 1398 1399 1400 1401 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be ** too old and the sha1 hash of TIME/SECRET must match HASH. ** SECRET is the "captcha-secret" value in the repository. */ double rTime = atof(zArg); Blob b; | > > > > | > > | | | | | | | | | | | > > | > > > > > > > > | 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 | }else if( fossil_strcmp(zUser, "anonymous")==0 ){ /* Cookies of the form "HASH/TIME/anonymous". The TIME must not be ** too old and the sha1 hash of TIME/SECRET must match HASH. ** SECRET is the "captcha-secret" value in the repository. */ double rTime = atof(zArg); Blob b; char *zSecret; int n = 0; do{ blob_zero(&b); zSecret = captcha_secret(n++); if( zSecret==0 ) break; blob_appendf(&b, "%s/%s", zArg, zSecret); sha1sum_blob(&b, &b); if( fossil_strcmp(zHash, blob_str(&b))==0 ){ uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND octet_length(cap)>0" " AND octet_length(pw)>0" " AND %.17g+0.25>julianday('now')", rTime ); } }while( uid==0 ); blob_reset(&b); }else{ /* Cookies of the form "HASH/CODE/USER". Search first in the ** local user table, then the user table for project CODE if we ** are part of a login-group. */ uid = login_find_user(zUser, zHash); if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){ uid = login_find_user(zUser, zHash); if( uid ){ record_login_attempt(zUser, zIpAddr, 1); }else{ /* The login cookie is a valid login for project CODE, but no ** user named USER exists on this repository. Cannot login as ** USER, but at least give them "anonymous" login. */ uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" " AND octet_length(cap)>0" " AND octet_length(pw)>0"); } } } login_create_csrf_secret(zHash); } /* If no user found and the REMOTE_USER environment variable is set, ** then accept the value of REMOTE_USER as the user. |
︙ | ︙ | |||
2171 2172 2173 2174 2175 2176 2177 | /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } | | | 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 | /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed, 0); 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") ){ |
︙ | ︙ | |||
2381 2382 2383 2384 2385 2386 2387 | /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } | | | 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 | /* Prepare the captcha. */ if( captchaIsCorrect ){ uSeed = strtoul(P("captchaseed"),0,10); }else{ uSeed = captcha_seed(); } zDecoded = captcha_decode(uSeed, 0); 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)"> |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> | < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.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; |
︙ | ︙ | |||
703 704 705 706 707 708 709 | const char *zCmdName = "unknown"; const CmdOrPage *pCmd = 0; int rc; g.zPhase = "init"; #if !defined(_WIN32_WCE) if( fossil_getenv("FOSSIL_BREAK") ){ | | | 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 | const char *zCmdName = "unknown"; const CmdOrPage *pCmd = 0; int rc; g.zPhase = "init"; #if !defined(_WIN32_WCE) if( fossil_getenv("FOSSIL_BREAK") ){ if( fossil_isatty(0) && fossil_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) DebugBreak(); |
︙ | ︙ | |||
3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 | if( g.httpUseSSL && g.httpSSLConn ){ ssl_close_server(g.httpSSLConn); g.httpSSLConn = 0; } #endif /* FOSSIL_ENABLE_SSL */ #else /* WIN32 */ /* Win32 implementation */ if( allowRepoList ){ flags |= HTTP_SERVER_REPOLIST; } if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zAltBase, zNotFound, zFileGlob, zIpAddr, flags); | > > > > | 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 | if( g.httpUseSSL && g.httpSSLConn ){ ssl_close_server(g.httpSSLConn); g.httpSSLConn = 0; } #endif /* FOSSIL_ENABLE_SSL */ #else /* WIN32 */ find_server_repository(2, 0); if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ allowRepoList = 1; } /* Win32 implementation */ if( allowRepoList ){ flags |= HTTP_SERVER_REPOLIST; } if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, zAltBase, zNotFound, zFileGlob, zIpAddr, flags); |
︙ | ︙ |
Changes to src/markdown.md.
︙ | ︙ | |||
128 129 130 131 132 133 134 | |:Left-aligned |:Centered :| Right-aligned:| | | ← Blank → | | | Row 4 Col 1 | Row 4 Col 2 | Row 4 Col 3 | > The first row is a header if followed by a horizontal rule or a blank line. > Placing **:** at the left, both, or right sides of a cell gives left-aligned, | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |:Left-aligned |:Centered :| Right-aligned:| | | ← Blank → | | | Row 4 Col 1 | Row 4 Col 2 | Row 4 Col 3 | > The first row is a header if followed by a horizontal rule or a blank line. > Placing **:** at the left, both, or right sides of a cell gives left-aligned, > centered, or right-aligned text, respectively. By default, both header and > body cells are left-aligned. > The leftmost or rightmost **\|** is required only if the first or last column, > respectively, contains at least one blank cell. ## Diagrams ## > |
︙ | ︙ |
Changes to src/markdown_html.c.
︙ | ︙ | |||
304 305 306 307 308 309 310 | if( flags & MKD_CELL_HEAD ){ blob_append_literal(ob, " <th"); }else{ blob_append_literal(ob, " <td"); } switch( flags & MKD_CELL_ALIGN_MASK ){ case MKD_CELL_ALIGN_LEFT: { | | | | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | if( flags & MKD_CELL_HEAD ){ blob_append_literal(ob, " <th"); }else{ blob_append_literal(ob, " <td"); } switch( flags & MKD_CELL_ALIGN_MASK ){ case MKD_CELL_ALIGN_LEFT: { blob_append_literal(ob, " style=\"text-align:left\""); break; } case MKD_CELL_ALIGN_RIGHT: { blob_append_literal(ob, " style=\"text-align:right\""); break; } case MKD_CELL_ALIGN_CENTER: { blob_append_literal(ob, " style=\"text-align:center\""); break; } } blob_append_literal(ob, ">"); blob_appendb(ob, text); if( flags & MKD_CELL_HEAD ){ blob_append_literal(ob, "</th>\n"); |
︙ | ︙ |
Changes to src/patch.c.
︙ | ︙ | |||
382 383 384 385 386 387 388 | void patch_apply(unsigned mFlags){ Stmt q; Blob cmd; blob_init(&cmd, 0, 0); if( unsaved_changes(0) ){ if( (mFlags & PATCH_FORCE)==0 ){ | > | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | void patch_apply(unsigned mFlags){ Stmt q; Blob cmd; blob_init(&cmd, 0, 0); if( unsaved_changes(0) ){ if( (mFlags & PATCH_FORCE)==0 ){ fossil_fatal("Cannot apply patch: there are unsaved changes " "in the current check-out"); }else{ blob_appendf(&cmd, "%$ revert", g.nameOfExe); if( mFlags & PATCH_DRYRUN ){ fossil_print("%s\n", blob_str(&cmd)); }else{ int rc = fossil_system(blob_str(&cmd)); if( rc ){ |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
1267 1268 1269 1270 1271 1272 1273 | ** argument. This is a no-op on unix but is necessary on windows. */ void fossil_binary_mode(FILE *p){ #if defined(_WIN32) _setmode(_fileno(p), _O_BINARY); #endif #ifdef __EMX__ /* OS/2 */ | | | 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 | ** argument. This is a no-op on unix but is necessary on windows. */ void fossil_binary_mode(FILE *p){ #if defined(_WIN32) _setmode(_fileno(p), _O_BINARY); #endif #ifdef __EMX__ /* OS/2 */ setmode(fossil_fileno(p), O_BINARY); #endif } |
Changes to src/setup.c.
︙ | ︙ | |||
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 | ** ** Change how the current repository participates in a login ** group. */ void setup_login_group(void){ const char *zGroup; char *zErrMsg = 0; Blob fullName; char *zSelfRepo; const char *zRepo = PD("repo", ""); const char *zLogin = PD("login", ""); const char *zPw = PD("pw", ""); const char *zNewName = PD("newname", "New Login Group"); login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = fossil_strdup(blob_str(&fullName)); blob_reset(&fullName); if( P("join")!=0 ){ login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg); }else if( P("leave") ){ login_group_leave(&zErrMsg); } style_set_current_feature("setup"); style_header("Login Group Configuration"); if( zErrMsg ){ @ <p class="generalError">%s(zErrMsg)</p> } zGroup = login_group_name(); | > > > | 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 | ** ** Change how the current repository participates in a login ** group. */ void setup_login_group(void){ const char *zGroup; char *zErrMsg = 0; Stmt q; Blob fullName; char *zSelfRepo; const char *zRepo = PD("repo", ""); const char *zLogin = PD("login", ""); const char *zPw = PD("pw", ""); const char *zNewName = PD("newname", "New Login Group"); login_check_credentials(); if( !g.perm.Setup ){ login_needed(0); return; } file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = fossil_strdup(blob_str(&fullName)); blob_reset(&fullName); if( P("join")!=0 ){ login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg); }else if( P("leave") ){ login_group_leave(&zErrMsg); }else if( P("rotate") ){ captcha_secret_rotate(); } style_set_current_feature("setup"); style_header("Login Group Configuration"); if( zErrMsg ){ @ <p class="generalError">%s(zErrMsg)</p> } zGroup = login_group_name(); |
︙ | ︙ | |||
819 820 821 822 823 824 825 | @ value="%h(zNewName)" name="newname"> @ (only used if creating a new login-group).</td></tr> @ @ <tr><td colspan="3" align="center"> @ <input type="submit" value="Join" name="join"></td></tr> @ </table></blockquote></div></form> }else{ | < | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 | @ value="%h(zNewName)" name="newname"> @ (only used if creating a new login-group).</td></tr> @ @ <tr><td colspan="3" align="center"> @ <input type="submit" value="Join" name="join"></td></tr> @ </table></blockquote></div></form> }else{ int n = 0; @ <p>This repository (in the file "%h(zSelfRepo)") @ is currently part of the "<b>%h(zGroup)</b>" login group. @ Other repositories in that group are:</p> @ <table border="0" cellspacing="4"> @ <tr><td colspan="2"><th align="left">Project Name<td> @ <th align="left">Repository File</tr> |
︙ | ︙ | |||
847 848 849 850 851 852 853 | @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr> } db_finalize(&q); @ </table> @ @ <p><form action="%R/setup_login_group" method="post"><div> login_insert_csrf_secret(); | | > > > > > > > > | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 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 | @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr> } db_finalize(&q); @ </table> @ @ <p><form action="%R/setup_login_group" method="post"><div> login_insert_csrf_secret(); @ <p>To leave this login group press: @ <input type="submit" value="Leave Login Group" name="leave"> @ <p>Setting a common captcha-secret on all repositories in the login-group @ allows anonymous logins for one repository in the login group to be used @ by all other repositories of the group within the same domain. Warning: @ If a captcha dialog was painted before setting the common captcha-secret @ and the "Speak password for 'anonymous'" button is pressed afterwards, @ the spoken text will be incorrect. @ <input type="submit" name="rotate" value="Set common captcha-secret"> @ </form></p> } @ <hr><h2>Implementation Details</h2> @ <p>The following are fields from the CONFIG table related to login-groups. @ </p> @ <table border='1' cellspacing="0" cellpadding="4"\ @ class='sortable' data-column-types='ttt' data-init-sort='1'> @ <thead><tr> @ <th>Config.Name<th>Config.Value<th>Config.mtime</tr> @ </thead><tbody> db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config" " WHERE name GLOB 'peer-*'" " OR name GLOB 'project-*'" " OR name GLOB 'login-group-*'" " ORDER BY name"); while( db_step(&q)==SQLITE_ROW ){ @ <tr><td>%h(db_column_text(&q,0))</td> @ <td>%h(db_column_text(&q,1))</td> @ <td>%h(db_column_text(&q,2))</td></tr> } db_finalize(&q); @ </tbody></table> @ <h2>Interpretation</h2> @ <ul> @ <li><p><b>login-group-code</b> → @ A random code assigned to each login-group. The login-group-code is @ a unique identifier for the login-group. @ @ <li><p><b>login-group-name</b> → @ The human-readable name of the login-group. @ @ <li><p><b>project-code</b> → @ A random code assigned to each project. The project-code is @ a unique identifier for the project. Multiple repositories can share @ the same project-code. When two or more repositories have the same @ project code, that mean those repositories are clones of each other. @ Repositories are only able to sync if they share the same project-code. @ @ <li><p><b>project-description</b> → @ A description of project in this repository. This is a verbose form @ of project-name. This description can be edited in the second entry @ box on the <a href="./setup_config">Setup/Configuration page</a>. @ @ <li><p><b>project-name</b> → @ The human-readable name for the project. The project-name can be @ modified in the first entry on the @ <a href="./setup_config">Setup/Configuration page</a>. @ @ <li><p><b>peer-repo-<i>CODE</i></b> → @ <i>CODE</i> is 16-character prefix of the project-code for another @ repository that is part of the same login-group. The value is the @ filename for the peer repository. @ @ <li><p><b>peer-name-<i>CODE</i></b> → @ <i>CODE</i> is 16-character prefix of the project-code for another @ repository that is part of the same login-group. The value is @ project-name value for the other repository. @ </ul> style_table_sorter(); style_finish_page(); } /* ** WEBPAGE: setup_timeline ** ** Edit administrative settings controlling the display of |
︙ | ︙ |
Changes to src/setupuser.c.
︙ | ︙ | |||
937 938 939 940 941 942 943 | @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_finish_page(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_finish_page(); } /* ** WEBPAGE: setup_uinfo ** ** Detailed information about a user account, available to administrators ** only. ** ** u=UID ** l=LOGIN */ void setup_uinfo_page(void){ Stmt q; Blob sql; const char *zLogin; int uid; /* Must have ADMIN privileges to access this page */ login_check_credentials(); if( !g.perm.Admin ){ login_needed(0); return; } style_set_current_feature("setup"); zLogin = P("l"); uid = atoi(PD("u","0")); if( zLogin==0 && uid==0 ){ uid = db_int(1,"SELECT uid FROM user"); } blob_init(&sql, 0, 0); blob_append_sql(&sql, "SELECT " /* 0 */ "uid," /* 1 */ "login," /* 2 */ "cap," /* 3 */ "cookie," /* 4 */ "datetime(cexpire)," /* 5 */ "info," /* 6 */ "datetime(user.mtime,'unixepoch')," ); if( db_table_exists("repository","subscriber") ){ blob_append_sql(&sql, /* 7 */ "subscriberId," /* 8 */ "semail," /* 9 */ "sverified," /* 10 */ "date(lastContact+2440587.5)" " FROM user LEFT JOIN subscriber ON suname=login" ); }else{ blob_append_sql(&sql, /* 7 */ "NULL," /* 8 */ "NULL," /* 9 */ "NULL," /* 10 */ "NULL" " FROM user" ); } if( zLogin!=0 ){ blob_append_sql(&sql, " WHERE login=%Q", zLogin); }else{ blob_append_sql(&sql, " WHERE uid=%d", uid); } db_prepare(&q, "%s", blob_sql_text(&sql)); blob_zero(&sql); if( db_step(&q)!=SQLITE_ROW ){ style_header("No Such User"); if( zLogin ){ @ <p>Cannot find any information on user %h(zLogin). }else{ @ <p>Cannot find any information on userid %d(uid). } style_finish_page(); db_finalize(&q); return; } style_header("User %h", db_column_text(&q,1)); @ <table class="label-value"> @ <tr><th>uid:</th><td>%d(db_column_int(&q,0)) @ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr> @ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr> @ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</th></tr> @ <tr><th valign="top">info:</th> @ <td valign="top"><span style='white-space:pre-line;'>\ @ %h(db_column_text(&q,5))</span></td></tr> @ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr> if( db_column_type(&q,7)!=SQLITE_NULL ){ @ <tr><th>subscriberId:</th><td>%d(db_column_int(&q,7)) @ (<a href="%R/alerts?sid=%d(db_column_int(&q,7))">edit</a>)</td></tr> @ <tr><th>semail:</th><td>%h(db_column_text(&q,8))</td></tr> @ <tr><th>verified:</th><td>%s(db_column_int(&q,9)?"yes":"no")</td></th> @ <tr><th>lastContact:</th><td>%h(db_column_text(&q,10))</td></tr> } @ </table> db_finalize(&q); style_finish_page(); } |
Changes to src/shun.c.
︙ | ︙ | |||
119 120 121 122 123 124 125 | } @ are no longer being shunned.</p> }else{ @ <p class="noMoreShun">Artifact(s)<br> for( p = zUuid ; *p ; p += strlen(p)+1 ){ @ %s(p)<br> } | | | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | } @ 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 @ before the artifact content can be pulled in @ from other repositories.</p> } } if( zUuid && P("add") && cgi_csrf_safe(2) ){ const char *p = zUuid; int rid, tagid; while( *p ){ db_multi_exec( |
︙ | ︙ |
Changes to src/skins.c.
︙ | ︙ | |||
1343 1344 1345 1346 1347 1348 1349 | builtin_request_js("skin.js"); style_finish_page(); } /* ** WEBPAGE: skins ** | | | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 | builtin_request_js("skin.js"); style_finish_page(); } /* ** WEBPAGE: skins ** ** Show a list of all of the built-in skins, plus the respository skin, ** and provide the user with an opportunity to change to any of them. */ void skins_page(void){ int i; char *zBase = fossil_strdup(g.zTop); size_t nBase = strlen(zBase); login_check_credentials(); |
︙ | ︙ |
Changes to src/style.c.
︙ | ︙ | |||
987 988 989 990 991 992 993 | if( p->zLink==0 ){ @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span> }else{ @ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a> } } } | | | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 | if( p->zLink==0 ){ @ <span class="label sml-%s(zClass)">%h(p->zLabel)</span> }else{ @ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a> } } } fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */ for(i=0; i<nSubmenuCtrl; i++){ const char *zQPN = aSubmenuCtrl[i].zName; const char *zDisabled = ""; const char *zXtraClass = ""; if( aSubmenuCtrl[i].eVisible & STYLE_DISABLED ){ zDisabled = " disabled"; }else if( zQPN ){ |
︙ | ︙ | |||
1353 1354 1355 1356 1357 1358 1359 | /* ** WEBPAGE: honeypot ** This page is a honeypot for spiders and bots. */ void honeypot_page(void){ unsigned int uSeed = captcha_seed(); | | | 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 | /* ** WEBPAGE: honeypot ** This page is a honeypot for spiders and bots. */ void honeypot_page(void){ unsigned int uSeed = captcha_seed(); const char *zDecoded = captcha_decode(uSeed, 0); int bAutoCaptcha = db_get_boolean("auto-captcha", 0); char *zCaptcha = captcha_render(zDecoded); style_header("I think you are a robot"); @ <p>You seem like a robot.</p> @ @ <p>Is that incorrect? Are you really human? @ If so, please prove it by transcribing the captcha text |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
2911 2912 2913 2914 2915 2916 2917 | tmFlags |= TIMELINE_DISJOINT; } if( cpOnly && showCherrypicks ){ blob_appendf(&desc, " that participate in a cherrypick merge"); tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT; } if( zUser ){ | > > > > | > | 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 | tmFlags |= TIMELINE_DISJOINT; } if( cpOnly && showCherrypicks ){ blob_appendf(&desc, " that participate in a cherrypick merge"); tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT; } if( zUser ){ if( g.perm.Admin ){ blob_appendf(&desc, " by user <a href='%R/setup_uinfo?l=%h'>%h</a>", zUser, zUser); }else{ blob_appendf(&desc, " by user %h", zUser); } tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; } if( zTagSql ){ if( matchStyle==MS_EXACT || matchStyle==MS_BRLIST ){ if( related ){ blob_appendf(&desc, " related to %h", zMatchDesc); }else{ |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
614 615 616 617 618 619 620 621 622 623 624 625 626 627 | } /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ if( dryRunFlag ){ db_end_transaction(1); /* With --dry-run, rollback changes */ }else{ char *zPwd; ensure_empty_dirs_created(1); sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, file_rmdir_sql_function, 0, 0); zPwd = file_getcwd(0,0); db_multi_exec( | > > > | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 | } /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ if( dryRunFlag ){ db_end_transaction(1); /* With --dry-run, rollback changes */ fossil_warning("\nREMINDER: this was a dry run -" " no files were actually changed " "(checkout is still %.10s).", rid_to_uuid(vid)); }else{ char *zPwd; ensure_empty_dirs_created(1); sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0, file_rmdir_sql_function, 0, 0); zPwd = file_getcwd(0,0); db_multi_exec( |
︙ | ︙ |
Changes to src/url.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "url.h" #include <stdio.h> #ifdef _WIN32 #include <io.h> | < < < < < < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | */ #include "config.h" #include "url.h" #include <stdio.h> #ifdef _WIN32 #include <io.h> #endif #if INTERFACE /* ** Flags for url_parse() */ #define URL_PROMPT_PW 0x0001 /* Prompt for password if needed */ |
︙ | ︙ | |||
322 323 324 325 326 327 328 | pUrlData->name = mprintf("%b", &cfile); pUrlData->canonical = mprintf("file://%T", pUrlData->name); blob_reset(&cfile); }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW)!=0 ){ url_prompt_for_password_local(pUrlData); }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ | > | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | pUrlData->name = mprintf("%b", &cfile); pUrlData->canonical = mprintf("file://%T", pUrlData->name); blob_reset(&cfile); }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW)!=0 ){ url_prompt_for_password_local(pUrlData); }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ if( fossil_isatty(fossil_fileno(stdin)) && ( urlFlags & URL_REMEMBER_PW )==0 ){ if( save_password_prompt(pUrlData->passwd) ){ pUrlData->flags = urlFlags |= URL_REMEMBER_PW; }else{ pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; } } } |
︙ | ︙ | |||
733 734 735 736 737 738 739 | /* ** Prompt the user for the password that corresponds to the "user" member of ** the provided UrlData structure. Store the result into the "passwd" member ** of the provided UrlData structure. */ void url_prompt_for_password_local(UrlData *pUrlData){ if( pUrlData->isSsh || pUrlData->isFile ) return; | | | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | /* ** Prompt the user for the password that corresponds to the "user" member of ** the provided UrlData structure. Store the result into the "passwd" member ** of the provided UrlData structure. */ void url_prompt_for_password_local(UrlData *pUrlData){ if( pUrlData->isSsh || pUrlData->isFile ) return; if( fossil_isatty(fossil_fileno(stdin)) && (pUrlData->flags & URL_PROMPT_PW)!=0 && (pUrlData->flags & URL_PROMPTED)==0 ){ pUrlData->flags |= URL_PROMPTED; pUrlData->passwd = prompt_for_user_password(pUrlData->canonical); if( pUrlData->passwd[0] && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
︙ | ︙ | |||
794 795 796 797 798 799 800 | /* Preemptively prompt for a password if a username is given in the ** URL but no password. */ void url_get_password_if_needed(void){ if( (g.url.user && g.url.user[0]) && (g.url.passwd==0 || g.url.passwd[0]==0) | | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | /* Preemptively prompt for a password if a username is given in the ** URL but no password. */ void url_get_password_if_needed(void){ if( (g.url.user && g.url.user[0]) && (g.url.passwd==0 || g.url.passwd[0]==0) && fossil_isatty(fossil_fileno(stdin)) ){ url_prompt_for_password(); } } /* ** Given a URL for a remote repository clone point, try to come up with a |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #include <math.h> /* ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> #else # include <sys/time.h> # include <sys/resource.h> # include <sys/types.h> # include <sys/stat.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ db_close(1); #ifndef _WIN32 | > > > > > > > > > > > > > > > > > > > > > > > | 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 | #include <math.h> /* ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> # include <io.h> #else # include <sys/time.h> # include <sys/resource.h> # include <sys/types.h> # include <sys/stat.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif /* ** Returns the same as the platform's isatty() or _isatty() function. */ int fossil_isatty(int fd){ #ifdef _WIN32 return _isatty(fd); #else return isatty(fd); #endif } /* ** Returns the same as the platform's fileno() or _fileno() function. */ int fossil_fileno(FILE *p){ #ifdef _WIN32 return _fileno(p); #else return fileno(p); #endif } /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ db_close(1); #ifndef _WIN32 |
︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 | if( munmap(p, n) ){ fossil_panic("munmap failed: %d\n", errno); } #else fossil_free(p); #endif } /* ** Translate every upper-case character in the input string into ** its equivalent lower-case. */ char *fossil_strtolwr(char *zIn){ char *zStart = zIn; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( munmap(p, n) ){ fossil_panic("munmap failed: %d\n", errno); } #else fossil_free(p); #endif } /* ** Duplicate a string. */ char *fossil_strndup(const char *zOrig, ssize_t len){ char *z = 0; if( zOrig ){ if( len<0 ) len = strlen(zOrig); z = fossil_malloc( len+1 ); memcpy(z, zOrig, len); z[len] = 0; } return z; } char *fossil_strdup(const char *zOrig){ return fossil_strndup(zOrig, -1); } char *fossil_strdup_nn(const char *zOrig){ if( zOrig==0 ) return fossil_strndup("", 0); return fossil_strndup(zOrig, -1); } /* ** strcpy() workalike to squelch an unwarranted warning from OpenBSD. */ void fossil_strcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } /* ** Translate every upper-case character in the input string into ** its equivalent lower-case. */ char *fossil_strtolwr(char *zIn){ char *zStart = zIn; |
︙ | ︙ |
Changes to src/xfer.c.
︙ | ︙ | |||
1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ int autopushFailed = 0; /* Autopush following commit failed if true */ const char *zCkinLock; /* Name of check-in to lock. NULL for none */ const char *zClientId; /* A unique identifier for this check-out */ unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ if( pnRcvd ) *pnRcvd = 0; if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 && configRcvMask==0 && configSendMask==0 ){ | > | 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ int autopushFailed = 0; /* Autopush following commit failed if true */ const char *zCkinLock; /* Name of check-in to lock. NULL for none */ const char *zClientId; /* A unique identifier for this check-out */ unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); if( pnRcvd ) *pnRcvd = 0; if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 && configRcvMask==0 && configSendMask==0 ){ |
︙ | ︙ | |||
2302 2303 2304 2305 2306 2307 2308 | if( syncFlags & SYNC_VERBOSE ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, xfer.nFileSent, xfer.nDeltaSent); }else{ nRoundtrip++; nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; | > | | > | 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 | if( syncFlags & SYNC_VERBOSE ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, xfer.nFileSent, xfer.nDeltaSent); }else{ nRoundtrip++; nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; if( bOutIsTty!=0 ){ fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, nRoundtrip, nArtifactSent, nArtifactRcvd); } } nCardSent = 0; nCardRcvd = 0; xfer.nFileSent = 0; xfer.nDeltaSent = 0; xfer.nGimmeSent = 0; xfer.nIGotSent = 0; |
︙ | ︙ | |||
2803 2804 2805 2806 2807 2808 2809 | } origConfigRcvMask = 0; if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", blob_size(&recv), nCardRcvd, xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); }else{ | > | | > | 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 | } origConfigRcvMask = 0; if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){ fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:", blob_size(&recv), nCardRcvd, xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile); }else{ if( bOutIsTty!=0 ){ fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, nRoundtrip, nArtifactSent, nArtifactRcvd); } } nUncRcvd += blob_size(&recv); blob_reset(&recv); nCycle++; /* Set go to 1 if we need to continue the sync/push/pull/clone for ** another round. Set go to 0 if it is time to quit. */ |
︙ | ︙ | |||
2862 2863 2864 2865 2866 2867 2868 | db_timespan_name(rSkew)); g.clockSkewSeen = 1; }else if( rSkew*24.0*3600.0 < -10.0 ){ fossil_warning("*** time skew *** server is slow by %s", db_timespan_name(-rSkew)); g.clockSkewSeen = 1; } | > > > > | | 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 | db_timespan_name(rSkew)); g.clockSkewSeen = 1; }else if( rSkew*24.0*3600.0 < -10.0 ){ fossil_warning("*** time skew *** server is slow by %s", db_timespan_name(-rSkew)); g.clockSkewSeen = 1; } if( bOutIsTty==0 ){ fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, nRoundtrip, nArtifactSent, nArtifactRcvd); fossil_force_newline(); } 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 : "", |
︙ | ︙ |
Changes to tools/makeheaders.c.
︙ | ︙ | |||
3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 | } } /* Done! */ return pFile; } /* MS-Windows and MS-DOS both have the following serious OS bug: the ** length of a command line is severely restricted. But this program ** occasionally requires long command lines. Hence the following ** work around. ** ** If the parameters "-f FILENAME" appear anywhere on the command line, | > > > > > | 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 | } } /* Done! */ return pFile; } /* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */ static void local_strcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } /* MS-Windows and MS-DOS both have the following serious OS bug: the ** length of a command line is severely restricted. But this program ** occasionally requires long command lines. Hence the following ** work around. ** ** If the parameters "-f FILENAME" appear anywhere on the command line, |
︙ | ︙ | |||
3241 3242 3243 3244 3245 3246 3247 | zNew = realloc( zNew, sizeof(char*) * nAlloc ); } } if( zNew ){ int j = nNew + index; zNew[j] = malloc( n + 1 ); if( zNew[j] ){ | | | 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 | zNew = realloc( zNew, sizeof(char*) * nAlloc ); } } if( zNew ){ int j = nNew + index; zNew[j] = malloc( n + 1 ); if( zNew[j] ){ local_strcpy( zNew[j], zBuf ); } } } } fclose(in); newArgc = argc + nNew - 1; for(i=0; i<=index; i++){ |
︙ | ︙ |
Changes to tools/mkindex.c.
︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 | if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return; z = zLine + 12; while( fossil_isspace(z[0]) ) z++; len = (int)strlen(z); while( len>0 && fossil_isspace(z[len-1]) ){ len--; } aEntry[nUsed-1].zDflt = string_dup(z,len); } /* ** Scan a line for a function that implements a web page or command. */ void scan_for_func(char *zLine){ int i,j,k; char *z; | > > > > > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return; z = zLine + 12; while( fossil_isspace(z[0]) ) z++; len = (int)strlen(z); while( len>0 && fossil_isspace(z[len-1]) ){ len--; } aEntry[nUsed-1].zDflt = string_dup(z,len); } /* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */ static void local_strcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } /* ** Scan a line for a function that implements a web page or command. */ void scan_for_func(char *zLine){ int i,j,k; char *z; |
︙ | ︙ | |||
347 348 349 350 351 352 353 | && strncmp(zLine,"** SETTING:",11)!=0 && strncmp(zLine,"** DEFAULT:",11)!=0 ){ if( zLine[2]=='\n' ){ zHelp[nHelp++] = '\n'; }else{ if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; | | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | && strncmp(zLine,"** SETTING:",11)!=0 && strncmp(zLine,"** DEFAULT:",11)!=0 ){ if( zLine[2]=='\n' ){ zHelp[nHelp++] = '\n'; }else{ if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0; local_strcpy(&zHelp[nHelp], &zLine[3]); nHelp += strlen(&zHelp[nHelp]); } return; } for(i=0; fossil_isspace(zLine[i]); i++){} if( zLine[i]==0 ) return; isSetting = (aEntry[nFixed].eType & CMDFLAG_SETTING)!=0; |
︙ | ︙ |
Changes to tools/mkversion.c.
︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 | s[j] = t; t += s[i]; zOut[n] = "0123456789abcdef"[(t>>4)&0xf]; zOut[n+1] = "0123456789abcdef"[t&0xf]; } zOut[n] = 0; } int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; #if defined(__DMC__) /* e.g. 0x857 */ int i = 0; #endif | > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | s[j] = t; t += s[i]; zOut[n] = "0123456789abcdef"[(t>>4)&0xf]; zOut[n+1] = "0123456789abcdef"[t&0xf]; } zOut[n] = 0; } /* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */ static void local_strcpy(char *dest, const char *src){ while( (*(dest++) = *(src++))!=0 ){} } int main(int argc, char *argv[]){ FILE *m,*u,*v; char *z; #if defined(__DMC__) /* e.g. 0x857 */ int i = 0; #endif |
︙ | ︙ | |||
171 172 173 174 175 176 177 | if( z[0]==0 ) break; } z++; } for(z=vx; z[0]=='0'; z++){} printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]); memset(vx,0,sizeof(vx)); | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | if( z[0]==0 ) break; } z++; } for(z=vx; z[0]=='0'; z++){} printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]); memset(vx,0,sizeof(vx)); local_strcpy(vx,b); for(z=vx; z[0]; z++){ if( z[0]=='-' ){ z[0] = 0; break; } if( z[0]!='.' ) continue; if ( d<3 ){ |
︙ | ︙ |