From 2344e5a2cd76186a3929ee9fe34478aca3a5cdbf Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 15 Jul 2026 18:32:04 +0200 Subject: [PATCH 01/27] fix(runtime): log service startup errors instead of printing them beside the log --- opencloud/pkg/runtime/service/service.go | 13 ++++++++++--- pkg/clihelper/app.go | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/opencloud/pkg/runtime/service/service.go b/opencloud/pkg/runtime/service/service.go index 13b0af8076..c360a623f2 100644 --- a/opencloud/pkg/runtime/service/service.go +++ b/opencloud/pkg/runtime/service/service.go @@ -389,9 +389,16 @@ func Start(ctx context.Context, o ...Option) error { if ev.Restarting { l = s.Log.Error() } - l.Str("event", e.String()).Str("service", ev.ServiceName).Str("supervisor", ev.SupervisorName). - Bool("restarting", ev.Restarting).Float64("failures", ev.CurrentFailures).Float64("threshold", ev.FailureThreshold). - Interface("error", ev.Err).Msg("service terminated") + l = l.Str("event", e.String()).Str("service", ev.ServiceName).Str("supervisor", ev.SupervisorName). + Bool("restarting", ev.Restarting).Float64("failures", ev.CurrentFailures).Float64("threshold", ev.FailureThreshold) + // ev.Err is an interface{}: marshaling an error yields {} because + // its fields are unexported, so the message has to go through Err + if err, ok := ev.Err.(error); ok { + l = l.Err(err) + } else { + l = l.Interface("error", ev.Err) + } + l.Msg("service terminated") case suture.EventBackoff: s.Log.Warn().Str("event", e.String()).Str("supervisor", ev.SupervisorName).Msg("service backoff") case suture.EventResume: diff --git a/pkg/clihelper/app.go b/pkg/clihelper/app.go index e2543faff0..2643391671 100644 --- a/pkg/clihelper/app.go +++ b/pkg/clihelper/app.go @@ -14,5 +14,11 @@ func DefaultApp(app *cobra.Command) *cobra.Command { // version info app.Version = fmt.Sprintf("%s (%s <%s>) (%s)", version.String, "OpenCloud GmbH", "support@opencloud.eu", version.Compiled()) + // a failing RunE is a runtime error, not a usage error: printing it here + // would put unstructured text next to the JSON log records, and the usage + // block on top of it is noise. main() reports what reaches it. + app.SilenceErrors = true + app.SilenceUsage = true + return app } From 8e9eb1b559b1cf94398c257eb44cc176d871df07 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Wed, 15 Jul 2026 19:01:03 +0200 Subject: [PATCH 02/27] fix(runtime): keep the usage block for flag errors only --- pkg/clihelper/app.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/clihelper/app.go b/pkg/clihelper/app.go index 2643391671..e654165d45 100644 --- a/pkg/clihelper/app.go +++ b/pkg/clihelper/app.go @@ -14,11 +14,17 @@ func DefaultApp(app *cobra.Command) *cobra.Command { // version info app.Version = fmt.Sprintf("%s (%s <%s>) (%s)", version.String, "OpenCloud GmbH", "support@opencloud.eu", version.Compiled()) - // a failing RunE is a runtime error, not a usage error: printing it here - // would put unstructured text next to the JSON log records, and the usage - // block on top of it is noise. main() reports what reaches it. + // cobra would print the error on top of what main() already prints app.SilenceErrors = true - app.SilenceUsage = true + + // keep the usage block for flag parse errors, drop it once RunE runs. + // Traversing runs the hook even below a subcommand that brings its own, + // e.g. every service below ServiceCommand. + cobra.EnableTraverseRunHooks = true + app.PersistentPreRunE = func(cmd *cobra.Command, _ []string) error { + cmd.SilenceUsage = true + return nil + } return app } From 2d9721065727be286d202d6825fea75a82eb578e Mon Sep 17 00:00:00 2001 From: Pascal Bleser Date: Wed, 29 Jul 2026 11:18:39 +0200 Subject: [PATCH 03/27] feat(posixfs): #3182 add basepath option in the "posixfs scan" command * add support for specifying a basepath using -p when running the posixfs scan command, to indicate a directory under which to start scanning, or a singular file to scan, as opposed to scanning from the storage root directory as is the default behaviour * implements https://github.com/opencloud-eu/opencloud/issues/3182 --- opencloud/pkg/command/posixfs.go | 47 +++++++++++++++++++++++++++++-- pkg/x/path/filepathx/path.go | 26 +++++++++++++++++ pkg/x/path/filepathx/path_test.go | 23 +++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index d43be52749..e38a400afb 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -13,6 +13,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/config/parser" oclog "github.com/opencloud-eu/opencloud/pkg/log" + "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/event" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig" @@ -95,6 +96,39 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { os.Exit(1) } + storageRoot := cfg.Drivers.Posix.Root + root := storageRoot + defaultRoot := true + if v, err := cmd.Flags().GetString("basepath"); err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse command-line parameter '--basepath': %v\n", err) + os.Exit(1) + } else if v != "" { + root = v + if !filepath.IsAbs(v) { + if v, err = filepath.Abs(v); err != nil { + fmt.Fprintf(os.Stderr, "Failed to make the basepath mentioned using '--basepath' absolute: %v\n", err) + os.Exit(1) + } else { + root = v + } + } else { + root = v + } + root = filepath.Clean(root) + defaultRoot = false + } + + // ensure that, if a basepath has been indicated, it is under the storage root + if !defaultRoot { + if contained, err := filepathx.IsSameOrContainedBy(storageRoot, root); err != nil { + fmt.Fprintf(os.Stderr, "Failed to determine whether the specified basepath %q is contained by the storage root %q: %v\n", root, storageRoot, err) + os.Exit(1) + } else if !contained { + fmt.Fprintf(os.Stderr, "The specified basepath %q is neither the storage root %q, nor a subdirectory thereof, nor a file underneath it\n", root, storageRoot) + os.Exit(1) + } + } + // We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards drivers := revaconfig.StorageProviderDrivers(cfg) drivers["posix"] = revaconfig.Posix(cfg, false, false) @@ -112,6 +146,10 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { oclog.Pretty(true), oclog.Color(false)).Logger + if !defaultRoot { + log = log.With().Str("basepath", root).Logger() + } + f, ok := registry.NewFuncs["posix"] if !ok { fmt.Fprintf(os.Stderr, "posix driver not found in registry\n") @@ -130,8 +168,12 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { os.Exit(1) } - fmt.Println("Starting posixfs scan...") - err = cacher.WarmupIDCache(cfg.Drivers.Posix.Root, true, false) + if defaultRoot { + fmt.Println("Starting posixfs scan...") + } else { + fmt.Printf("Starting posixfs scan at '%s'...\n", root) + } + err = cacher.WarmupIDCache(root, true, false) if err != nil { fmt.Fprintf(os.Stderr, "Scan failed: %v\n", err) return err @@ -141,6 +183,7 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { return nil }, } + cmd.Flags().StringP("basepath", "p", "", "the root under which to scan files, which may be a directory or a file (when omitted, detaults to using the storage root)") return cmd } diff --git a/pkg/x/path/filepathx/path.go b/pkg/x/path/filepathx/path.go index 8101e89331..d105290fc3 100644 --- a/pkg/x/path/filepathx/path.go +++ b/pkg/x/path/filepathx/path.go @@ -1,7 +1,9 @@ package filepathx import ( + "fmt" "path/filepath" + "strings" ) // JailJoin joins any number of path elements into a single path, @@ -10,3 +12,27 @@ import ( func JailJoin(jail string, elem ...string) string { return filepath.Join(jail, filepath.Join(append([]string{"/"}, elem...)...)) } + +// Determines whether the file or directory 'child' is same as or underneath the directory 'parent'. +// +// Note that 'parent' is expected to be a directory. +func IsSameOrContainedBy(parent string, child string) (bool, error) { + absParent, err := filepath.Abs(parent) + if err != nil { + return false, fmt.Errorf("failed to make parent directory absolute: %q: %w", parent, err) + } + + absChild, err := filepath.Abs(child) + if err != nil { + return false, fmt.Errorf("failed to make child file/directory absolute: %q: %w", child, err) + } + + rel, err := filepath.Rel(absParent, absChild) + if err != nil { + return false, fmt.Errorf("failed to determine the relative path between the parent directory %q and the child file/directory: %q: %w", absParent, absChild, err) + } + if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) { + return false, nil + } + return true, nil +} diff --git a/pkg/x/path/filepathx/path_test.go b/pkg/x/path/filepathx/path_test.go index ca8782d942..c5b098f21d 100644 --- a/pkg/x/path/filepathx/path_test.go +++ b/pkg/x/path/filepathx/path_test.go @@ -1,9 +1,12 @@ package filepathx_test import ( + "fmt" + "strings" "testing" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" + "github.com/stretchr/testify/require" ) func TestJailJoin(t *testing.T) { @@ -61,3 +64,23 @@ func TestJailJoin(t *testing.T) { }) } } + +func TestIsSameOrContainedBy(t *testing.T) { + for _, tt := range []struct { + parent string + child string + expected bool + }{ + {"foo", "foo", true}, + {"/foo", "/foo", true}, + {"foo", "foo/bar", true}, + {"foo", "bar", false}, + } { + t.Run(fmt.Sprintf("%s: %s vs %s", t.Name(), strings.ReplaceAll(tt.parent, "/", "."), strings.ReplaceAll(tt.child, "/", ".")), func(t *testing.T) { + require := require.New(t) + b, err := filepathx.IsSameOrContainedBy(tt.parent, tt.child) + require.NoError(err) + require.Equal(tt.expected, b) + }) + } +} From 930d41c452f5a0f8c9710cb507abc5d0f3b99a63 Mon Sep 17 00:00:00 2001 From: opencloudeu Date: Fri, 31 Jul 2026 23:16:44 +0000 Subject: [PATCH 04/27] [tx] updated from transifex --- .bingo/go-xgettext.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.bingo/go-xgettext.mod b/.bingo/go-xgettext.mod index 8e3c6210b5..b14946a0b8 100644 --- a/.bingo/go-xgettext.mod +++ b/.bingo/go-xgettext.mod @@ -3,3 +3,5 @@ module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT go 1.23.4 require github.com/gosexy/gettext v0.0.0-20160830220431-74466a0a0c4a // go-xgettext + +require github.com/jessevdk/go-flags v1.6.1 // indirect From ccb177c2e4f0b161f515c3b6611e50dfc80a8f1a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:43:33 +0000 Subject: [PATCH 05/27] build(deps): bump github.com/beevik/etree from 1.6.0 to 1.7.0 Bumps [github.com/beevik/etree](https://github.com/beevik/etree) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/beevik/etree/releases) - [Changelog](https://github.com/beevik/etree/blob/main/RELEASE_NOTES.md) - [Commits](https://github.com/beevik/etree/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: github.com/beevik/etree dependency-version: 1.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../github.com/beevik/etree/RELEASE_NOTES.md | 24 ++++ vendor/github.com/beevik/etree/etree.go | 48 +++++--- vendor/github.com/beevik/etree/helpers.go | 114 ++++++++++++++++++ vendor/github.com/beevik/etree/path.go | 16 ++- vendor/modules.txt | 2 +- 7 files changed, 187 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 82ce8b7126..38187d0266 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/MicahParks/keyfunc/v2 v2.1.0 github.com/Nerzal/gocloak/v13 v13.9.0 github.com/bbalet/stopwords v1.0.0 - github.com/beevik/etree v1.6.0 + github.com/beevik/etree v1.7.0 github.com/blevesearch/bleve/v2 v2.6.0 github.com/cenkalti/backoff v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.20.0 diff --git a/go.sum b/go.sum index d4d95c24ab..86ad20f141 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo= github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc= -github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE= -github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc= +github.com/beevik/etree v1.7.0 h1:xjBk9O4p4x7D1YajePjfLzdaFC4/uYUENA7P0pv6gXA= +github.com/beevik/etree v1.7.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= diff --git a/vendor/github.com/beevik/etree/RELEASE_NOTES.md b/vendor/github.com/beevik/etree/RELEASE_NOTES.md index 96fe151389..7a01758c44 100644 --- a/vendor/github.com/beevik/etree/RELEASE_NOTES.md +++ b/vendor/github.com/beevik/etree/RELEASE_NOTES.md @@ -1,3 +1,27 @@ +Release 1.7.0 +============= + +**Changes** + +**Breaking changes** + +* To address a security issue, it was necessary to add a `MaxDepth` option to + `ReadSettings` to limit the depth of XML trees during parsing. A generous + default value of 1024 was chosen to avoid breaking most existing code. + However, if your code is processing XML hierarchies with a depth greater + than 1024, you will need to assign your `Document` a `ReadSettings` that has + a `MaxDepth` set to a higher value. + +**Security Fixes** + +* Limited the depth of XML trees processed by all `ReadFrom` functions during + parsing. +* Fixed a `CompilePath` index-out-of-range panic that could be caused by a + missing path filter key. +* Sanitized the contents of XML text, comment, ProcInst and Directive tokens + provided by the user. + + Release 1.6.0 ============= diff --git a/vendor/github.com/beevik/etree/etree.go b/vendor/github.com/beevik/etree/etree.go index bfe1f06e82..0fd1e7ed67 100644 --- a/vendor/github.com/beevik/etree/etree.go +++ b/vendor/github.com/beevik/etree/etree.go @@ -13,6 +13,7 @@ import ( "errors" "io" "iter" + "maps" "os" "slices" "strings" @@ -27,6 +28,10 @@ const ( // ErrXML is returned when XML parsing fails due to incorrect formatting. var ErrXML = errors.New("etree: invalid XML format") +// ErrMaxDepth is returned when the depth of the XML tree being read exceeds +// the maximum depth allowed by ReadSettings.MaxDepth. +var ErrMaxDepth = errors.New("etree: XML tree exceeds maximum depth") + // cdataPrefix is used to detect CDATA text when ReadSettings.PreserveCData is // true. var cdataPrefix = []byte(" maxDepth { + return r.Bytes(), ErrMaxDepth + } e := newElement(t.Name.Space, t.Name.Local, top) if settings.PreserveDuplicateAttrs || len(t.Attr) < 2 { for _, a := range t.Attr { @@ -1622,7 +1636,7 @@ func (c *CharData) Index() int { func (c *CharData) WriteTo(w Writer, s *WriteSettings) { if c.IsCData() { w.WriteString(``) } else { var m escapeMode @@ -1704,7 +1718,7 @@ func (c *Comment) Index() int { // WriteTo serialies the comment to the writer. func (c *Comment) WriteTo(w Writer, s *WriteSettings) { w.WriteString("") } @@ -1769,7 +1783,7 @@ func (d *Directive) Index() int { // WriteTo serializes the XML directive to the writer. func (d *Directive) WriteTo(w Writer, s *WriteSettings) { w.WriteString("") } @@ -1837,10 +1851,10 @@ func (p *ProcInst) Index() int { // WriteTo serializes the processing instruction to the writer. func (p *ProcInst) WriteTo(w Writer, s *WriteSettings) { w.WriteString("") } diff --git a/vendor/github.com/beevik/etree/helpers.go b/vendor/github.com/beevik/etree/helpers.go index ea789b62a7..e4e0ac1064 100644 --- a/vendor/github.com/beevik/etree/helpers.go +++ b/vendor/github.com/beevik/etree/helpers.go @@ -384,6 +384,120 @@ func escapeString(w Writer, s string, m escapeMode) { w.WriteString(s[last:]) } +// sanitizeCData writes the sanitized contents of a CDATA section to the +// writer. XML provides no way to escape the "]]>" sequence within a CDATA +// section, so any occurrence of it is split across two CDATA sections. +func sanitizeCData(w Writer, s string) { + for { + i := strings.Index(s, "]]>") + if i < 0 { + break + } + w.WriteString(s[:i+2]) + w.WriteString("]]>" sequence within a +// processing instruction, so a space is inserted between the two characters. +func sanitizeProcInst(w Writer, s string) { + for { + i := strings.Index(s, "?>") + if i < 0 { + break + } + w.WriteString(s[:i+1]) + w.WriteByte(' ') + s = s[i+1:] + } + w.WriteString(s) +} + +// sanitizeDirective writes the sanitized contents of an XML directive to the +// writer. +func sanitizeDirective(w Writer, s string) { + // The XML decoder reserves the character following "" as an unterminated + // directive. Insert a space to avoid conflicts with reserved sequences. + scan := s + if s == "" || s[0] == '-' || s[0] == '[' { + w.WriteByte(' ') + } else { + scan = s[1:] + } + + // A directive's contents may legitimately contain '<' and '>' characters, + // so write them without modification when they are balanced. + if isDirectiveBalanced(scan) { + w.WriteString(s) + return + } + + // The contents are unbalanced, so escape every character in the string. + escapeString(w, s, escapeNormal) +} + +// isDirectiveBalanced returns true if the interpreted portion of an XML +// directive's contents may be enclosed by "" without changing the +// extents of the resulting directive. +func isDirectiveBalanced(s string) bool { + var quote byte + var depth int + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case quote != 0: + if c == quote { + quote = 0 + } + case c == '\'' || c == '"': + quote = c + case c == '>': + if depth == 0 { + return false + } + depth-- + case c == '<': + if !strings.HasPrefix(s[i+1:], "!--") { + depth++ + break + } + j := strings.Index(s[i+4:], "-->") + if j < 0 { + return false + } + i += 4 + j + 2 + } + } + return quote == 0 && depth == 0 +} + func isInCharacterRange(r rune) bool { return r == 0x09 || r == 0x0A || diff --git a/vendor/github.com/beevik/etree/path.go b/vendor/github.com/beevik/etree/path.go index 21760d34d4..7e9e67a1a3 100644 --- a/vendor/github.com/beevik/etree/path.go +++ b/vendor/github.com/beevik/etree/path.go @@ -281,7 +281,11 @@ func (c *compiler) parseSegment(path string) segment { c.err = ErrPath("path has invalid filter [brackets].") break } - seg.filters = append(seg.filters, c.parseFilter(fpath[:len(fpath)-1])) + filter := c.parseFilter(fpath[:len(fpath)-1]) + if c.err != ErrPath("") { + break + } + seg.filters = append(seg.filters, filter) } return seg } @@ -320,7 +324,11 @@ func (c *compiler) parseFilter(path string) filter { // Filter contains [@attr='val'], [@attr="val"], [fn()='val'], // [fn()="val"], [tag='val'] or [tag="val"]? eqindex := strings.IndexByte(path, '=') - if eqindex >= 0 && eqindex+1 < len(path) { + if eqindex == 0 { + c.err = ErrPath("path contains a filter expression with no key.") + return nil + } + if eqindex > 0 && eqindex+1 < len(path) { quote := path[eqindex+1] if quote == '\'' || quote == '"' { rindex := nextIndex(path, quote, eqindex+2) @@ -334,6 +342,10 @@ func (c *compiler) parseFilter(path string) filter { switch { case key[0] == '@': + if len(key) == 1 { + c.err = ErrPath("path contains a filter expression with no key.") + return nil + } return newFilterAttrVal(key[1:], value) case strings.HasSuffix(key, "()"): name := key[:len(key)-2] diff --git a/vendor/modules.txt b/vendor/modules.txt index 6c17e2b6eb..e99af0830f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -106,7 +106,7 @@ github.com/asaskevich/govalidator # github.com/bbalet/stopwords v1.0.0 ## explicit github.com/bbalet/stopwords -# github.com/beevik/etree v1.6.0 +# github.com/beevik/etree v1.7.0 ## explicit; go 1.23.0 github.com/beevik/etree # github.com/beorn7/perks v1.0.1 From 944adaaeb2b333d21addf8a2b0a67ee3f5fff552 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:44:14 +0000 Subject: [PATCH 06/27] build(deps): bump golang.org/x/net from 0.56.0 to 0.57.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.56.0 to 0.57.0. - [Commits](https://github.com/golang/net/compare/v0.56.0...v0.57.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-version: 0.57.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 8 +-- vendor/golang.org/x/crypto/argon2/argon2.go | 35 ++++++------ .../golang.org/x/crypto/ssh/agent/forward.go | 2 + .../golang.org/x/crypto/ssh/agent/server.go | 53 ++++++++++++++++--- vendor/golang.org/x/crypto/ssh/keys.go | 36 +++++++++---- vendor/golang.org/x/crypto/ssh/messages.go | 11 +++- vendor/golang.org/x/net/bpf/doc.go | 7 +++ .../golang.org/x/net/http2/transport_wrap.go | 24 +++++---- vendor/golang.org/x/net/idna/idna.go | 6 ++- vendor/modules.txt | 4 +- 11 files changed, 136 insertions(+), 54 deletions(-) diff --git a/go.mod b/go.mod index 38187d0266..ba0442f695 100644 --- a/go.mod +++ b/go.mod @@ -102,10 +102,10 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 - golang.org/x/crypto v0.53.0 + golang.org/x/crypto v0.54.0 golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/image v0.44.0 - golang.org/x/net v0.56.0 + golang.org/x/net v0.57.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.22.0 golang.org/x/term v0.45.0 diff --git a/go.sum b/go.sum index 86ad20f141..df01393cba 100644 --- a/go.sum +++ b/go.sum @@ -1356,8 +1356,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1447,8 +1447,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/vendor/golang.org/x/crypto/argon2/argon2.go b/vendor/golang.org/x/crypto/argon2/argon2.go index 2b65ec91ac..57ab8371cb 100644 --- a/vendor/golang.org/x/crypto/argon2/argon2.go +++ b/vendor/golang.org/x/crypto/argon2/argon2.go @@ -17,8 +17,8 @@ // It uses data-independent memory access, which is preferred for password // hashing and password-based key derivation. Argon2i requires more passes over // memory than Argon2id to protect from trade-off attacks. The recommended -// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to -// use the maximum available memory. +// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive +// operations are time=3 and to use the maximum available memory. // // # Argon2id // @@ -26,11 +26,14 @@ // Argon2i and Argon2d. It uses data-independent memory access for the first // half of the first iteration over the memory and data-dependent memory access // for the rest. Argon2id is side-channel resistant and provides better brute- -// force cost savings due to time-memory tradeoffs than Argon2i. The recommended -// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to -// use the maximum available memory. +// force cost savings due to time-memory tradeoffs than Argon2i. [RFC 9106 +// Section 4] recommends time=1, memory=2*1024*1024 KiB (2 GiB), and threads=4 +// as the first recommended option. If much less memory is available, it +// recommends time=3, memory=64*1024 KiB (64 MiB), and threads=4 as the second +// recommended option. // // [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf +// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4 // [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 package argon2 @@ -59,9 +62,9 @@ const ( // // key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32) // -// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number. -// If using that amount of memory (32 MB) is not possible in some contexts then -// the time parameter can be increased to compensate. +// The example above uses time=3 and memory=32*1024. Argon2i generally +// requires more passes over memory than Argon2id. If in doubt, prefer IDKey +// and its Argon2id parameter recommendations. // // The time parameter specifies the number of passes over the memory and the // memory parameter specifies the size of the memory in KiB. For example @@ -69,8 +72,6 @@ const ( // adjusted to the number of available CPUs. The cost parameters should be // increased as memory latency and CPU parallelism increases. Remember to get a // good random salt. -// -// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen) } @@ -83,20 +84,20 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3 // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // -// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32) +// key := argon2.IDKey([]byte("some password"), salt, 1, 2*1024*1024, 4, 32) // -// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number. -// If using that amount of memory (64 MB) is not possible in some contexts then -// the time parameter can be increased to compensate. +// The example above uses the first [RFC 9106 Section 4] recommended option. +// If much less memory is available, the second recommended option is time=3, +// memory=64*1024 KiB (64 MiB), and threads=4. // // The time parameter specifies the number of passes over the memory and the // memory parameter specifies the size of the memory in KiB. For example -// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be -// adjusted to the numbers of available CPUs. The cost parameters should be +// memory=2*1024*1024 sets the memory cost to ~2 GiB. The number of threads can +// be adjusted to the numbers of available CPUs. The cost parameters should be // increased as memory latency and CPU parallelism increases. Remember to get a // good random salt. // -// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3 +// [RFC 9106 Section 4]: https://www.rfc-editor.org/rfc/rfc9106.html#section-4 func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen) } diff --git a/vendor/golang.org/x/crypto/ssh/agent/forward.go b/vendor/golang.org/x/crypto/ssh/agent/forward.go index fd24ba900d..5e7a0ea40d 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/forward.go +++ b/vendor/golang.org/x/crypto/ssh/agent/forward.go @@ -41,6 +41,7 @@ func ForwardToAgent(client *ssh.Client, keyring Agent) error { continue } go ssh.DiscardRequests(reqs) + go io.Copy(io.Discard, channel.Stderr()) go func() { ServeAgent(keyring, channel) channel.Close() @@ -72,6 +73,7 @@ func ForwardToRemote(client *ssh.Client, addr string) error { continue } go ssh.DiscardRequests(reqs) + go io.Copy(io.Discard, channel.Stderr()) go forwardUnixSocket(channel, addr) } }() diff --git a/vendor/golang.org/x/crypto/ssh/agent/server.go b/vendor/golang.org/x/crypto/ssh/agent/server.go index f05d22fb3d..782c54eb7c 100644 --- a/vendor/golang.org/x/crypto/ssh/agent/server.go +++ b/vendor/golang.org/x/crypto/ssh/agent/server.go @@ -304,19 +304,60 @@ func parseEd25519Key(req []byte) (*AddedKey, error) { return addedKey, nil } +func checkDSAParams(param *dsa.Parameters) error { + // SSH specifies FIPS 186-2, which only provided a single size + // (1024 bits) DSA key. FIPS 186-3 allows for larger key + // sizes, which would confuse SSH. + if l := param.P.BitLen(); l != 1024 { + return fmt.Errorf("ssh: unsupported DSA key size %d", l) + } + + // FIPS 186-2 specifies that Q must be exactly 160 bits. We must enforce + // this to prevent DoS attacks where an attacker sends a huge Q which makes + // verification slow. + if l := param.Q.BitLen(); l != 160 { + return fmt.Errorf("ssh: unsupported DSA sub-prime size %d", l) + } + + // The generator G is an element of the group, so it must be strictly less + // than the modulus P. + if param.G.Cmp(param.P) >= 0 { + return errors.New("ssh: DSA generator larger than modulus") + } + + // G must be positive. + if param.G.Sign() <= 0 { + return errors.New("ssh: DSA generator must be positive") + } + + return nil +} + func parseDSAKey(req []byte) (*AddedKey, error) { var k dsaKeyMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } + params := dsa.Parameters{ + P: k.P, + Q: k.Q, + G: k.G, + } + if err := checkDSAParams(¶ms); err != nil { + return nil, err + } + + // The public value Y must be a non-zero element of the group, i.e. strictly + // between 0 and P, to prevent a maliciously oversized Y from slowing + // signature operations. + if k.Y.Sign() <= 0 || k.Y.Cmp(k.P) >= 0 { + return nil, errors.New("agent: DSA public value Y out of range") + } + priv := &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ - Parameters: dsa.Parameters{ - P: k.P, - Q: k.Q, - G: k.G, - }, - Y: k.Y, + Parameters: params, + Y: k.Y, }, X: k.X, } diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index 334861b7f1..64377715e1 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -182,14 +182,19 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey } hosts := string(keyFields[0]) - // keyFields[1] contains the key type (e.g. “ssh-rsa”). - // However, that information is duplicated inside the - // base64-encoded key and so is ignored here. + // keyFields[1] contains the key type (e.g. "ssh-rsa"). This information + // is duplicated within the base64-encoded key blob. As OpenSSH's + // sshkey_read does, we verify that the declared key type matches the + // type embedded in the key blob. + wantType := string(keyFields[1]) key := bytes.Join(keyFields[2:], []byte(" ")) if pubKey, comment, err = parseAuthorizedKey(key); err != nil { return "", nil, nil, "", nil, err } + if pubKey.Type() != wantType { + return "", nil, nil, "", nil, fmt.Errorf("ssh: known hosts key type mismatch: human-readable type %q, encoded type %q", wantType, pubKey.Type()) + } return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil } @@ -228,10 +233,17 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str } if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - return out, comment, options, rest, nil - } else { - lastErr = err + // The first field contains the declared key type. As OpenSSH's + // sshkey_read does, we verify that it matches the type embedded in + // the key blob. Without this check, a single-token option (e.g. + // "restrict") appearing in the key type position could be silently + // discarded along with its intended effect. + if string(in[:i]) == out.Type() { + return out, comment, options, rest, nil + } + err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type()) } + lastErr = err // No key type recognised. Maybe there's an options field at // the beginning. @@ -271,11 +283,15 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str } if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - options = candidateOptions - return out, comment, options, rest, nil - } else { - lastErr = err + // As above, the declared key type (here following the options + // field) must match the type embedded in the key blob. + if string(in[:i]) == out.Type() { + options = candidateOptions + return out, comment, options, rest, nil + } + err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type()) } + lastErr = err in = rest continue diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go index ab22c3d38d..de86f71cf4 100644 --- a/vendor/golang.org/x/crypto/ssh/messages.go +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -44,7 +44,16 @@ type disconnectMsg struct { } func (d *disconnectMsg) Error() string { - return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message) + return fmt.Sprintf("ssh: disconnect, reason %d: %q", d.Reason, sanitizeString(d.Message)) +} + +func sanitizeString(s string) string { + return strings.Map(func(r rune) rune { + if r == '\t' || (r >= ' ' && r < 0x7f) { + return r + } + return -1 + }, s) } // See RFC 4253, section 7.1. diff --git a/vendor/golang.org/x/net/bpf/doc.go b/vendor/golang.org/x/net/bpf/doc.go index 04ec1c8ab5..1ea566b7ff 100644 --- a/vendor/golang.org/x/net/bpf/doc.go +++ b/vendor/golang.org/x/net/bpf/doc.go @@ -49,6 +49,13 @@ to extensions, which are essentially calls to kernel utility functions. Currently, the only extensions supported by this package are the Linux packet filter extensions. +# Security Considerations + +The implementation of the BPF VM in this package is suitable for +testing BPF programs. It aims for consistency with other BPF VM +implementations, but divergence in behavior is not considered a +security issue. + # Examples This packet filter selects all ARP packets. diff --git a/vendor/golang.org/x/net/http2/transport_wrap.go b/vendor/golang.org/x/net/http2/transport_wrap.go index eab2e6b073..534e77ab96 100644 --- a/vendor/golang.org/x/net/http2/transport_wrap.go +++ b/vendor/golang.org/x/net/http2/transport_wrap.go @@ -55,7 +55,7 @@ type transportConfig struct { // Registered is called by net/http.Transport.RegisterProtocol, // to let us know that it understands the registration mechanism we're using. func (t transportConfig) Registered(t1 *http.Transport) { - t.t.t1 = t1 + t.t.lazyt1 = t1 } func (t transportConfig) DisableCompression() bool { @@ -145,29 +145,30 @@ func (t transportConfig) DialFromContext(ctx context.Context, network, address s type transportInternal struct { initOnce sync.Once - t1 *http.Transport + lazyt1 *http.Transport } -func (t *Transport) init() { +func (t *Transport) init() *http.Transport { t.initOnce.Do(func() { - if t.t1 != nil { + if t.lazyt1 != nil { return } t1 := &http.Transport{} t.configure(t1) }) + return t.lazyt1 } func (t *Transport) configure(t1 *http.Transport) { t1.RegisterProtocol("http/2", transportConfig{t}) - // tr2.t1 is set by transportConfig.Registered. - if t.t1 != t1 { + // tr2.lazyt1 is set by transportConfig.Registered. + if t.lazyt1 != t1 { panic("http2: net/http does not support this version of x/net/http2") } } func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { - t.init() + t1 := t.init() if req.URL.Scheme == "http" && !t.AllowHTTP { return nil, errors.New("http2: unencrypted HTTP/2 not enabled") @@ -188,22 +189,23 @@ func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res ctx := context.WithValue(req.Context(), http2TransportContextKey{}, t) req = req.WithContext(ctx) - return t.t1.RoundTrip(req) + return t1.RoundTrip(req) } func (t *Transport) closeIdleConnections() { - t.init() - t.t1.CloseIdleConnections() + t1 := t.init() + t1.CloseIdleConnections() } func (t *Transport) newUserClientConn(c net.Conn) (*ClientConn, error) { + t1 := t.init() // http.Transport's NewClientConn doesn't provide a supported way to create // a connection from a net.Conn. (This might be useful to add in the future?) // We're going to craftily sneak one in via the context key, with the // scheme of "http/2" telling NewClientConn to look for it. ctx := context.WithValue(context.Background(), netConnContextKey{}, c) - nhcc, err := t.t1.NewClientConn(ctx, "http/2", "") + nhcc, err := t1.NewClientConn(ctx, "http/2", "") if err != nil { return nil, err } diff --git a/vendor/golang.org/x/net/idna/idna.go b/vendor/golang.org/x/net/idna/idna.go index 22767125bf..e2f28fed48 100644 --- a/vendor/golang.org/x/net/idna/idna.go +++ b/vendor/golang.org/x/net/idna/idna.go @@ -400,7 +400,11 @@ func (p *Profile) process(s string, toASCII bool) (string, error) { // Spec says keep the old label. continue } - if unicode16 && err == nil && len(u) > 0 && isASCII(u) { + if err == nil && len(u) > 0 && isASCII(u) { + // UTS 43 pre-revision 33 doesn't classify a xn-- label + // which contains only ASCII characters as an error, + // but that's a specification bug and a security issue. + // Always return an error in this case. err = punyError(enc) } isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight diff --git a/vendor/modules.txt b/vendor/modules.txt index e99af0830f..b2c9a37f60 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2398,7 +2398,7 @@ go.yaml.in/yaml/v2 # go.yaml.in/yaml/v3 v3.0.4 ## explicit; go 1.16 go.yaml.in/yaml/v3 -# golang.org/x/crypto v0.53.0 +# golang.org/x/crypto v0.54.0 ## explicit; go 1.25.0 golang.org/x/crypto/argon2 golang.org/x/crypto/bcrypt @@ -2456,7 +2456,7 @@ golang.org/x/image/webp golang.org/x/mod/internal/lazyregexp golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.56.0 +# golang.org/x/net v0.57.0 ## explicit; go 1.25.0 golang.org/x/net/bpf golang.org/x/net/context From b46353fd56238b5d133ee51022b18cf853533121 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 07:31:56 +0000 Subject: [PATCH 07/27] build(deps): bump github.com/gabriel-vasile/mimetype Bumps [github.com/gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) from 1.4.13 to 1.4.15. - [Release notes](https://github.com/gabriel-vasile/mimetype/releases) - [Commits](https://github.com/gabriel-vasile/mimetype/compare/v1.4.13...v1.4.15) --- updated-dependencies: - dependency-name: github.com/gabriel-vasile/mimetype dependency-version: 1.4.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../gabriel-vasile/mimetype/.golangci.yml | 7 + .../gabriel-vasile/mimetype/README.md | 11 +- .../gabriel-vasile/mimetype/codecov.yml | 1 + .../mimetype/internal/cdf/cdf.go | 667 ++++++++++++++++++ .../mimetype/internal/charset/charset.go | 22 +- .../mimetype/internal/csv/parser.go | 6 +- .../mimetype/internal/json/parser.go | 5 + .../mimetype/internal/magic/audio.go | 83 ++- .../mimetype/internal/magic/binary.go | 69 +- .../mimetype/internal/magic/font.go | 49 +- .../mimetype/internal/magic/geo.go | 9 +- .../mimetype/internal/magic/image.go | 28 +- .../mimetype/internal/magic/magic.go | 9 +- .../mimetype/internal/magic/ms_office.go | 52 +- .../mimetype/internal/magic/text.go | 229 +++--- .../mimetype/internal/magic/text_csv.go | 21 +- .../mimetype/internal/magic/video.go | 6 +- .../mimetype/internal/magic/zip.go | 83 ++- .../mimetype/internal/mp3/frame.go | 140 ++++ .../mimetype/internal/scan/bytes.go | 44 +- .../gabriel-vasile/mimetype/mime.go | 20 +- .../gabriel-vasile/mimetype/mimetype.go | 12 +- .../mimetype/supported_mimes.md | 15 +- .../gabriel-vasile/mimetype/tree.go | 41 +- vendor/modules.txt | 4 +- 27 files changed, 1407 insertions(+), 232 deletions(-) create mode 100644 vendor/github.com/gabriel-vasile/mimetype/codecov.yml create mode 100644 vendor/github.com/gabriel-vasile/mimetype/internal/cdf/cdf.go create mode 100644 vendor/github.com/gabriel-vasile/mimetype/internal/mp3/frame.go diff --git a/go.mod b/go.mod index ba0442f695..6bf7dbcc40 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/davidbyttow/govips/v2 v2.18.0 github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e - github.com/gabriel-vasile/mimetype v1.4.13 + github.com/gabriel-vasile/mimetype v1.4.15 github.com/ggwhite/go-masker v1.1.0 github.com/go-chi/chi/v5 v5.3.1 github.com/go-chi/render v1.0.3 diff --git a/go.sum b/go.sum index df01393cba..fda09e95b8 100644 --- a/go.sum +++ b/go.sum @@ -350,8 +350,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= -github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI= +github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ= github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U= github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM= github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= diff --git a/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml b/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml index 5b30cd614d..835dea6fa7 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml +++ b/vendor/github.com/gabriel-vasile/mimetype/.golangci.yml @@ -7,6 +7,12 @@ linters: exclusions: presets: - std-error-handling + rules: + # Test fixtures construct CDF binary blobs from known small constants, so + # gosec's integer-overflow checks (G115) add no value there. + - path: internal/cdf/cdf_test\.go + linters: + - gosec enable: - gosec # Detects security problems. # Keep all extras disabled for now to focus on the integer overflow problem. @@ -31,6 +37,7 @@ linters: - unused - usestdlibvars # Detects the possibility to use variables/constants from the Go standard library. - usetesting # Reports uses of functions with replacement inside the testing package. + - asciicheck # https://daniel.haxx.se/blog/2025/05/16/detecting-malicious-unicode/ settings: govet: disable: diff --git a/vendor/github.com/gabriel-vasile/mimetype/README.md b/vendor/github.com/gabriel-vasile/mimetype/README.md index 9fe71ac945..b024316999 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/README.md +++ b/vendor/github.com/gabriel-vasile/mimetype/README.md @@ -13,8 +13,8 @@ Go Reference - - Go report card + + Code coverage License @@ -103,3 +103,10 @@ shows which file formats are most often misidentified and can help prioritise. When submitting a PR for detection of a new file format, please make sure to add a record to the list of testcases in [mimetype_test.go](mimetype_test.go). For complex files a record can be added in the [testdata](testdata) directory. +Code contributions must respect following rules: + - code must be test covered + - code must be formatted using the `gofmt` tool + - exported names must be documented + +**Important**: By submitting a pull request, you agree to allow the project +owner to license your work under the same license as that used by the project. diff --git a/vendor/github.com/gabriel-vasile/mimetype/codecov.yml b/vendor/github.com/gabriel-vasile/mimetype/codecov.yml new file mode 100644 index 0000000000..69cb76019a --- /dev/null +++ b/vendor/github.com/gabriel-vasile/mimetype/codecov.yml @@ -0,0 +1 @@ +comment: false diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/cdf/cdf.go b/vendor/github.com/gabriel-vasile/mimetype/internal/cdf/cdf.go new file mode 100644 index 0000000000..926cef7b0a --- /dev/null +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/cdf/cdf.go @@ -0,0 +1,667 @@ +// Package cdf implements parsing of CDF (OLE2) files. It is greatly inspired +// by src/readcdf.c from libmagic. One difference is this implementation is +// permissive of truncated inputs. See readLimit in mimetype.go for the +// reason why truncated inputs need to be handled. +// http://sc.openoffice.org/compdocfileformat.pdf +package cdf + +import ( + "bytes" + "encoding/binary" + + "github.com/gabriel-vasile/mimetype/internal/scan" +) + +type CDFType int8 + +const ( + CDFTypeGeneric CDFType = iota + CDFTypeInstaller + CDFTypeDoc + CDFTypePpt + CDFTypeXls + CDFTypeMsg +) + +// Detect parses raw as a CDF (OLE2) compound file and returns the document type +// it contains. It returns CDFTypeGeneric for input that is not a CDF file or +// whose type cannot be narrowed down. +func Detect(raw []byte) CDFType { + if len(raw) < 512 { + return CDFTypeGeneric + } + var c cdf + if !parse(raw, &c) { + return CDFTypeGeneric + } + return c.detect() +} + +// cdf holds everything we need from a CDF file to do detection. +type cdf struct { + data []byte + secSize int + shortSecSize int + minStdStream uint32 + satSecs int32s // list of SAT sector ids; usually a sub-slice of raw input + satEntries int // number of valid SAT entries reachable through satSecs + firstSSAT int32 + dirRaw []byte // directory stream bytes (entries are decoded on demand) + sst []byte // short-stream pool (root storage's stream) + sstBuilt bool // whether sst was already loaded (it is loaded lazily) + rootStreamFirst int32 // first sector of the root storage short-stream pool + rootStreamSize uint32 // size of the root storage short-stream pool + rootStorageUUID []byte +} + +// parse reads the entire on-disk structure required for type detection. It +// returns true on success and false if the header does not look like a CDF file. +// Truncated or partially malformed bodies are tolerated: sector reads degrade +// to whatever could be collected so detection can still succeed from partial data. +func parse(raw []byte, c *cdf) bool { + if len(raw) < 512 || binary.LittleEndian.Uint64(raw) != cdfMagic { + return false + } + secP2 := binary.LittleEndian.Uint16(raw[30:32]) + shortP2 := binary.LittleEndian.Uint16(raw[32:34]) + if secP2 > 20 || shortP2 > 20 { + return false + } + c.data = raw + c.secSize = 1 << secP2 + c.shortSecSize = 1 << shortP2 + c.minStdStream = binary.LittleEndian.Uint32(raw[56:60]) + if c.secSize < dirEntrySize { + return false + } + firstDirSec := readSecID(raw[48:52]) + c.firstSSAT = readSecID(raw[60:64]) + firstMSAT := readSecID(raw[68:72]) + nMSAT := binary.LittleEndian.Uint32(raw[72:76]) + masterSAT := int32s{b: raw[76 : 76+4*masterSATSize]} + + c.buildSAT(masterSAT, firstMSAT, nMSAT) + c.dirRaw = c.readLong(firstDirSec, 0) + + c.rootStreamFirst = -1 + var d dirEntry + for i, n := 0, c.dirLen(); i < n; i++ { + c.dirAt(i, &d) + if d.typ != dirTypeRootStorage || d.streamFirst < 0 { + continue + } + c.rootStorageUUID = d.storageUUID[:] + // Record where the short-stream pool lives; it is loaded lazily by + // shortStream the first time a short stream is actually read. + c.rootStreamFirst = d.streamFirst + c.rootStreamSize = d.size + break + } + return true +} + +func (c *cdf) detect() CDFType { + for _, name := range []string{"\x05SummaryInformation", "\x05DocumentSummaryInformation"} { + if t, ok := c.detectFromSummary(name); ok { + return t + } + } + var d dirEntry + for i, n := 0, c.dirLen(); i < n; i++ { + c.dirAt(i, &d) + if t, ok := lookupSection(d.nameBytes(), d.typ); ok { + return t + } + } + return CDFTypeGeneric +} + +// detectFromSummary inspects a (Doc)SummaryInformation stream and tries to +// derive a CDFType from the root-storage CLSID, the property NameOfApplication, +// and finally the names of sibling user streams. +func (c *cdf) detectFromSummary(streamName string) (CDFType, bool) { + if c.rootStorageUUID != nil && bytes.Equal(c.rootStorageUUID, msiCLSID) { + return CDFTypeInstaller, true + } + raw, ok := c.userStream(streamName) + if !ok { + return CDFTypeGeneric, false + } + if app := summaryAppName(raw); len(app) > 0 { + if t, ok := lookupSubstring(app, app2type); ok { + return t, true + } + } + for i, n := 0, c.dirLen(); i < n; i++ { + var d dirEntry + c.dirAt(i, &d) + if d.nameLen == 0 { + continue + } + if t, ok := lookupSubstring(d.nameBytes(), name2type); ok { + return t, true + } + } + return CDFTypeGeneric, true +} + +const ( + cdfMagic uint64 = 0xE11AB1A1E011CFD0 + + dirTypeUserStorage = 1 + dirTypeUserStream = 2 + dirTypeRootStorage = 5 + + dirEntrySize = 128 + masterSATSize = 109 // first 109 SAT secids live in the file header +) + +// dirEntry is a single CDF directory record. The UTF-16LE name is pre-decoded +// into an inline ASCII buffer at parse time, avoiding a per-entry heap +// allocation while keeping comparisons trivial. CDF names are at most 32 +// UTF-16 code units, so 32 bytes always suffice. +type dirEntry struct { + name [32]byte + nameLen uint8 + typ uint8 + streamFirst int32 + size uint32 + storageUUID [16]byte +} + +// nameBytes returns the decoded ASCII name without copying. +func (d *dirEntry) nameBytes() []byte { return d.name[:d.nameLen] } + +func (c *cdf) ssatAt(i int32) int32 { + for sid := c.firstSSAT; sid >= 0; { + if int(sid) >= c.satLen() { + break // SAT is truncated; stop collecting + } + buf, ok := c.sector(sid) + if !ok { + break + } + lbuf := int32(len(buf) / 4) //nolint:gosec // anything divided by 4 fits int32 + if i < lbuf { + return int32(binary.LittleEndian.Uint32(buf[4*i:])) //nolint:gosec // intentional two's-complement reinterpretation of a sector id + } + i -= lbuf + sid = c.satAt(sid) + } + return -1 +} + +// shortStream returns the root storage short-stream pool, loading it on first +// use. Detection often finishes (e.g. via the root CLSID or a long-stream +// summary) without ever reading a short stream, so building this eagerly would +// be wasted work. +func (c *cdf) shortStream() []byte { + if !c.sstBuilt { + c.sstBuilt = true + if c.rootStreamFirst >= 0 { + c.sst = c.readLong(c.rootStreamFirst, c.rootStreamSize) + } + } + return c.sst +} + +// int32s works like a slice of LE int32 and is backed by a slice of bytes. +// int32s could very well be type int32s []byte, but that would mean +// len function can be called on it. We don't want that, we always want to use +// the len method. +type int32s struct { + b []byte +} + +func (b int32s) at(i int) int32 { + //nolint:gosec // intentional two's-complement reinterpretation of a sector id + return int32(binary.LittleEndian.Uint32(b.b[4*i:])) +} +func (b int32s) len() int { + return len(b.b) / 4 +} + +// readSecID reinterprets four little-endian bytes as a signed sector id. +// Every 32-bit pattern is a valid id (values >= 0 are sector numbers, +// negatives are CDF sentinels such as -2 end-of-chain), so the conversion is +// an intentional two's-complement reinterpretation rather than an overflow. +func readSecID(b []byte) int32 { + return int32(binary.LittleEndian.Uint32(b)) //nolint:gosec // intentional two's-complement reinterpretation +} + +// satLen is the number of sector ids reachable through the SAT. +func (c *cdf) satLen() int { return c.satEntries } + +// satAt returns the i-th sector id from the SAT. Callers must ensure +// i < satLen(). The SAT is not materialized; the entry is fetched directly +// from the input by translating i into (SAT sector index, entry offset). +func (c *cdf) satAt(i int32) int32 { + perSec := c.secSize / 4 + secIdx := int(i) / perSec + entryIdx := int(i) % perSec + secID := c.satSecs.at(secIdx) + off := c.secSize*(1+int(secID)) + 4*entryIdx + return readSecID(c.data[off:]) +} + +// sector returns the bytes of long sector secid. If the file is truncated +// inside the requested sector the result is the available bytes (no padding). +// If the sector starts past EOF or secid is negative, then ok is false. +func (c *cdf) sector(secid int32) (_ []byte, ok bool) { + if secid < 0 { + return nil, false + } + off := int64(c.secSize) * (1 + int64(secid)) + if off >= int64(len(c.data)) { + return nil, false + } + // The returned sector might be truncated, + // but we still return it as best effort. + end := min(off+int64(c.secSize), int64(len(c.data))) + // If not even one int32 fits, then fail. + if end-off < 4 { + return nil, false + } + return c.data[off:end], true +} + +func (c *cdf) sectorIDs(secid int32) (int32s, bool) { + buf, ok := c.sector(secid) + if !ok { + return int32s{}, ok + } + return int32s{b: buf}, true +} + +// buildSAT records the list of SAT sector ids from the master-SAT (header) +// plus any extension blocks chained via firstMSAT. The SAT itself is not +// materialized: satAt computes the requested entry directly from c.data via +// satSecs. In the common case (no extension chain) satSecs is a zero-copy +// sub-slice of the input header. +func (c *cdf) buildSAT(masterSAT int32s, firstMSAT int32, nMSAT uint32) { + // Fast path: no extension chain. masterSAT is already a sub-slice of raw + // input; reuse it directly. + if firstMSAT < 0 || nMSAT == 0 { + c.satSecs = masterSAT + c.satEntries = c.computeSATLen() + return + } + + // Slow path: gather sector ids from the header plus the extension chain + // into a fresh buffer. Even here we only allocate space for ids (4 bytes + // each), not the full SAT contents. + maxIDs := len(c.data)/c.secSize + 1 + buf := make([]byte, 0, 4*masterSATSize) + for i := 0; i < masterSAT.len(); i++ { + if masterSAT.at(i) < 0 { + break + } + buf = append(buf, masterSAT.b[4*i:4*i+4]...) + } + perSec := c.secSize/4 - 1 + mid := firstMSAT +chain: + for j := uint32(0); j < nMSAT && mid >= 0; j++ { + msa, ok := c.sectorIDs(mid) + if !ok { + break + } + for k := 0; k < perSec; k++ { + if k >= msa.len() || msa.at(k) < 0 { + break chain + } + buf = append(buf, msa.b[4*k:4*k+4]...) + if len(buf)/4 > maxIDs { + break chain // cyclic MSAT chain; stop allocating + } + } + if perSec >= msa.len() { + break // no next-MSAT pointer available + } + mid = msa.at(perSec) + } + c.satSecs = int32s{b: buf} + c.satEntries = c.computeSATLen() +} + +// computeSATLen walks satSecs and counts how many SAT entries are actually +// reachable in c.data, stopping at the first sentinel id or sector that is not +// fully present in the file. +func (c *cdf) computeSATLen() int { + perSec := c.secSize / 4 + total := 0 + for i := 0; i < c.satSecs.len(); i++ { + sec := c.satSecs.at(i) + if sec < 0 { + break + } + off := int64(c.secSize) * (1 + int64(sec)) + if off >= int64(len(c.data)) { + break + } + avail := int64(len(c.data)) - off + if avail >= int64(c.secSize) { + total += perSec + continue + } + total += int(avail / 4) + break + } + return total +} + +// readLong reads a long-sector chain starting at sid. If length > 0 the +// result is truncated to that many bytes. On truncation or any other failure +// it returns whatever sectors were readable. +func (c *cdf) readLong(sid int32, length uint32) []byte { + // Fast path: when the chain is a single physically contiguous run of + // sectors (the common case for the directory and summary streams) the data + // is already laid out sequentially in the input, so return a sub-slice of + // it instead of allocating a buffer and copying every sector. + if sid >= 0 { + maxSec := len(c.data)/c.secSize + 1 + n, s := 0, sid + contiguous := true + for s >= 0 { + if int(s) >= c.satLen() { + break // SAT truncated; what remains is still contiguous + } + n++ + if n > maxSec { + contiguous = false // cyclic chain; let the slow path guard it + break + } + next := c.satAt(s) + if next >= 0 && int64(next) != int64(s)+1 { + contiguous = false + break + } + s = next + } + if contiguous { + off64 := int64(c.secSize) * (1 + int64(sid)) + if off64 >= int64(len(c.data)) { + return nil + } + end64 := min(off64+int64(n)*int64(c.secSize), int64(len(c.data))) + out := c.data[off64:end64] + if length > 0 && int64(length) < int64(len(out)) { + out = out[:length] + } + return out + } + } + + // Slow path: gather a fragmented chain into a fresh buffer. Real-world + // writers (MSI builders, edited Office documents) routinely produce + // non-contiguous directory and stream chains, so this fallback is required + // for correct detection on those files. + maxBytes := len(c.data) + out := make([]byte, 0, c.secSize) + for sid >= 0 { + if int(sid) >= c.satLen() { + break // SAT truncated; return what we have + } + buf, ok := c.sector(sid) + if !ok { + break + } + out = append(out, buf...) + if len(out) >= maxBytes { + break // chain longer than the file: cyclic SAT, stop + } + sid = c.satAt(sid) + } + if length > 0 && int64(length) < int64(len(out)) { + out = out[:length] + } + return out +} + +// readShort reads a short-sector chain at sid by indexing into the short-stream +// pool. On truncation or if the pool is unavailable it returns whatever was +// readable (possibly nil). +func (c *cdf) readShort(sid int32, length uint32) []byte { + sst := c.shortStream() + if sst == nil { + return nil + } + // TODO: anyway to avoid allocating and copying the bytes? + out := make([]byte, 0, c.shortSecSize) + for sid >= 0 { + off64 := int64(sid) * int64(c.shortSecSize) + if off64+int64(c.shortSecSize) > int64(len(sst)) { + break // short-stream pool truncated or sid out of range + } + off := int(off64) + out = append(out, sst[off:off+c.shortSecSize]...) + if len(out) >= len(sst) { + break // chain longer than the pool: cyclic SSAT, stop + } + sid = c.ssatAt(sid) + } + if length > 0 && int64(length) < int64(len(out)) { + out = out[:length] + } + return out +} + +// readChain dispatches to the long or short reader depending on stream size. +func (c *cdf) readChain(sid int32, length uint32) []byte { + if length < c.minStdStream && c.rootStreamFirst >= 0 { + return c.readShort(sid, length) + } + return c.readLong(sid, length) +} + +// dirLen returns the number of directory entries in dirRaw. +func (c *cdf) dirLen() int { return len(c.dirRaw) / dirEntrySize } + +// dirAt decodes the i-th directory entry into *out. Callers must ensure +// i < dirLen(). The UTF-16LE name is decoded into out.name, ASCII-style, +// stopping at the first NUL. +func (c *cdf) dirAt(i int, out *dirEntry) { + raw := c.dirRaw[i*dirEntrySize:] + nameLen := min(int(binary.LittleEndian.Uint16(raw[64:])), 64) + k := uint8(0) + for j := 0; j < nameLen/2; j++ { + // Names are ASCII; keep the low byte of each little-endian UTF-16 + // code unit and stop at the first NUL. + lo, hi := raw[2*j], raw[2*j+1] + if lo == 0 && hi == 0 { + break + } + out.name[k] = lo + k++ + } + out.nameLen = k + out.typ = raw[66] + out.streamFirst = readSecID(raw[116:120]) + out.size = binary.LittleEndian.Uint32(raw[120:]) + copy(out.storageUUID[:], raw[80:96]) +} + +// userStream finds a user stream by name and returns its bytes. +func (c *cdf) userStream(name string) ([]byte, bool) { + var d dirEntry + for i, n := 0, c.dirLen(); i < n; i++ { + c.dirAt(i, &d) + if d.typ == dirTypeUserStream && string(d.nameBytes()) == name { + buf := c.readChain(d.streamFirst, d.size) + if buf == nil { + return nil, false + } + return buf, true + } + } + return nil, false +} + +const ( + propIDNameOfApplication = 0x12 + + typeMask = 0x0fff + typeVector = 0x1000 + typeStringASCII = 0x1e + typeStringWide = 0x1f + + sectionDeclOffset = 0x1c // section declaration in property-set header +) + +// summaryAppName parses a (Doc)SummaryInformation stream and returns the +// value of property NameOfApplication (0x12) as printable ASCII, or nil if +// not present or the stream is malformed. This is the only summary property +// the detection logic ever consults. +func summaryAppName(stream []byte) []byte { + if len(stream) < sectionDeclOffset+20 { + return nil + } + sdOff := binary.LittleEndian.Uint32(stream[sectionDeclOffset+16:]) + if uint64(sdOff)+8 > uint64(len(stream)) { + return nil + } + section := stream[sdOff:] + shLen := binary.LittleEndian.Uint32(section[0:]) + nProps := binary.LittleEndian.Uint32(section[4:]) + if uint64(shLen) > uint64(len(section)) || nProps > 1<<16 || 8+8*nProps > shLen { + return nil + } + for i := uint32(0); i < nProps; i++ { + base := 8 + 8*i + id := binary.LittleEndian.Uint32(section[base:]) + if id != propIDNameOfApplication { + continue + } + off := binary.LittleEndian.Uint32(section[base+4:]) + if uint64(off)+8 > uint64(shLen) { + return nil + } + typ := binary.LittleEndian.Uint32(section[off:]) + if typ&typeVector != 0 { + return nil + } + step := uint32(0) + switch typ & typeMask { + case typeStringASCII: + step = 1 + case typeStringWide: + step = 2 + default: + return nil + } + slen := binary.LittleEndian.Uint32(section[off+4:]) + start := uint64(off) + 8 + end := start + uint64(slen)*uint64(step) + if end > uint64(shLen) { + return nil + } + return printableLowBytes(section[start:end], int(step)) + } + return nil +} + +// printableLowBytes copies the printable low byte of each step-byte unit +// in b, stopping at the first NUL. +func printableLowBytes(b []byte, step int) []byte { + out := make([]byte, 0, len(b)/step) + for i := 0; i+step <= len(b); i += step { + c := b[i] + if c == 0 { + break + } + if c >= 0x20 && c < 0x7f { + out = append(out, c) + } + } + return out +} + +// pattern is a case-insensitive substring → CDFType mapping. Entries are +// tested in order; first match wins. needle is stored upper-cased so it can be +// matched case-insensitively by scan.Bytes.Search with scan.IgnoreCase. +type pattern struct { + needle []byte + typ CDFType +} + +// app2type maps NameOfApplication values to CDFTypes. +// Mirrors app2mime[] in libmagic. Needles are upper-cased for case-insensitive +// matching via scan.IgnoreCase. +var app2type = []pattern{ + {[]byte("WORD"), CDFTypeDoc}, + {[]byte("EXCEL"), CDFTypeXls}, + {[]byte("POWERPOINT"), CDFTypePpt}, + {[]byte("ADVANCED INSTALLER"), CDFTypeInstaller}, + {[]byte("INSTALLSHIELD"), CDFTypeInstaller}, + {[]byte("MICROSOFT PATCH COMPILER"), CDFTypeInstaller}, + {[]byte("NANT"), CDFTypeInstaller}, + {[]byte("WINDOWS INSTALLER"), CDFTypeInstaller}, +} + +// name2type maps directory entry names to CDFTypes. +// Mirrors name2mime[] in libmagic. Needles are upper-cased for case-insensitive +// matching via scan.IgnoreCase. +var name2type = []pattern{ + {[]byte("BOOK"), CDFTypeXls}, + {[]byte("WORKBOOK"), CDFTypeXls}, + {[]byte("WORDDOCUMENT"), CDFTypeDoc}, + {[]byte("POWERPOINT"), CDFTypePpt}, + {[]byte("DIGITALSIGNATURE"), CDFTypeInstaller}, +} + +// lookupSubstring returns the CDFType for the first entry in t whose needle +// is a case-insensitive substring of v. Mirrors C's strcasestr semantics +// under the C locale. It allocates nothing: scan.IgnoreCase matches the +// upper-cased needle against input of either case. +func lookupSubstring(v []byte, t []pattern) (CDFType, bool) { + s := scan.Bytes(v) + for _, p := range t { + if i, _ := s.Search(p.needle, scan.IgnoreCase); i != -1 { + return p.typ, true + } + } + return CDFTypeGeneric, false +} + +// msiCLSID is the Microsoft Installer root-storage CLSID, in on-disk byte +// order (cdf_directory_t.d_storage_uuid stores two little-endian uint64s). +var msiCLSID = []byte{ + 0x84, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, +} + +// section is a (directory entry name, type) → CDFType mapping. +type section struct { + name string + typ uint8 + cdf CDFType +} + +// sectionTypes maps distinctive directory entries to CDFTypes — a flattened +// equivalent of sectioninfo[] in libmagic. Used as a fallback when no +// SummaryInformation stream is present. A slice (rather than a map) lets +// lookupSection compare entry names without allocating a string key. +var sectionTypes = []section{ + // libmagic uses application/encrypted, but that is not a registered media type. + // For now, we skip identifying that and fall-back on CDFTypeGeneric + // {"EncryptedPackage", dirTypeUserStream, CDFTypeEncrypted}, + // {"EncryptedSummary", dirTypeUserStream, CDFTypeEncrypted}, + {"Book", dirTypeUserStream, CDFTypeXls}, + {"Workbook", dirTypeUserStream, CDFTypeXls}, + {"WordDocument", dirTypeUserStream, CDFTypeDoc}, + {"PowerPoint Document", dirTypeUserStream, CDFTypePpt}, + {"__properties_version1.0", dirTypeUserStream, CDFTypeMsg}, + {"__recip_version1.0_#00000000", dirTypeUserStorage, CDFTypeMsg}, +} + +// lookupSection returns the CDFType for a directory entry whose name and type +// match a sectionTypes entry exactly. The string(name) == comparison is +// optimized by the compiler to avoid allocating. +func lookupSection(name []byte, typ uint8) (CDFType, bool) { + for _, s := range sectionTypes { + if s.typ == typ && string(name) == s.name { + return s.cdf, true + } + } + return CDFTypeGeneric, false +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/charset/charset.go b/vendor/github.com/gabriel-vasile/mimetype/internal/charset/charset.go index 3373274ad9..d17e629180 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/charset/charset.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/charset/charset.go @@ -84,19 +84,8 @@ func FromPlain(content []byte) string { break } } - hasHighBit := false - for _, c := range content { - if c >= 0x80 { - hasHighBit = true - break - } - } - if hasHighBit && utf8.Valid(content) { - return "utf-8" - } - // ASCII is a subset of UTF8. Follow W3C recommendation and replace with UTF8. - if ascii(origContent) { + if utf8.Valid(content) { return "utf-8" } @@ -123,15 +112,6 @@ func latin(content []byte) string { return "iso-8859-1" } -func ascii(content []byte) bool { - for _, b := range content { - if textChars[b] != T { - return false - } - } - return true -} - // FromXML returns the charset of an XML document. It relies on the XML // header and falls back on the plain // text content. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/csv/parser.go b/vendor/github.com/gabriel-vasile/mimetype/internal/csv/parser.go index 87ff697b9f..0cd797e18f 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/csv/parser.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/csv/parser.go @@ -12,10 +12,10 @@ import ( type Parser struct { comma byte comment byte - s scan.Bytes + s *scan.Bytes } -func NewParser(comma, comment byte, s scan.Bytes) *Parser { +func NewParser(comma, comment byte, s *scan.Bytes) *Parser { return &Parser{ comma: comma, comment: comment, @@ -55,7 +55,7 @@ func (r *Parser) CountFields(collectIndexes bool) (fields int, fieldPos []int, h if finished { return 0, nil, false } - finished = len(r.s) == 0 && len(line) == 0 + finished = len(*r.s) == 0 && len(line) == 0 if len(line) == lengthNL(line) { line = nil continue // Skip empty lines. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go b/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go index 570889b7b1..a1c6912dc3 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go @@ -10,6 +10,7 @@ const ( QueryGeo = "geo" QueryHAR = "har" QueryGLTF = "gltf" + QueryCDX = "cdx" maxRecursion = 4096 ) @@ -40,6 +41,10 @@ var queries = map[string][]query{ SearchPath: [][]byte{[]byte("asset"), []byte("version")}, SearchVals: [][]byte{[]byte(`"1.0"`), []byte(`"2.0"`)}, }}, + QueryCDX: {{ + SearchPath: [][]byte{[]byte("bomFormat")}, + SearchVals: [][]byte{[]byte(`"CycloneDX"`)}, + }}, } var parserPool = sync.Pool{ diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go index a285001709..ad48d8fb8d 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go @@ -3,6 +3,8 @@ package magic import ( "bytes" "encoding/binary" + + "github.com/gabriel-vasile/mimetype/internal/mp3" ) // Flac matches a Free Lossless Audio Codec file. @@ -51,32 +53,83 @@ func AAC(raw []byte, _ uint32) bool { return len(raw) > 1 && ((raw[0] == 0xFF && raw[1] == 0xF1) || (raw[0] == 0xFF && raw[1] == 0xF9)) } -// Mp3 matches an mp3 file. -func Mp3(raw []byte, limit uint32) bool { +// MP3 matches a .mp3 file. +func MP3(raw []byte, limit uint32) bool { if len(raw) < 3 { return false } - if bytes.HasPrefix(raw, []byte("ID3")) { - // MP3s with an ID3v2 tag will start with "ID3" - // ID3v1 tags, however appear at the end of the file. + // Any ID3v2 is reported as MP3. Not entirely correct, but the mimesniff + // standard says so. https://mimesniff.spec.whatwg.org/#matching-an-audio-or-video-type-pattern + // Despite the standard only checking for "ID3", we do more validations to + // avoid false positives. + if id3v2(raw) { return true } - // Match MP3 files without tags + // If no ID3v2 tag found, then we will look for MP3 frames, but: + // a. Layer III files are a lot more prevalent than Layer I and II. + // b. Layer I frame header has looser constraints than the others: many files + // with regularly repeating 0xFFFF bytes can be misidentified as MP3. + // c. MP3 files are composed of individual frames and those frames can have + // leading garbage bytes: if we want to find all valid MP3s, we have to do a + // linear search. #775, #310 + // d. There are file formats that contain MP3s inside: .mo3 and .swa + // + // Given a, b, c and d, this code: + // - initially tries to match by first two bytes in header + // - checks for .mo3 and .swa and disqualifies them + // - does linear search for Layer III switch binary.BigEndian.Uint16(raw[:2]) & 0xFFFE { - case 0xFFFA: - // MPEG ADTS, layer III, v1 - return true - case 0xFFF2: - // MPEG ADTS, layer III, v2 - return true - case 0xFFE2: - // MPEG ADTS, layer III, v2.5 + case 0xFFFA, 0xFFF2, 0xFFE2, // layer III: v1, v2, v2.5 + 0xFFFC, 0xFFF4, // layer II: v1, v2 + 0xFFF5: // layer I: v2 return true } + // http://lclevy.free.fr/mo3/ + if bytes.HasPrefix(raw, []byte("MO3")) { + return false + } + + // From PRONOM: + // Macromedia licensed the MP3 technology in 1995 to use in their Shockwave + // product. .swa or Shockwave Audio was originally added as a free plugin + // (Xtras) to SoundEdit 16 to export AIFF files to .swa. + // There is no media type assigned for .swa. + if bytes.HasPrefix(raw, []byte{0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00}) { + return false + } + + _, size := mp3.ExtractFrame(raw) + return size > 0 +} + +// Based on https://id3.org/Developer%20Information. +func id3v2(raw []byte) bool { + if len(raw) < 10 || !bytes.HasPrefix(raw, []byte("ID3")) { + return false + } + if raw[3] < 2 || raw[3] > 4 { // Version: ID3v2.2 - ID3v2.4. + return false + } + if raw[4] != 0 { // Revision is 0 for all versions. + return false + } + + // v2.2 uses 2 bits, v2.3 uses 3 bits and v2.4 uses 4. + // For all versions least significant 4 bits should be 0 + if raw[5]&0b1111 != 0 { + return false + } + + // Size bytes are synchsafe: most significant bit always 0. + if raw[6]&0x80 != 0 || raw[7]&0x80 != 0 || raw[8]&0x80 != 0 || raw[9]&0x80 != 0 { + return false + } - return false + size := uint32(raw[6])<<21 | uint32(raw[7])<<14 | uint32(raw[8])<<7 | uint32(raw[9]) + // Disallow too big frames, let's say 10MB. + return size > 0 && size < 10*1024*1024 } // Wav matches a Waveform Audio File Format file. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go index 37ad6a9fb1..d217968735 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/binary.go @@ -4,6 +4,7 @@ import ( "bytes" "debug/macho" "encoding/binary" + "slices" ) // Lnk matches Microsoft lnk binary format. @@ -117,13 +118,7 @@ func Dbf(raw []byte, limit uint32) bool { 0x02, 0x03, 0x04, 0x05, 0x30, 0x31, 0x32, 0x42, 0x62, 0x7B, 0x82, 0x83, 0x87, 0x8A, 0x8B, 0x8E, 0xB3, 0xCB, 0xE5, 0xF5, 0xF4, 0xFB, } - for _, b := range dbfTypes { - if raw[0] == b { - return true - } - } - - return false + return slices.Contains(dbfTypes, raw[0]) } // ElfObj matches an object file. @@ -229,3 +224,63 @@ func TzIf(raw []byte, limit uint32) bool { // Version has to be NUL (0x00), '2' (0x32) or '3' (0x33). return raw[4] == 0x00 || raw[4] == 0x32 || raw[4] == 0x33 } + +// Pyc matches a Python compiled file. +// The signatures are sourced from libmagic v5.47 +func Pyc(raw []byte, limit uint32) bool { + if len(raw) < 8 { + return false + } + + // python 1.0 through 3.7 signatures, magic/Magdir/python:13:190 + pycMagic := []uint32{ + 0x02099900, 0x03099900, 0x892e0d0a, 0x04170d0a, 0x994e0d0a, 0xfcc40d0a, + 0xfdc40d0a, 0x87c60d0a, 0x88c60d0a, 0x2aeb0d0a, 0x2beb0d0a, 0x2ded0d0a, + 0x2eed0d0a, 0x3bf20d0a, 0x3cf20d0a, 0x45f20d0a, 0x59f20d0a, 0x63f20d0a, + 0x6df20d0a, 0x6ef20d0a, 0x77f20d0a, 0x81f20d0a, 0x8bf20d0a, 0x8cf20d0a, + 0x95f20d0a, 0x9ff20d0a, 0xa9f20d0a, 0xb3f20d0a, 0xb4f20d0a, 0xc7f20d0a, + 0xd1f20d0a, 0xd2f20d0a, 0xdbf20d0a, 0xe5f20d0a, 0xeff20d0a, 0xf9f20d0a, + 0x03f30d0a, 0x04f30d0a, 0x0af30d0a, 0xb80b0d0a, 0xc20b0d0a, 0xcc0b0d0a, + 0xd60b0d0a, 0xe00b0d0a, 0xea0b0d0a, 0xf40b0d0a, 0xf50b0d0a, 0xff0b0d0a, + 0x090c0d0a, 0x130c0d0a, 0x1d0c0d0a, 0x1f0c0d0a, 0x270c0d0a, 0x3b0c0d0a, + 0x450c0d0a, 0x4f0c0d0a, 0x580c0d0a, 0x620c0d0a, 0x6c0c0d0a, 0x760c0d0a, + 0x800c0d0a, 0x8a0c0d0a, 0x940c0d0a, 0x9e0c0d0a, 0xb20c0d0a, 0xbc0c0d0a, + 0xc60c0d0a, 0xd00c0d0a, 0xda0c0d0a, 0xe40c0d0a, 0xee0c0d0a, 0xf80c0d0a, + 0x020d0d0a, 0x0c0d0d0a, 0x160d0d0a, 0x170d0d0a, 0x200d0d0a, 0x210d0d0a, + 0x2a0d0d0a, 0x2b0d0d0a, 0x2c0d0d0a, 0x2d0d0d0a, 0x2f0d0d0a, 0x300d0d0a, + 0x310d0d0a, 0x320d0d0a, 0x330d0d0a, 0x3e0d0d0a, 0x3f0d0d0a, + } + + n := binary.BigEndian.Uint32(raw) + + if slices.Contains(pycMagic, n) { + return true + } + + if raw[2] == 0x0d && raw[3] == 0x0a { + // Only two bits of flag field are currently used. + if l := binary.LittleEndian.Uint32(raw[4:]); l > 3 { + return false + } + if raw[1] == 0x0d || raw[1] == 0x0e { + return true + } + // PyPy magic numbers, magic/Magdir/python:233 + n := binary.LittleEndian.Uint16(raw) + return n == 240 || n == 256 || n == 336 || n == 384 || n == 416 + } + + return false +} + +// Pcap identifies "libpcap" capture files. +// https://www.tcpdump.org/manpages/pcap-savefile.5.html +func Pcap(raw []byte, _ uint32) bool { + if len(raw) < 4 { + return false + } + be := binary.BigEndian.Uint32(raw) + le := binary.LittleEndian.Uint32(raw) + return be == 0xa1b2c3d4 || be == 0xa1b23c4d || + le == 0xa1b2c3d4 || le == 0xa1b23c4d +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go index e1dda7cf06..9a76d5340f 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go @@ -3,6 +3,7 @@ package magic import ( "bytes" "encoding/binary" + "slices" ) // Woff matches a Web Open Font Format file. @@ -29,12 +30,13 @@ func Ttf(raw []byte, limit uint32) bool { if !bytes.HasPrefix(raw, []byte{0x00, 0x01, 0x00, 0x00}) { return false } + // We cannot rely on the first 4 bytes because of false-positives. + // We have to digg deeper into the SFNT tables. return hasSFNTTable(raw) } func hasSFNTTable(raw []byte) bool { - // 49 possible tables as explained below - if len(raw) < 16 || binary.BigEndian.Uint16(raw[4:]) >= 49 { + if len(raw) < 16 { return false } @@ -87,14 +89,45 @@ func hasSFNTTable(raw []byte) bool { 0x6e616d65, // "name" 0x6f706264, // "opbd" 0x4f532f32, // "OS/2" + // The above tables come from the original Apple TTF specification, + // but the later Microsoft specification has additional tables. + // Common tables: https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats + // Layout tables: https://learn.microsoft.com/en-us/typography/opentype/spec/chapter2 + // Even if the Microsoft specification says OpenType, the tables are + // valid for TrueType as well. + 0x47535542, // "GSUB" + 0x47504f53, // "GPOS" + 0x42415345, // "BASE" + 0x4a535446, // "JSTF" + 0x47444546, // "GDEF" + 0x4d415448, // "MATH" + 0x43424454, // "CBDT" + 0x43424c43, // "CBLC" + 0x43464620, // "CFF " + 0x43464632, // "CFF2" + 0x434f4c52, // "COLR" + 0x4350414c, // "CPAL" + 0x44534947, // "DSIG" + 0x45424454, // "EBDT" + 0x45424c43, // "EBLC" + 0x48564152, // "HVAR" + 0x4c545348, // "LTSH" + 0x4d455247, // "MERG" + 0x4d564152, // "MVAR" + 0x50434c54, // "PCLT" + 0x706f7374, // "post" + 0x70726570, // "prep" + 0x73626978, // "sbix" + 0x53544154, // "STAT" + 0x53564720, // "SVG " + 0x56444d58, // "VDMX" + 0x76686561, // "vhea" + 0x766d7478, // "vmtx" + 0x564f5247, // "VORG" + 0x56564152, // "VVAR" } ourTable := binary.BigEndian.Uint32(raw[12:16]) - for _, t := range possibleTables { - if ourTable == t { - return true - } - } - return false + return slices.Contains(possibleTables, ourTable) } // Eot matches an Embedded OpenType font file. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/geo.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/geo.go index cade91f18c..6cd479cc40 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/geo.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/geo.go @@ -3,6 +3,7 @@ package magic import ( "bytes" "encoding/binary" + "slices" ) // Shp matches a shape format file. @@ -39,13 +40,7 @@ func Shp(raw []byte, limit uint32) bool { 31, // MultiPatch } - for _, st := range shapeTypes { - if st == int(binary.LittleEndian.Uint32(raw[108:112])) { - return true - } - } - - return false + return slices.Contains(shapeTypes, int(binary.LittleEndian.Uint32(raw[108:112]))) } // Shx matches a shape index format file. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go index 3a86858684..d46faca737 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/binary" "slices" + + "github.com/gabriel-vasile/mimetype/internal/scan" ) // Png matches a Portable Network Graphics file. @@ -15,7 +17,31 @@ func Png(raw []byte, _ uint32) bool { // Apng matches an Animated Portable Network Graphics file. // https://wiki.mozilla.org/APNG_Specification func Apng(raw []byte, _ uint32) bool { - return offset(raw, []byte("acTL"), 37) + b := scan.Bytes(raw) + b.Advance(8) // the first 8 bytes matched by regular png + + // PNG chunks are composed of: + // 4 bytes: length in big endian + // 4 bytes: chunk type + // length bytes: chunk data + // 4 bytes: CRC + // + // Limit to 32, so we don't waste time on huge inputs. + // acTL chunk must come before any IDAT chunks. + // https://www.w3.org/TR/png-3/#structure + for i := 0; i < 32 && len(b) > 0; i++ { + sz, _ := b.Uint32be() + if bytes.HasPrefix(b, []byte("acTL")) { + return true + } + if bytes.HasPrefix(b, []byte("IDAT")) { + return false + } + if !b.Advance(int(sz + 8)) { + return false + } + } + return false } // Jpg matches a Joint Photographic Experts Group file. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go index 6103c12d36..f078ff3322 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/magic.go @@ -136,6 +136,11 @@ func ftyp(raw []byte, sigs ...[]byte) bool { return false } +type shebangSig struct { + sig []byte + flag scan.Flags +} + // A valid shebang starts with the "#!" characters, // followed by any number of spaces, // followed by the path to the interpreter, @@ -146,7 +151,7 @@ func ftyp(raw []byte, sigs ...[]byte) bool { // #! /usr/bin/env php // // /usr/bin/env is the interpreter, php is the first and only argument. -func shebang(b scan.Bytes, matchFlags scan.Flags, sigs ...[]byte) bool { +func shebang(b scan.Bytes, sigs ...shebangSig) bool { line := b.Line() if len(line) < 2 || line[0] != '#' || line[1] != '!' { return false @@ -154,7 +159,7 @@ func shebang(b scan.Bytes, matchFlags scan.Flags, sigs ...[]byte) bool { line = line[2:] line.TrimLWS() for _, s := range sigs { - if line.Match(s, matchFlags) != -1 { + if line.Match(s.sig, s.flag) != -1 { return true } } diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go index e689e92a36..62cced078c 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/ms_office.go @@ -3,6 +3,8 @@ package magic import ( "bytes" "encoding/binary" + + "github.com/gabriel-vasile/mimetype/internal/cdf" ) // Xlsx matches a Microsoft Excel 2007 file. @@ -47,6 +49,15 @@ func Ole(raw []byte, limit uint32) bool { // Doc matches a Microsoft Word 97-2003 file. // See: https://github.com/decalage2/oletools/blob/412ee36ae45e70f42123e835871bac956d958461/oletools/common/clsid.py func Doc(raw []byte, _ uint32) bool { + fromParsing := cdf.Detect(raw) + if fromParsing == cdf.CDFTypeDoc { + return true + } + if fromParsing != cdf.CDFTypeGeneric { + return false + } + // Fallback for inputs where the CDF directory is past the read limit: match + // the root storage CLSID, which often lies within the first sectors. clsids := [][]byte{ // Microsoft Word 97-2003 Document (Word.Document.8) {0x06, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}, @@ -55,19 +66,25 @@ func Doc(raw []byte, _ uint32) bool { // Microsoft Word Picture (Word.Picture.8) {0x07, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}, } - for _, clsid := range clsids { if matchOleClsid(raw, clsid) { return true } } - return false } // Ppt matches a Microsoft PowerPoint 97-2003 file or a PowerPoint 95 presentation. func Ppt(raw []byte, limit uint32) bool { - // Root CLSID test is the safest way to detect identify OLE, however, the format + fromParsing := cdf.Detect(raw) + if fromParsing == cdf.CDFTypePpt { + return true + } + if fromParsing != cdf.CDFTypeGeneric { + return false + } + // Fallback for inputs where the CDF directory is past the read limit. + // Root CLSID test is the safest way to identify the OLE, however, the format // often places the root CLSID at the end of the file. if matchOleClsid(raw, []byte{ 0x10, 0x8d, 0x81, 0x64, 0x9b, 0x4f, 0xcf, 0x11, @@ -94,18 +111,21 @@ func Ppt(raw []byte, limit uint32) bool { } } - if bytes.HasPrefix(raw[512:], []byte{0xFD, 0xFF, 0xFF, 0xFF}) && - raw[518] == 0x00 && raw[519] == 0x00 { - return true - } - return lin > 1152 && bytes.Contains(raw[1152:min(4096, lin)], []byte("P\x00o\x00w\x00e\x00r\x00P\x00o\x00i\x00n\x00t\x00 D\x00o\x00c\x00u\x00m\x00e\x00n\x00t")) } // Xls matches a Microsoft Excel 97-2003 file. func Xls(raw []byte, limit uint32) bool { - // Root CLSID test is the safest way to detect identify OLE, however, the format + fromParsing := cdf.Detect(raw) + if fromParsing == cdf.CDFTypeXls { + return true + } + if fromParsing != cdf.CDFTypeGeneric { + return false + } + // Fallback for inputs where the CDF directory is past the read limit. + // Root CLSID test is the safest way to identify the OLE, however, the format // often places the root CLSID at the end of the file. if matchOleClsid(raw, []byte{ 0x10, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -148,6 +168,15 @@ func Pub(raw []byte, limit uint32) bool { // Msg matches a Microsoft Outlook email file. func Msg(raw []byte, limit uint32) bool { + fromParsing := cdf.Detect(raw) + if fromParsing == cdf.CDFTypeMsg { + return true + } + if fromParsing != cdf.CDFTypeGeneric { + return false + } + // Fallback for inputs where the CDF directory does not carry the streams the + // parser keys on: match the root storage CLSID instead. return matchOleClsid(raw, []byte{ 0x0B, 0x0D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, @@ -157,10 +186,7 @@ func Msg(raw []byte, limit uint32) bool { // Msi matches a Microsoft Windows Installer file. // http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File func Msi(raw []byte, limit uint32) bool { - return matchOleClsid(raw, []byte{ - 0x84, 0x10, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, - }) + return cdf.Detect(raw) == cdf.CDFTypeInstaller } // One matches a Microsoft OneNote file. diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go index 3fa6711813..d36dc3ccd4 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go @@ -130,6 +130,14 @@ func Xfdf(raw []byte, _ uint32) bool { return xml(raw, xmlSig{[]byte(" 0 && uint64(len(in)) >= uint64(limit) + if maybeTruncated && fields < headerFields { + // Allow the last row to have any number of fields + // if the input is maybeTruncated. + // BUG: if len(input) == limit, then the input is not truncated + // but it is still allowed to have the wrong number of fields + // and it will be reported as valid CSV. + if len(s) == 0 { + break + } + } return false } if csvLines >= 10 { diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go index 23e30da2b9..a730a24543 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/video.go @@ -49,10 +49,8 @@ func isMatroskaFileTypeMatched(in []byte, flType string) bool { // The logic of search is: find first instance of \x42\x82 and then // search for given string after n bytes of above instance. func isFileTypeNamePresent(in []byte, flType string) bool { - ind, maxInd, lenIn := 0, 4096, len(in) - if lenIn < maxInd { // restricting length to 4096 - maxInd = lenIn - } + ind, lenIn := 0, len(in) + maxInd := min(4096, lenIn) ind = bytes.Index(in[:maxInd], []byte("\x42\x82")) if ind > 0 && lenIn > ind+2 { ind += 2 diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go index f3bfa2ac37..9b2611bbb6 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/magic/zip.go @@ -187,11 +187,11 @@ func msoxml(raw scan.Bytes, searchFor zipEntries, stopAfter int) bool { return false } +var zipLocalFileHeader = []byte("PK\003\004") + // next extracts the name of the next zip entry. func (i *zipIterator) next() []byte { - pk := []byte("PK\003\004") - - n := bytes.Index(i.b, pk) + n := bytes.Index(i.b, zipLocalFileHeader) if n == -1 { return nil } @@ -212,10 +212,85 @@ func (i *zipIterator) next() []byte { return i.b[:l] } +// skipZipflingerEntry tries to detect a Zipflinger virtual entry and skips it. +// The detection is based on the following properties: +// - compression method is 0 +// - CRC32 is 0 +// - compressed size is 0 +// - uncompressed size is 0 +// - file name is empty +// Returns true if it was found and skipped. +func (i *zipIterator) skipZipflingerEntry() (skipped bool) { + // Make a backup of the data so the inspection does not loses it. + b := i.b + defer func() { + // If no zipflinger was found, restore the original data. + if !skipped { + i.b = b + } + }() + + n := bytes.Index(i.b, zipLocalFileHeader) + if n == -1 { + return false + } + if !i.b.Advance(0x08) { + return false + } + + // Check compression method + if cm, ok := i.b.Uint16(); !ok || cm != 0 { + return false + } + + // Advance up to the CRC32 field + if !i.b.Advance(0x04) { + return false + } + + // Check CRC32 + if crc32, ok := i.b.Uint32(); !ok || crc32 != 0 { + return false + } + + // Check compressed size + if compressedSize, ok := i.b.Uint32(); !ok || compressedSize != 0 { + return false + } + + // Check uncompressed size + if uncompressedSize, ok := i.b.Uint32(); !ok || uncompressedSize != 0 { + return false + } + + // Check for empty file name + if l, ok := i.b.Uint16(); !ok || l != 0 { + return false + } + + // Reached a zipflinger virtual entry: skip extra data + l, ok := i.b.Uint16() + if !ok { + return false + } + + if !i.b.Advance(int(l)) { + return false + } + return true +} + // APK matches an Android Package Archive. // The source of signatures is https://github.com/file/file/blob/1778642b8ba3d947a779a36fcd81f8e807220a19/magic/Magdir/archive#L1820-L1887 func APK(raw []byte, _ uint32) bool { - return zipHas(raw, zipEntries{{ + iter := zipIterator{raw} + + // If a Zipflinger Virtual Entry is detected, then the data is considered APK + if iter.skipZipflingerEntry() { + return true + } + + return zipHas(iter.b, zipEntries{{ name: []byte("AndroidManifest.xml"), }, { name: []byte("META-INF/com/android/build/gradle/app-metadata.properties"), diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/mp3/frame.go b/vendor/github.com/gabriel-vasile/mimetype/internal/mp3/frame.go new file mode 100644 index 0000000000..e1ea6f25e7 --- /dev/null +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/mp3/frame.go @@ -0,0 +1,140 @@ +package mp3 + +import "bytes" + +// minTruncatedSyncMatches is the minimum number of confirmed successive +// header matches required to accept a candidate frame when the buffer ends +// before maxFrameSyncMatches confirmations can be performed. +const minTruncatedSyncMatches = 2 + +func ExtractFrame(b []byte) (start, size int) { + limit := min(len(b), 2048+headerSize) + for i := 0; i < limit-headerSize; i++ { + j := bytes.IndexByte(b[i:limit-headerSize], 0xFF) + if j < 0 { + break + } + i += j + hdr := header{b[i], b[i+1], b[i+2], b[i+3]} + if !hdr.valid() { + continue + } + frameBytes := hdr.frameBytes() + frameAndPad := frameBytes + hdr.padding() + + validHere := frameBytes > 0 && i+frameAndPad <= len(b) && matchFrame(b[i:]) + // When the buffer is exactly one frame, matchFrame cannot look ahead for + // a subsequent header to confirm the stream. Trust the validated header. + exact := i == 0 && frameAndPad == len(b) + if validHere || exact { + return i, frameAndPad + } + } + return 0, 0 +} + +// matchFrame confirms a candidate header by stepping forward and checking that +// subsequent headers are consistent. +func matchFrame(buf []byte) bool { + // maxFrameSyncMatches limits how many valid frames we look at. + const maxFrameSyncMatches = 10 + hdr := header{buf[0], buf[1], buf[2], buf[3]} + i := hdr.frameBytes() + hdr.padding() + for nmatch := 0; nmatch < maxFrameSyncMatches; nmatch++ { + if i+headerSize > len(buf) { + return nmatch >= minTruncatedSyncMatches + } + cmp := header{buf[i], buf[i+1], buf[i+2], buf[i+3]} + if !hdr.compatibleWith(cmp) { + return false + } + i += cmp.frameBytes() + cmp.padding() + } + return true +} + +const headerSize = 4 + +type header [headerSize]byte + +func (h header) isFreeFormat() bool { return h[2]&0xF0 == 0 } +func (h header) isMPEG1() bool { return h[1]&0x8 != 0 } +func (h header) isMPEG25() bool { return h[1]&0x10 == 0 } +func (h header) rawLayer() byte { return h[1] >> 1 & 3 } +func (h header) rawBitrate() byte { return h[2] >> 4 } +func (h header) rawSampleRate() byte { return h[2] >> 2 & 3 } +func (h header) rawEmphasis() byte { return h[3] & 0b11 } +func (h header) isFrame576() bool { return h[1]&14 == 2 } +func (h header) padding() int { + if h[2]&0x2 != 0 { + return 1 + } + return 0 +} + +// valid reports whether the four bytes form a syntactically valid MP3 header. +func (h header) valid() bool { + return h[0] == 0xff && + ((h[1]&0xF0) == 0xf0 || (h[1]&0xFE) == 0xe2) && + h.rawLayer() == 1 && // Layer III + h.rawBitrate() != 15 && // Not allowed by spec. + h.rawSampleRate() != 3 && + h.rawEmphasis() != 2 && + // The code for extracting frame size for free-format is tedious and + // free-format MP3s are extinct. + !h.isFreeFormat() +} + +// compatibleWith reports whether two headers describe frames belonging to the +// same MP3 stream — same MPEG version, layer, sample-rate index. +func (h header) compatibleWith(o header) bool { + return o.valid() && + (h[1]^o[1])&0xFE == 0 && + (h[2]^o[2])&0x0C == 0 +} + +// bitrateKbps returns the bitrate of the frame in kilobits per second. +func (h header) bitrateKbps() int { + // halfrate[mpeg1?][bitrate_idx] holds bitrate/2 in kbps. + halfrate := [2][15]uint8{ + {0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 72, 80}, + {0, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160}, + } + mpeg1 := 0 + if h.isMPEG1() { + mpeg1 = 1 + } + return 2 * int(halfrate[mpeg1][h.rawBitrate()]) +} + +// sampleRateHz returns the sampling rate of the frame in Hz. +func (h header) sampleRateHz() int { + base := [3]int{44100, 48000, 32000}[h.rawSampleRate()] + if !h.isMPEG1() { + base >>= 1 + } + if h.isMPEG25() { + base >>= 1 + } + return base +} + +// frameSamples returns the number of audio samples per channel encoded in +// the frame. +func (h header) frameSamples() int { + if h.isFrame576() { + return 576 + } + return 1152 +} + +// frameBytes returns the size of the frame body (header + side info + audio +// data, excluding padding) in bytes. +func (h header) frameBytes() int { + br := h.bitrateKbps() + sr := h.sampleRateHz() + if br == 0 || sr == 0 { + return 0 + } + return h.frameSamples() * br * 125 / sr +} diff --git a/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go b/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go index 552b4ead90..0503719f3e 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go +++ b/vendor/github.com/gabriel-vasile/mimetype/internal/scan/bytes.go @@ -122,32 +122,30 @@ func (b *Bytes) Line() Bytes { return line } -// DropLastLine drops the last incomplete line from b. -// -// mimetype limits itself to ReadLimit bytes when performing a detection. -// This means, for file formats like CSV for NDJSON, the last line of the input -// can be an incomplete line. -// If b length is less than readLimit, it means we received an incomplete file -// and proceed with dropping the last line. -func (b *Bytes) DropLastLine(readLimit uint32) { - if readLimit == 0 || uint64(len(*b)) < uint64(readLimit) { - return +func (b *Bytes) Uint16() (uint16, bool) { + if len(*b) < 2 { + return 0, false } + v := binary.LittleEndian.Uint16(*b) + *b = (*b)[2:] + return v, true +} - for i := len(*b) - 1; i > 0; i-- { - if (*b)[i] == '\n' { - *b = (*b)[:i] - return - } +func (b *Bytes) Uint32() (uint32, bool) { + if len(*b) < 4 { + return 0, false } + v := binary.LittleEndian.Uint32(*b) + *b = (*b)[4:] + return v, true } -func (b *Bytes) Uint16() (uint16, bool) { - if len(*b) < 2 { +func (b *Bytes) Uint32be() (uint32, bool) { + if len(*b) < 4 { return 0, false } - v := binary.LittleEndian.Uint16(*b) - *b = (*b)[2:] + v := binary.BigEndian.Uint32(*b) + *b = (*b)[4:] return v, true } @@ -205,10 +203,8 @@ func (b Bytes) Match(p []byte, flags Flags) int { if l == 0 { return -1 } - // If no flags, or scanning for full word at the end of pattern then - // do a fast HasPrefix check. - // For other flags it's not possible to use HasPrefix. - if flags == 0 || flags&FullWord > 0 { + // Some cases we can handle with a simple bytes.HasPrefix. + if flags == 0 || flags == FullWord { if bytes.HasPrefix(b, p) { b = b[len(p):] p = p[len(p):] @@ -232,7 +228,7 @@ func (b Bytes) Match(p []byte, flags Flags) int { return -1 } b = b[1:] - if !ByteIsWS(p[0]) { + if len(p) > 0 && !ByteIsWS(p[0]) { b.TrimLWS() } } else { diff --git a/vendor/github.com/gabriel-vasile/mimetype/mime.go b/vendor/github.com/gabriel-vasile/mimetype/mime.go index 30c41ac04c..5382fb1c8a 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/mime.go +++ b/vendor/github.com/gabriel-vasile/mimetype/mime.go @@ -23,6 +23,14 @@ type MIME struct { } // String returns the string representation of the MIME type, e.g., "application/zip". +// String return values can change between releases, for example, when [IANA] +// assigns a new media type. Use [MIME.Is] to avoid breaking changes. +// +// mtype := mimetype.Detect(zipFile) +// if mtype.String() == "application/zip" { /* Plain string comparison is brittle. */ } +// if mtype.Is("application/zip") { /* Will continue to work between releases */ } +// +// [IANA]: https://www.iana.org/assignments/media-types/media-types.xhtml func (m *MIME) String() string { return m.mime } @@ -38,17 +46,19 @@ func (m *MIME) Extension() string { // Each MIME type has a non-nil parent, except for the root MIME type. // // For example, the application/json and text/html MIME types have text/plain as -// their parent because they are text files who happen to contain JSON or HTML. +// their parent because they are text files that happen to contain JSON or HTML. // Another example is the ZIP format, which is used as container // for Microsoft Office files, EPUB files, JAR files, and others. func (m *MIME) Parent() *MIME { return m.parent } -// Is checks whether this MIME type, or any of its aliases, is equal to the +// Is checks whether this MIME type, or any of its [aliases], is equal to the // expected MIME type. MIME type equality test is done on the "type/subtype" // section, ignores any optional MIME parameters, ignores any leading and // trailing whitespace, and is case insensitive. +// +// [aliases]: https://github.com/gabriel-vasile/mimetype/blob/master/supported_mimes.md func (m *MIME) Is(expectedMIME string) bool { // Parsing is needed because some detected MIME types contain parameters // that need to be stripped for the comparison. @@ -129,7 +139,7 @@ func (m *MIME) flatten() []*MIME { // hierarchy returns an easy to read list of ancestors for m. // For example, application/json would return json>txt>root. func (m *MIME) hierarchy() string { - h := "" + var h strings.Builder for m := m; m != nil; m = m.Parent() { e := strings.TrimPrefix(m.Extension(), ".") if e == "" { @@ -142,9 +152,9 @@ func (m *MIME) hierarchy() string { e = "root" } } - h += ">" + e + h.WriteString(">" + e) } - return strings.TrimPrefix(h, ">") + return strings.TrimPrefix(h.String(), ">") } // clone creates a new MIME with the provided optional MIME parameters. diff --git a/vendor/github.com/gabriel-vasile/mimetype/mimetype.go b/vendor/github.com/gabriel-vasile/mimetype/mimetype.go index 792741732b..e6f1e77a49 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/mimetype.go +++ b/vendor/github.com/gabriel-vasile/mimetype/mimetype.go @@ -1,6 +1,6 @@ // Package mimetype uses magic number signatures to detect the MIME type of a file. // -// File formats are stored in a hierarchy with application/octet-stream at its root. +// File formats are stored in a hierarchy with "application/octet-stream" at its root. // For example, the hierarchy for HTML format is application/octet-stream -> // text/plain -> text/html. package mimetype @@ -12,14 +12,14 @@ import ( "sync/atomic" ) -const defaultLimit uint32 = 3072 +const defaultLimit uint32 = 4096 // readLimit is the maximum number of bytes from the input used when detecting. var readLimit uint32 = defaultLimit // Detect returns the MIME type found from the provided byte slice. // -// The result is always a valid MIME type, with application/octet-stream +// The result is always a valid MIME type, with "application/octet-stream" // returned when identification failed. func Detect(in []byte) *MIME { // Using atomic because readLimit can be written at the same time in other goroutine. @@ -34,7 +34,7 @@ func Detect(in []byte) *MIME { // DetectReader returns the MIME type of the provided reader. // -// The result is always a valid MIME type, with application/octet-stream +// The result is always a valid MIME type, with "application/octet-stream" // returned when identification failed with or without an error. // Any error returned is related to the reading from the input reader. // @@ -72,7 +72,7 @@ func DetectReader(r io.Reader) (*MIME, error) { // DetectFile returns the MIME type of the provided file. // -// The result is always a valid MIME type, with application/octet-stream +// The result is always a valid MIME type, with "application/octet-stream" // returned when identification failed with or without an error. // Any error returned is related to the opening and reading from the input file. func DetectFile(path string) (*MIME, error) { @@ -112,7 +112,7 @@ func SetLimit(limit uint32) { } // Extend adds detection for other file formats. -// It is equivalent to calling Extend() on the root MIME type "application/octet-stream". +// It is equivalent to calling [MIME.Extend] on the root MIME type "application/octet-stream". func Extend(detector func(raw []byte, limit uint32) bool, mime, extension string, aliases ...string) { root.Extend(detector, mime, extension, aliases...) } diff --git a/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md b/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md index 79a3617fcf..f014ff4b50 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md +++ b/vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md @@ -1,4 +1,4 @@ -## 199 Supported MIME types +## 204 Supported MIME types This file is automatically generated when running tests. Do not edit manually. Extension | MIME type
Aliases | Hierarchy @@ -42,7 +42,7 @@ Extension | MIME type
Aliases | Hierarchy **.oga** | **audio/ogg** | oga>ogg>root **.ogv** | **video/ogg** | ogv>ogg>root **.png** | **image/png** | png>root -**.png** | **image/vnd.mozilla.apng** | png>png>root +**.apng** | **image/apng**
image/vnd.mozilla.apng | apng>png>root **.jpg** | **image/jpeg** | jpg>root **.jxl** | **image/jxl** | jxl>root **.jp2** | **image/jp2** | jp2>root @@ -67,7 +67,6 @@ Extension | MIME type
Aliases | Hierarchy **.bmp** | **image/bmp**
image/x-bmp, image/x-ms-bmp | bmp>root **.123** | **application/vnd.lotus-1-2-3** | 123>root **.ico** | **image/x-icon** | ico>root -**.mp3** | **audio/mpeg**
audio/x-mpeg, audio/mp3 | mp3>root **.flac** | **audio/flac** | flac>root **.midi** | **audio/midi**
audio/mid, audio/sp-midi, audio/x-mid, audio/x-midi | midi>root **.ape** | **audio/ape** | ape>root @@ -95,7 +94,7 @@ Extension | MIME type
Aliases | Hierarchy **.webm** | **video/webm**
audio/webm | webm>root **.avi** | **video/x-msvideo**
video/avi, video/msvideo | avi>root **.flv** | **video/x-flv** | flv>root -**.mkv** | **video/x-matroska** | mkv>root +**.mkv** | **video/matroska**
video/x-matroska | mkv>root **.asf** | **video/x-ms-asf**
video/asf, video/x-ms-wmv | asf>root **.aac** | **audio/aac** | aac>root **.voc** | **audio/x-unknown** | voc>root @@ -116,7 +115,7 @@ Extension | MIME type
Aliases | Hierarchy **.shp** | **application/vnd.shp** | shp>shx>root **.dbf** | **application/x-dbf** | dbf>root **.dcm** | **application/dicom** | dcm>root -**.rar** | **application/x-rar-compressed**
application/x-rar | rar>root +**.rar** | **application/vnd.rar**
application/x-rar-compressed, application/x-rar | rar>root **.djvu** | **image/vnd.djvu** | djvu>root **.mobi** | **application/x-mobipocket-ebook** | mobi>root **.lit** | **application/x-ms-reader** | lit>root @@ -158,6 +157,9 @@ Extension | MIME type
Aliases | Hierarchy **.hlp** | **application/x-os2-hlp** | hlp>root **.fm** | **application/vnd.framemaker** | fm>root **.bufr** | **application/bufr** | bufr>root +**.pyc** | **application/x-bytecode.python** | pyc>root +**.pcap** | **application/vnd.tcpdump.pcap** | pcap>root +**.mp3** | **audio/mpeg**
audio/x-mpeg, audio/mp3 | mp3>root **.txt** | **text/plain** | txt>root **.svg** | **image/svg+xml** | svg>txt>root **.html** | **text/html** | html>txt>root @@ -176,6 +178,7 @@ Extension | MIME type
Aliases | Hierarchy **.xfdf** | **application/vnd.adobe.xfdf** | xfdf>xml>txt>root **.owl** | **application/owl+xml** | owl>xml>txt>root **.html** | **application/xhtml+xml** | html>xml>txt>root +**.xml** | **application/vnd.cyclonedx+xml** | xml>xml>txt>root **.php** | **text/x-php** | php>txt>root **.js** | **text/javascript**
application/x-javascript, application/javascript | js>txt>root **.lua** | **text/x-lua** | lua>txt>root @@ -186,6 +189,7 @@ Extension | MIME type
Aliases | Hierarchy **.geojson** | **application/geo+json** | geojson>json>txt>root **.har** | **application/json** | har>json>txt>root **.gltf** | **model/gltf+json** | gltf>json>txt>root +**.json** | **application/vnd.cyclonedx+json** | json>json>txt>root **.ndjson** | **application/x-ndjson** | ndjson>txt>root **.rtf** | **text/rtf**
application/rtf | rtf>txt>root **.srt** | **application/x-subrip**
application/x-srt, text/x-srt | srt>txt>root @@ -202,3 +206,4 @@ Extension | MIME type
Aliases | Hierarchy **.ppm** | **image/x-portable-pixmap** | ppm>txt>root **.pam** | **image/x-portable-arbitrarymap** | pam>txt>root **.eml** | **message/rfc822** | eml>txt>root +**.ged** | **text/vnd.familysearch.gedcom** | ged>txt>root diff --git a/vendor/github.com/gabriel-vasile/mimetype/tree.go b/vendor/github.com/gabriel-vasile/mimetype/tree.go index 55023baef6..bec8b43f21 100644 --- a/vendor/github.com/gabriel-vasile/mimetype/tree.go +++ b/vendor/github.com/gabriel-vasile/mimetype/tree.go @@ -19,12 +19,16 @@ var root = newMIME("application/octet-stream", "", func([]byte, uint32) bool { return true }, xpm, sevenZ, zip, pdf, fdf, ole, ps, psd, p7s, ogg, png, jpg, jxl, jp2, jpx, jpm, jxs, gif, webp, exe, elf, ar, tar, xar, bz2, fits, tiff, bmp, lotus, ico, - mp3, flac, midi, ape, musePack, amr, wav, aiff, au, mpeg, quickTime, mp4, webM, + flac, midi, ape, musePack, amr, wav, aiff, au, mpeg, quickTime, mp4, webM, avi, flv, mkv, asf, aac, voc, m3u, rmvb, gzip, class, swf, crx, ttf, woff, woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, cbor, sqlite3, dwg, nes, lnk, macho, qcp, icns, hdr, mrc, mdb, accdb, zstd, cab, rpm, xz, lzip, torrent, cpio, tzif, xcf, pat, gbr, glb, cabIS, jxr, parquet, - oneNote, chm, wpd, dxf, grib, zlib, inf, hlp, fm, bufr, + oneNote, chm, wpd, dxf, grib, zlib, inf, hlp, fm, bufr, pyc, pcap, + // MP3 is late because it does a linear search in the input. That means + // containers that embed an MP3, for example: an mp4 file, or a zip without + // compression, would pass as MP3s. + mp3, // Keep text last because it is the slowest check. text, ) @@ -82,16 +86,17 @@ var ( alias("application/x-ogg") oggAudio = newMIME("audio/ogg", ".oga", magic.OggAudio) oggVideo = newMIME("video/ogg", ".ogv", magic.OggVideo) - text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam, rfc822) - xml = newMIME("text/xml", ".xml", magic.XML, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf, xfdf, owl2, xhtml). + text = newMIME("text/plain", ".txt", magic.Text, svg, html, xml, php, js, lua, perl, python, ruby, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt, shell, netpbm, netpgm, netppm, netpam, rfc822, gedcom) + xml = newMIME("text/xml", ".xml", magic.XML, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf, xfdf, owl2, xhtml, cdxxml). alias("application/xml") xhtml = newMIME("application/xhtml+xml", ".html", magic.XHTML) - json = newMIME("application/json", ".json", magic.JSON, geoJSON, har, gltf) + json = newMIME("application/json", ".json", magic.JSON, geoJSON, har, gltf, cdxJSON) har = newMIME("application/json", ".har", magic.HAR) csv = newMIME("text/csv", ".csv", magic.CSV) tsv = newMIME("text/tab-separated-values", ".tsv", magic.TSV) geoJSON = newMIME("application/geo+json", ".geojson", magic.GeoJSON) ndJSON = newMIME("application/x-ndjson", ".ndjson", magic.NdJSON) + cdxJSON = newMIME("application/vnd.cyclonedx+json", ".json", magic.CDXJSON) html = newMIME("text/html", ".html", magic.HTML) php = newMIME("text/x-php", ".php", magic.Php) rtf = newMIME("text/rtf", ".rtf", magic.Rtf).alias("application/rtf") @@ -104,6 +109,7 @@ var ( perl = newMIME("text/x-perl", ".pl", magic.Perl) python = newMIME("text/x-python", ".py", magic.Python). alias("text/x-script.python", "application/x-python") + pyc = newMIME("application/x-bytecode.python", ".pyc", magic.Pyc) ruby = newMIME("text/x-ruby", ".rb", magic.Ruby). alias("application/x-ruby") shell = newMIME("text/x-shellscript", ".sh", magic.Shell). @@ -127,13 +133,15 @@ var ( tcx = newMIME("application/vnd.garmin.tcx+xml", ".tcx", magic.Tcx) amf = newMIME("application/x-amf", ".amf", magic.Amf) threemf = newMIME("application/vnd.ms-package.3dmanufacturing-3dmodel+xml", ".3mf", magic.Threemf) + cdxxml = newMIME("application/vnd.cyclonedx+xml", ".xml", magic.CDXXML) png = newMIME("image/png", ".png", magic.Png, apng) - apng = newMIME("image/vnd.mozilla.apng", ".png", magic.Apng) - jpg = newMIME("image/jpeg", ".jpg", magic.Jpg) - jxl = newMIME("image/jxl", ".jxl", magic.Jxl) - jp2 = newMIME("image/jp2", ".jp2", magic.Jp2) - jpx = newMIME("image/jpx", ".jpf", magic.Jpx) - jpm = newMIME("image/jpm", ".jpm", magic.Jpm). + apng = newMIME("image/apng", ".apng", magic.Apng). + alias("image/vnd.mozilla.apng") + jpg = newMIME("image/jpeg", ".jpg", magic.Jpg) + jxl = newMIME("image/jxl", ".jxl", magic.Jxl) + jp2 = newMIME("image/jp2", ".jp2", magic.Jp2) + jpx = newMIME("image/jpx", ".jpf", magic.Jpx) + jpm = newMIME("image/jpm", ".jpm", magic.Jpm). alias("video/jpm") jxs = newMIME("image/jxs", ".jxs", magic.Jxs) xpm = newMIME("image/x-xpixmap", ".xpm", magic.Xpm) @@ -156,7 +164,7 @@ var ( heifSeq = newMIME("image/heif-sequence", ".heif", magic.HeifSequence) hdr = newMIME("image/vnd.radiance", ".hdr", magic.Hdr) avif = newMIME("image/avif", ".avif", magic.AVIF) - mp3 = newMIME("audio/mpeg", ".mp3", magic.Mp3). + mp3 = newMIME("audio/mpeg", ".mp3", magic.MP3). alias("audio/x-mpeg", "audio/mp3") flac = newMIME("audio/flac", ".flac", magic.Flac) midi = newMIME("audio/midi", ".midi", magic.Midi). @@ -192,7 +200,8 @@ var ( avi = newMIME("video/x-msvideo", ".avi", magic.Avi). alias("video/avi", "video/msvideo") flv = newMIME("video/x-flv", ".flv", magic.Flv) - mkv = newMIME("video/x-matroska", ".mkv", magic.Mkv) + mkv = newMIME("video/matroska", ".mkv", magic.Mkv). + alias("video/x-matroska") asf = newMIME("video/x-ms-asf", ".asf", magic.Asf). alias("video/asf", "video/x-ms-wmv") rmvb = newMIME("application/vnd.rn-realmedia-vbr", ".rmvb", magic.Rmvb) @@ -242,8 +251,8 @@ var ( odc = newMIME("application/vnd.oasis.opendocument.chart", ".odc", magic.Odc). alias("application/x-vnd.oasis.opendocument.chart") sxc = newMIME("application/vnd.sun.xml.calc", ".sxc", magic.Sxc) - rar = newMIME("application/x-rar-compressed", ".rar", magic.RAR). - alias("application/x-rar") + rar = newMIME("application/vnd.rar", ".rar", magic.RAR). + alias("application/x-rar-compressed", "application/x-rar") djvu = newMIME("image/vnd.djvu", ".djvu", magic.DjVu) mobi = newMIME("application/x-mobipocket-ebook", ".mobi", magic.Mobi) lit = newMIME("application/x-ms-reader", ".lit", magic.Lit) @@ -294,4 +303,6 @@ var ( hlp = newMIME("application/x-os2-hlp", ".hlp", magic.Hlp) fm = newMIME("application/vnd.framemaker", ".fm", magic.FrameMaker) bufr = newMIME("application/bufr", ".bufr", magic.BUFR) + gedcom = newMIME("text/vnd.familysearch.gedcom", ".ged", magic.GEDCOM) + pcap = newMIME("application/vnd.tcpdump.pcap", ".pcap", magic.Pcap) ) diff --git a/vendor/modules.txt b/vendor/modules.txt index b2c9a37f60..fbc6d8b2d1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -430,14 +430,16 @@ github.com/felixge/httpsnoop ## explicit; go 1.23 github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify/internal -# github.com/gabriel-vasile/mimetype v1.4.13 +# github.com/gabriel-vasile/mimetype v1.4.15 ## explicit; go 1.21 github.com/gabriel-vasile/mimetype +github.com/gabriel-vasile/mimetype/internal/cdf github.com/gabriel-vasile/mimetype/internal/charset github.com/gabriel-vasile/mimetype/internal/csv github.com/gabriel-vasile/mimetype/internal/json github.com/gabriel-vasile/mimetype/internal/magic github.com/gabriel-vasile/mimetype/internal/markup +github.com/gabriel-vasile/mimetype/internal/mp3 github.com/gabriel-vasile/mimetype/internal/scan # github.com/gdexlab/go-render v1.0.1 ## explicit From 5e8363520cd8ea892506b15bc7785a195e227c62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 07:47:49 +0000 Subject: [PATCH 08/27] build(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.23.2 to 1.24.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/v1.24.1/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.23.2...v1.24.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-version: 1.24.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 +- go.sum | 16 +- .../github.com/klauspost/compress/README.md | 12 + .../klauspost/compress/flate/dict_decoder.go | 24 +- .../klauspost/compress/flate/inflate.go | 92 + .../klauspost/compress/flate/inflate_gen.go | 116 +- .../klauspost/compress/huff0/build_table.go | 168 + .../compress/internal/snapref/decode.go | 2 +- .../klauspost/compress/s2/decode.go | 2 +- .../github.com/klauspost/compress/s2/dict.go | 6 +- .../klauspost/compress/s2/encode_all.go | 2 +- .../klauspost/compress/s2/hashtable_pool.go | 6 +- .../klauspost/compress/s2/reader.go | 19 +- .../compress/snappy/decode_strict.go | 130 + .../klauspost/compress/zstd/README.md | 37 +- .../klauspost/compress/zstd/dict.go | 3 +- .../klauspost/compress/zstd/enc_base.go | 28 + .../klauspost/compress/zstd/enc_best.go | 15 + .../klauspost/compress/zstd/enc_better.go | 18 + .../klauspost/compress/zstd/enc_dfast.go | 16 + .../klauspost/compress/zstd/enc_fast.go | 17 + .../klauspost/compress/zstd/enc_jobs.go | 352 +++ .../klauspost/compress/zstd/encoder.go | 210 +- .../compress/zstd/encoder_options.go | 69 +- .../compress/zstd/fse_decoder_amd64.s | 2 +- .../compress/zstd/fse_decoder_arm64.s | 153 + ...se_decoder_amd64.go => fse_decoder_asm.go} | 8 +- .../compress/zstd/fse_decoder_generic.go | 2 +- .../klauspost/compress/zstd/seqdec_amd64.go | 362 +-- .../klauspost/compress/zstd/seqdec_amd64.s | 2 +- .../klauspost/compress/zstd/seqdec_arm64.go | 70 + .../klauspost/compress/zstd/seqdec_arm64.s | 2705 +++++++++++++++++ .../klauspost/compress/zstd/seqdec_asm.go | 289 ++ .../klauspost/compress/zstd/seqdec_generic.go | 2 +- .../klauspost/compress/zstd/snappy.go | 7 +- .../golang/gddo/httputil/header/header.go | 2 +- .../client_golang/prometheus/counter.go | 11 +- .../client_golang/prometheus/desc.go | 37 +- .../prometheus/expvar_collector.go | 8 +- .../client_golang/prometheus/gauge.go | 11 +- .../prometheus/go_collector_go116.go | 122 - .../prometheus/go_collector_latest.go | 20 +- .../client_golang/prometheus/histogram.go | 29 +- .../prometheus/internal/difflib.go | 4 +- .../client_golang/prometheus/labels.go | 3 +- .../client_golang/prometheus/metric.go | 3 + .../prometheus/process_collector_darwin.go | 13 +- .../prometheus/process_collector_windows.go | 19 +- .../client_golang/prometheus/promhttp/http.go | 197 +- .../prometheus/promhttp/instrument_client.go | 12 +- .../prometheus/promhttp/instrument_server.go | 88 +- .../prometheus/promhttp/option.go | 42 +- .../client_golang/prometheus/registry.go | 63 +- .../client_golang/prometheus/summary.go | 9 +- .../client_golang/prometheus/timer.go | 10 +- .../client_golang/prometheus/vec.go | 10 +- .../client_golang/prometheus/wrap.go | 5 +- .../prometheus/common/expfmt/expfmt.go | 4 +- .../common/expfmt/openmetrics_create.go | 26 +- .../prometheus/common/expfmt/text_create.go | 4 +- .../prometheus/common/expfmt/text_parse.go | 10 + .../common/helpers/templates/time.go | 6 +- .../prometheus/common/model/labels.go | 2 +- .../prometheus/common/model/labelset.go | 13 +- .../prometheus/common/model/metric.go | 14 +- .../prometheus/common/model/time.go | 44 +- .../prometheus/common/model/value.go | 8 +- .../prometheus/common/model/value_float.go | 2 +- .../common/model/value_histogram.go | 8 +- .../prometheus/common/promslog/slog.go | 2 +- .../prometheus/procfs/Makefile.common | 84 +- vendor/github.com/prometheus/procfs/README.md | 2 +- .../github.com/prometheus/procfs/SECURITY.md | 2 +- vendor/github.com/prometheus/procfs/crypto.go | 8 +- .../github.com/prometheus/procfs/mountinfo.go | 22 +- .../prometheus/procfs/net_wireless.go | 18 +- .../prometheus/procfs/proc_cgroup.go | 2 +- vendor/modules.txt | 12 +- 78 files changed, 5126 insertions(+), 855 deletions(-) create mode 100644 vendor/github.com/klauspost/compress/huff0/build_table.go create mode 100644 vendor/github.com/klauspost/compress/snappy/decode_strict.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_jobs.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s rename vendor/github.com/klauspost/compress/zstd/{fse_decoder_amd64.go => fse_decoder_asm.go} (81%) create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_asm.go delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go diff --git a/go.mod b/go.mod index 6bf7dbcc40..8d249ec43a 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.12 - github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/client_golang v1.24.1 github.com/r3labs/sse/v2 v2.10.0 github.com/riandyrn/otelchi v0.12.3 github.com/rogpeppe/go-internal v1.15.0 @@ -255,7 +255,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/juliangruber/go-intersect v1.1.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.18.6 // indirect + github.com/klauspost/compress v1.19.1 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/kovidgoyal/go-parallel v1.1.1 // indirect @@ -325,8 +325,8 @@ require ( github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/alertmanager v0.33.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.20.1 // indirect + github.com/prometheus/common v0.70.1 // indirect + github.com/prometheus/procfs v0.21.1 // indirect github.com/prometheus/statsd_exporter v0.22.8 // indirect github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect github.com/rs/xid v1.6.0 // indirect diff --git a/go.sum b/go.sum index fda09e95b8..e497b4c827 100644 --- a/go.sum +++ b/go.sum @@ -715,8 +715,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= @@ -1016,8 +1016,8 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -1037,8 +1037,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.0.0-20170703101242-e645f4e5aaa8/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1049,8 +1049,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= -github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/statsd_exporter v0.22.8 h1:Qo2D9ZzaQG+id9i5NYNGmbf1aa/KxKbB9aKfMS+Yib0= github.com/prometheus/statsd_exporter v0.22.8/go.mod h1:/DzwbTEaFTE0Ojz5PqcSk6+PFHOPWGxdXVr6yC8eFOM= diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index fb023f2cf2..0e9f170d01 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -27,6 +27,18 @@ Use the links above for more information on each. # changelog +* Jul 1st, 2026 [1.19.0](https://github.com/klauspost/compress/releases/tag/v1.19.0) + * zstd: Add true concurrent stream encodingin https://github.com/klauspost/compress/pull/1136 + * zstd: arm64 decoder asm by @lizthegrey in https://github.com/klauspost/compress/pull/1160 + * flate: Add inflate checkpoints in https://github.com/klauspost/compress/pull/1154 + * zstd: avoid unused BuildDict encoder allocation by @snissn in https://github.com/klauspost/compress/pull/1147 + * snappy/s2: Limit length of varint in `decodedLen` by @eustas in https://github.com/klauspost/compress/pull/1148 + * gzhttp: match qvalue parameter case-insensitively (RFC 7231) by @z9z in https://github.com/klauspost/compress/pull/1149 + * zip: add NameDecoder callback for legacy encoding rewrite by @SAY-5 in https://github.com/klauspost/compress/pull/1150 + * huff0: Allow building tables from histogram in https://github.com/klauspost/compress/pull/1155 + * huff0: Allow building table from oversized histogram in https://github.com/klauspost/compress/pull/1156 + * s2sx: Clean symlink targets in https://github.com/klauspost/compress/pull/1163 + * Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 diff --git a/vendor/github.com/klauspost/compress/flate/dict_decoder.go b/vendor/github.com/klauspost/compress/flate/dict_decoder.go index cb855abc4b..37861cf6af 100644 --- a/vendor/github.com/klauspost/compress/flate/dict_decoder.go +++ b/vendor/github.com/klauspost/compress/flate/dict_decoder.go @@ -28,9 +28,10 @@ type dictDecoder struct { hist []byte // Sliding window history // Invariant: 0 <= rdPos <= wrPos <= len(hist) - wrPos int // Current output position in buffer - rdPos int // Have emitted hist[:rdPos] already - full bool // Has a full window length been written yet? + wrPos int // Current output position in buffer + rdPos int // Have emitted hist[:rdPos] already + flushed int64 // Total bytes returned by readFlush since init + full bool // Has a full window length been written yet? } // init initializes dictDecoder to have a sliding window dictionary of the given @@ -167,11 +168,22 @@ loop: return dstPos - dstBase } +// appendWindow appends the current sliding window (up to len(hist) most recent +// bytes, oldest first) to dst. +func (dd *dictDecoder) appendWindow(dst []byte) []byte { + if dd.full { + dst = append(dst, dd.hist[dd.wrPos:]...) + return append(dst, dd.hist[:dd.wrPos]...) + } + return append(dst, dd.hist[:dd.wrPos]...) +} + // readFlush returns a slice of the historical buffer that is ready to be // emitted to the user. The data returned by readFlush must be fully consumed // before calling any other dictDecoder methods. func (dd *dictDecoder) readFlush() []byte { toRead := dd.hist[dd.rdPos:dd.wrPos] + dd.flushed += int64(len(toRead)) dd.rdPos = dd.wrPos if dd.wrPos == len(dd.hist) { dd.wrPos, dd.rdPos = 0, 0 @@ -179,3 +191,9 @@ func (dd *dictDecoder) readFlush() []byte { } return toRead } + +// decoded reports the total number of bytes written into the dictionary since +// init (i.e. excluding any preset dict bytes). +func (dd *dictDecoder) decoded() int64 { + return dd.flushed + int64(dd.wrPos-dd.rdPos) +} diff --git a/vendor/github.com/klauspost/compress/flate/inflate.go b/vendor/github.com/klauspost/compress/flate/inflate.go index 6e90126db0..39dd683b2d 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate.go +++ b/vendor/github.com/klauspost/compress/flate/inflate.go @@ -342,6 +342,11 @@ type decompressor struct { final bool flushMode flushMode + cb func(InflateCheckpoint) + cp InflateCheckpoint + hasCP bool // WithResumeFrom was supplied + uncOffset int64 // baseline uncompressed offset (from a resume checkpoint) + cpBuf []byte } func (f *decompressor) nextBlock() { @@ -676,6 +681,18 @@ func (f *decompressor) finishBlock() { f.toRead = f.dict.readFlush() } + if f.cb != nil { + bitPos := f.roffset*8 - int64(f.nb) + f.cpBuf = f.dict.appendWindow(f.cpBuf[:0]) + f.cb(InflateCheckpoint{ + UncompressedOffset: f.uncOffset + f.dict.decoded(), + CompressedOffset: bitPos / 8, + Final: f.final, + BitOffset: uint8(bitPos & 7), + Window: f.cpBuf, + }) + } + f.step = nextBlock } @@ -806,6 +823,45 @@ func (f *decompressor) Reset(r io.Reader, dict []byte) error { return nil } +// ResetCP will adjust the input to the provided checkpoint. +// It is assumed the input stream is forwarded to cp.CompressedOffset. +func (f *decompressor) ResetCP(r io.Reader, cp InflateCheckpoint) error { + *f = decompressor{ + r: makeReader(r), + bits: f.bits, + codebits: f.codebits, + h1: f.h1, + h2: f.h2, + dict: f.dict, + step: nextBlock, + cpBuf: f.cpBuf, + } + return f.applyCP(cp) +} + +// applyCP seeds the decompressor state from a resume checkpoint: +// loads the sliding window, sets the absolute compressed/uncompressed +// offsets, and skips cp.BitOffset bits into the first input byte so +// the next decode aligns with the start of a deflate block. +func (f *decompressor) applyCP(cp InflateCheckpoint) error { + f.dict.init(maxMatchOffset, cp.Window) + f.roffset = cp.CompressedOffset + f.uncOffset = cp.UncompressedOffset + f.final = cp.Final + f.b = 0 + f.nb = 0 + if cp.BitOffset > 0 { + c, err := f.r.ReadByte() + if err != nil { + return noEOF(err) + } + f.roffset++ + f.b = uint32(c) >> cp.BitOffset + f.nb = 8 - uint(cp.BitOffset) + } + return nil +} + type ReaderOpt func(*decompressor) // WithPartialBlock tells decompressor to return after each block, @@ -823,6 +879,36 @@ func WithDict(dict []byte) ReaderOpt { } } +// InflateCheckpoint provides a resumable checkpoint for inflate. +type InflateCheckpoint struct { + UncompressedOffset int64 // Byte offset in the decompressed stream + CompressedOffset int64 // Byte offset in the compressed stream + Final bool // True if this is the final block + BitOffset uint8 // 0-7 bits + Window []byte // 32KB sliding window dictionary +} + +// WithEobCallback will call the provided function after each block +// with the current gzip checkpoint. +// After returning the provided window can no longer be referenced. +// The callback will not be triggered after a block is marked "final". +// The callback is not retained after Reset. +func WithEobCallback(cb func(InflateCheckpoint)) ReaderOpt { + return func(f *decompressor) { + f.cb = cb + } +} + +// WithResumeFrom will adjust the input to the provided checkpoint. +// It is assumed the input stream is forwarded to the provided offset. +// The checkpoint is removed when Reset is called. +func WithResumeFrom(cp InflateCheckpoint) ReaderOpt { + return func(f *decompressor) { + f.cp = cp + f.hasCP = true + } +} + // NewReaderOpts returns new reader with provided options func NewReaderOpts(r io.Reader, opts ...ReaderOpt) io.ReadCloser { fixedHuffmanDecoderInit() @@ -838,6 +924,12 @@ func NewReaderOpts(r io.Reader, opts ...ReaderOpt) io.ReadCloser { opt(&f) } + if f.hasCP { + if err := f.applyCP(f.cp); err != nil { + f.err = err + } + } + return &f } diff --git a/vendor/github.com/klauspost/compress/flate/inflate_gen.go b/vendor/github.com/klauspost/compress/flate/inflate_gen.go index 2b2f993f75..e80f196560 100644 --- a/vendor/github.com/klauspost/compress/flate/inflate_gen.go +++ b/vendor/github.com/klauspost/compress/flate/inflate_gen.go @@ -10,6 +10,15 @@ import ( "strings" ) +func peekBufio(fr *bufio.Reader) ([]byte, error) { + if fr.Buffered() == 0 { + if _, err := fr.Peek(1); err != nil && fr.Buffered() == 0 { + return nil, err + } + } + return fr.Peek(fr.Buffered()) +} + // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the @@ -527,6 +536,8 @@ func (f *decompressor) huffmanBufioReader() { // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict + pbuf, _ := fr.Peek(fr.Buffered()) + pos := 0 switch f.stepState { case stateInit: @@ -548,12 +559,19 @@ readLiteral: n := uint(f.hl.maxRead) for { for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - f.err = noEOF(err) - return + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -566,6 +584,7 @@ readLiteral: } if n <= fnb { if n == 0 { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") @@ -586,6 +605,7 @@ readLiteral: case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { + fr.Discard(pos) f.toRead = dict.readFlush() f.step = huffmanBufioReader f.stepState = stateInit @@ -594,6 +614,7 @@ readLiteral: } goto readLiteral case v == 256: + fr.Discard(pos) f.b, f.nb = fb, fnb f.finishBlock() return @@ -605,15 +626,22 @@ readLiteral: length = int(val.length) + 3 n := uint(val.extra) for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits n>0:", err) + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits n>0:", err) + } + f.err = err + return } - f.err = err - return } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -622,6 +650,7 @@ readLiteral: fb >>= n & regSizeMaskUint32 fnb -= n default: + fr.Discard(pos) if debugDecode { fmt.Println(v, ">= maxNumLit") } @@ -633,15 +662,22 @@ readLiteral: var dist uint32 if f.hd == nil { for fnb < 5 { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits f.nb<5:", err) + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb<5:", err) + } + f.err = err + return } - f.err = err - return } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -660,12 +696,19 @@ readLiteral: // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - f.err = noEOF(err) - return + if pos >= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + f.err = noEOF(err) + return + } } + c := pbuf[pos] + pos++ f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 @@ -678,6 +721,7 @@ readLiteral: } if n <= fnb { if n == 0 { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") @@ -701,15 +745,22 @@ readLiteral: // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { - c, err := fr.ReadByte() - if err != nil { - f.b, f.nb = fb, fnb - if debugDecode { - fmt.Println("morebits f.nb= len(pbuf) { + fr.Discard(pos) + var err error + pbuf, err = peekBufio(fr) + pos = 0 + if len(pbuf) == 0 { + f.b, f.nb = fb, fnb + if debugDecode { + fmt.Println("morebits f.nb uint32(dict.histSize()) { + fr.Discard(pos) f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) @@ -752,6 +805,7 @@ copyHistory: f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { + fr.Discard(pos) f.toRead = dict.readFlush() f.step = huffmanBufioReader // We need to continue this work f.stepState = stateDict diff --git a/vendor/github.com/klauspost/compress/huff0/build_table.go b/vendor/github.com/klauspost/compress/huff0/build_table.go new file mode 100644 index 0000000000..e3757c87e0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/build_table.go @@ -0,0 +1,168 @@ +package huff0 + +import "errors" + +// BuildCTable builds a Huffman compression table from a precomputed symbol +// histogram and installs it as the previous (reuse) table on s. +// +// After this call: +// - EstimateSize/CanUseTable can probe the table against other histograms. +// - Compress1X/Compress4X with Reuse = ReusePolicyMust will encode without +// emitting a new table header. +// - TransferCTable can hand the table to a sibling Scratch. +// +// count[i] is the number of occurrences of symbol i. The histogram must have +// at least 2 distinct non-zero symbols; ErrUseRLE is returned for a single +// symbol and an error is returned for an empty histogram. +func (s *Scratch) BuildCTable(count *[256]uint32) error { + if s == nil { + return errors.New("huff0: BuildCTable on nil Scratch") + } + if count == nil { + return errors.New("huff0: nil count passed to BuildCTable") + } + var err error + s, err = s.prepare(nil) + if err != nil { + return err + } + s.count = *count + var total, maxCount int + var symLen uint16 + for i, v := range s.count { + total += int(v) + if int(v) > maxCount { + maxCount = int(v) + } + if v != 0 { + symLen = uint16(i) + 1 + } + } + if total == 0 { + return errors.New("huff0: empty histogram") + } + if symLen < 2 || maxCount == total { + return ErrUseRLE + } + // huff0's internal rank table assumes total ≤ BlockSizeMax (it uses + // highBit32(count+1) + 1 as a rank index into a fixed-size array). + // Histograms summed across multiple blocks can exceed that; scale the + // counts down preserving the distribution. Non-zero entries round up so + // rare symbols stay representable. + if total > BlockSizeMax { + shift := uint(0) + for total>>shift > BlockSizeMax { + shift++ + } + round := uint32(1<> shift + if scaled == 0 { + scaled = 1 + } + s.count[i] = scaled + newTotal += int(scaled) + if int(scaled) > newMax { + newMax = int(scaled) + } + } + total = newTotal + maxCount = newMax + if maxCount == total { + return ErrUseRLE + } + } + s.symbolLen = symLen + s.maxCount = maxCount + s.srcLen = total + if err := s.buildCTable(); err != nil { + return err + } + if cap(s.prevTable) < len(s.cTable) { + s.prevTable = make(cTable, 0, maxSymbolValue+1) + } + s.prevTable = s.prevTable[:len(s.cTable)] + copy(s.prevTable, s.cTable) + s.prevTableLog = s.actualTableLog + // Force the next Compress* to recount from real input. + s.clearCount = true + s.maxCount = 0 + return nil +} + +// EstimateSize returns an estimated compressed payload size in bytes for the +// supplied histogram using the table currently stored in prevTable. It returns +// -1 when the table cannot encode every non-zero symbol of hist (i.e. when +// CanUseTable would return false). The estimate excludes the table header. +func (s *Scratch) EstimateSize(hist *[256]uint32) int { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return -1 + } + pt := s.prevTable + nbBits := uint32(7) + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return -1 + } + nbBits += uint32(pt[i].nBits) * v + } + return int(nbBits >> 3) +} + +// CanUseTable reports whether the table in prevTable can encode every +// non-zero symbol present in hist. +func (s *Scratch) CanUseTable(hist *[256]uint32) bool { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return false + } + pt := s.prevTable + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return false + } + } + return true +} + +// AppendTable serializes the table currently stored in prevTable (e.g. as +// installed by BuildCTable or carried over from a previous Compress call) +// into a self-delimiting zstd-style header and appends it to dst. The +// returned slice can be parsed back by ReadTable. +func (s *Scratch) AppendTable(dst []byte) ([]byte, error) { + if s == nil || len(s.prevTable) == 0 { + return dst, errors.New("huff0: AppendTable with empty table") + } + // cTable.write reads s.actualTableLog, s.symbolLen, s.huffWeight, s.fse + // and writes into s.Out. Save/restore Out so we don't disturb in-flight + // compression buffers. + saveOut := s.Out + saveTL := s.actualTableLog + saveSL := s.symbolLen + if s.fse == nil { + // Lazily init in case AppendTable is called on a fresh Scratch. + if _, err := s.prepare(nil); err != nil { + return dst, err + } + saveOut = s.Out + } + s.Out = s.Out[:0] + s.actualTableLog = s.prevTableLog + s.symbolLen = uint16(len(s.prevTable)) + if err := s.prevTable.write(s); err != nil { + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, err + } + dst = append(dst, s.Out...) + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, nil +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/decode.go b/vendor/github.com/klauspost/compress/internal/snapref/decode.go index a2c82fcd22..584b7574b2 100644 --- a/vendor/github.com/klauspost/compress/internal/snapref/decode.go +++ b/vendor/github.com/klauspost/compress/internal/snapref/decode.go @@ -31,7 +31,7 @@ func DecodedLen(src []byte) (int, error) { // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { + if n <= 0 || n > 5 || v > 0xffffffff { return 0, 0, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/s2/decode.go b/vendor/github.com/klauspost/compress/s2/decode.go index 264ffd0a9b..17abb515a5 100644 --- a/vendor/github.com/klauspost/compress/s2/decode.go +++ b/vendor/github.com/klauspost/compress/s2/decode.go @@ -35,7 +35,7 @@ func DecodedLen(src []byte) (int, error) { // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { + if n <= 0 || n > 5 || v > 0xffffffff { return 0, 0, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/s2/dict.go b/vendor/github.com/klauspost/compress/s2/dict.go index f125ad0963..f8dc652800 100644 --- a/vendor/github.com/klauspost/compress/s2/dict.go +++ b/vendor/github.com/klauspost/compress/s2/dict.go @@ -56,10 +56,12 @@ func NewDict(dict []byte) *Dict { if len(dict) < MinDictSize || len(dict) > MaxDictSize { return nil } - d.repeat = int(r) - if d.repeat > len(dict) { + // Compare as uint64: int(r) would wrap negative for r > MaxInt64, + // slipping past the bounds check and causing an OOB read in encode. + if r > uint64(len(dict)) { return nil } + d.repeat = int(r) return &d } diff --git a/vendor/github.com/klauspost/compress/s2/encode_all.go b/vendor/github.com/klauspost/compress/s2/encode_all.go index 9d12c44f38..794ec8a687 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_all.go +++ b/vendor/github.com/klauspost/compress/s2/encode_all.go @@ -981,7 +981,7 @@ searchDict: cv = load64(src, s) continue } - } else if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + } else if repeat > 0 && uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { base := s + checkRep // Extend back for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { diff --git a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go index bc7cabd5c5..ec972132b5 100644 --- a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go +++ b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go @@ -25,7 +25,7 @@ type betterTables struct { sTable [betterShortTableSize]uint32 } -var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }} +var betterTablePool = sync.Pool{New: func() any { return &betterTables{} }} // betterSnappyTables holds better-snappy compression hash tables. type betterSnappyTables struct { @@ -33,7 +33,7 @@ type betterSnappyTables struct { sTable [betterShortTableSize]uint32 } -var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }} +var betterSnappyTablePool = sync.Pool{New: func() any { return &betterSnappyTables{} }} // bestTables holds best compression hash tables. type bestTables struct { @@ -41,7 +41,7 @@ type bestTables struct { sTable [bestShortTableSize]uint64 } -var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }} +var bestTablePool = sync.Pool{New: func() any { return &bestTables{} }} // getBetterTables gets a zeroed betterTables from the pool. func getBetterTables() *betterTables { diff --git a/vendor/github.com/klauspost/compress/s2/reader.go b/vendor/github.com/klauspost/compress/s2/reader.go index 4d01c4190c..17443e2c14 100644 --- a/vendor/github.com/klauspost/compress/s2/reader.go +++ b/vendor/github.com/klauspost/compress/s2/reader.go @@ -216,13 +216,18 @@ func (r *Reader) skippable(tmp []byte, n int, allowEOF bool, id uint8) (ok bool) return r.err == nil } if rs, ok := r.r.(io.ReadSeeker); ok { - _, err := rs.Seek(int64(n), io.SeekCurrent) - if err == nil { - return true - } - if err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { - r.err = ErrCorrupt - return false + if cur, err := rs.Seek(0, io.SeekCurrent); err == nil { + if end, err := rs.Seek(0, io.SeekEnd); err == nil { + if cur+int64(n) <= end { + if _, err := rs.Seek(cur+int64(n), io.SeekStart); err == nil { + return true + } + } + if _, err := rs.Seek(cur, io.SeekStart); err != nil { + r.err = ErrCorrupt + return false + } + } } } for n > 0 { diff --git a/vendor/github.com/klauspost/compress/snappy/decode_strict.go b/vendor/github.com/klauspost/compress/snappy/decode_strict.go new file mode 100644 index 0000000000..6c28bcbbbc --- /dev/null +++ b/vendor/github.com/klauspost/compress/snappy/decode_strict.go @@ -0,0 +1,130 @@ +// Copyright 2011 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package snappy + +import "encoding/binary" + +// DecodeStrict returns the decoded form of src, like Decode. +// +// Unlike Decode, which delegates to the s2 decoder and accepts the s2 +// "repeat last offset" extension (a copy whose encoded offset is 0), +// DecodeStrict uses a strict standard-Snappy block decoder. Offset 0 is not +// valid in the standard Snappy block format, so such a copy is rejected with +// ErrCorrupt. This makes DecodeStrict match github.com/golang/snappy and the +// C++ reference decoder, at the cost of being slower than Decode. +// +// The returned slice may be a sub-slice of dst if dst was large enough to +// hold the entire decoded block. Otherwise, a newly allocated slice will be +// returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +// +// DecodeStrict handles the Snappy block format, not the Snappy stream format. +func DecodeStrict(dst, src []byte) ([]byte, error) { + v, n := binary.Uvarint(src) + if n <= 0 || n > 5 || v > 0xffffffff { + return nil, ErrCorrupt + } + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return nil, ErrTooLarge + } + dLen := int(v) + if dLen <= cap(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + if decodeStrict(dst, src[n:]) != 0 { + return nil, ErrCorrupt + } + return dst, nil +} + +func decodeStrict(dst, src []byte) int { + var d, s, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case 0x00: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { + return 1 + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { + return 1 + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { + return 1 + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { + return 1 + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length <= 0 || length > len(dst)-d || length > len(src)-s { + return 1 + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + case 0x01: + s += 2 + if uint(s) > uint(len(src)) { + return 1 + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + case 0x02: + s += 3 + if uint(s) > uint(len(src)) { + return 1 + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + case 0x03: + s += 5 + if uint(s) > uint(len(src)) { + return 1 + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + if offset <= 0 || d < offset || length > len(dst)-d { + return 1 + } + if offset >= length { + copy(dst[d:d+length], dst[d-offset:]) + d += length + continue + } + a := dst[d : d+length] + b := dst[d-offset:] + b = b[:len(a)] + for i := range a { + a[i] = b[i] + } + d += length + } + if d != len(dst) { + return 1 + } + return 0 +} diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index c11d7fa28e..a5aeeaed06 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -75,14 +75,47 @@ The above is fine for big encodes. However, whenever possible try to *reuse* the To reuse the encoder, you can use the `Reset(io.Writer)` function to change to another output. This will allow the encoder to reuse all resources and avoid wasteful allocations. -Currently stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part -of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change +By default, stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part +of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change in the future. So if you want to limit concurrency for future updates, specify the concurrency you would like. If you would like stream encoding to be done without spawning async goroutines, use `WithEncoderConcurrency(1)` which will compress input as each block is completed, blocking on writes until each has completed. +#### Parallel Stream Compression + +For maximum throughput on large streams, use `WithConcurrentBlocks(true)` together with +`WithEncoderConcurrency(n)` where n is the number of CPU cores you want to use. +This splits the input into large sections (jobs) that are compressed simultaneously by multiple goroutines, +similar to how the C zstd library does multithreaded compression. + +```Go +enc, err := zstd.NewWriter(out, + zstd.WithEncoderLevel(zstd.SpeedDefault), + zstd.WithEncoderConcurrency(runtime.GOMAXPROCS(0)), + zstd.WithConcurrentBlocks(true), +) +``` + +Each non-first job receives an overlap prefix from the previous job for match context, +so compression ratio is only marginally affected. Output is flushed in order, +producing a valid single-frame zstd stream. + +Benchmark on 1.8GB GOB stream (AMD Ryzen 9 9950X): + +| Level | 1 thread | 4 threads | 16 threads | 1T ratio | 16T ratio | +|---------|:----------:|:------------------:|:-------------------:|:--------:|:---------:| +| fastest | 783 MB/s | 2950 MB/s (3.8×) | 6939 MB/s (8.9×) | 12.24% | 12.26% | +| default | 728 MB/s | 2533 MB/s (3.5×) | 5340 MB/s (7.3×) | 10.67% | 10.68% | +| better | 434 MB/s | 1105 MB/s (2.5×) | 2206 MB/s (5.1×) | 9.14% | 9.21% | +| best | 129 MB/s | 367 MB/s (2.8×) | 884 MB/s (6.8×) | 8.48% | 8.63% | + +Notes: +* Not compatible with dictionary encoding. +* `Flush()` dispatches the current partial job, so latency-sensitive callers can force output. +* `EncodeAll` is unaffected — it uses its own concurrency via the encoder pool. + You can specify your desired compression level using `WithEncoderLevel()` option. Currently only pre-defined compression settings can be specified. diff --git a/vendor/github.com/klauspost/compress/zstd/dict.go b/vendor/github.com/klauspost/compress/zstd/dict.go index 2ffbfdf379..4f1c4938cd 100644 --- a/vendor/github.com/klauspost/compress/zstd/dict.go +++ b/vendor/github.com/klauspost/compress/zstd/dict.go @@ -230,7 +230,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { } block := blockEnc{lowMem: false} block.init() - enc := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) + var enc encoder if o.Level != 0 { eOpts := encoderOptions{ level: o.Level, @@ -242,6 +242,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { enc = eOpts.encoder() } else { o.Level = SpeedBestCompression + enc = encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) } var ( remain [256]int diff --git a/vendor/github.com/klauspost/compress/zstd/enc_base.go b/vendor/github.com/klauspost/compress/zstd/enc_base.go index c4de134a7a..c4fea575d6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_base.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_base.go @@ -128,6 +128,34 @@ func (e *fastBase) matchlen(s, t int32, src []byte) int32 { return int32(matchLen(src[s:], src[t:])) } +// resetBasePrefix resets the encoder state and loads prefix as initial history. +// This is used for parallel job encoding where non-first jobs need overlap context. +// Rep offsets are set to defaults [1,4,8] (invalidated, matching C behavior). +func (e *fastBase) resetBasePrefix(prefix []byte) { + if e.blk == nil { + e.blk = &blockEnc{lowMem: e.lowMem} + e.blk.init() + } else { + e.blk.reset(nil) + } + e.blk.initNewEncode() + if e.crc == nil { + e.crc = xxhash.New() + } else { + e.crc.Reset() + } + e.blk.dictLitEnc = nil + e.ensureHist(len(prefix) + maxCompressedBlockSize) + // Bump cur so old table entries fall outside the window. + // When cur >= bufferReset, leave it; the first Encode call + // will shift/clear tables, preserving valid prefix entries. + if e.cur < e.bufferReset { + e.cur += e.maxMatchOff + int32(len(e.hist)) + } + e.hist = e.hist[:0] + e.hist = append(e.hist, prefix...) +} + // Reset the encoding table. func (e *fastBase) resetBase(d *dict, singleBlock bool) { if e.blk == nil { diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go index 851799322b..c71382dde6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_best.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -551,3 +551,18 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { // Reset table to initial state copy(e.table[:], e.dictTable) } + +func (e *bestFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i++ { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, bestLongTableBits, bestLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + h0 := hashLen(cv, bestShortTableBits, bestShortLen) + e.table[h0] = prevEntry{offset: i, prev: e.table[h0].offset} + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go index 3305f09248..523d57f3ad 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -1096,6 +1096,20 @@ func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *betterFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, betterLongTableBits, betterLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + e.table[hashLen(cv>>8, betterShortTableBits, betterShortLen)] = tableEntry{val: uint32(cv >> 8), offset: i + 1} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -1229,6 +1243,10 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *betterFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/betterLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index 2fb6da112b..712ba7ab58 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -1037,6 +1037,18 @@ func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoder) ResetPrefix(prefix []byte) { + e.fastEncoder.ResetPrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur + 1; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + e.longTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { allDirty := e.allDirty @@ -1102,6 +1114,10 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *doubleFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/dLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 5e104f1a48..06045e2463 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -797,6 +797,19 @@ func (e *fastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *fastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + // Index every 4th + for i := e.cur + 1; i < end; i += 4 { + cv := load6432(prefix, i-e.cur) + e.table[hashLen(cv, tableBits, tableFastHashLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -866,6 +879,10 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *fastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *fastEncoderDict) markAllShardsDirty() { e.allDirty = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_jobs.go b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go new file mode 100644 index 0000000000..95ce67ac05 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go @@ -0,0 +1,352 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + rdebug "runtime/debug" + "sync" +) + +type encJob struct { + prefix []byte // overlap from previous job (nil for first) + input []byte // job's own input data (swapped from filling) + last bool // last block of last job gets last=true + output []byte // compressed blocks (filled by worker) + err error // encoding error + done chan struct{} // closed when complete +} + +type jobState struct { + jobSize int + overlapSize int + filling []byte // accumulates input up to jobSize + nextPrefix []byte // overlap prefix prepared for the next dispatched job + + jobSeq int // next job sequence number + + jobCh chan *encJob // dispatch to workers + resultCh chan *encJob // ordered results to flusher + + workerWg sync.WaitGroup + flusherWg sync.WaitGroup + + mu sync.Mutex + flushedSeq int // last flushed sequence number + cond *sync.Cond + + flusherErr error + started bool + + inputPool sync.Pool // *[]byte buffers of jobSize cap + outputPool sync.Pool // *[]byte buffers for compressed output + overlapPool sync.Pool // *[]byte buffers for overlap prefixes +} + +func (e *Encoder) startJobWorkers() { + js := &e.state.jobs + n := e.o.concurrent + js.jobCh = make(chan *encJob, n) + js.resultCh = make(chan *encJob, n) + js.flushedSeq = 0 + js.cond = sync.NewCond(&js.mu) + + // Workers borrow encoders from the shared e.encoders pool per-job. + // Ensure the pool is initialized before any worker tries to borrow. + e.init.Do(e.initialize) + + for range n { + js.workerWg.Add(1) + go e.jobWorker() + } + js.flusherWg.Add(1) + go e.jobFlusher() + js.started = true +} + +func (e *Encoder) jobWorker() { + js := &e.state.jobs + defer js.workerWg.Done() + for job := range js.jobCh { + enc := <-e.encoders + e.compressJob(enc, job) + e.encoders <- enc + close(job.done) + } +} + +func (e *Encoder) compressJob(enc encoder, job *encJob) { + defer func() { + if r := recover(); r != nil { + job.err = fmt.Errorf("panic in parallel job: %v", r) + rdebug.PrintStack() + } + }() + + if len(job.prefix) > 0 { + enc.ResetPrefix(job.prefix) + } else { + enc.Reset(nil, false) + } + + data := job.input + if len(data) == 0 && job.last { + blk := enc.Block() + blk.reset(nil) + blk.last = true + blk.encodeRaw(nil) + job.output = append(job.output, blk.output...) + return + } + + blk := enc.Block() + for len(data) > 0 { + todo := data + if len(todo) > e.o.blockSize { + todo = todo[:e.o.blockSize] + } + data = data[len(todo):] + + blk.pushOffsets() + enc.Encode(blk, todo) + blk.last = len(data) == 0 && job.last + + err := blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy) + if err != nil { + job.err = err + return + } + job.output = append(job.output, blk.output...) + blk.reset(nil) + } +} + +func (js *jobState) getInputBuf(size int) []byte { + if v := js.inputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putInputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.inputPool.Put(&b) + } +} + +func (js *jobState) getOutputBuf(size int) []byte { + if v := js.outputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putOutputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.outputPool.Put(&b) + } +} + +func (js *jobState) getOverlapBuf(size int) []byte { + if v := js.overlapPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:size] + } + } + return make([]byte, size) +} + +func (js *jobState) putOverlapBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.overlapPool.Put(&b) + } +} + +func (e *Encoder) jobFlusher() { + js := &e.state.jobs + defer js.flusherWg.Done() + for job := range js.resultCh { + <-job.done + // Worker has fully exited compressJob, so the prefix is no longer + // in use. Return it to the pool regardless of outcome. + if job.prefix != nil { + js.putOverlapBuf(job.prefix) + job.prefix = nil + } + if job.err != nil { + js.mu.Lock() + js.flusherErr = job.err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + if len(job.output) > 0 { + _, err := e.state.w.Write(job.output) + if err != nil { + js.mu.Lock() + js.flusherErr = err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + e.state.nWritten += int64(len(job.output)) + } + // Return buffers to pools. + js.putInputBuf(job.input) + js.putOutputBuf(job.output) + job.input = nil + job.output = nil + + js.mu.Lock() + js.flushedSeq++ + js.cond.Broadcast() + js.mu.Unlock() + } +} + +func (e *Encoder) shutdownJobWorkers() { + js := &e.state.jobs + if !js.started { + return + } + close(js.jobCh) + js.workerWg.Wait() + close(js.resultCh) + js.flusherWg.Wait() + js.started = false +} + +// waitAllJobs blocks until all dispatched jobs have been flushed. +func (e *Encoder) waitAllJobs() { + js := &e.state.jobs + if !js.started { + return + } + js.mu.Lock() + for js.flushedSeq < js.jobSeq && js.flusherErr == nil { + js.cond.Wait() + } + js.mu.Unlock() +} + +func (e *Encoder) dispatchJob(final bool) error { + s := &e.state + js := &s.jobs + + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + if fErr != nil { + return fErr + } + + if !s.headerWritten { + // Single-block optimization: fall through to encodeAll path. + if final && len(js.filling) > 0 && len(js.filling) <= e.o.blockSize { + s.current = e.encodeAll(s.encoder, js.filling, s.current[:0]) + var n2 int + n2, s.err = s.w.Write(s.current) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.nInput += int64(len(js.filling)) + s.current = s.current[:0] + js.filling = js.filling[:0] + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + if final && len(js.filling) == 0 && !e.o.fullZero { + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + + var tmp [maxHeaderSize]byte + fh := frameHeader{ + ContentSize: uint64(s.frameContentSize), + WindowSize: uint32(s.encoder.WindowSize(s.frameContentSize)), + SingleSegment: false, + Checksum: e.o.crc, + DictID: 0, + } + dst := fh.appendTo(tmp[:0]) + var n2 int + n2, s.err = s.w.Write(dst) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.headerWritten = true + } + + if len(js.filling) == 0 && !final { + return nil + } + + if !js.started { + e.startJobWorkers() + } + + // Estimate output size for pooled buffer. + outputEst := max(len(js.filling)/2, 512) + + job := &encJob{ + last: final, + done: make(chan struct{}), + output: js.getOutputBuf(outputEst), + } + + // Each job owns its prefix slice; the flusher returns it to the pool + // after <-job.done, so workers and dispatch never share a buffer. + if js.nextPrefix != nil { + job.prefix = js.nextPrefix + js.nextPrefix = nil + } + + // Build the next job's prefix from the tail of this job's input. + if !final && len(js.filling) > 0 { + overlapLen := min(js.overlapSize, len(js.filling)) + np := js.getOverlapBuf(overlapLen) + copy(np, js.filling[len(js.filling)-overlapLen:]) + js.nextPrefix = np + } + + // Swap filling buffer into job — zero-copy for the input data. + job.input = js.filling + js.filling = js.getInputBuf(js.jobSize) + + s.nInput += int64(len(job.input)) + js.jobSeq++ + + if final { + s.eofWritten = true + } + + js.resultCh <- job + js.jobCh <- job + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index 0f2a00a003..6ee96d8730 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -38,6 +38,7 @@ type encoder interface { WindowSize(size int64) int32 UseBlock(*blockEnc) Reset(d *dict, singleBlock bool) + ResetPrefix(prefix []byte) } type encoderState struct { @@ -60,6 +61,9 @@ type encoderState struct { wg sync.WaitGroup // This waitgroup indicates we have a block encoding/writing. wWg sync.WaitGroup + + // Parallel job state (used when concurrentBlocks is enabled). + jobs jobState } // NewWriter will create a new Zstandard encoder. @@ -74,6 +78,9 @@ func NewWriter(w io.Writer, opts ...EOption) (*Encoder, error) { return nil, err } } + if e.o.concurrentBlocks && (e.o.dict != nil || e.o.concurrent <= 1) { + e.o.concurrentBlocks = false + } if w != nil { e.Reset(w) } @@ -95,12 +102,31 @@ func (e *Encoder) initialize() { // as a new, independent stream. func (e *Encoder) Reset(w io.Writer) { s := &e.state + + if e.o.concurrentBlocks { + e.shutdownJobWorkers() + js := &s.jobs + js.jobSize = e.o.jobSize() + js.overlapSize = e.o.overlapSize() + // js.filling is allocated lazily on first Write/ReadFrom so callers + // that only use EncodeAll don't pay the (up to ~32 MB) jobSize cost. + js.filling = js.filling[:0] + if js.nextPrefix != nil { + js.putOverlapBuf(js.nextPrefix) + js.nextPrefix = nil + } + js.jobSeq = 0 + js.flushedSeq = 0 + js.flusherErr = nil + js.started = false + } + s.wg.Wait() s.wWg.Wait() if cap(s.filling) == 0 { s.filling = make([]byte, 0, e.o.blockSize) } - if e.o.concurrent > 1 { + if e.o.concurrent > 1 && !e.o.concurrentBlocks { if cap(s.current) == 0 { s.current = make([]byte, 0, e.o.blockSize) } @@ -145,6 +171,9 @@ func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error { } } hasDict := e.o.dict != nil + if e.o.concurrentBlocks && hasDict { + e.o.concurrentBlocks = false + } if hadDict != hasDict { // Dict presence changed — encoder type must be recreated. e.state.encoder = nil @@ -176,6 +205,49 @@ func (e *Encoder) Write(p []byte) (n int, err error) { if s.eofWritten { return 0, ErrEncoderClosed } + if e.o.concurrentBlocks { + return e.writeJobs(p) + } + return e.writeBlocks(p) +} + +func (e *Encoder) writeJobs(p []byte) (n int, err error) { + s := &e.state + js := &s.jobs + jobSize := js.jobSize + if cap(js.filling) == 0 && len(p) > 0 { + js.filling = make([]byte, 0, jobSize) + } + for len(p) > 0 { + if len(p)+len(js.filling) < jobSize { + if e.o.crc { + _, _ = s.encoder.CRC().Write(p) + } + js.filling = append(js.filling, p...) + return n + len(p), nil + } + add := p + if len(p)+len(js.filling) > jobSize { + add = add[:jobSize-len(js.filling)] + } + if e.o.crc { + _, _ = s.encoder.CRC().Write(add) + } + js.filling = append(js.filling, add...) + p = p[len(add):] + n += len(add) + if len(js.filling) < jobSize { + return n, nil + } + if err := e.dispatchJob(false); err != nil { + return n, err + } + } + return n, nil +} + +func (e *Encoder) writeBlocks(p []byte) (n int, err error) { + s := &e.state for len(p) > 0 { if len(p)+len(s.filling) < e.o.blockSize { if e.o.crc { @@ -374,6 +446,10 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { println("Using ReadFrom") } + if e.o.concurrentBlocks { + return e.readFromJobs(r) + } + // Flush any current writes. if len(e.state.filling) > 0 { if err := e.nextBlock(false); err != nil { @@ -387,7 +463,6 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { if e.o.crc { _, _ = e.state.encoder.CRC().Write(src[:n2]) } - // src is now the unfilled part... src = src[n2:] n += int64(n2) switch err { @@ -420,15 +495,63 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { } } +func (e *Encoder) readFromJobs(r io.Reader) (n int64, err error) { + js := &e.state.jobs + jobSize := js.jobSize + + // Flush any current filling. + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return 0, err + } + } + + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src := js.filling + for { + n2, err := r.Read(src) + if e.o.crc { + _, _ = e.state.encoder.CRC().Write(src[:n2]) + } + src = src[n2:] + n += int64(n2) + switch err { + case io.EOF: + js.filling = js.filling[:len(js.filling)-len(src)] + return n, nil + case nil: + default: + e.state.err = err + return n, err + } + if len(src) > 0 { + continue + } + if err = e.dispatchJob(false); err != nil { + return n, err + } + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src = js.filling + } +} + // Flush will send the currently written data to output // and block until everything has been written. // This should only be used on rare occasions where pushing the currently queued data is critical. func (e *Encoder) Flush() error { s := &e.state + if e.o.concurrentBlocks { + return e.flushJobs() + } if len(s.filling) > 0 { err := e.nextBlock(false) if err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -438,7 +561,6 @@ func (e *Encoder) Flush() error { s.wg.Wait() s.wWg.Wait() if s.err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -447,6 +569,20 @@ func (e *Encoder) Flush() error { return s.writeErr } +func (e *Encoder) flushJobs() error { + js := &e.state.jobs + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return err + } + } + e.waitAllJobs() + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + return fErr +} + // Close will flush the final output and close the stream. // The function will block until everything has been written. // The Encoder can still be re-used after calling this. @@ -455,12 +591,16 @@ func (e *Encoder) Close() error { if s.encoder == nil { return nil } + if e.o.concurrentBlocks { + return e.closeJobs() + } if s.w == nil { if len(s.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { return nil } return errors.New("zstd: encoder has no writer") } + err := e.nextBlock(true) if err != nil { if errors.Is(s.err, ErrEncoderClosed) { @@ -511,6 +651,68 @@ func (e *Encoder) Close() error { return s.err } +func (e *Encoder) closeJobs() error { + s := &e.state + js := &s.jobs + + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + + if s.w == nil { + if len(js.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { + return nil + } + return errors.New("zstd: encoder has no writer") + } + + if err := e.dispatchJob(true); err != nil { + e.shutdownJobWorkers() + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return err + } + + if s.frameContentSize > 0 && s.nInput != s.frameContentSize { + e.shutdownJobWorkers() + return fmt.Errorf("frame content size %d given, but %d bytes was written", s.frameContentSize, s.nInput) + } + + if s.fullFrameWritten { + e.shutdownJobWorkers() + s.err = ErrEncoderClosed + return nil + } + + e.shutdownJobWorkers() + if js.flusherErr != nil { + return js.flusherErr + } + + // Write CRC + if e.o.crc { + var tmp [4]byte + _, s.err = s.w.Write(s.encoder.AppendCRC(tmp[:0])) + s.nWritten += 4 + } + + // Add padding + if s.err == nil && e.o.pad > 0 { + add := calcSkippableFrame(s.nWritten, int64(e.o.pad)) + frame, err := skippableFrame(js.filling[:0], add, rand.Reader) + if err != nil { + return err + } + _, s.err = s.w.Write(frame) + } + if s.err == nil { + s.err = ErrEncoderClosed + return nil + } + return s.err +} + // EncodeAll will encode all input in src and append it to dst. // This function can be called concurrently, but each call will only run on a single goroutine. // If empty input is given, nothing is returned, unless WithZeroFrames is specified. diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index e217be0a17..a808149673 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -14,22 +14,23 @@ type EOption func(*encoderOptions) error // options retains accumulated state of multiple options. type encoderOptions struct { - resetOpt bool - concurrent int - level EncoderLevel - single *bool - pad int - blockSize int - windowSize int - crc bool - fullZero bool - noEntropy bool - allLitEntropy bool - customWindow bool - customALEntropy bool - customBlockSize bool - lowMem bool - dict *dict + resetOpt bool + concurrent int + level EncoderLevel + single *bool + pad int + blockSize int + windowSize int + crc bool + fullZero bool + noEntropy bool + allLitEntropy bool + customWindow bool + customALEntropy bool + customBlockSize bool + lowMem bool + dict *dict + concurrentBlocks bool } func (o *encoderOptions) setDefault() { @@ -333,6 +334,42 @@ func WithLowerEncoderMem(b bool) EOption { } } +// WithConcurrentBlocks enables job-based parallel compression for streams. +// When enabled and concurrent > 1, input is split into large sections (jobs) +// that are compressed simultaneously by multiple goroutines. +// Each non-first job receives an overlap prefix from the previous job for match context. +// Output is flushed in order, producing a valid single-frame zstd stream. +// +// Currently disabled when used with dictionary encoding. +// Cannot be changed with ResetWithOptions. +func WithConcurrentBlocks(b bool) EOption { + return func(o *encoderOptions) error { + if o.resetOpt && b != o.concurrentBlocks { + return errors.New("WithConcurrentBlocks cannot be changed on Reset") + } + o.concurrentBlocks = b + return nil + } +} + +// jobSize returns the input section size per parallel job. +func (o *encoderOptions) jobSize() int { + s := max(o.windowSize*4, 512<<10) + return s +} + +// overlapSize returns the overlap prefix size for parallel jobs. +func (o *encoderOptions) overlapSize() int { + switch o.level { + case SpeedBestCompression: + return o.windowSize / 2 + case SpeedBetterCompression: + return o.windowSize / 4 + default: + return o.windowSize / 8 + } +} + // WithEncoderDict allows to register a dictionary that will be used for the encode. // // The slice dict must be in the [dictionary format] produced by diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s index bcde398695..deeadc49eb 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s new file mode 100644 index 0000000000..77ee3913f0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s @@ -0,0 +1,153 @@ +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int +TEXT ·buildDtable_asm(SB), $0-24 + MOVD ctx+8(FP), R1 + MOVD s+0(FP), R6 + + // Load values + MOVBU 4098(R6), R2 + MOVD $0, R0 + MOVD $1, R16 + LSL R2, R16, R16 + ORR R16, R0, R0 + MOVD (R1), R3 + MOVD 16(R1), R5 + SUB $1, R0, R7 + MOVD 8(R1), R1 + MOVHU 4096(R6), R6 + + // End load values + // Init, lay down lowprob symbols + MOVD $0, R8 + JMP init_main_loop_condition + +init_main_loop: + ADD R8<<1, R1, R15 + MOVH (R15), R9 + AND $0xffff, R9, R15 + MOVD $-1, R16 + AND $0xffff, R16, R16 + CMP R16, R15 + BNE do_not_update_high_threshold + ADD R7<<3, R5, R15 + MOVB R8, 1(R15) + SUB $1, R7, R7 + MOVD $0x0000000000000001, R9 + +do_not_update_high_threshold: + ADD R8<<1, R3, R15 + MOVH R9, (R15) + ADD $1, R8, R8 + +init_main_loop_condition: + CMP R6, R8 + BLT init_main_loop + + // Spread symbols + // Calculate table step + MOVD R0, R8 + LSR $0x01, R8, R8 + MOVD R0, R9 + LSR $0x03, R9, R9 + ADD R9, R8, R8 + ADD $3, R8, R8 + + // Fill add bits values + SUB $1, R0, R9 + MOVD $0, R10 + MOVD $0, R11 + JMP spread_main_loop_condition + +spread_main_loop: + MOVD $0, R12 + ADD R11<<1, R1, R15 + MOVH (R15), R13 + JMP spread_inner_loop_condition + +spread_inner_loop: + ADD R10<<3, R5, R15 + MOVB R11, 1(R15) + +adjust_position: + ADD R8, R10, R10 + AND R9, R10, R10 + CMP R7, R10 + BGT adjust_position + ADD $1, R12, R12 + +spread_inner_loop_condition: + CMP R13, R12 + BLT spread_inner_loop + ADD $1, R11, R11 + +spread_main_loop_condition: + CMP R6, R11 + BLT spread_main_loop + TST R10, R10 + BEQ spread_check_ok + MOVD ctx+8(FP), R0 + MOVD R10, 24(R0) + MOVD $+1, R16 + MOVD R16, ret+16(FP) + RET + +spread_check_ok: + // Build Decoding table + MOVD $0, R6 + +build_table_main_table: + ADD R6<<3, R5, R15 + MOVBU 1(R15), R1 + ADD R1<<1, R3, R15 + MOVHU (R15), R7 + ADD $1, R7, R8 + ADD R1<<1, R3, R15 + MOVH R8, (R15) + MOVD R7, R8 + CLZ R8, R16 + MOVD $63, R8 + SUB R16, R8, R8 + MOVD R2, R1 + SUB R8, R1, R1 + LSL R1, R7, R7 + SUB R0, R7, R7 + ADD R6<<3, R5, R15 + MOVB R1, (R15) + ADD R6<<3, R5, R15 + MOVH R7, 2(R15) + CMP R0, R7 + BLE build_table_check1_ok + MOVD ctx+8(FP), R1 + MOVD R7, 24(R1) + MOVD R0, 32(R1) + MOVD $+2, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check1_ok: + AND $0xff, R1, R15 + AND $0xff, R1, R16 + TST R16, R15 + BNE build_table_check2_ok + AND $0xffff, R7, R15 + AND $0xffff, R6, R16 + CMP R16, R15 + BNE build_table_check2_ok + MOVD ctx+8(FP), R0 + MOVD R7, 24(R0) + MOVD R6, 32(R0) + MOVD $+3, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check2_ok: + ADD $1, R6, R6 + CMP R0, R6 + BLT build_table_main_table + MOVD $+0, R16 + MOVD R16, ret+16(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go similarity index 81% rename from vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go rename to vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go index b8c8607b5d..4ffc7e3c9f 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go @@ -1,4 +1,4 @@ -//go:build amd64 && !appengine && !noasm && gc +//go:build (amd64 || arm64) && !appengine && !noasm && gc package zstd @@ -6,6 +6,10 @@ import ( "fmt" ) +// buildDtable_asm is generated by _generate/gen_fse.go and lowered to each +// architecture (amd64 by goasm, arm64 by the avo arm64 lowering printer). The +// Go side is identical across architectures, so it lives here. + type buildDtableAsmContext struct { // inputs stateTable *uint16 @@ -18,7 +22,7 @@ type buildDtableAsmContext struct { errParam2 uint64 } -// buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. +// buildDtable_asm is an assembly implementation of fseDecoder.buildDtable. // Function returns non-zero exit code on error. // //go:noescape diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go index 2138f8091a..38fd2ccb2a 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 18c3703ddc..1281da885c 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -3,321 +3,83 @@ package zstd import ( - "fmt" - "io" - "github.com/klauspost/compress/internal/cpuinfo" ) -type decodeSyncAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - litRemain int - out []byte - outPosition int - literals []byte - litPosition int - history []byte - windowSize int - ll int // set on error (not for all errors, please refer to _generate/gen.go) - ml int // set on error (not for all errors, please refer to _generate/gen.go) - mo int // set on error (not for all errors, please refer to _generate/gen.go) -} +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the amd64 asm routines and the +// dispatch helpers that pick the BMI2 / non-BMI2 (and 56-bit / safe) variant. -// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape -func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. +// sequenceDecs_decode_56_amd64 implements the main loop of sequenceDecs in x86 asm. // //go:noescape -func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. +// sequenceDecs_decode_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. +// sequenceDecs_decode_56_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int - -// decode sequences from the stream with the provided history but without a dictionary. -func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { - if len(s.dict) > 0 { - return false, nil - } - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { - return false, nil - } - - // FIXME: Using unsafe memory copies leads to rare, random crashes - // with fuzz testing. It is therefore disabled for now. - const useSafe = true - /* - useSafe := false - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { - useSafe = true - } - if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { - useSafe = true - } - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - useSafe = true - } - */ - - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeSyncAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - iteration: s.nSeqs - 1, - litRemain: len(s.literals), - out: s.out, - outPosition: len(s.out), - literals: s.literals, - windowSize: s.windowSize, - history: hist, - } - - s.seqSize = 0 - startSize := len(s.out) +func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - var errCode int +// decodeAsm runs the sequenceDecs decode loop, choosing the BMI2 / 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { if cpuinfo.HasBMI2() { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx) - } - } else { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx) - } - } - switch errCode { - case noError: - break - - case errorMatchLenOfsMismatch: - return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) - - case errorMatchLenTooBig: - return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) - - case errorMatchOffTooBig: - return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", - ctx.mo, ctx.outPosition+len(hist)-startSize) - - case errorNotEnoughLiterals: - return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", - ctx.ll, ctx.litRemain+ctx.ll) - - case errorOverread: - return true, io.ErrUnexpectedEOF - - case errorNotEnoughSpace: - size := ctx.outPosition + ctx.ll + ctx.ml - if debugDecoder { - println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + if lte56bits { + return sequenceDecs_decode_56_bmi2(s, br, ctx) } - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - - default: - return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) - } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + return sequenceDecs_decode_bmi2(s, br, ctx) } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - return true, err + if lte56bits { + return sequenceDecs_decode_56_amd64(s, br, ctx) } - - s.literals = s.literals[ctx.litPosition:] - t := ctx.outPosition - s.out = s.out[:t] - - // Add final literals - s.out = append(s.out, s.literals...) - if debugDecoder { - t += len(s.literals) - if t != len(s.out) { - panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) - } - } - - return true, nil + return sequenceDecs_decode_amd64(s, br, ctx) } -// -------------------------------------------------------------------------------- - -type decodeAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - seqs []seqVals - litRemain int -} - -const noError = 0 - -// error reported when mo == 0 && ml > 0 -const errorMatchLenOfsMismatch = 1 - -// error reported when ml > maxMatchLen -const errorMatchLenTooBig = 2 - -// error reported when mo > available history or mo > s.windowSize -const errorMatchOffTooBig = 3 - -// error reported when the sum of literal lengths exeeceds the literal buffer size -const errorNotEnoughLiterals = 4 - -// error reported when capacity of `out` is too small -const errorNotEnoughSpace = 5 - -// error reported when bits are overread. -const errorOverread = 6 - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. +// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape -func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. -// -// Please refer to seqdec_generic.go for the reference implementation. +// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. // //go:noescape -func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. // //go:noescape -func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// decode sequences from the stream without the provided history. -func (s *sequenceDecs) decode(seqs []seqVals) error { - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - seqs: seqs, - iteration: len(seqs) - 1, - litRemain: len(s.literals), - } - - if debugDecoder { - println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") - } +func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int - s.seqSize = 0 - lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 - var errCode int +// decodeSyncAsm runs the decodeSync loop, choosing the BMI2 / safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { if cpuinfo.HasBMI2() { - if lte56bits { - errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_bmi2(s, br, &ctx) - } - } else { - if lte56bits { - errCode = sequenceDecs_decode_56_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_amd64(s, br, &ctx) + if safe { + return sequenceDecs_decodeSync_safe_bmi2(s, br, ctx) } + return sequenceDecs_decodeSync_bmi2(s, br, ctx) } - if errCode != 0 { - i := len(seqs) - ctx.iteration - 1 - switch errCode { - case errorMatchLenOfsMismatch: - ml := ctx.seqs[i].ml - return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) - - case errorMatchLenTooBig: - ml := ctx.seqs[i].ml - return fmt.Errorf("match len (%d) bigger than max allowed length", ml) - - case errorNotEnoughLiterals: - ll := ctx.seqs[i].ll - return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) - case errorOverread: - return io.ErrUnexpectedEOF - } - - return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + if safe { + return sequenceDecs_decodeSync_safe_amd64(s, br, ctx) } - - if ctx.litRemain < 0 { - return fmt.Errorf("literal count is too big: total available %d, total requested %d", - len(s.literals), len(s.literals)-ctx.litRemain) - } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - } - if debugDecoder { - println("decode: ", br.remain(), "bits remain on stream. code:", errCode) - } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - } - return err -} - -// -------------------------------------------------------------------------------- - -type executeAsmContext struct { - seqs []seqVals - seqIndex int - out []byte - history []byte - literals []byte - outPosition int - litPosition int - windowSize int + return sequenceDecs_decodeSync_amd64(s, br, ctx) } // sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm. @@ -334,54 +96,10 @@ func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool //go:noescape func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool -// executeSimple handles cases when dictionary is not used. -func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { - // Ensure we have enough output size... - if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { - addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc - s.out = append(s.out, make([]byte, addBytes)...) - s.out = s.out[:len(s.out)-addBytes] - } - - if debugDecoder { - printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) - } - - var t = len(s.out) - out := s.out[:t+s.seqSize] - - ctx := executeAsmContext{ - seqs: seqs, - seqIndex: 0, - out: out, - history: hist, - outPosition: t, - litPosition: 0, - literals: s.literals, - windowSize: s.windowSize, +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_amd64(ctx) } - var ok bool - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - ok = sequenceDecs_executeSimple_safe_amd64(&ctx) - } else { - ok = sequenceDecs_executeSimple_amd64(&ctx) - } - if !ok { - return fmt.Errorf("match offset (%d) bigger than current history (%d)", - seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) - } - s.literals = s.literals[ctx.litPosition:] - t = ctx.outPosition - - // Add final literals - copy(out[t:], s.literals) - if debugDecoder { - t += len(s.literals) - if t != len(out) { - panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) - } - } - s.out = out - - return nil + return sequenceDecs_executeSimple_amd64(ctx) } diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index a708ca6d3d..3fc381c7a7 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go new file mode 100644 index 0000000000..5ad262acff --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go @@ -0,0 +1,70 @@ +//go:build arm64 && !appengine && !noasm && gc + +package zstd + +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the arm64 asm routines (generated +// by the avo arm64 lowering printer) and the dispatch helpers. arm64 has no +// BMI2, so each helper selects only between the 56-bit / safe variants. + +// sequenceDecs_decode_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_56_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +//go:noescape +func sequenceDecs_decode_56_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// decodeAsm runs the sequenceDecs decode loop, choosing the 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { + if lte56bits { + return sequenceDecs_decode_56_arm64(s, br, ctx) + } + return sequenceDecs_decode_arm64(s, br, ctx) +} + +// sequenceDecs_decodeSync_arm64 implements the main loop of sequenceDecs.decodeSync in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decodeSync_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_safe_arm64 does the same as above, but does not write more than output buffer. +// +//go:noescape +func sequenceDecs_decodeSync_safe_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// decodeSyncAsm runs the decodeSync loop, choosing the safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { + if safe { + return sequenceDecs_decodeSync_safe_arm64(s, br, ctx) + } + return sequenceDecs_decodeSync_arm64(s, br, ctx) +} + +// sequenceDecs_executeSimple_arm64 implements the main loop of sequenceDecs.executeSimple in arm64 asm. +// +// Returns false if a match offset is too big. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_executeSimple_arm64(ctx *executeAsmContext) bool + +// Same as above, but with safe memcopies +// +//go:noescape +func sequenceDecs_executeSimple_safe_arm64(ctx *executeAsmContext) bool + +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_arm64(ctx) + } + return sequenceDecs_executeSimple_arm64(ctx) +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s new file mode 100644 index 0000000000..a468e5fc2c --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s @@ -0,0 +1,2705 @@ +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_amd64_main_loop: + MOVD (RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_end + +sequenceDecs_decode_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_byte_by_byte + +sequenceDecs_decode_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_2_end + +sequenceDecs_decode_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_2_byte_by_byte + +sequenceDecs_decode_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_amd64_adjust_offset_nonzero + +sequenceDecs_decode_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_amd64_adjust_zero + BEQ sequenceDecs_decode_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_amd64_adjust_three + JMP sequenceDecs_decode_amd64_adjust_two + +sequenceDecs_decode_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_56_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_56_amd64_main_loop: + MOVD (RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_56_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_56_amd64_fill_end + +sequenceDecs_decode_56_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_56_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_56_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_56_amd64_fill_byte_by_byte + +sequenceDecs_decode_56_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_56_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_56_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_56_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_56_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_56_amd64_adjust_offset_nonzero + +sequenceDecs_decode_56_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_56_amd64_adjust_zero + BEQ sequenceDecs_decode_56_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_56_amd64_adjust_three + JMP sequenceDecs_decode_56_amd64_adjust_two + +sequenceDecs_decode_56_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_56_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_56_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_56_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_56_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_56_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_56_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_56_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decode_bmi2 (generic twin preferred on arm64) + +// skipped sequenceDecs_decode_56_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R5, R15 + VLD1 (R15), [V0.B16] + ADD R13, R3, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R10, R13 + BLO copy_1 + ADD R10, R5, R5 + ADD R10, R3, R3 + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R10 + MOVB 2(R13), R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R10, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R3, R11 + ADD R12, R3, R3 + +copy_2: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R11) + ADD $0x10, R10, R10 + ADD $0x10, R11, R11 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVB (R10), R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_safe_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD R10, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R5), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R5, R5 + ADD $0x10, R3, R3 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R5, R5 + ADD $16, R5, R5 + ADD R13, R3, R3 + ADD $16, R3, R3 + ADD $-16, R5, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R10 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R10 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R5), R13 + ADD R10, R5, R15 + MOVB -1(R15), R14 + MOVB R13, (R3) + ADD R10, R3, R15 + MOVB R14, -1(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_3: + MOVH (R5), R13 + MOVB 2(R5), R14 + MOVH R13, (R3) + MOVB R14, 2(R3) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R5), R13 + ADD R10, R5, R15 + MOVWU -4(R15), R14 + MOVW R13, (R3) + ADD R10, R3, R15 + MOVW R14, -4(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R5), R13 + ADD R10, R5, R15 + MOVD -8(R15), R14 + MOVD R13, (R3) + ADD R10, R3, R15 + MOVD R14, -8(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + +copy_1_end: + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R10 + MOVB 2(R13), R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R10, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R12, R11 + SUBS $0x10, R11, R11 + BLO copy_2_small + +copy_2_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R10, R10 + ADD $0x10, R3, R3 + SUBS $0x10, R11, R11 + BHS copy_2_loop + ADD R11, R10, R10 + ADD $16, R10, R10 + ADD R11, R3, R3 + ADD $16, R3, R3 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (R10), R11 + ADD R12, R10, R15 + MOVB -1(R15), R13 + MOVB R11, (R3) + ADD R12, R3, R15 + MOVB R13, -1(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_3: + MOVH (R10), R11 + MOVB 2(R10), R13 + MOVH R11, (R3) + MOVB R13, 2(R3) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R10), R11 + ADD R12, R10, R15 + MOVWU -4(R15), R13 + MOVW R11, (R3) + ADD R12, R3, R15 + MOVW R13, -4(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R10), R11 + ADD R12, R10, R15 + MOVD -8(R15), R13 + MOVD R11, (R3) + ADD R12, R3, R15 + MOVD R13, -8(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVB (R10), R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 8(RSP) + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 32(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 56(RSP) + MOVD 176(R0), R1 + MOVD R1, 48(RSP) + MOVD 184(R0), R0 + MOVD R0, 40(RSP) + MOVD 40(RSP), R0 + MOVD 48(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 48(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 32(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 32(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_amd64_main_loop: + MOVD (RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_end + +sequenceDecs_decodeSync_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_of_update_zero: + MOVD R0, 8(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ml_update_zero: + MOVD R0, 16(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_2_end + +sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ll_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader for state updates + MOVD R12, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 8(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: + MOVD 24(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_amd64_after_adjust: + MOVD R12, 8(RSP) + + // Check values + MOVD 16(RSP), R0 + MOVD 24(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_amd64_match_len_ofs_ok: + MOVD 24(RSP), R0 + MOVD 8(RSP), R1 + MOVD 16(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD R9, R13, R13 + MOVD 32(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R10, R15 + VLD1 (R15), [V0.B16] + ADD R13, R9, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R0, R13 + BLO copy_1 + ADD R0, R10, R10 + ADD R0, R9, R9 + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 40(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 56(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 48(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R0 + MOVB 2(R13), R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R0, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R9, R1 + ADD R12, R9, R9 + +copy_2: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R1) + ADD $0x10, R0, R0 + ADD $0x10, R1, R1 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVB (R0), R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch: + MOVD 16(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 8(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_safe_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 8(RSP) + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 32(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 56(RSP) + MOVD 176(R0), R1 + MOVD R1, 48(RSP) + MOVD 184(R0), R0 + MOVD R0, 40(RSP) + MOVD 40(RSP), R0 + MOVD 48(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 48(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 32(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 32(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_safe_amd64_main_loop: + MOVD (RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_end + +sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_of_update_zero: + MOVD R0, 8(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ml_update_zero: + MOVD R0, 16(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_end + +sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ll_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader for state updates + MOVD R12, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_safe_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_safe_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 8(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: + MOVD 24(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_safe_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_safe_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_safe_amd64_after_adjust: + MOVD R12, 8(RSP) + + // Check values + MOVD 16(RSP), R0 + MOVD 24(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: + MOVD 24(RSP), R0 + MOVD 8(RSP), R1 + MOVD 16(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD R9, R13, R13 + MOVD 32(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD R0, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R10, R10 + ADD $0x10, R9, R9 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R10, R10 + ADD $16, R10, R10 + ADD R13, R9, R9 + ADD $16, R9, R9 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R0 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R0 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R10), R13 + ADD R0, R10, R15 + MOVB -1(R15), R14 + MOVB R13, (R9) + ADD R0, R9, R15 + MOVB R14, -1(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_3: + MOVH (R10), R13 + MOVB 2(R10), R14 + MOVH R13, (R9) + MOVB R14, 2(R9) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R10), R13 + ADD R0, R10, R15 + MOVWU -4(R15), R14 + MOVW R13, (R9) + ADD R0, R9, R15 + MOVW R14, -4(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R10), R13 + ADD R0, R10, R15 + MOVD -8(R15), R14 + MOVD R13, (R9) + ADD R0, R9, R15 + MOVD R14, -8(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + +copy_1_end: + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 40(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 56(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 48(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R0 + MOVB 2(R13), R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R0, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R12, R1 + SUBS $0x10, R1, R1 + BLO copy_2_small + +copy_2_loop: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R0, R0 + ADD $0x10, R9, R9 + SUBS $0x10, R1, R1 + BHS copy_2_loop + ADD R1, R0, R0 + ADD $16, R0, R0 + ADD R1, R9, R9 + ADD $16, R9, R9 + ADD $-16, R0, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (R0), R1 + ADD R12, R0, R15 + MOVB -1(R15), R13 + MOVB R1, (R9) + ADD R12, R9, R15 + MOVB R13, -1(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_3: + MOVH (R0), R1 + MOVB 2(R0), R13 + MOVH R1, (R9) + MOVB R13, 2(R9) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R0), R1 + ADD R12, R0, R15 + MOVWU -4(R15), R13 + MOVW R1, (R9) + ADD R12, R9, R15 + MOVW R13, -4(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R0), R1 + ADD R12, R0, R15 + MOVD -8(R15), R13 + MOVD R1, (R9) + ADD R12, R9, R15 + MOVD R13, -8(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVB (R0), R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_safe_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch: + MOVD 16(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 8(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_safe_bmi2 (generic twin preferred on arm64) diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go new file mode 100644 index 0000000000..55405f3914 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go @@ -0,0 +1,289 @@ +//go:build (amd64 || arm64) && !appengine && !noasm && gc + +package zstd + +import ( + "fmt" + "io" +) + +// This file holds the parts of the assembly sequence decoder that are identical +// across architectures: the context structs exchanged with the asm, the error +// codes, and the decode/decodeSync/executeSimple wrappers. Each architecture +// supplies the small dispatch helpers (decodeAsm, decodeSyncAsm, +// executeSimpleAsm) that select the concrete asm routine — amd64 also chooses a +// BMI2 variant, arm64 has a single implementation. + +type decodeSyncAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + litRemain int + out []byte + outPosition int + literals []byte + litPosition int + history []byte + windowSize int + ll int // set on error (not for all errors, please refer to _generate/gen.go) + ml int // set on error (not for all errors, please refer to _generate/gen.go) + mo int // set on error (not for all errors, please refer to _generate/gen.go) +} + +type decodeAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + seqs []seqVals + litRemain int +} + +type executeAsmContext struct { + seqs []seqVals + seqIndex int + out []byte + history []byte + literals []byte + outPosition int + litPosition int + windowSize int +} + +const noError = 0 + +// error reported when mo == 0 && ml > 0 +const errorMatchLenOfsMismatch = 1 + +// error reported when ml > maxMatchLen +const errorMatchLenTooBig = 2 + +// error reported when mo > available history or mo > s.windowSize +const errorMatchOffTooBig = 3 + +// error reported when the sum of literal lengths exeeceds the literal buffer size +const errorNotEnoughLiterals = 4 + +// error reported when capacity of `out` is too small +const errorNotEnoughSpace = 5 + +// error reported when bits are overread. +const errorOverread = 6 + +// decode sequences from the stream with the provided history but without a dictionary. +func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { + if len(s.dict) > 0 { + return false, nil + } + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { + return false, nil + } + + // FIXME: Using unsafe memory copies leads to rare, random crashes + // with fuzz testing. It is therefore disabled for now. + const useSafe = true + + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeSyncAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + iteration: s.nSeqs - 1, + litRemain: len(s.literals), + out: s.out, + outPosition: len(s.out), + literals: s.literals, + windowSize: s.windowSize, + history: hist, + } + + s.seqSize = 0 + startSize := len(s.out) + + errCode := decodeSyncAsm(s, br, &ctx, useSafe) + switch errCode { + case noError: + break + + case errorMatchLenOfsMismatch: + return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) + + case errorMatchLenTooBig: + return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) + + case errorMatchOffTooBig: + return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", + ctx.mo, ctx.outPosition+len(hist)-startSize) + + case errorNotEnoughLiterals: + return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", + ctx.ll, ctx.litRemain+ctx.ll) + + case errorOverread: + return true, io.ErrUnexpectedEOF + + case errorNotEnoughSpace: + size := ctx.outPosition + ctx.ll + ctx.ml + if debugDecoder { + println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + } + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + + default: + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + return true, err + } + + s.literals = s.literals[ctx.litPosition:] + t := ctx.outPosition + s.out = s.out[:t] + + // Add final literals + s.out = append(s.out, s.literals...) + if debugDecoder { + t += len(s.literals) + if t != len(s.out) { + panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) + } + } + + return true, nil +} + +// decode sequences from the stream without the provided history. +func (s *sequenceDecs) decode(seqs []seqVals) error { + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + seqs: seqs, + iteration: len(seqs) - 1, + litRemain: len(s.literals), + } + + if debugDecoder { + println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") + } + + s.seqSize = 0 + lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 + errCode := decodeAsm(s, br, &ctx, lte56bits) + if errCode != 0 { + i := len(seqs) - ctx.iteration - 1 + switch errCode { + case errorMatchLenOfsMismatch: + ml := ctx.seqs[i].ml + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + + case errorMatchLenTooBig: + ml := ctx.seqs[i].ml + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + + case errorNotEnoughLiterals: + ll := ctx.seqs[i].ll + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) + case errorOverread: + return io.ErrUnexpectedEOF + } + + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + } + + if ctx.litRemain < 0 { + return fmt.Errorf("literal count is too big: total available %d, total requested %d", + len(s.literals), len(s.literals)-ctx.litRemain) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + if debugDecoder { + println("decode: ", br.remain(), "bits remain on stream. code:", errCode) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + } + return err +} + +// executeSimple handles cases when dictionary is not used. +func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { + // Ensure we have enough output size... + if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { + addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + ctx := executeAsmContext{ + seqs: seqs, + seqIndex: 0, + out: out, + history: hist, + outPosition: t, + litPosition: 0, + literals: s.literals, + windowSize: s.windowSize, + } + // useSafe avoids overwriting the output buffer when the literals slice has + // not been allocated with the required over-allocation slack. + useSafe := cap(s.literals) < len(s.literals)+compressedBlockOverAlloc + + ok := executeSimpleAsm(&ctx, useSafe) + if !ok { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", + seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) + } + s.literals = s.literals[ctx.litPosition:] + t = ctx.outPosition + + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go index 516cd9b070..8a3db6ba22 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/snappy.go b/vendor/github.com/klauspost/compress/zstd/snappy.go index 336c288930..36c56f36d7 100644 --- a/vendor/github.com/klauspost/compress/zstd/snappy.go +++ b/vendor/github.com/klauspost/compress/zstd/snappy.go @@ -334,9 +334,10 @@ func decodeSnappy(blk *blockEnc, src []byte) error { return errUnsupportedLiteralLength } - //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { - // return ErrSnappyCorrupt - //} + if length > len(src)-s { + println("length > len(src)-s", length, len(src)-s) + return ErrSnappyCorrupt + } blk.literals = append(blk.literals, src[s:s+length]...) //println(length, "litLen") diff --git a/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go b/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go index 8547c8dfd1..820bf436ab 100644 --- a/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go +++ b/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go @@ -90,7 +90,7 @@ loop: s = skipSpace(s[1:]) } } - return + return specs } func skipSpace(s string) (rest string) { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index 4ce84e7a80..7d963d3afb 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -85,11 +85,12 @@ type CounterVecOpts struct { // Both internal tracking values are added up in the Write method. This has to // be taken into account when it comes to precision and overflow behavior. func NewCounter(opts CounterOpts) Counter { - desc := NewDesc( + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ) if opts.now == nil { opts.now = time.Now @@ -205,6 +206,7 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) if opts.now == nil { opts.now = time.Now @@ -349,10 +351,11 @@ type CounterFunc interface { // // Check out the ExampleGaugeFunc examples for the similar GaugeFunc. func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc { - return newValueFunc(NewDesc( + return newValueFunc(V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), CounterValue, function) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 2331b8b4f3..a3c92e7a4c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -47,6 +47,8 @@ type Desc struct { fqName string // help provides some helpful information about this metric. help string + // unit provides the unit of this metric. + unit string // constLabelPairs contains precalculated DTO label pairs based on // the constant labels. constLabelPairs []*dto.LabelPair @@ -66,6 +68,16 @@ type Desc struct { err error } +// DescOpt allows setting optional fields for NewDesc. +type DescOpt func(*Desc) + +// WithUnit sets the unit for a Desc. +func WithUnit(unit string) DescOpt { + return func(d *Desc) { + d.unit = unit + } +} + // NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc // and will be reported on registration time. variableLabels and constLabels can // be nil if no such labels should be set. fqName must not be empty. @@ -89,14 +101,17 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // // For constLabels, the label values are constant. Therefore, they are fully // specified in the Desc. See the Collector example for a usage pattern. -func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc { +func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels, opts ...DescOpt) *Desc { d := &Desc{ fqName: fqName, help: help, variableLabels: variableLabels.compile(), } - //nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme. - if !model.NameValidationScheme.IsValidMetricName(fqName) { + + for _, opt := range opts { + opt(d) + } + if !model.UTF8Validation.IsValidMetricName(fqName) { d.err = fmt.Errorf("%q is not a valid metric name", fqName) return d } @@ -150,11 +165,13 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const d.id = xxh.Sum64() // Sort labelNames so that order doesn't matter for the hash. sort.Strings(labelNames) - // Now hash together (in this order) the help string and the sorted + // Now hash together (in this order) the help string, the unit string and the sorted // label names. xxh.Reset() xxh.WriteString(help) xxh.Write(separatorByteSlice) + xxh.WriteString(d.unit) + xxh.Write(separatorByteSlice) for _, labelName := range labelNames { xxh.WriteString(labelName) xxh.Write(separatorByteSlice) @@ -182,6 +199,15 @@ func NewInvalidDesc(err error) *Desc { } } +// Err returns an error that occurred during construction, if any. +// +// Calling this method is optional. It can be used to detect construction +// errors early, before invoking other methods on the Desc. If an error is +// present, later operations may not behave as expected. +func (d *Desc) Err() error { + return d.err +} + func (d *Desc) String() string { lpStrings := make([]string, 0, len(d.constLabelPairs)) for _, lp := range d.constLabelPairs { @@ -202,9 +228,10 @@ func (d *Desc) String() string { } } return fmt.Sprintf( - "Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}", + "Desc{fqName: %q, help: %q, unit: %q, constLabels: {%s}, variableLabels: {%s}}", d.fqName, d.help, + d.unit, strings.Join(lpStrings, ","), strings.Join(vlStrings, ","), ) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go index de5a856293..327746f433 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go @@ -47,14 +47,14 @@ func (e *expvarCollector) Collect(ch chan<- Metric) { if expVar == nil { continue } - var v interface{} + var v any labels := make([]string, len(desc.variableLabels.names)) if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil { ch <- NewInvalidMetric(desc, err) continue } - var processValue func(v interface{}, i int) - processValue = func(v interface{}, i int) { + var processValue func(v any, i int) + processValue = func(v any, i int) { if i >= len(labels) { copiedLabels := append(make([]string, 0, len(labels)), labels...) switch v := v.(type) { @@ -72,7 +72,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) { ch <- m return } - vm, ok := v.(map[string]interface{}) + vm, ok := v.(map[string]any) if !ok { return } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go index dd2eac9406..41e54bf270 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go @@ -76,11 +76,12 @@ type GaugeVecOpts struct { // scenarios for Gauges and Counters, where the former tends to be Set-heavy and // the latter Inc-heavy. func NewGauge(opts GaugeOpts) Gauge { - desc := NewDesc( + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ) result := &gauge{desc: desc, labelPairs: desc.constLabelPairs} result.init(result) // Init self-collection. @@ -163,6 +164,7 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &GaugeVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { @@ -302,10 +304,11 @@ type GaugeFunc interface { // value of 1. Example: // https://github.com/prometheus/common/blob/8558a5b7db3c84fa38b4766966059a7bd5bfa2ee/version/info.go#L36-L56 func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc { - return newValueFunc(NewDesc( + return newValueFunc(V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), GaugeValue, function) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go deleted file mode 100644 index 897a6e906b..0000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2021 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !go1.17 -// +build !go1.17 - -package prometheus - -import ( - "runtime" - "sync" - "time" -) - -type goCollector struct { - base baseGoCollector - - // ms... are memstats related. - msLast *runtime.MemStats // Previously collected memstats. - msLastTimestamp time.Time - msMtx sync.Mutex // Protects msLast and msLastTimestamp. - msMetrics memStatsMetrics - msRead func(*runtime.MemStats) // For mocking in tests. - msMaxWait time.Duration // Wait time for fresh memstats. - msMaxAge time.Duration // Maximum allowed age of old memstats. -} - -// NewGoCollector is the obsolete version of collectors.NewGoCollector. -// See there for documentation. -// -// Deprecated: Use collectors.NewGoCollector instead. -func NewGoCollector() Collector { - msMetrics := goRuntimeMemStats() - msMetrics = append(msMetrics, struct { - desc *Desc - eval func(*runtime.MemStats) float64 - valType ValueType - }{ - // This metric is omitted in Go1.17+, see https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 - desc: NewDesc( - memstatNamespace("gc_cpu_fraction"), - "The fraction of this program's available CPU time used by the GC since the program started.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction }, - valType: GaugeValue, - }) - return &goCollector{ - base: newBaseGoCollector(), - msLast: &runtime.MemStats{}, - msRead: runtime.ReadMemStats, - msMaxWait: time.Second, - msMaxAge: 5 * time.Minute, - msMetrics: msMetrics, - } -} - -// Describe returns all descriptions of the collector. -func (c *goCollector) Describe(ch chan<- *Desc) { - c.base.Describe(ch) - for _, i := range c.msMetrics { - ch <- i.desc - } -} - -// Collect returns the current state of all metrics of the collector. -func (c *goCollector) Collect(ch chan<- Metric) { - var ( - ms = &runtime.MemStats{} - done = make(chan struct{}) - ) - // Start reading memstats first as it might take a while. - go func() { - c.msRead(ms) - c.msMtx.Lock() - c.msLast = ms - c.msLastTimestamp = time.Now() - c.msMtx.Unlock() - close(done) - }() - - // Collect base non-memory metrics. - c.base.Collect(ch) - - timer := time.NewTimer(c.msMaxWait) - select { - case <-done: // Our own ReadMemStats succeeded in time. Use it. - timer.Stop() // Important for high collection frequencies to not pile up timers. - c.msCollect(ch, ms) - return - case <-timer.C: // Time out, use last memstats if possible. Continue below. - } - c.msMtx.Lock() - if time.Since(c.msLastTimestamp) < c.msMaxAge { - // Last memstats are recent enough. Collect from them under the lock. - c.msCollect(ch, c.msLast) - c.msMtx.Unlock() - return - } - // If we are here, the last memstats are too old or don't exist. We have - // to wait until our own ReadMemStats finally completes. For that to - // happen, we have to release the lock. - c.msMtx.Unlock() - <-done - c.msCollect(ch, ms) -} - -func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) { - for _, i := range c.msMetrics { - ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms)) - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go index 6b8684731c..1db1c4be09 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go @@ -98,7 +98,7 @@ type goCollector struct { // snapshot is always produced by Collect. mu sync.Mutex - // Contains all samples that has to retrieved from runtime/metrics (not all of them will be exposed). + // Contains all samples that have to be retrieved from runtime/metrics (not all of them will be exposed). sampleBuf []metrics.Sample // sampleMap allows lookup for MemStats metrics and runtime/metrics histograms for exact sums. sampleMap map[string]*metrics.Sample @@ -210,16 +210,26 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name}) sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1] + // Extract unit from the runtime/metrics name (e.g., "/gc/heap/allocs:bytes" -> "bytes") + // and sanitize to match Prometheus naming conventions (e.g., "cpu-seconds" -> "cpu_seconds") + var unit string + if idx := strings.IndexRune(d.Name, ':'); idx >= 0 { + unit = d.Name[idx+1:] + unit = strings.ReplaceAll(unit, "-", "_") + unit = strings.ReplaceAll(unit, "*", "_") + unit = strings.ReplaceAll(unit, "/", "_per_") + } + var m collectorMetric if d.Kind == metrics.KindFloat64Histogram { _, hasSum := opt.RuntimeMetricSumForHist[d.Name] - unit := d.Name[strings.IndexRune(d.Name, ':')+1:] m = newBatchHistogram( - NewDesc( + V2.NewDesc( BuildFQName(namespace, subsystem, name), help, + UnconstrainedLabels(nil), nil, - nil, + WithUnit(unit), ), internal.RuntimeMetricsBucketsForUnit(bucketsMap[d.Name], unit), hasSum, @@ -230,6 +240,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { Subsystem: subsystem, Name: name, Help: help, + Unit: unit, }, ) } else { @@ -238,6 +249,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { Subsystem: subsystem, Name: name, Help: help, + Unit: unit, }) } metricSet = append(metricSet, m) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index c453b754a7..88bae3b32c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -378,6 +378,9 @@ type HistogramOpts struct { // string. Help string + // Unit provides the unit of this Histogram. + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. @@ -522,11 +525,12 @@ type HistogramVecOpts struct { // for each bucket. func NewHistogram(opts HistogramOpts) Histogram { return newHistogram( - NewDesc( + V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), opts, ) @@ -966,7 +970,7 @@ func (h *histogram) maybeReset( // We are using the possibly mocked h.now() rather than // time.Since(h.lastResetTime) to enable testing. if h.nativeHistogramMinResetDuration == 0 || // No reset configured. - h.resetScheduled || // Do not interefere if a reset is already scheduled. + h.resetScheduled || // Do not interfere if a reset is already scheduled. h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration { return false } @@ -1053,8 +1057,8 @@ func (h *histogram) maybeWidenZeroBucket(hot, cold *histogramCounts) bool { atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold)) // ...and then merge the newly deleted buckets into the wider zero // bucket. - mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v interface{}) bool { - return func(k, v interface{}) bool { + mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v any) bool { + return func(k, v any) bool { key := k.(int) bucket := v.(*int64) if key == smallestKey { @@ -1107,8 +1111,8 @@ func (h *histogram) doubleBucketWidth(hot, cold *histogramCounts) { // ...adjust the schema in the cold counts, too... atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema) // ...and then merge the cold buckets into the wider hot buckets. - merge := func(hotBuckets *sync.Map) func(k, v interface{}) bool { - return func(k, v interface{}) bool { + merge := func(hotBuckets *sync.Map) func(k, v any) bool { + return func(k, v any) bool { key := k.(int) bucket := v.(*int64) // Adjust key to match the bucket to merge into. @@ -1190,6 +1194,7 @@ func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &HistogramVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { @@ -1476,7 +1481,7 @@ func pickSchema(bucketFactor float64) int32 { func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) { var ii []int - buckets.Range(func(k, v interface{}) bool { + buckets.Range(func(k, v any) bool { ii = append(ii, k.(int)) return true }) @@ -1553,8 +1558,8 @@ func addToBucket(buckets *sync.Map, key int, increment int64) bool { // according to the buckets ranged through. It then resets all buckets ranged // through to 0 (but leaves them in place so that they don't need to get // recreated on the next scrape). -func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface{}) bool { - return func(k, v interface{}) bool { +func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v any) bool { + return func(k, v any) bool { bucket := v.(*int64) if addToBucket(hotBuckets, k.(int), atomic.LoadInt64(bucket)) { atomic.AddUint32(bucketNumber, 1) @@ -1565,7 +1570,7 @@ func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface } func deleteSyncMap(m *sync.Map) { - m.Range(func(k, v interface{}) bool { + m.Range(func(k, v any) bool { m.Delete(k) return true }) @@ -1573,7 +1578,7 @@ func deleteSyncMap(m *sync.Map) { func findSmallestKey(m *sync.Map) int { result := math.MaxInt32 - m.Range(func(k, v interface{}) bool { + m.Range(func(k, v any) bool { key := k.(int) if key < result { result = key diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go index 7bac0da33d..2db270f216 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go @@ -78,7 +78,7 @@ type OpCode struct { // notion, pairing up elements that appear uniquely in each sequence. // That, and the method here, appear to yield more intuitive difference // reports than does diff. This method appears to be the least vulnerable -// to synching up on blocks of "junk lines", though (like blank lines in +// to syncing up on blocks of "junk lines", though (like blank lines in // ordinary text files, or maybe "

" lines in HTML files). That may be // because this is the only method of the 3 that has a *concept* of // "junk" . @@ -567,7 +567,7 @@ type UnifiedDiff struct { func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { buf := bufio.NewWriter(writer) defer buf.Flush() - wf := func(format string, args ...interface{}) error { + wf := func(format string, args ...any) error { _, err := fmt.Fprintf(buf, format, args...) return err } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go index 5fe8d3b4d2..a0285489a0 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go @@ -184,6 +184,5 @@ func validateLabelValues(vals []string, expectedNumberOfValues int) error { } func checkLabelName(l string) bool { - //nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme. - return model.NameValidationScheme.IsValidLabelName(l) && !strings.HasPrefix(l, reservedLabelPrefix) + return model.UTF8Validation.IsValidLabelName(l) && !strings.HasPrefix(l, reservedLabelPrefix) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go index 76e59f1288..c5cb90adf8 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go @@ -81,6 +81,9 @@ type Opts struct { // string. Help string + // Unit provides the unit of this metric as per https://prometheus.io/docs/specs/om + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go index b32c95fa3f..2b16298f40 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go @@ -72,7 +72,13 @@ func getOpenFileCount() (float64, error) { } func (c *processCollector) processCollect(ch chan<- Metric) { - if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil { + pid, err := c.pidFn() + if err != nil { + c.reportError(ch, nil, err) + return + } + + if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", pid); err == nil { if len(procs) == 1 { startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9) ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) @@ -84,6 +90,11 @@ func (c *processCollector) processCollect(ch chan<- Metric) { c.reportError(ch, c.startTime, err) } + if pid != os.Getpid() { + c.reportError(ch, nil, fmt.Errorf("collecting metrics for pid %d is not supported on darwin: process metrics collection is limited to the current process (pid %d)", pid, os.Getpid())) + return + } + // The proc structure returned by kern.proc.pid above has an Rusage member, // but it is not filled in, so it needs to be fetched by getrusage(2). For // that call, the UTime, STime, and Maxrss members are filled out, but not diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go index fa474289ef..c08dd05f03 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go @@ -30,6 +30,10 @@ var ( procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") + + openProcess = windows.OpenProcess + closeHandle = windows.CloseHandle + getProcessTimes = windows.GetProcessTimes ) type processMemoryCounters struct { @@ -79,10 +83,21 @@ func getProcessHandleCount(handle windows.Handle) (uint32, error) { } func (c *processCollector) processCollect(ch chan<- Metric) { - h := windows.CurrentProcess() + pid, err := c.pidFn() + if err != nil { + c.reportError(ch, nil, err) + return + } + + h, err := openProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, uint32(pid)) + if err != nil { + c.reportError(ch, nil, err) + return + } + defer closeHandle(h) var startTime, exitTime, kernelTime, userTime windows.Filetime - err := windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) + err = getProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) if err != nil { c.reportError(ch, nil, err) return diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go index 763d99e362..c28af5ce2b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -37,10 +37,12 @@ import ( "fmt" "io" "net/http" + "slices" "strconv" "sync" "time" + dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" "github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil" @@ -74,11 +76,118 @@ func defaultCompressionFormats() []Compression { } var gzipPool = sync.Pool{ - New: func() interface{} { + New: func() any { return gzip.NewWriter(nil) }, } +// coalescingGatherer wraps a TransactionalGatherer to deduplicate concurrent +// Gather calls. When a Gather is already in flight, new callers join the +// existing cycle and receive the same result once it completes. The underlying +// done function is called exactly once, when the last joined caller releases. +// +// This prevents goroutine pile-up when the scrape rate is faster than the +// time collectors need to produce metrics. +type coalescingGatherer struct { + g prometheus.TransactionalGatherer + mu sync.Mutex + cycle *gatherCycle +} + +// gatherCycle tracks a single in-flight Gather and all HTTP handlers sharing it. +type gatherCycle struct { + ready chan struct{} // closed when Gather completes; happens-before reads of mfs/err/done + mfs []*dto.MetricFamily // canonical result, set before ready is closed; callers get a slices.Clone, the element values stay shared and must not be mutated + err error // set before ready is closed + done func() // underlying done callback; set before ready is closed + refs int // number of handlers using this cycle; protected by coalescingGatherer.mu +} + +var _ prometheus.TransactionalGatherer = (*coalescingGatherer)(nil) // compile-time interface check + +// errGatherPanicked is returned to callers that joined an in-flight coalesced +// Gather whose underlying gatherer panicked. See the panic guard in Gather for +// why joiners receive this error instead of the panic itself. +var errGatherPanicked = errors.New("coalesced gather panicked") + +func (c *coalescingGatherer) Gather() ([]*dto.MetricFamily, func(), error) { + c.mu.Lock() + if cy := c.cycle; cy != nil { + // c.cycle is non-nil while Gather runs or handlers are still consuming its results. + cy.refs++ + c.mu.Unlock() + <-cy.ready + // Each caller gets its own slice header so it can filter or reorder + // without racing other callers sharing this cycle. The *dto.MetricFamily + // values remain shared and must not be mutated in place. + return slices.Clone(cy.mfs), c.releaseFunc(cy), cy.err + } + cy := &gatherCycle{ + ready: make(chan struct{}), + done: func() {}, + refs: 1, + } + c.cycle = cy + c.mu.Unlock() + + // Guard against a panic in c.g.Gather. The common case, a panicking + // Collector, never reaches here: Registry.Gather recovers Collector panics + // and returns them as an error. This guard only covers the rare case where + // the wrapped gatherer itself panics. + // + // We deliberately do not recover: the leader's panic propagates and is + // handled by net/http exactly as it would be without coalescing. We only + // set cy.err before closing cy.ready so joiners waiting on <-cy.ready fail + // with that error instead of silently returning an empty, successful + // response, and we clear c.cycle so the next Gather starts a fresh cycle. + // + // The leader never runs its own releaseFunc on this path, so its ref is + // not decremented; that is harmless because the cycle is detached (c.cycle + // = nil) and cy.done is still the no-op set at construction (c.g.Gather + // panicked before assigning a real done). If cy.done is ever made non-nil + // before c.g.Gather runs, this path would need to release it. + panicked := true + defer func() { + if panicked { + c.mu.Lock() + if c.cycle == cy { + c.cycle = nil + } + c.mu.Unlock() + cy.err = errGatherPanicked // set before close: happens-before joiners' reads + close(cy.ready) + } + }() + cy.mfs, cy.done, cy.err = c.g.Gather() + panicked = false + close(cy.ready) // happens-before joiners' reads of cy.mfs/err/done + + // Clone here too so cy.mfs stays the write-once canonical slice: joiners + // read it concurrently via slices.Clone, so the leader must not hand out + // (and potentially reorder) the same backing array. + return slices.Clone(cy.mfs), c.releaseFunc(cy), cy.err +} + +// releaseFunc returns the done callback for one caller sharing cy. +// When the last caller releases, the underlying done is invoked and the +// cycle is cleared so the next Gather starts fresh. +func (c *coalescingGatherer) releaseFunc(cy *gatherCycle) func() { + return func() { + c.mu.Lock() + cy.refs-- + if cy.refs > 0 { + c.mu.Unlock() + return + } + // Last caller. + if c.cycle == cy { + c.cycle = nil + } + c.mu.Unlock() + cy.done() // called outside the lock to avoid holding it during done + } +} + // Handler returns an http.Handler for the prometheus.DefaultGatherer, using // default HandlerOpts, i.e. it reports the first error as an HTTP error, it has // no error logging, and it applies compression if requested by the client. @@ -89,6 +198,10 @@ var gzipPool = sync.Pool{ // metrics used for instrumentation will be shared between them, providing // global scrape counts. // +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. +// // This function is meant to cover the bulk of basic use cases. If you are doing // anything that requires more customization (including using a non-default // Gatherer, different instrumentation, and non-default HandlerOpts), use the @@ -105,6 +218,10 @@ func Handler() http.Handler { // Gatherers, with non-default HandlerOpts, and/or with custom (or no) // instrumentation. Use the InstrumentMetricHandler function to apply the same // kind of instrumentation as it is used by the Handler function. +// +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { return HandlerForTransactional(prometheus.ToTransactionalGatherer(reg), opts) } @@ -112,7 +229,15 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { // HandlerForTransactional is like HandlerFor, but it uses transactional gather, which // can safely change in-place returned *dto.MetricFamily before call to `Gather` and after // call to `done` of that `Gather`. +// +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerOpts) http.Handler { + if opts.CoalesceGather { + reg = &coalescingGatherer{g: reg} + } + var ( inFlightSem chan struct{} errCnt = prometheus.NewCounterVec( @@ -214,12 +339,14 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO rsp.Header().Set(contentEncodingHeader, encodingHeader) } - var enc expfmt.Encoder + var ( + enc expfmt.Encoder + encOpts []expfmt.EncoderOption + ) if opts.EnableOpenMetricsTextCreatedSamples { - enc = expfmt.NewEncoder(w, contentType, expfmt.WithCreatedLines()) - } else { - enc = expfmt.NewEncoder(w, contentType) + encOpts = append(encOpts, expfmt.WithCreatedLines()) } + enc = expfmt.NewEncoder(w, contentType, encOpts...) // handleError handles the error according to opts.ErrorHandling // and returns true if we have to abort after the handling. @@ -245,7 +372,24 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO return false } + // Build metric name filter set from query params (if any). The URL + // can be nil on hand-constructed requests. + var metricFilter map[string]struct{} + if req.URL != nil { + if metricNames := req.URL.Query()["name[]"]; len(metricNames) > 0 { + metricFilter = make(map[string]struct{}, len(metricNames)) + for _, name := range metricNames { + metricFilter[name] = struct{}{} + } + } + } + for _, mf := range mfs { + if metricFilter != nil { + if _, ok := metricFilter[mf.GetName()]; !ok { + continue + } + } if handleError(enc.Encode(mf)) { return } @@ -353,7 +497,7 @@ const ( // log.Logger from the standard library implements this interface, and it is // easy to implement by custom loggers, if they don't do so already anyway. type Logger interface { - Println(v ...interface{}) + Println(v ...any) } // HandlerOpts specifies options how to serve metrics via an http.Handler. The @@ -400,6 +544,40 @@ type HandlerOpts struct { // Service Unavailable and a suitable message in the body. If // MaxRequestsInFlight is 0 or negative, no limit is applied. MaxRequestsInFlight int + // CoalesceGather, if true, deduplicates concurrent Gather calls so that + // only one collection runs at a time. Additional requests that arrive + // while a Gather is in flight will receive the same result once it + // completes. This prevents goroutine pile-up when the scrape rate is + // faster than the time collectors need to produce metrics. + // + // When enabled, concurrent scrapers share a single metric snapshot per + // collection cycle. Each request receives its own copy of the returned + // slice, so filtering or reordering it (for example via name[] query + // parameters) is safe. The pointed-to MetricFamily values are still + // shared: the built-in handler only reads them, so this is safe in + // practice, but a custom TransactionalGatherer that mutates the returned + // families in place after Gather returns must not use this option. + // + // Because the snapshot is shared, a request that arrives while a cycle is + // in flight receives that cycle's result even though collection began + // before the request; two scrapers joined to one cycle observe the same + // timestamps rather than independently gathered data. + // + // Consider using CoalesceGather together with Timeout. Timeout bounds the + // client-facing response time and keeps at most one collection running at + // a time, but it does not cancel the underlying Gather: a joined request + // that times out still holds a MaxRequestsInFlight slot until the shared + // collection completes. + // + // Panic handling: a panicking Collector is already turned into an error by + // the registry, so joiners receive that error like any other. In the rare + // case where the wrapped gatherer itself panics, the panicking request's + // panic propagates as usual (handled by net/http), while requests that + // joined the same cycle receive an error rather than an empty response. + // + // NOTE: This option is experimental and may change or be removed in a + // future release. + CoalesceGather bool // If handling a request takes longer than Timeout, it is responded to // with 503 ServiceUnavailable and a suitable Message. No timeout is // applied if Timeout is 0 or negative. Note that with the current @@ -407,8 +585,9 @@ type HandlerOpts struct { // described above (and even that only if sending of the body hasn't // started yet), while the bulk work of gathering all the metrics keeps // running in the background (with the eventual result to be thrown - // away). Until the implementation is improved, it is recommended to - // implement a separate timeout in potentially slow Collectors. + // away). When CoalesceGather is enabled, only one such background Gather + // can be in flight at a time. It is also recommended to implement a + // separate timeout in potentially slow Collectors. Timeout time.Duration // If true, the experimental OpenMetrics encoding is added to the // possible options during content negotiation. Note that Prometheus @@ -460,7 +639,7 @@ func httpError(rsp http.ResponseWriter, err error) { // negotiateEncodingWriter reads the Accept-Encoding header from a request and // selects the right compression based on an allow-list of supported -// compressions. It returns a writer implementing the compression and an the +// compressions. It returns a writer implementing the compression and the // correct value that the caller can set in the response header. func negotiateEncodingWriter(r *http.Request, rw io.Writer, compressions []string) (_ io.Writer, encodingHeaderValue string, closeWriter func(), _ error) { if len(compressions) == 0 { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go index d3482c40ca..0248579742 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go @@ -75,10 +75,10 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou resp, err := next.RoundTrip(r) if err == nil { l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) - for label, resolve := range rtOpts.extraLabelsFromCtx { - l[label] = resolve(resp.Request.Context()) + for label, resolve := range rtOpts.extraLabelsFromRequest { + l[label] = resolve(resp.Request) } - addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r)) } return resp, err } @@ -119,10 +119,10 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT resp, err := next.RoundTrip(r) if err == nil { l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) - for label, resolve := range rtOpts.extraLabelsFromCtx { - l[label] = resolve(resp.Request.Context()) + for label, resolve := range rtOpts.extraLabelsFromRequest { + l[label] = resolve(resp.Request) } - observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r)) } return resp, err } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go index 9332b0249a..9dec091ac7 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go @@ -28,24 +28,36 @@ import ( // magicString is used for the hacky label test in checkLabels. Remove once fixed. const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" -// observeWithExemplar is a wrapper for [prometheus.ExemplarAdder.ExemplarObserver], -// which falls back to [prometheus.Observer.Observe] if no labels are provided. +// observeWithExemplar records val on obs. If labels is non-nil and obs +// implements [prometheus.ExemplarObserver], the exemplar is attached via +// ObserveWithExemplar; otherwise the exemplar is dropped and the value is +// recorded with a plain [prometheus.Observer.Observe]. This mirrors the +// safe-cast pattern in [prometheus.Timer.ObserveDurationWithExemplar] and +// ensures we never panic when callers pass an ObserverVec backed by a +// summary, which cannot carry exemplars in the Prometheus exposition format. func observeWithExemplar(obs prometheus.Observer, val float64, labels map[string]string) { - if labels == nil { - obs.Observe(val) - return + if labels != nil { + if eo, ok := obs.(prometheus.ExemplarObserver); ok { + eo.ObserveWithExemplar(val, labels) + return + } } - obs.(prometheus.ExemplarObserver).ObserveWithExemplar(val, labels) + obs.Observe(val) } -// addWithExemplar is a wrapper for [prometheus.ExemplarAdder.AddWithExemplar], -// which falls back to [prometheus.Counter.Add] if no labels are provided. -func addWithExemplar(obs prometheus.Counter, val float64, labels map[string]string) { - if labels == nil { - obs.Add(val) - return +// addWithExemplar records val on c. If labels is non-nil and c implements +// [prometheus.ExemplarAdder], the exemplar is attached via AddWithExemplar; +// otherwise the exemplar is dropped and the value is recorded with a plain +// [prometheus.Counter.Add]. The safe-cast keeps the helper robust against +// custom Counter implementations that do not advertise exemplar support. +func addWithExemplar(c prometheus.Counter, val float64, labels map[string]string) { + if labels != nil { + if ea, ok := c.(prometheus.ExemplarAdder); ok { + ea.AddWithExemplar(val, labels) + return + } } - obs.(prometheus.ExemplarAdder).AddWithExemplar(val, labels) + c.Add(val) } // InstrumentHandlerInFlight is a middleware that wraps the provided @@ -97,10 +109,10 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) } } @@ -108,10 +120,10 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op now := time.Now() next.ServeHTTP(w, r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) } } @@ -147,10 +159,10 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r)) } } @@ -158,10 +170,10 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, next.ServeHTTP(w, r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r)) } } @@ -200,10 +212,10 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha now := time.Now() d := newDelegator(w, func(status int) { l := labels(code, method, r.Method, status, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) }) next.ServeHTTP(d, r) } @@ -244,10 +256,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, size := computeApproximateRequestSize(r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r)) } } @@ -256,10 +268,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, size := computeApproximateRequestSize(r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r)) } } @@ -296,10 +308,10 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r)) }) } @@ -366,7 +378,7 @@ func checkLabels(c prometheus.Collector) (code, method bool) { panic("metric partitioned with non-supported labels") } } - return + return code, method } func isLabelCurried(c prometheus.Collector, label string) bool { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go index 5d4383aa14..d4c0954f36 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go @@ -15,6 +15,7 @@ package promhttp import ( "context" + "net/http" "github.com/prometheus/client_golang/prometheus" ) @@ -24,28 +25,31 @@ type Option interface { apply(*options) } +// LabelValueFromRequest is used to compute the label value from request. +type LabelValueFromRequest func(request *http.Request) string + // LabelValueFromCtx are used to compute the label value from request context. // Context can be filled with values from request through middleware. type LabelValueFromCtx func(ctx context.Context) string // options store options for both a handler or round tripper. type options struct { - extraMethods []string - getExemplarFn func(requestCtx context.Context) prometheus.Labels - extraLabelsFromCtx map[string]LabelValueFromCtx + extraMethods []string + getExemplarFn func(req *http.Request) prometheus.Labels + extraLabelsFromRequest map[string]LabelValueFromRequest } func defaultOptions() *options { return &options{ - getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }, - extraLabelsFromCtx: map[string]LabelValueFromCtx{}, + getExemplarFn: func(req *http.Request) prometheus.Labels { return nil }, + extraLabelsFromRequest: map[string]LabelValueFromRequest{}, } } func (o *options) emptyDynamicLabels() prometheus.Labels { labels := prometheus.Labels{} - for label := range o.extraLabelsFromCtx { + for label := range o.extraLabelsFromRequest { labels[label] = "" } @@ -66,19 +70,39 @@ func WithExtraMethods(methods ...string) Option { }) } -// WithExemplarFromContext allows to inject function that will get exemplar from context that will be put to counter and histogram metrics. +// WithExemplarFromRequest allows you to inject a function that will get exemplar from request that will be put to counter and histogram metrics. // If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but // metric will continue to observe/increment. -func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { +func WithExemplarFromRequest(getExemplarFn func(req *http.Request) prometheus.Labels) Option { return optionApplyFunc(func(o *options) { o.getExemplarFn = getExemplarFn }) } +// WithExemplarFromContext allows you to inject a function that will get exemplar from context that will be put to counter and histogram metrics. +// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but +// metric will continue to observe/increment. +func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { + return optionApplyFunc(func(o *options) { + o.getExemplarFn = func(req *http.Request) prometheus.Labels { + return getExemplarFn(req.Context()) + } + }) +} + +// WithLabelFromRequest registers a label for dynamic resolution with access to the request. +func WithLabelFromRequest(name string, valueFn LabelValueFromRequest) Option { + return optionApplyFunc(func(o *options) { + o.extraLabelsFromRequest[name] = valueFn + }) +} + // WithLabelFromCtx registers a label for dynamic resolution with access to context. // See the example for ExampleInstrumentHandlerWithLabelResolver for example usage func WithLabelFromCtx(name string, valueFn LabelValueFromCtx) Option { return optionApplyFunc(func(o *options) { - o.extraLabelsFromCtx[name] = valueFn + o.extraLabelsFromRequest[name] = func(req *http.Request) string { + return valueFn(req.Context()) + } }) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go index c6fd2f58b7..ed0681c8b4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go @@ -214,6 +214,19 @@ func (err AlreadyRegisteredError) Error() string { // by a Gatherer to report multiple errors during MetricFamily gathering. type MultiError []error +// SafeMultiError is a thread-safe wrapper around MultiError using a mutex. +type SafeMultiError struct { + mu sync.Mutex + errs MultiError +} + +// Appends the provided error to the contained MultiError in a thread-safe way. +func (s *SafeMultiError) Append(err error) { + s.mu.Lock() + s.errs.Append(err) + s.mu.Unlock() +} + // Error formats the contained errors as a bullet point list, preceded by the // total number of errors. Note that this results in a multi-line string. func (errs MultiError) Error() string { @@ -408,6 +421,16 @@ func (r *Registry) MustRegister(cs ...Collector) { } } +// MustGather implements Gatherer. +// Wraps around Gather and panics if Gather fails for any reason. +func (r *Registry) MustGather() []*dto.MetricFamily { + mfs, err := r.Gather() + if err != nil { + panic(err) + } + return mfs +} + // Gather implements Gatherer. func (r *Registry) Gather() ([]*dto.MetricFamily, error) { r.mtx.RLock() @@ -423,7 +446,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { uncheckedMetricChan = make(chan Metric, capMetricChan) metricHashes = map[uint64]struct{}{} wg sync.WaitGroup - errs MultiError // The collected errors to return in the end. + safeErrs = &SafeMultiError{} // To collect errors in a threadsafe way registeredDescIDs map[uint64]struct{} // Only used for pedantic checks ) @@ -453,9 +476,9 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { for { select { case collector := <-checkedCollectors: - collector.Collect(checkedMetricChan) + safeErrs.Append((safeCollect(collector, checkedMetricChan))) case collector := <-uncheckedCollectors: - collector.Collect(uncheckedMetricChan) + safeErrs.Append(safeCollect(collector, uncheckedMetricChan)) default: return } @@ -499,7 +522,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { cmc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, registeredDescIDs, @@ -509,7 +532,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { umc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, nil, @@ -526,7 +549,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { cmc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, registeredDescIDs, @@ -536,7 +559,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { umc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, nil, @@ -556,7 +579,8 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { break } } - return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() + + return internal.NormalizeMetricFamilies(metricFamiliesByName), safeErrs.errs.MaybeUnwrap() } // Describe implements Collector. @@ -571,6 +595,24 @@ func (r *Registry) Describe(ch chan<- *Desc) { } } +// Helper wrapper around Collector.Collect. +// It tries to collect from the channel, recovers on panic and +// if it has recovered from a panic, then it sends an InvalidMetric into +// the channel with an InvalidDesc, and an error that includes a stack trace. +func safeCollect(c Collector, ch chan<- Metric) (err error) { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 64<<10) // 64 KB + n := runtime.Stack(buf, false) + err = fmt.Errorf("prometheus collector panic recovered: type=%T: error=%v\nstack trace=%s", c, r, buf[:n]) + ch <- NewInvalidMetric(NewInvalidDesc(err), err) + } + }() + c.Collect(ch) + + return err +} + // Collect implements Collector. func (r *Registry) Collect(ch chan<- Metric) { r.mtx.RLock() @@ -599,10 +641,12 @@ func WriteToTextfile(filename string, g Gatherer) error { mfs, err := g.Gather() if err != nil { + tmp.Close() return err } for _, mf := range mfs { if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil { + tmp.Close() return err } } @@ -685,6 +729,9 @@ func processMetric( metricFamily = &dto.MetricFamily{} metricFamily.Name = proto.String(desc.fqName) metricFamily.Help = proto.String(desc.help) + if desc.unit != "" { + metricFamily.Unit = proto.String(desc.unit) + } // TODO(beorn7): Simplify switch once Desc has type. switch { case dtoMetric.Gauge != nil: diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index ac5203c6fa..c12b8d13d4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -101,6 +101,9 @@ type SummaryOpts struct { // string. Help string + // Unit provides the unit of this Summary. + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. @@ -181,11 +184,12 @@ type SummaryVecOpts struct { // NewSummary creates a new Summary based on the provided SummaryOpts. func NewSummary(opts SummaryOpts) Summary { return newSummary( - NewDesc( + V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), opts, ) @@ -578,6 +582,7 @@ func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &SummaryVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go index 52344fef53..c1318ffb51 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/timer.go @@ -37,10 +37,10 @@ type Timer struct { // or // // func TimeMeWithExemplar() { -// timer := NewTimer(myHistogram) -// defer timer.ObserveDurationWithExemplar(exemplar) -// // Do actual work. -// } +// timer := NewTimer(myHistogram) +// defer timer.ObserveDurationWithExemplar(exemplar) +// // Do actual work. +// } func NewTimer(o Observer) *Timer { return &Timer{ begin: time.Now(), @@ -66,7 +66,7 @@ func (t *Timer) ObserveDuration() time.Duration { // ObserveDurationWithExemplar is like ObserveDuration, but it will also // observe exemplar with the duration unless exemplar is nil or provided Observer can't -// be casted to ExemplarObserver. +// be cast to ExemplarObserver. func (t *Timer) ObserveDurationWithExemplar(exemplar Labels) time.Duration { d := time.Since(t.begin) eo, ok := t.observer.(ExemplarObserver) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/prometheus/client_golang/prometheus/vec.go index 487b466563..121d2a9639 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go @@ -193,9 +193,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { // // Keeping the Metric for later use is possible (and should be considered if // performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Metric from the MetricVec. In that case, the -// Metric will still exist, but it will not be exported anymore, even if a -// Metric with the same label values is created later. +// Delete can be used to delete the Metric from the MetricVec. In that case, if +// you have previously kept a reference to that Metric, the Metric object still +// exists and can be used, but it will not be exported anymore. If a Metric with +// the same label values is created later, updates to the old Metric reference +// will not be exported. // // An error is returned if the number of label values is not the same as the // number of variable labels in Desc (minus any curried labels). @@ -657,7 +659,7 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { } var labelsPool = &sync.Pool{ - New: func() interface{} { + New: func() any { return make(Labels) }, } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go index 2ed1285068..697f55558b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go @@ -230,6 +230,7 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { return &Desc{ fqName: desc.fqName, help: desc.help, + unit: desc.unit, variableLabels: desc.variableLabels, constLabelPairs: desc.constLabelPairs, err: fmt.Errorf("attempted wrapping with already existing label name %q", ln), @@ -238,8 +239,8 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { constLabels[ln] = lv } // NewDesc will do remaining validations. - newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) - // Propagate errors if there was any. This will override any errer + newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels, WithUnit(desc.unit)) + // Propagate errors if there was any. This will override any error // created by NewDesc above, i.e. earlier errors get precedence. if desc.err != nil { newDesc.err = desc.err diff --git a/vendor/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/prometheus/common/expfmt/expfmt.go index 4e4c13e724..10bf35708c 100644 --- a/vendor/github.com/prometheus/common/expfmt/expfmt.go +++ b/vendor/github.com/prometheus/common/expfmt/expfmt.go @@ -122,7 +122,7 @@ func NewOpenMetricsFormat(version string) (Format, error) { // removed. func (f Format) WithEscapingScheme(s model.EscapingScheme) Format { var terms []string - for _, p := range strings.Split(string(f), ";") { + for p := range strings.SplitSeq(string(f), ";") { toks := strings.Split(p, "=") if len(toks) != 2 { trimmed := strings.TrimSpace(p) @@ -194,7 +194,7 @@ func (f Format) FormatType() FormatType { // "escaping" term exists, that will be used. Otherwise, the global default will // be returned. func (f Format) ToEscapingScheme() model.EscapingScheme { - for _, p := range strings.Split(string(f), ";") { + for p := range strings.SplitSeq(string(f), ";") { toks := strings.Split(p, "=") if len(toks) != 2 { continue diff --git a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go index 21b93bca36..0480e7af5b 100644 --- a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go +++ b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go @@ -30,7 +30,6 @@ import ( type encoderOption struct { withCreatedLines bool - withUnit bool } type EncoderOption func(*encoderOption) @@ -51,17 +50,6 @@ func WithCreatedLines() EncoderOption { } } -// WithUnit is an EncoderOption enabling a set unit to be written to the output -// and to be added to the metric name, if it's not there already, as a suffix. -// Without opting in this way, the unit will not be added to the metric name and, -// on top of that, the unit will not be passed onto the output, even if it -// were declared in the *dto.MetricFamily struct, i.e. even if in.Unit !=nil. -func WithUnit() EncoderOption { - return func(t *encoderOption) { - t.withUnit = true - } -} - // MetricFamilyToOpenMetrics converts a MetricFamily proto message into the // OpenMetrics text format and writes the resulting lines to 'out'. It returns // the number of bytes written and any error encountered. The output will have @@ -99,15 +87,6 @@ func WithUnit() EncoderOption { // its type will be set to `unknown` in that case to avoid invalid OpenMetrics // output. // -// - According to the OM specs, the `# UNIT` line is optional, but if populated, -// the unit has to be present in the metric name as its suffix: -// (see https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#unit). -// However, in order to accommodate any potential scenario where such a change in the -// metric name is not desirable, the users are here given the choice of either explicitly -// opt in, in case they wish for the unit to be included in the output AND in the metric name -// as a suffix (see the description of the WithUnit function above), -// or not to opt in, in case they don't want for any of that to happen. -// // - No support for the following (optional) features: info type, // stateset type, gaugehistogram type. // @@ -151,9 +130,6 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E if metricType == dto.MetricType_COUNTER && strings.HasSuffix(compliantName, "_total") { compliantName = name[:len(name)-6] } - if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, "_"+*in.Unit) { - compliantName = compliantName + "_" + *in.Unit - } // Comments, first HELP, then TYPE. if in.Help != nil { @@ -217,7 +193,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E if err != nil { return written, err } - if toOM.withUnit && in.Unit != nil { + if in.Unit != nil { n, err = w.WriteString("# UNIT ") written += n if err != nil { diff --git a/vendor/github.com/prometheus/common/expfmt/text_create.go b/vendor/github.com/prometheus/common/expfmt/text_create.go index 6b89781456..f4074ae9a3 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_create.go +++ b/vendor/github.com/prometheus/common/expfmt/text_create.go @@ -42,12 +42,12 @@ const ( var ( bufPool = sync.Pool{ - New: func() interface{} { + New: func() any { return bufio.NewWriter(io.Discard) }, } numBufPool = sync.Pool{ - New: func() interface{} { + New: func() any { b := make([]byte, 0, initialNumBufSize) return &b }, diff --git a/vendor/github.com/prometheus/common/expfmt/text_parse.go b/vendor/github.com/prometheus/common/expfmt/text_parse.go index 00c8841a10..4ce1f40b81 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_parse.go +++ b/vendor/github.com/prometheus/common/expfmt/text_parse.go @@ -339,6 +339,16 @@ func (p *TextParser) startLabelName() stateFn { return nil // Unexpected end of input. } if p.currentByte == '}' { + if p.currentMF == nil { + // The closing brace was reached before any metric name was read, + // e.g. for the input "{}". There is no metric to attach labels to, + // so this is a malformed exposition. This mirrors the guard in + // startLabelValue. currentMF (not currentMetric) is checked because + // reset only clears currentMF between parses. + p.parseError("invalid metric name") + p.currentLabelPairs = nil + return nil + } p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPairs...) p.currentLabelPairs = nil if p.skipBlankTab(); p.err != nil { diff --git a/vendor/github.com/prometheus/common/helpers/templates/time.go b/vendor/github.com/prometheus/common/helpers/templates/time.go index b7dc655f67..d9fcaa0ab7 100644 --- a/vendor/github.com/prometheus/common/helpers/templates/time.go +++ b/vendor/github.com/prometheus/common/helpers/templates/time.go @@ -25,7 +25,7 @@ import ( var errNaNOrInf = errors.New("value is NaN or Inf") -func ConvertToFloat(i interface{}) (float64, error) { +func ConvertToFloat(i any) (float64, error) { switch v := i.(type) { case float64: return v, nil @@ -58,7 +58,7 @@ func FloatToTime(v float64) (*time.Time, error) { return &t, nil } -func HumanizeDuration(i interface{}) (string, error) { +func HumanizeDuration(i any) (string, error) { v, err := ConvertToFloat(i) if err != nil { return "", err @@ -105,7 +105,7 @@ func HumanizeDuration(i interface{}) (string, error) { return fmt.Sprintf("%.4g%ss", v, prefix), nil } -func HumanizeTimestamp(i interface{}) (string, error) { +func HumanizeTimestamp(i any) (string, error) { v, err := ConvertToFloat(i) if err != nil { return "", err diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go index dfeb34be5f..29688a13c8 100644 --- a/vendor/github.com/prometheus/common/model/labels.go +++ b/vendor/github.com/prometheus/common/model/labels.go @@ -124,7 +124,7 @@ func (ln LabelName) IsValidLegacy() bool { } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (ln *LabelName) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err diff --git a/vendor/github.com/prometheus/common/model/labelset.go b/vendor/github.com/prometheus/common/model/labelset.go index 9de47b2568..6010b26a88 100644 --- a/vendor/github.com/prometheus/common/model/labelset.go +++ b/vendor/github.com/prometheus/common/model/labelset.go @@ -16,6 +16,7 @@ package model import ( "encoding/json" "fmt" + "maps" "sort" ) @@ -107,9 +108,7 @@ func (ls LabelSet) Before(o LabelSet) bool { // Clone returns a copy of the label set. func (ls LabelSet) Clone() LabelSet { lsn := make(LabelSet, len(ls)) - for ln, lv := range ls { - lsn[ln] = lv - } + maps.Copy(lsn, ls) return lsn } @@ -117,13 +116,9 @@ func (ls LabelSet) Clone() LabelSet { func (ls LabelSet) Merge(other LabelSet) LabelSet { result := make(LabelSet, len(ls)) - for k, v := range ls { - result[k] = v - } + maps.Copy(result, ls) - for k, v := range other { - result[k] = v - } + maps.Copy(result, other) return result } diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go index 3feebf328a..2fe461511d 100644 --- a/vendor/github.com/prometheus/common/model/metric.go +++ b/vendor/github.com/prometheus/common/model/metric.go @@ -17,6 +17,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "regexp" "sort" "strconv" @@ -24,7 +25,6 @@ import ( "unicode/utf8" dto "github.com/prometheus/client_model/go" - "go.yaml.in/yaml/v2" "google.golang.org/protobuf/proto" ) @@ -78,14 +78,6 @@ const ( UTF8Validation ) -var _ interface { - yaml.Marshaler - yaml.Unmarshaler - json.Marshaler - json.Unmarshaler - fmt.Stringer -} = new(ValidationScheme) - // String returns the string representation of s. func (s ValidationScheme) String() string { switch s { @@ -267,9 +259,7 @@ func (m Metric) Before(o Metric) bool { // Clone returns a copy of the Metric. func (m Metric) Clone() Metric { clone := make(Metric, len(m)) - for k, v := range m { - clone[k] = v - } + maps.Copy(clone, m) return clone } diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index 1730b0fdc1..0854753f4a 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -123,44 +123,38 @@ func (t Time) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. func (t *Time) UnmarshalJSON(b []byte) error { - p := strings.Split(string(b), ".") - switch len(p) { - case 1: - v, err := strconv.ParseInt(p[0], 10, 64) + base, frac, found := strings.Cut(string(b), ".") + if !found { + v, err := strconv.ParseInt(base, 10, 64) if err != nil { return err } *t = Time(v * second) - - case 2: - v, err := strconv.ParseInt(p[0], 10, 64) + } else { + v, err := strconv.ParseInt(base, 10, 64) if err != nil { return err } - v *= second - prec := dotPrecision - len(p[1]) + prec := dotPrecision - len(frac) if prec < 0 { - p[1] = p[1][:dotPrecision] - } else if prec > 0 { - p[1] += strings.Repeat("0", prec) + frac = frac[:dotPrecision] } - - va, err := strconv.ParseInt(p[1], 10, 32) + va, err := strconv.ParseInt(frac, 10, 32) if err != nil { return err } - - // If the value was something like -0.1 the negative is lost in the - // parsing because of the leading zero, this ensures that we capture it. - if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 { - *t = Time(v+va) * -1 - } else { - *t = Time(v + va) + switch prec { + case 1: + va *= 10 + case 2: + va *= 100 } - default: - return fmt.Errorf("invalid time %q", string(b)) + if len(base) > 0 && base[0] == '-' { + va = -va + } + *t = Time(v*second + va) } return nil } @@ -340,12 +334,12 @@ func (d *Duration) UnmarshalText(text []byte) error { } // MarshalYAML implements the yaml.Marshaler interface. -func (d Duration) MarshalYAML() (interface{}, error) { +func (d Duration) MarshalYAML() (any, error) { return d.String(), nil } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err diff --git a/vendor/github.com/prometheus/common/model/value.go b/vendor/github.com/prometheus/common/model/value.go index a9995a37ee..8dffd9c4a5 100644 --- a/vendor/github.com/prometheus/common/model/value.go +++ b/vendor/github.com/prometheus/common/model/value.go @@ -259,13 +259,13 @@ func (s Scalar) String() string { // MarshalJSON implements json.Marshaler. func (s Scalar) MarshalJSON() ([]byte, error) { v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) - return json.Marshal([...]interface{}{s.Timestamp, v}) + return json.Marshal([...]any{s.Timestamp, v}) } // UnmarshalJSON implements json.Unmarshaler. func (s *Scalar) UnmarshalJSON(b []byte) error { var f string - v := [...]interface{}{&s.Timestamp, &f} + v := [...]any{&s.Timestamp, &f} if err := json.Unmarshal(b, &v); err != nil { return err @@ -291,12 +291,12 @@ func (s *String) String() string { // MarshalJSON implements json.Marshaler. func (s String) MarshalJSON() ([]byte, error) { - return json.Marshal([]interface{}{s.Timestamp, s.Value}) + return json.Marshal([]any{s.Timestamp, s.Value}) } // UnmarshalJSON implements json.Unmarshaler. func (s *String) UnmarshalJSON(b []byte) error { - v := [...]interface{}{&s.Timestamp, &s.Value} + v := [...]any{&s.Timestamp, &s.Value} return json.Unmarshal(b, &v) } diff --git a/vendor/github.com/prometheus/common/model/value_float.go b/vendor/github.com/prometheus/common/model/value_float.go index 6bfc757d18..b7d93615e2 100644 --- a/vendor/github.com/prometheus/common/model/value_float.go +++ b/vendor/github.com/prometheus/common/model/value_float.go @@ -79,7 +79,7 @@ func (s SamplePair) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil + return fmt.Appendf(nil, "[%s,%s]", t, v), nil } // UnmarshalJSON implements json.Unmarshaler. diff --git a/vendor/github.com/prometheus/common/model/value_histogram.go b/vendor/github.com/prometheus/common/model/value_histogram.go index 91ce5b7a45..f27856ccc4 100644 --- a/vendor/github.com/prometheus/common/model/value_histogram.go +++ b/vendor/github.com/prometheus/common/model/value_histogram.go @@ -67,11 +67,11 @@ func (s HistogramBucket) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil + return fmt.Appendf(nil, "[%s,%s,%s,%s]", b, l, u, c), nil } func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { - tmp := []interface{}{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} + tmp := []any{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} wantLen := len(tmp) if err := json.Unmarshal(buf, &tmp); err != nil { return err @@ -152,11 +152,11 @@ func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil + return fmt.Appendf(nil, "[%s,%s]", t, v), nil } func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { - tmp := []interface{}{&s.Timestamp, &s.Histogram} + tmp := []any{&s.Timestamp, &s.Histogram} wantLen := len(tmp) if err := json.Unmarshal(buf, &tmp); err != nil { return err diff --git a/vendor/github.com/prometheus/common/promslog/slog.go b/vendor/github.com/prometheus/common/promslog/slog.go index f5b9e98ba2..f8f77165a6 100644 --- a/vendor/github.com/prometheus/common/promslog/slog.go +++ b/vendor/github.com/prometheus/common/promslog/slog.go @@ -61,7 +61,7 @@ func NewLevel() *Level { } } -func (l *Level) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (l *Level) UnmarshalYAML(unmarshal func(any) error) error { var s string type plain string if err := unmarshal((*plain)(&s)); err != nil { diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index cce3ef1d16..a7c5f553e1 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -55,13 +55,13 @@ ifneq ($(shell command -v gotestsum 2> /dev/null),) endif endif -PROMU_VERSION ?= 0.18.0 +PROMU_VERSION ?= 0.20.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v2.10.1 +GOLANGCI_LINT_VERSION ?= v2.11.4 GOLANGCI_FMT_OPTS ?= # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. @@ -90,8 +90,8 @@ ifdef DOCKERFILE_PATH $(error DOCKERFILE_PATH is deprecated. Use DOCKERFILE_VARIANTS ?= $(DOCKERFILE_PATH) in the Makefile) endif -DOCKER_ARCHS ?= amd64 -DOCKERFILE_VARIANTS ?= Dockerfile $(wildcard Dockerfile.*) +DOCKER_ARCHS ?= amd64 arm64 armv7 ppc64le riscv64 s390x +DOCKERFILE_VARIANTS ?= $(wildcard Dockerfile Dockerfile.*) # Function to extract variant from Dockerfile label. # Returns the variant name from io.prometheus.image.variant label, or "default" if not found. @@ -109,24 +109,6 @@ endif # Build variant:dockerfile pairs for shell iteration. DOCKERFILE_VARIANTS_WITH_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df)):$(df)) -# Shell helper to check whether a dockerfile/arch pair is excluded. -define dockerfile_arch_is_excluded -case " $(DOCKERFILE_ARCH_EXCLUSIONS) " in \ - *" $$dockerfile:$(1) "*) true ;; \ - *) false ;; \ -esac -endef - -# Shell helper to check whether a registry/arch pair is excluded. -# Extracts registry from DOCKER_REPO (e.g., quay.io/prometheus -> quay.io) -define registry_arch_is_excluded -registry=$$(echo "$(DOCKER_REPO)" | cut -d'/' -f1); \ -case " $(DOCKER_REGISTRY_ARCH_EXCLUSIONS) " in \ - *" $$registry:$(1) "*) true ;; \ - *) false ;; \ -esac -endef - BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) @@ -268,10 +250,6 @@ $(BUILD_DOCKER_ARCHS): common-docker-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ - if $(call dockerfile_arch_is_excluded,$*); then \ - echo "Skipping $$variant_name variant for linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ distroless_arch="$*"; \ if [ "$*" = "armv7" ]; then \ distroless_arch="arm"; \ @@ -306,14 +284,6 @@ $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ - if $(call dockerfile_arch_is_excluded,$*); then \ - echo "Skipping push for $$variant_name variant on linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$*); then \ - echo "Skipping push for $$variant_name variant on linux-$* to $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Pushing $$variant_name variant for linux-$*"; \ docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ @@ -341,14 +311,6 @@ $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ dockerfile=$${variant#*:}; \ variant_name=$${variant%%:*}; \ - if $(call dockerfile_arch_is_excluded,$*); then \ - echo "Skipping tag for $$variant_name variant on linux-$* (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$*); then \ - echo "Skipping tag for $$variant_name variant on linux-$* for $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ echo "Tagging $$variant_name variant for linux-$* as latest"; \ docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest-$$variant_name"; \ @@ -370,14 +332,6 @@ common-docker-manifest: echo "Creating manifest for $$variant_name variant"; \ refs=""; \ for arch in $(DOCKER_ARCHS); do \ - if $(call dockerfile_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for $$variant_name (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for $$variant_name on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ done; \ if [ -z "$$refs" ]; then \ @@ -391,14 +345,6 @@ common-docker-manifest: echo "Creating default variant ($$variant_name) manifest"; \ refs=""; \ for arch in $(DOCKER_ARCHS); do \ - if $(call dockerfile_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for default variant (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for default variant on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)"; \ done; \ if [ -z "$$refs" ]; then \ @@ -413,14 +359,6 @@ common-docker-manifest: echo "Creating manifest for $$variant_name variant version tag"; \ refs=""; \ for arch in $(DOCKER_ARCHS); do \ - if $(call dockerfile_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for $$variant_name version tag (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for $$variant_name version tag on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ done; \ if [ -z "$$refs" ]; then \ @@ -434,14 +372,6 @@ common-docker-manifest: echo "Creating default variant version tag manifest"; \ refs=""; \ for arch in $(DOCKER_ARCHS); do \ - if $(call dockerfile_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for default variant version tag (excluded by DOCKERFILE_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ - if $(call registry_arch_is_excluded,$$arch); then \ - echo " Skipping $$arch for default variant version tag on $(DOCKER_REPO) (excluded by DOCKER_REGISTRY_ARCH_EXCLUSIONS)"; \ - continue; \ - fi; \ refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)"; \ done; \ if [ -z "$$refs" ]; then \ @@ -495,9 +425,3 @@ $(1)_precheck: exit 1; \ fi endef - -govulncheck: install-govulncheck - govulncheck ./... - -install-govulncheck: - command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 0718239cf1..363524094b 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -7,7 +7,7 @@ metrics from the pseudo-filesystems /proc and /sys. backwards-incompatible ways without warnings. Use it at your own risk. [![Go Reference](https://pkg.go.dev/badge/github.com/prometheus/procfs.svg)](https://pkg.go.dev/github.com/prometheus/procfs) -[![CircleCI](https://circleci.com/gh/prometheus/procfs/tree/master.svg?style=svg)](https://circleci.com/gh/prometheus/procfs/tree/master) +[![Build Status](https://github.com/prometheus/procfs/actions/workflows/ci.yml/badge.svg)](https://github.com/prometheus/procfs/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/procfs)](https://goreportcard.com/report/github.com/prometheus/procfs) ## Usage diff --git a/vendor/github.com/prometheus/procfs/SECURITY.md b/vendor/github.com/prometheus/procfs/SECURITY.md index fed02d85c7..5e6f976dbf 100644 --- a/vendor/github.com/prometheus/procfs/SECURITY.md +++ b/vendor/github.com/prometheus/procfs/SECURITY.md @@ -3,4 +3,4 @@ The Prometheus security policy, including how to report vulnerabilities, can be found here: - +[https://prometheus.io/docs/operating/security/](https://prometheus.io/docs/operating/security/) diff --git a/vendor/github.com/prometheus/procfs/crypto.go b/vendor/github.com/prometheus/procfs/crypto.go index e4a5876eaf..d93b712e05 100644 --- a/vendor/github.com/prometheus/procfs/crypto.go +++ b/vendor/github.com/prometheus/procfs/crypto.go @@ -48,11 +48,13 @@ type Crypto struct { Walksize *uint64 } +var cryptoFile = "crypto" + // Crypto parses an crypto-file (/proc/crypto) and returns a slice of // structs containing the relevant info. More information available here: // https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html func (fs FS) Crypto() ([]Crypto, error) { - path := fs.proc.Path("crypto") + path := fs.proc.Path(cryptoFile) b, err := util.ReadFileNoStat(path) if err != nil { return nil, fmt.Errorf("%w: Cannot read file %v: %w", ErrFileRead, b, err) @@ -82,6 +84,10 @@ func parseCrypto(r io.Reader) ([]Crypto, error) { continue } + if len(out) == 0 { + return nil, fmt.Errorf("%w: parsed invalid line before name parsed: %q", ErrFileParse, text) + } + kv := strings.Split(text, ":") if len(kv) != 2 { return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, text) diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go index 9414a12f42..8594ae7f1e 100644 --- a/vendor/github.com/prometheus/procfs/mountinfo.go +++ b/vendor/github.com/prometheus/procfs/mountinfo.go @@ -17,10 +17,10 @@ import ( "bufio" "bytes" "fmt" + "io" + "os" "strconv" "strings" - - "github.com/prometheus/procfs/internal/util" ) // A MountInfo is a type that describes the details, options @@ -160,9 +160,19 @@ func mountOptionsParser(mountOptions string) map[string]string { return opts } +// readMountInfo reads a full mountinfo file (no 1 MiB cap, unlike util.ReadFileNoStat). +func readMountInfo(path string) ([]byte, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + return io.ReadAll(f) +} + // GetMounts retrieves mountinfo information from `/proc/self/mountinfo`. func GetMounts() ([]*MountInfo, error) { - data, err := util.ReadFileNoStat("/proc/self/mountinfo") + data, err := readMountInfo("/proc/self/mountinfo") if err != nil { return nil, err } @@ -171,7 +181,7 @@ func GetMounts() ([]*MountInfo, error) { // GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`. func GetProcMounts(pid int) ([]*MountInfo, error) { - data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid)) + data, err := readMountInfo(fmt.Sprintf("/proc/%d/mountinfo", pid)) if err != nil { return nil, err } @@ -180,7 +190,7 @@ func GetProcMounts(pid int) ([]*MountInfo, error) { // GetMounts retrieves mountinfo information from `/proc/self/mountinfo`. func (fs FS) GetMounts() ([]*MountInfo, error) { - data, err := util.ReadFileNoStat(fs.proc.Path("self/mountinfo")) + data, err := readMountInfo(fs.proc.Path("self/mountinfo")) if err != nil { return nil, err } @@ -189,7 +199,7 @@ func (fs FS) GetMounts() ([]*MountInfo, error) { // GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`. func (fs FS) GetProcMounts(pid int) ([]*MountInfo, error) { - data, err := util.ReadFileNoStat(fs.proc.Path(fmt.Sprintf("%d/mountinfo", pid))) + data, err := readMountInfo(fs.proc.Path(fmt.Sprintf("%d/mountinfo", pid))) if err != nil { return nil, err } diff --git a/vendor/github.com/prometheus/procfs/net_wireless.go b/vendor/github.com/prometheus/procfs/net_wireless.go index 69d0794451..f74dd3bed0 100644 --- a/vendor/github.com/prometheus/procfs/net_wireless.go +++ b/vendor/github.com/prometheus/procfs/net_wireless.go @@ -114,47 +114,47 @@ func parseWireless(r io.Reader) ([]*Wireless, error) { qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], ".")) if err != nil { - return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, qlink, err) + return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, stats[1], err) } qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], ".")) if err != nil { - return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, qlevel, err) + return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, stats[2], err) } qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], ".")) if err != nil { - return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, qnoise, err) + return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, stats[3], err) } dnwid, err := strconv.Atoi(stats[4]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, dnwid, err) + return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, stats[4], err) } dcrypt, err := strconv.Atoi(stats[5]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, dcrypt, err) + return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, stats[5], err) } dfrag, err := strconv.Atoi(stats[6]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, dfrag, err) + return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, stats[6], err) } dretry, err := strconv.Atoi(stats[7]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, dretry, err) + return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, stats[7], err) } dmisc, err := strconv.Atoi(stats[8]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, dmisc, err) + return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, stats[8], err) } mbeacon, err := strconv.Atoi(stats[9]) if err != nil { - return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, mbeacon, err) + return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, stats[9], err) } w := &Wireless{ diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go index 535c08d6fc..7e8a122978 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroup.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go @@ -60,7 +60,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) { } cgroup.HierarchyID, err = strconv.Atoi(fields[0]) if err != nil { - return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, cgroup.HierarchyID) + return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, fields[0]) } if fields[1] != "" { ssNames := strings.Split(fields[1], ",") diff --git a/vendor/modules.txt b/vendor/modules.txt index fbc6d8b2d1..f6c29fa8b9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -844,7 +844,7 @@ github.com/justinas/alice # github.com/kevinburke/ssh_config v1.2.0 ## explicit github.com/kevinburke/ssh_config -# github.com/klauspost/compress v1.18.6 +# github.com/klauspost/compress v1.19.1 ## explicit; go 1.24 github.com/klauspost/compress github.com/klauspost/compress/flate @@ -1819,8 +1819,8 @@ github.com/prometheus/alertmanager/matcher/parse github.com/prometheus/alertmanager/pkg/labels github.com/prometheus/alertmanager/template github.com/prometheus/alertmanager/types -# github.com/prometheus/client_golang v1.23.2 -## explicit; go 1.23.0 +# github.com/prometheus/client_golang v1.24.1 +## explicit; go 1.25.0 github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header github.com/prometheus/client_golang/prometheus @@ -1831,13 +1831,13 @@ github.com/prometheus/client_golang/prometheus/promhttp/internal # github.com/prometheus/client_model v0.6.2 ## explicit; go 1.22.0 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.67.5 -## explicit; go 1.24.0 +# github.com/prometheus/common v0.70.1 +## explicit; go 1.25.0 github.com/prometheus/common/expfmt github.com/prometheus/common/helpers/templates github.com/prometheus/common/model github.com/prometheus/common/promslog -# github.com/prometheus/procfs v0.20.1 +# github.com/prometheus/procfs v0.21.1 ## explicit; go 1.25.0 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs From 37279aa7fed5e11f5825a6ddd26b37b61570ba6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:11:12 +0000 Subject: [PATCH 09/27] build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.13 to 3.4.14 Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.4.13 to 3.4.14. - [Release notes](https://github.com/go-ldap/ldap/releases) - [Commits](https://github.com/go-ldap/ldap/compare/v3.4.13...v3.4.14) --- updated-dependencies: - dependency-name: github.com/go-ldap/ldap/v3 dependency-version: 3.4.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 8 +- vendor/github.com/go-asn1-ber/asn1-ber/ber.go | 33 ++++-- .../github.com/go-asn1-ber/asn1-ber/length.go | 9 +- vendor/github.com/go-ldap/ldap/v3/add.go | 3 + vendor/github.com/go-ldap/ldap/v3/bind.go | 77 ++++++++++--- vendor/github.com/go-ldap/ldap/v3/compare.go | 3 + vendor/github.com/go-ldap/ldap/v3/conn.go | 66 +++++++++-- vendor/github.com/go-ldap/ldap/v3/control.go | 109 +++++++++++++----- vendor/github.com/go-ldap/ldap/v3/del.go | 3 + vendor/github.com/go-ldap/ldap/v3/dn.go | 61 ++++++++-- vendor/github.com/go-ldap/ldap/v3/extended.go | 8 ++ vendor/github.com/go-ldap/ldap/v3/filter.go | 16 +-- vendor/github.com/go-ldap/ldap/v3/moddn.go | 3 + vendor/github.com/go-ldap/ldap/v3/modify.go | 7 ++ .../go-ldap/ldap/v3/passwdmodify.go | 8 +- vendor/github.com/go-ldap/ldap/v3/request.go | 8 ++ vendor/github.com/go-ldap/ldap/v3/response.go | 56 ++++++--- vendor/github.com/go-ldap/ldap/v3/search.go | 8 +- vendor/github.com/go-ldap/ldap/v3/unbind.go | 9 +- vendor/github.com/go-ldap/ldap/v3/whoami.go | 8 +- vendor/modules.txt | 6 +- 22 files changed, 401 insertions(+), 112 deletions(-) diff --git a/go.mod b/go.mod index 8d249ec43a..3334661b2c 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/go-chi/chi/v5 v5.3.1 github.com/go-chi/render v1.0.3 github.com/go-jose/go-jose/v3 v3.0.5 - github.com/go-ldap/ldap/v3 v3.4.13 + github.com/go-ldap/ldap/v3 v3.4.14 github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 github.com/go-micro/plugins/v4/client/grpc v1.2.1 github.com/go-micro/plugins/v4/logger/zerolog v1.2.0 @@ -203,7 +203,7 @@ require ( github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/gdexlab/go-render v1.0.1 // indirect github.com/go-acme/lego/v4 v4.4.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.9.0 // indirect github.com/go-git/go-git/v5 v5.19.1 // indirect diff --git a/go.sum b/go.sum index e497b4c827..605cf4761b 100644 --- a/go.sum +++ b/go.sum @@ -370,8 +370,8 @@ github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= -github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.8 h1:H9AZkK22UOmfX8J84ubyaZxKJZ3FMHVwn8swoMML7iQ= +github.com/go-asn1-ber/asn1-ber v1.5.8/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8= github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= @@ -402,8 +402,8 @@ github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= -github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= -github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0= +github.com/go-ldap/ldap/v3 v3.4.14 h1:D6PYdEgsaVzsXyr6w/yDC06Ria4uUhWm+Rb+er8lfAs= +github.com/go-ldap/ldap/v3 v3.4.14/go.mod h1:S4eJUMUNjDkE0ZJtIZdybwyb03sGGLW6gxXT1Hs8VKA= github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 h1:sfz1YppV05y4sYaW7kXZtrocU/+vimnIWt4cxAYh7+o= github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3/go.mod h1:ZXFhGda43Z2TVbfGZefXyMJzsDHhCh0go3bZUcwTx7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go index f27229ed60..f6e3e0d576 100644 --- a/vendor/github.com/go-asn1-ber/asn1-ber/ber.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/ber.go @@ -19,6 +19,10 @@ import ( // no limit. var MaxPacketLengthBytes int64 = math.MaxInt32 +// MaxNestingDepth specifies the maximum allowed nesting depth when calling ReadPacket, DecodePacket, or +// DecodePacketErr. Set to 0 for no limit. +var MaxNestingDepth int = 1000 + type Packet struct { Identifier Value interface{} @@ -218,7 +222,7 @@ func printPacket(out io.Writer, p *Packet, indent int, printBytes bool) { // ReadPacket reads a single Packet from the reader. func ReadPacket(reader io.Reader) (*Packet, error) { - p, _, err := readPacket(reader) + p, _, err := readPacket(reader, 0) if err != nil { return nil, err } @@ -278,7 +282,7 @@ func int64Length(i int64) (numBytes int) { // DecodePacket decodes the given bytes into a single Packet // If a decode error is encountered, nil is returned. func DecodePacket(data []byte) *Packet { - p, _, _ := readPacket(bytes.NewBuffer(data)) + p, _, _ := readPacket(bytes.NewBuffer(data), 0) return p } @@ -286,7 +290,7 @@ func DecodePacket(data []byte) *Packet { // DecodePacketErr decodes the given bytes into a single Packet // If a decode error is encountered, nil is returned. func DecodePacketErr(data []byte) (*Packet, error) { - p, _, err := readPacket(bytes.NewBuffer(data)) + p, _, err := readPacket(bytes.NewBuffer(data), 0) if err != nil { return nil, err } @@ -294,12 +298,20 @@ func DecodePacketErr(data []byte) (*Packet, error) { } // readPacket reads a single Packet from the reader, returning the number of bytes read. -func readPacket(reader io.Reader) (*Packet, int, error) { +func readPacket(reader io.Reader, depth int) (*Packet, int, error) { + if MaxNestingDepth > 0 && depth >= MaxNestingDepth { + return nil, 0, fmt.Errorf("nesting depth %d exceeds maximum %d", depth, MaxNestingDepth) + } + identifier, length, read, err := readHeader(reader) if err != nil { return nil, read, err } + if length != LengthIndefinite && MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes { + return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes) + } + p := &Packet{ Identifier: identifier, } @@ -326,13 +338,19 @@ func readPacket(reader io.Reader) (*Packet, int, error) { } // Read the next packet - child, r, err := readPacket(reader) + child, r, err := readPacket(reader, depth+1) if err != nil { return nil, read, unexpectedEOF(err) } contentRead += r read += r + // Enforce the aggregate size limit for constructed packets. Indefinite length declares + // no bound up front, so the content bytes are only known as they are read. + if MaxPacketLengthBytes > 0 && int64(contentRead) > MaxPacketLengthBytes { + return nil, read, fmt.Errorf("length %d greater than maximum %d", contentRead, MaxPacketLengthBytes) + } + // Test is this is the EOC marker for our packet if isEOCPacket(child) { if length == LengthIndefinite { @@ -351,11 +369,6 @@ func readPacket(reader io.Reader) (*Packet, int, error) { return nil, read, errors.New("indefinite length used with primitive type") } - // Read definite-length content - if MaxPacketLengthBytes > 0 && int64(length) > MaxPacketLengthBytes { - return nil, read, fmt.Errorf("length %d greater than maximum %d", length, MaxPacketLengthBytes) - } - var content []byte if length > 0 { // Read the content and limit it to the parsed length. diff --git a/vendor/github.com/go-asn1-ber/asn1-ber/length.go b/vendor/github.com/go-asn1-ber/asn1-ber/length.go index 2c81cc3fd2..fc533f0f85 100644 --- a/vendor/github.com/go-asn1-ber/asn1-ber/length.go +++ b/vendor/github.com/go-asn1-ber/asn1-ber/length.go @@ -40,7 +40,7 @@ func readLength(reader io.Reader) (length int, read int, err error) { } // Accumulate into a 64-bit variable - var length64 int64 + var length64 uint64 for i := 0; i < lengthBytes; i++ { b, err = readByte(reader) if err != nil { @@ -53,13 +53,14 @@ func readLength(reader io.Reader) (length int, read int, err error) { // x.600, 8.1.3.5 length64 <<= 8 - length64 |= int64(b) + length64 |= uint64(b) } // Cast to a platform-specific integer length = int(length64) - // Ensure we didn't overflow - if int64(length) != length64 { + // Ensure we didn't overflow or wrap negative. Length octets are unsigned + // (x.600, 8.1.3.5), so a negative result is unrepresentable, not indefinite. + if length < 0 || uint64(length) != length64 { return 0, read, errors.New("long-form length overflow") } diff --git a/vendor/github.com/go-ldap/ldap/v3/add.go b/vendor/github.com/go-ldap/ldap/v3/add.go index 6d8854e083..ca4329b3c9 100644 --- a/vendor/github.com/go-ldap/ldap/v3/add.go +++ b/vendor/github.com/go-ldap/ldap/v3/add.go @@ -83,6 +83,9 @@ func (l *Conn) Add(addRequest *AddRequest) error { return err } + if len(packet.Children) < 2 { + return fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationAddResponse { err := GetLDAPError(packet) if err != nil { diff --git a/vendor/github.com/go-ldap/ldap/v3/bind.go b/vendor/github.com/go-ldap/ldap/v3/bind.go index 6cfd37ebeb..c3ee84e316 100644 --- a/vendor/github.com/go-ldap/ldap/v3/bind.go +++ b/vendor/github.com/go-ldap/ldap/v3/bind.go @@ -3,13 +3,13 @@ package ldap import ( "bytes" "crypto/md5" + "crypto/rand" "encoding/binary" "encoding/hex" enchex "encoding/hex" "errors" "fmt" "io/ioutil" - "math/rand" "strings" "unicode/utf16" @@ -221,12 +221,15 @@ func (l *Conn) DigestMD5Bind(digestMD5BindRequest *DigestMD5BindRequest) (*Diges } if len(params) > 0 { - resp := computeResponse( + resp, err := computeResponse( params, "ldap/"+strings.ToLower(digestMD5BindRequest.Host), digestMD5BindRequest.Username, digestMD5BindRequest.Password, ) + if err != nil { + return nil, fmt.Errorf("compute digest-md5 response: %s", err) + } packet = ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request") packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID")) @@ -291,12 +294,22 @@ func parseParams(str string) (map[string]string, error) { m := make(map[string]string) var key, value string var state int + var escaped bool for i := 0; i <= len(str); i++ { switch state { case 0: // reading key if i == len(str) { return nil, fmt.Errorf("syntax error on %d", i) } + // The digest-challenge is an RFC 2068 #rule (RFC 2831 section 2.1.1), + // which permits optional linear whitespace around the comma directive + // separators. Directive names are tokens that never contain + // whitespace, so skip it here; otherwise a directive following + // "..., name" is keyed with a leading space and the lookups in + // computeResponse (realm, nonce, authzid) miss it. + if str[i] == ' ' || str[i] == '\t' { + continue + } if str[i] != '=' { key += string(str[i]) continue @@ -307,6 +320,14 @@ func parseParams(str string) (map[string]string, error) { m[key] = value break } + // Linear whitespace outside a quoted string is not part of the + // value: an unquoted value is a token and a quoted value's content + // is read in the quoted state below. Skipping it lets a challenge + // using the whitespace the #rule allows (e.g. `nonce="n" , qop=auth`) + // parse the same as the unspaced form. + if str[i] == ' ' || str[i] == '\t' { + continue + } switch str[i] { case ',': m[key] = value @@ -325,20 +346,34 @@ func parseParams(str string) (map[string]string, error) { if i == len(str) { return nil, fmt.Errorf("syntax error on %d", i) } - if str[i] != '"' { + switch { + case escaped: + // RFC 2831 section 7.1 quoted-pair: a backslash escapes the + // following character, so the next byte is taken literally + // (this is how a server sends a literal " or \ in a realm or + // nonce). value += string(str[i]) - } else { + escaped = false + case str[i] == '\\': + escaped = true + case str[i] == '"': state = 1 + default: + value += string(str[i]) } } } return m, nil } -func computeResponse(params map[string]string, uri, username, password string) string { +func computeResponse(params map[string]string, uri, username, password string) (string, error) { nc := "00000001" qop := "auth" - cnonce := enchex.EncodeToString(randomBytes(16)) + rb, err := randomBytes(16) + if err != nil { + return "", err + } + cnonce := enchex.EncodeToString(rb) x := username + ":" + params["realm"] + ":" + password y := md5Hash([]byte(x)) @@ -361,14 +396,24 @@ func computeResponse(params map[string]string, uri, username, password string) s resp := enchex.EncodeToString(md5Hash([]byte(kd))) return fmt.Sprintf( `username="%s",realm="%s",nonce="%s",cnonce="%s",nc=00000001,qop=%s,digest-uri="%s",response=%s`, - username, - params["realm"], - params["nonce"], + quotedStringEscape(username), + quotedStringEscape(params["realm"]), + quotedStringEscape(params["nonce"]), cnonce, qop, - uri, + quotedStringEscape(uri), resp, - ) + ), nil +} + +// quotedStringEscape escapes the two characters that may not appear unescaped +// inside a DIGEST-MD5 quoted string per RFC 2831 section 7.1: the backslash +// and the double quote. The backslash is replaced first so the quotes escaped +// afterwards are not doubled. +func quotedStringEscape(s string) string { + s = strings.ReplaceAll(s, `\`, `\\`) + s = strings.ReplaceAll(s, `"`, `\"`) + return s } func md5Hash(b []byte) []byte { @@ -377,12 +422,12 @@ func md5Hash(b []byte) []byte { return hasher.Sum(nil) } -func randomBytes(len int) []byte { - b := make([]byte, len) - for i := 0; i < len; i++ { - b[i] = byte(rand.Intn(256)) +func randomBytes(length int) ([]byte, error) { + b := make([]byte, length) + if _, err := rand.Read(b); err != nil { + return nil, err } - return b + return b, nil } var externalBindRequest = requestFunc(func(envelope *ber.Packet) error { diff --git a/vendor/github.com/go-ldap/ldap/v3/compare.go b/vendor/github.com/go-ldap/ldap/v3/compare.go index a1cd760b34..4ce669e059 100644 --- a/vendor/github.com/go-ldap/ldap/v3/compare.go +++ b/vendor/github.com/go-ldap/ldap/v3/compare.go @@ -46,6 +46,9 @@ func (l *Conn) Compare(dn, attribute, value string) (bool, error) { return false, err } + if len(packet.Children) < 2 { + return false, fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationCompareResponse { err := GetLDAPError(packet) diff --git a/vendor/github.com/go-ldap/ldap/v3/conn.go b/vendor/github.com/go-ldap/ldap/v3/conn.go index 05febbcaf7..3c3e67e5da 100644 --- a/vendor/github.com/go-ldap/ldap/v3/conn.go +++ b/vendor/github.com/go-ldap/ldap/v3/conn.go @@ -8,6 +8,7 @@ import ( "fmt" "net" "net/url" + "strings" "sync" "sync/atomic" "time" @@ -112,7 +113,11 @@ type Conn struct { outstandingRequests uint messageMutex sync.Mutex - err error + // errMutex guards err only. It is a leaf lock: processMessages and reader + // record errors while another goroutine may hold messageMutex, so err must + // not share messageMutex or those writers could deadlock. + errMutex sync.Mutex + err error } var _ Client = &Conn{} @@ -160,10 +165,18 @@ type DialContext struct { func (dc *DialContext) dial(u *url.URL) (net.Conn, error) { if u.Scheme == "ldapi" { - if u.Path == "" || u.Path == "/" { - u.Path = "/var/run/slapd/ldapi" + // RFC 4516 (and draft-chu-ldap-ldapi) put the socket path in the + // host component, percent-encoded; the path is an optional DN. + // parseLDAPURL has already decoded the host. Accept the older + // ldapi:///path form too so existing callers keep working. + path := u.Host + if path == "" { + path = u.Path } - return dc.dialer.Dial("unix", u.Path) + if path == "" || path == "/" { + path = "/var/run/slapd/ldapi" + } + return dc.dialer.Dial("unix", path) } host, port, err := net.SplitHostPort(u.Host) @@ -222,12 +235,33 @@ func DialTLS(network, addr string, config *tls.Config) (*Conn, error) { return conn, nil } +// parseLDAPURL parses an LDAP URL. It defers to net/url for the common +// ldap/ldaps/cldap schemes, but handles ldapi specially: the spec puts the +// unix socket path in the host, percent-encoded with %2F, and net/url rejects +// that as invalid. Pull the host out manually and decode it. +func parseLDAPURL(addr string) (*url.URL, error) { + const ldapi = "ldapi://" + if !strings.HasPrefix(addr, ldapi) { + return url.Parse(addr) + } + rest := addr[len(ldapi):] + host, path := rest, "" + if i := strings.IndexByte(rest, '/'); i >= 0 { + host, path = rest[:i], rest[i:] + } + decodedHost, err := url.PathUnescape(host) + if err != nil { + return nil, fmt.Errorf("ldapi: invalid host %q: %w", host, err) + } + return &url.URL{Scheme: "ldapi", Host: decodedHost, Path: path}, nil +} + // DialURL connects to the given ldap URL. // The following schemas are supported: ldap://, ldaps://, ldapi://, // and cldap:// (RFC1798, deprecated but used by Active Directory). // On success a new Conn for the connection is returned. func DialURL(addr string, opts ...DialOpt) (*Conn, error) { - u, err := url.Parse(addr) + u, err := parseLDAPURL(addr) if err != nil { return nil, NewError(ErrorNetwork, err) } @@ -338,11 +372,21 @@ func (l *Conn) nextMessageID() int64 { // GetLastError returns the last recorded error from goroutines like processMessages and reader. // Only the last recorded error will be returned. func (l *Conn) GetLastError() error { - l.messageMutex.Lock() - defer l.messageMutex.Unlock() + l.errMutex.Lock() + defer l.errMutex.Unlock() return l.err } +// setError records the connection's last error. The background goroutines that +// call it (processMessages, reader, the per-request timeout helper and the +// SearchAsync worker) run concurrently with callers of GetLastError, so the +// write must take the mutex the getter reads under. +func (l *Conn) setError(err error) { + l.errMutex.Lock() + defer l.errMutex.Unlock() + l.err = err +} + // StartTLS sends the command to start a TLS session and then creates a new TLS Client func (l *Conn) StartTLS(config *tls.Config) error { if l.isTLS { @@ -491,7 +535,7 @@ func (l *Conn) sendProcessMessage(message *messagePacket) bool { func (l *Conn) processMessages() { defer func() { if err := recover(); err != nil { - l.err = fmt.Errorf("ldap: recovered panic in processMessages: %v", err) + l.setError(fmt.Errorf("ldap: recovered panic in processMessages: %v", err)) } for messageID, msgCtx := range l.messageContexts { // If we are closing due to an error, inform anyone who @@ -541,7 +585,7 @@ func (l *Conn) processMessages() { timer := time.NewTimer(time.Duration(requestTimeout)) defer func() { if err := recover(); err != nil { - l.err = fmt.Errorf("ldap: recovered panic in RequestTimeout: %v", err) + l.setError(fmt.Errorf("ldap: recovered panic in RequestTimeout: %v", err)) } timer.Stop() @@ -563,7 +607,7 @@ func (l *Conn) processMessages() { if msgCtx, ok := l.messageContexts[message.MessageID]; ok { msgCtx.sendResponse(&PacketResponse{message.Packet, nil}, time.Duration(l.getTimeout())) } else { - l.err = fmt.Errorf("ldap: received unexpected message %d, %v", message.MessageID, l.IsClosing()) + l.setError(fmt.Errorf("ldap: received unexpected message %d, %v", message.MessageID, l.IsClosing())) l.Debug.PrintPacket(message.Packet) } case MessageTimeout: @@ -590,7 +634,7 @@ func (l *Conn) reader() { cleanstop := false defer func() { if err := recover(); err != nil { - l.err = fmt.Errorf("ldap: recovered panic in reader: %v", err) + l.setError(fmt.Errorf("ldap: recovered panic in reader: %v", err)) } if !cleanstop { l.Close() diff --git a/vendor/github.com/go-ldap/ldap/v3/control.go b/vendor/github.com/go-ldap/ldap/v3/control.go index 1f93b38025..bfaf67aa6e 100644 --- a/vendor/github.com/go-ldap/ldap/v3/control.go +++ b/vendor/github.com/go-ldap/ldap/v3/control.go @@ -565,12 +565,20 @@ func DecodeControl(packet *ber.Packet) (Control, error) { case 1: // just type, no criticality or value packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" - ControlType = packet.Children[0].Value.(string) + ct, ok := packet.Children[0].Value.(string) + if !ok { + return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value) + } + ControlType = ct case 2: packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" if packet.Children[0].Value != nil { - ControlType = packet.Children[0].Value.(string) + ct, ok := packet.Children[0].Value.(string) + if !ok { + return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value) + } + ControlType = ct } else if packet.Children[0].Data != nil { ControlType = packet.Children[0].Data.String() } else { @@ -579,9 +587,9 @@ func DecodeControl(packet *ber.Packet) (Control, error) { // Children[1] could be criticality or value (both are optional) // duck-type on whether this is a boolean - if _, ok := packet.Children[1].Value.(bool); ok { + if crit, ok := packet.Children[1].Value.(bool); ok { packet.Children[1].Description = "Criticality" - Criticality = packet.Children[1].Value.(bool) + Criticality = crit } else { packet.Children[1].Description = "Control Value" value = packet.Children[1] @@ -589,10 +597,18 @@ func DecodeControl(packet *ber.Packet) (Control, error) { case 3: packet.Children[0].Description = "Control Type (" + ControlTypeMap[ControlType] + ")" - ControlType = packet.Children[0].Value.(string) + ct, ok := packet.Children[0].Value.(string) + if !ok { + return nil, fmt.Errorf("control type is not a string: %T", packet.Children[0].Value) + } + ControlType = ct packet.Children[1].Description = "Criticality" - Criticality = packet.Children[1].Value.(bool) + crit, ok := packet.Children[1].Value.(bool) + if !ok { + return nil, fmt.Errorf("criticality is not a bool: %T", packet.Children[1].Value) + } + Criticality = crit packet.Children[2].Description = "Control Value" value = packet.Children[2] @@ -606,6 +622,9 @@ func DecodeControl(packet *ber.Packet) (Control, error) { case ControlTypeManageDsaIT: return NewControlManageDsaIT(Criticality), nil case ControlTypePaging: + if value == nil { + return nil, fmt.Errorf("paging control value is missing") + } value.Description += " (Paging)" c := new(ControlPaging) if value.Value != nil { @@ -617,11 +636,21 @@ func DecodeControl(packet *ber.Packet) (Control, error) { value.Value = nil value.AppendChild(valueChildren) } + if len(value.Children) == 0 { + return nil, fmt.Errorf("paging control value is empty") + } value = value.Children[0] value.Description = "Search Control Value" + if len(value.Children) < 2 { + return nil, fmt.Errorf("paging control value has %d children, expected 2", len(value.Children)) + } value.Children[0].Description = "Paging Size" value.Children[1].Description = "Cookie" - c.PagingSize = uint32(value.Children[0].Value.(int64)) + pagingSize, ok := value.Children[0].Value.(int64) + if !ok { + return nil, fmt.Errorf("paging size is not an integer: %T", value.Children[0].Value) + } + c.PagingSize = uint32(pagingSize) c.Cookie = value.Children[1].Data.Bytes() value.Children[1].Value = c.Cookie return c, nil @@ -729,7 +758,16 @@ func DecodeControl(packet *ber.Packet) (Control, error) { c.ControlType = ControlType c.Criticality = Criticality if value != nil { - c.ControlValue = value.Value.(string) + // A non-conforming or malicious server can send a non-string + // (or nil) value here; the previous unchecked cast panicked + // the calling goroutine, see #561. Fall back to the raw bytes + // when the value isn't a string so we surface an error + // instead of crashing. + if s, ok := value.Value.(string); ok { + c.ControlValue = s + } else if value.Data != nil { + c.ControlValue = value.Data.String() + } } return c, nil } @@ -921,28 +959,44 @@ func (c *ControlServerSideSorting) GetControlType() string { } func NewControlServerSideSorting(value *ber.Packet) (*ControlServerSideSorting, error) { - sortKeys := []*SortKey{} - - val := value.Children[1].Children + val, err := ber.DecodePacketErr(value.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("decode packet err: %s", err) + } - if len(val) != 1 { + if len(val.Children) == 0 { return nil, fmt.Errorf("no sequence value in packet") } - sequences := val[0].Children + var sortKeys []*SortKey + + for i, sequence := range val.Children { + if len(sequence.Children) < 1 || len(sequence.Children) > 3 { + return nil, fmt.Errorf("attributeType is missing from sequence %d", i) + } - for i, sequence := range sequences { sortKey := new(SortKey) - if len(sequence.Children) < 2 { - return nil, fmt.Errorf("attributeType or matchingRule is missing from sequence %d", i) - } + for _, child := range sequence.Children { + switch { + case child.ClassType == ber.ClassUniversal && child.Tag == ber.TagOctetString: + // A constructed-form OCTET STRING matches this case but leaves + // Value nil; guard the assertion so a malformed attributeType is + // rejected below rather than panicking. + if attrType, ok := child.Value.(string); ok { + sortKey.AttributeType = attrType + } - sortKey.AttributeType = sequence.Children[0].Value.(string) - sortKey.MatchingRule = sequence.Children[1].Value.(string) + case child.ClassType == ber.ClassContext && child.Tag == 0: + sortKey.MatchingRule = child.Data.String() - if len(sequence.Children) == 3 { - sortKey.Reverse = sequence.Children[2].Value.(bool) + case child.ClassType == ber.ClassContext && child.Tag == 1: + b := child.Data.Bytes() + sortKey.Reverse = len(b) > 0 && b[0] != 0 + } + } + if sortKey.AttributeType == "" { + return nil, fmt.Errorf("attributeType is missing from sequence %d", i) } sortKeys = append(sortKeys, sortKey) @@ -959,7 +1013,6 @@ func (c *ControlServerSideSorting) Encode() *ber.Packet { packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control") control := ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, c.GetControlType(), "Control Type") - value := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, nil, "Control Value") seqs := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "SortKeyList") for _, f := range c.SortKeys { @@ -968,9 +1021,11 @@ func (c *ControlServerSideSorting) Encode() *ber.Packet { seq.AppendChild( ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, f.AttributeType, "attributeType"), ) - seq.AppendChild( - ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, f.MatchingRule, "orderingRule"), - ) + if f.MatchingRule != "" { + seq.AppendChild( + ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, f.MatchingRule, "orderingRule"), + ) + } if f.Reverse { seq.AppendChild( ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, 1, f.Reverse, "reverseOrder"), @@ -980,7 +1035,7 @@ func (c *ControlServerSideSorting) Encode() *ber.Packet { seqs.AppendChild(seq) } - value.AppendChild(seqs) + value := ber.Encode(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, string(seqs.Bytes()), "Control Value") packet.AppendChild(control) packet.AppendChild(value) @@ -1060,6 +1115,8 @@ func NewControlServerSideSortingResult(pkt *ber.Packet) (*ControlServerSideSorti return nil, err } + control.Result = ControlServerSideSortingCode(codeInt) + return control, nil } diff --git a/vendor/github.com/go-ldap/ldap/v3/del.go b/vendor/github.com/go-ldap/ldap/v3/del.go index cb7a683f5a..076e6c5195 100644 --- a/vendor/github.com/go-ldap/ldap/v3/del.go +++ b/vendor/github.com/go-ldap/ldap/v3/del.go @@ -52,6 +52,9 @@ func (l *Conn) Del(delRequest *DelRequest) error { return err } + if len(packet.Children) < 2 { + return fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationDelResponse { err := GetLDAPError(packet) if err != nil { diff --git a/vendor/github.com/go-ldap/ldap/v3/dn.go b/vendor/github.com/go-ldap/ldap/v3/dn.go index 6520b8ea90..a124486476 100644 --- a/vendor/github.com/go-ldap/ldap/v3/dn.go +++ b/vendor/github.com/go-ldap/ldap/v3/dn.go @@ -1,14 +1,16 @@ package ldap import ( + "bytes" "encoding/hex" "errors" "fmt" - ber "github.com/go-asn1-ber/asn1-ber" "sort" "strings" "unicode" "unicode/utf8" + + ber "github.com/go-asn1-ber/asn1-ber" ) // AttributeTypeAndValue represents an attributeTypeAndValue from https://tools.ietf.org/html/rfc4514 @@ -94,9 +96,21 @@ func (d *DN) String() string { func stripLeadingAndTrailingSpaces(inVal string) string { noSpaces := strings.Trim(inVal, " ") - // Re-add the trailing space if it was an escaped space - if len(noSpaces) > 0 && noSpaces[len(noSpaces)-1] == '\\' && inVal[len(inVal)-1] == ' ' { - noSpaces = noSpaces + " " + // Re-add the trailing space only if it was escaped. A trailing space is + // escaped when it is preceded by an odd number of backslashes; an even + // number leaves the space unescaped (each "\\" is a literal backslash), so + // the space is insignificant and stays stripped. Counting only the final + // backslash treated "\\ " (a literal backslash plus an insignificant + // space) as an escaped space, keeping a spurious trailing space in the + // decoded value. + if len(noSpaces) > 0 && inVal[len(inVal)-1] == ' ' { + backslashes := 0 + for i := len(noSpaces) - 1; i >= 0 && noSpaces[i] == '\\'; i-- { + backslashes++ + } + if backslashes%2 == 1 { + noSpaces = noSpaces + " " + } } return noSpaces @@ -116,6 +130,16 @@ func decodeString(str string) (string, error) { // If the character is not an escape character, just add it to the // builder and continue if char != '\\' { + // RFC 4514 section 2.4: these characters must appear escaped + // (either as "\X" or as "\XX" hex) when present in an AttributeValue. + // Reject the raw form here so that callers don't silently accept + // input that violates the grammar. + switch char { + case '"', ';', '<', '>': + return "", fmt.Errorf("got unescaped character: '%s'", string(char)) + case 0: + return "", fmt.Errorf("got unescaped NULL character") + } builder.WriteRune(char) continue } @@ -233,10 +257,18 @@ func decodeEncodedString(str string) (string, error) { return "", fmt.Errorf("failed to decode BER encoding: %w", err) } - packet, err := ber.DecodePacketErr(decoded) + // RFC 4514 section 2.4: the value following '#' is the hex encoding of the + // BER encoding of a single AttributeValue. Read exactly one element and + // reject any leftover octets, otherwise bytes appended after the value are + // silently dropped and two different DN strings decode to the same value. + reader := bytes.NewBuffer(decoded) + packet, err := ber.ReadPacket(reader) if err != nil { return "", fmt.Errorf("failed to decode BER encoding: %w", err) } + if reader.Len() != 0 { + return "", errors.New("failed to decode BER encoding: trailing bytes after value") + } return packet.Data.String(), nil } @@ -352,10 +384,19 @@ func (r *RelativeDN) Equal(other *RelativeDN) bool { } func (r *RelativeDN) hasAllAttributes(attrs []*AttributeTypeAndValue) bool { + // Each candidate attribute must match a distinct attribute of the receiver. + // Without consuming matches this is a set containment test, so a multi-valued + // RDN that repeats an attributeTypeAndValue would compare equal to one that + // repeats a different pair the same number of times. + matched := make([]bool, len(r.Attributes)) for _, attr := range attrs { found := false - for _, myattr := range r.Attributes { + for i, myattr := range r.Attributes { + if matched[i] { + continue + } if myattr.Equal(attr) { + matched[i] = true found = true break } @@ -415,10 +456,16 @@ func (r *RelativeDN) EqualFold(other *RelativeDN) bool { } func (r *RelativeDN) hasAllAttributesFold(attrs []*AttributeTypeAndValue) bool { + // See hasAllAttributes: matches are consumed so multiplicity is respected. + matched := make([]bool, len(r.Attributes)) for _, attr := range attrs { found := false - for _, myattr := range r.Attributes { + for i, myattr := range r.Attributes { + if matched[i] { + continue + } if myattr.EqualFold(attr) { + matched[i] = true found = true break } diff --git a/vendor/github.com/go-ldap/ldap/v3/extended.go b/vendor/github.com/go-ldap/ldap/v3/extended.go index 84cffbeea0..3533e4fd5e 100644 --- a/vendor/github.com/go-ldap/ldap/v3/extended.go +++ b/vendor/github.com/go-ldap/ldap/v3/extended.go @@ -89,6 +89,14 @@ func (l *Conn) Extended(er *ExtendedRequest) (*ExtendedResponse, error) { } for _, child := range extResp.Children { + // responseName [10] and responseValue [11] are context-class and + // optional. The preceding resultCode is a universal ENUMERATED whose + // tag number (10) is the same as responseName, so a child must be + // matched on its class as well, otherwise the resultCode is read as + // the responseName whenever the server omits the latter. + if child.ClassType != ber.ClassContext { + continue + } switch child.Tag { case ber.TagEnumerated: response.Name = child.Data.String() diff --git a/vendor/github.com/go-ldap/ldap/v3/filter.go b/vendor/github.com/go-ldap/ldap/v3/filter.go index db76210c10..b5e89b1e53 100644 --- a/vendor/github.com/go-ldap/ldap/v3/filter.go +++ b/vendor/github.com/go-ldap/ldap/v3/filter.go @@ -131,7 +131,7 @@ func DecompileFilter(packet *ber.Packet) (_ string, err error) { buf.WriteString(childStr) case FilterSubstrings: - buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[0].Data.Bytes()))) buf.WriteByte('=') for i, child := range packet.Children[1].Children { if i == 0 && child.Tag != FilterSubstringsInitial { @@ -143,22 +143,22 @@ func DecompileFilter(packet *ber.Packet) (_ string, err error) { } } case FilterEqualityMatch: - buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[0].Data.Bytes()))) buf.WriteByte('=') buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterGreaterOrEqual: - buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[0].Data.Bytes()))) buf.WriteString(">=") buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterLessOrEqual: - buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[0].Data.Bytes()))) buf.WriteString("<=") buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterPresent: - buf.WriteString(ber.DecodeString(packet.Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Data.Bytes()))) buf.WriteString("=*") case FilterApproxMatch: - buf.WriteString(ber.DecodeString(packet.Children[0].Data.Bytes())) + buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[0].Data.Bytes()))) buf.WriteString("~=") buf.WriteString(EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))) case FilterExtensibleMatch: @@ -181,14 +181,14 @@ func DecompileFilter(packet *ber.Packet) (_ string, err error) { } if len(attr) > 0 { - buf.WriteString(attr) + buf.WriteString(EscapeFilter(attr)) } if dnAttributes { buf.WriteString(":dn") } if len(matchingRule) > 0 { buf.WriteString(":") - buf.WriteString(matchingRule) + buf.WriteString(EscapeFilter(matchingRule)) } buf.WriteString(":=") buf.WriteString(EscapeFilter(value)) diff --git a/vendor/github.com/go-ldap/ldap/v3/moddn.go b/vendor/github.com/go-ldap/ldap/v3/moddn.go index 84a6488e42..92acea0bbe 100644 --- a/vendor/github.com/go-ldap/ldap/v3/moddn.go +++ b/vendor/github.com/go-ldap/ldap/v3/moddn.go @@ -89,6 +89,9 @@ func (l *Conn) ModifyDN(m *ModifyDNRequest) error { return err } + if len(packet.Children) < 2 { + return fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationModifyDNResponse { err := GetLDAPError(packet) if err != nil { diff --git a/vendor/github.com/go-ldap/ldap/v3/modify.go b/vendor/github.com/go-ldap/ldap/v3/modify.go index 0e5013601f..663be5fd6f 100644 --- a/vendor/github.com/go-ldap/ldap/v3/modify.go +++ b/vendor/github.com/go-ldap/ldap/v3/modify.go @@ -121,6 +121,9 @@ func (l *Conn) Modify(modifyRequest *ModifyRequest) error { return err } + if len(packet.Children) < 2 { + return fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationModifyResponse { err := GetLDAPError(packet) if err != nil { @@ -159,6 +162,10 @@ func (l *Conn) ModifyWithResult(modifyRequest *ModifyRequest) (*ModifyResult, er return nil, err } + if len(packet.Children) < 2 { + return nil, fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } + switch packet.Children[1].Tag { case ApplicationModifyResponse: if err = GetLDAPError(packet); err != nil { diff --git a/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go index 72a2351a24..6ab177e26e 100644 --- a/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go +++ b/vendor/github.com/go-ldap/ldap/v3/passwdmodify.go @@ -93,6 +93,9 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa result := &PasswordModifyResult{} + if len(packet.Children) < 2 { + return nil, fmt.Errorf("ldap: malformed response: expected at least 2 children, got %d", len(packet.Children)) + } if packet.Children[1].Tag == ApplicationExtendedResponse { if err = GetLDAPError(packet); err != nil { result.Referral = getReferral(err, packet) @@ -106,7 +109,10 @@ func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*Pa extendedResponse := packet.Children[1] for _, child := range extendedResponse.Children { if child.Tag == ber.TagEmbeddedPDV { - passwordModifyResponseValue := ber.DecodePacket(child.Data.Bytes()) + passwordModifyResponseValue, err := ber.DecodePacketErr(child.Data.Bytes()) + if err != nil { + return nil, fmt.Errorf("ldap: failed to decode PasswordModifyResponseValue: %s", err) + } if len(passwordModifyResponseValue.Children) == 1 { if passwordModifyResponseValue.Children[0].Tag == ber.TagEOC { result.GeneratedPassword = ber.DecodeString(passwordModifyResponseValue.Children[0].Data.Bytes()) diff --git a/vendor/github.com/go-ldap/ldap/v3/request.go b/vendor/github.com/go-ldap/ldap/v3/request.go index b64f232dc3..f6dca73204 100644 --- a/vendor/github.com/go-ldap/ldap/v3/request.go +++ b/vendor/github.com/go-ldap/ldap/v3/request.go @@ -101,6 +101,14 @@ func getReferral(err error, packet *ber.Packet) (referral string) { continue } + // A Referral is a SEQUENCE SIZE (1..MAX) OF uri, but a non-conforming or + // malicious server can send an empty SEQUENCE. Skip it instead of indexing + // child.Children[0], which would panic the goroutine that called Modify or + // PasswordModify. + if len(child.Children) == 0 { + continue + } + if referral, ok = child.Children[0].Value.(string); ok { return referral } diff --git a/vendor/github.com/go-ldap/ldap/v3/response.go b/vendor/github.com/go-ldap/ldap/v3/response.go index 0eae10019b..d795d72693 100644 --- a/vendor/github.com/go-ldap/ldap/v3/response.go +++ b/vendor/github.com/go-ldap/ldap/v3/response.go @@ -66,12 +66,28 @@ func (r *searchResponse) Next() bool { return true } +// send enqueues a result on the result channel, giving up when ctx is +// cancelled so an abandoned consumer cannot block the search goroutine +// forever on a full buffer. It reports whether the result was handed off to +// the channel; a true return does not mean the consumer received it. The +// give-up is best-effort: if ctx is already cancelled but buffer space is +// available, the result may still be enqueued. Callers that terminate the +// stream regardless of the outcome may ignore the return value. +func (r *searchResponse) send(ctx context.Context, res *SearchSingleResult) bool { + select { + case r.ch <- res: + return true + case <-ctx.Done(): + return false + } +} + func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest) { go func() { defer func() { close(r.ch) if err := recover(); err != nil { - r.conn.err = fmt.Errorf("ldap: recovered panic in searchResponse: %v", err) + r.conn.setError(fmt.Errorf("ldap: recovered panic in searchResponse: %v", err)) } }() @@ -84,14 +100,14 @@ func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest // encode search request err := searchRequest.appendTo(packet) if err != nil { - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } r.conn.Debug.PrintPacket(packet) msgCtx, err := r.conn.sendMessage(packet) if err != nil { - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } defer r.conn.finishMessage(msgCtx) @@ -106,19 +122,19 @@ func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest case packetResponse, ok := <-msgCtx.responses: if !ok { err := NewError(ErrorNetwork, errors.New("ldap: response channel closed")) - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } packet, err = packetResponse.ReadPacket() r.conn.Debug.Printf("%d: got response %p", msgCtx.id, packet) if err != nil { - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } if r.conn.Debug { if err := addLDAPDescriptions(packet); err != nil { - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } ber.PrintPacket(packet) @@ -133,22 +149,26 @@ func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest }, } if len(packet.Children) != 3 { - r.ch <- result + if !r.send(ctx, result) { + return + } continue } decoded, err := DecodeControl(packet.Children[2].Children[0]) if err != nil { werr := fmt.Errorf("failed to decode search result entry: %w", err) result.Error = werr - r.ch <- result + r.send(ctx, result) return } result.Controls = append(result.Controls, decoded) - r.ch <- result + if !r.send(ctx, result) { + return + } case ApplicationSearchResultDone: if err := GetLDAPError(packet); err != nil { - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } if len(packet.Children) == 3 { @@ -157,33 +177,37 @@ func (r *searchResponse) start(ctx context.Context, searchRequest *SearchRequest decodedChild, err := DecodeControl(child) if err != nil { werr := fmt.Errorf("failed to decode child control: %w", err) - r.ch <- &SearchSingleResult{Error: werr} + r.send(ctx, &SearchSingleResult{Error: werr}) return } result.Controls = append(result.Controls, decodedChild) } - r.ch <- result + r.send(ctx, result) } foundSearchSingleResultDone = true case ApplicationSearchResultReference: ref := packet.Children[1].Children[0].Value.(string) - r.ch <- &SearchSingleResult{Referral: ref} + if !r.send(ctx, &SearchSingleResult{Referral: ref}) { + return + } case ApplicationIntermediateResponse: decoded, err := DecodeControl(packet.Children[1]) if err != nil { werr := fmt.Errorf("failed to decode intermediate response: %w", err) - r.ch <- &SearchSingleResult{Error: werr} + r.send(ctx, &SearchSingleResult{Error: werr}) return } result := &SearchSingleResult{} result.Controls = append(result.Controls, decoded) - r.ch <- result + if !r.send(ctx, result) { + return + } default: err := fmt.Errorf("unknown tag: %d", packet.Children[1].Tag) - r.ch <- &SearchSingleResult{Error: err} + r.send(ctx, &SearchSingleResult{Error: err}) return } } diff --git a/vendor/github.com/go-ldap/ldap/v3/search.go b/vendor/github.com/go-ldap/ldap/v3/search.go index e1c684e12b..c0f19e34fd 100644 --- a/vendor/github.com/go-ldap/ldap/v3/search.go +++ b/vendor/github.com/go-ldap/ldap/v3/search.go @@ -624,7 +624,10 @@ func (l *Conn) Search(searchRequest *SearchRequest) (*SearchResult, error) { // SearchAsync performs a search request and returns all search results asynchronously. // This means you get all results until an error happens (or the search successfully finished), // e.g. for size / time limited requests all are received until the limit is reached. -// To stop the search, call cancel function of the context. +// To stop the search, call the cancel function of the context; Next may +// still deliver a few results received before the cancellation took effect. +// Cancellation is not reported as an error: Err returns nil, same as a +// successfully completed search. func (l *Conn) SearchAsync( ctx context.Context, searchRequest *SearchRequest, bufferSize int) Response { r := newSearchResponse(l, bufferSize) @@ -635,7 +638,8 @@ func (l *Conn) SearchAsync( // Syncrepl is a short name for LDAP Sync Replication engine that works on the // consumer-side. This can perform a persistent search and returns an entry // when the entry is updated on the server side. -// To stop the search, call cancel function of the context. +// To stop the search, call the cancel function of the context; cancellation +// is not reported as an error, Err returns nil. func (l *Conn) Syncrepl( ctx context.Context, searchRequest *SearchRequest, bufferSize int, mode ControlSyncRequestMode, cookie []byte, reloadHint bool, diff --git a/vendor/github.com/go-ldap/ldap/v3/unbind.go b/vendor/github.com/go-ldap/ldap/v3/unbind.go index 10cf75c679..6111fb1f39 100644 --- a/vendor/github.com/go-ldap/ldap/v3/unbind.go +++ b/vendor/github.com/go-ldap/ldap/v3/unbind.go @@ -24,11 +24,18 @@ func (l *Conn) Unbind() error { return ErrConnUnbound } - _, err := l.doRequest(unbindRequest{}) + msgCtx, err := l.doRequest(unbindRequest{}) if err != nil { return err } + // Finish the message context so its done channel is closed. Without + // this, a server-initiated disconnect racing with Close can deadlock: + // processMessages cleanup tries to deliver closeErr to the orphaned + // context via sendResponse, which blocks forever on the unclosed done + // channel, preventing chanConfirm from being signalled. + l.finishMessage(msgCtx) + // Sending an unbindRequest will make the connection unusable. // Pending requests will fail with: // LDAP Result Code 200 "Network Error": ldap: response channel closed diff --git a/vendor/github.com/go-ldap/ldap/v3/whoami.go b/vendor/github.com/go-ldap/ldap/v3/whoami.go index 0d743d2243..cecb50f016 100644 --- a/vendor/github.com/go-ldap/ldap/v3/whoami.go +++ b/vendor/github.com/go-ldap/ldap/v3/whoami.go @@ -19,5 +19,11 @@ func (l *Conn) WhoAmI(controls []Control) (*WhoAmIResult, error) { return nil, err } - return &WhoAmIResult{AuthzID: resp.Value.Data.String()}, nil + // responseValue is OPTIONAL (RFC 4532); Extended leaves Value nil when the + // server omits it. Guard the dereference and report an empty authzId. + result := &WhoAmIResult{} + if resp.Value != nil { + result.AuthzID = resp.Value.Data.String() + } + return result, nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index f6c29fa8b9..34862c0e50 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -451,7 +451,7 @@ github.com/ggwhite/go-masker ## explicit; go 1.15 github.com/go-acme/lego/v4/acme github.com/go-acme/lego/v4/challenge -# github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 +# github.com/go-asn1-ber/asn1-ber v1.5.8 ## explicit; go 1.13 github.com/go-asn1-ber/asn1-ber # github.com/go-chi/chi/v5 v5.3.1 @@ -539,8 +539,8 @@ github.com/go-jose/go-jose/v4/json ## explicit; go 1.17 github.com/go-kit/log github.com/go-kit/log/level -# github.com/go-ldap/ldap/v3 v3.4.13 -## explicit; go 1.24.0 +# github.com/go-ldap/ldap/v3 v3.4.14 +## explicit; go 1.25.0 github.com/go-ldap/ldap/v3 # github.com/go-ldap/ldif v0.0.0-20200320164324-fd88d9b715b3 ## explicit; go 1.14 From 73a17705e64b3c79c7117d88e37f06efe19fb5f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:10:10 +0000 Subject: [PATCH 10/27] build(deps): bump github.com/nats-io/nats-server/v2 Bumps [github.com/nats-io/nats-server/v2](https://github.com/nats-io/nats-server) from 2.14.3 to 2.14.4. - [Release notes](https://github.com/nats-io/nats-server/releases) - [Changelog](https://github.com/nats-io/nats-server/blob/main/RELEASES.md) - [Commits](https://github.com/nats-io/nats-server/compare/v2.14.3...v2.14.4) --- updated-dependencies: - dependency-name: github.com/nats-io/nats-server/v2 dependency-version: 2.14.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 8 +- .../antithesis-sdk-go/assert/assert.go | 2 +- .../antithesis-sdk-go/internal/emit.go | 2 +- .../antithesis-sdk-go/internal/sdk_const.go | 2 +- .../internal/voidstar_handler.go | 2 +- .../nats-io/nats-server/v2/server/auth.go | 85 +- .../nats-server/v2/server/avl/seqset.go | 81 +- .../nats-io/nats-server/v2/server/client.go | 217 +++- .../nats-io/nats-server/v2/server/const.go | 2 +- .../nats-io/nats-server/v2/server/consumer.go | 217 ++-- .../nats-io/nats-server/v2/server/dios.go | 85 ++ .../nats-server/v2/server/filestore.go | 934 +++++++++++------- .../nats-server/v2/server/jetstream.go | 2 +- .../nats-server/v2/server/jetstream_api.go | 20 +- .../v2/server/jetstream_batching.go | 16 +- .../v2/server/jetstream_cluster.go | 232 +++-- .../nats-io/nats-server/v2/server/leafnode.go | 16 +- .../nats-io/nats-server/v2/server/memstore.go | 45 +- .../nats-io/nats-server/v2/server/monitor.go | 79 +- .../nats-io/nats-server/v2/server/mqtt.go | 280 +++++- .../nats-io/nats-server/v2/server/opts.go | 76 +- .../nats-io/nats-server/v2/server/parser.go | 16 +- .../nats-io/nats-server/v2/server/raft.go | 197 ++-- .../nats-server/v2/server/raft_transport.go | 114 +++ .../nats-io/nats-server/v2/server/reload.go | 9 + .../nats-io/nats-server/v2/server/server.go | 26 +- .../nats-io/nats-server/v2/server/signal.go | 60 +- .../nats-server/v2/server/signal_wasm.go | 6 + .../nats-io/nats-server/v2/server/store.go | 22 + .../nats-io/nats-server/v2/server/stream.go | 204 ++-- .../nats-server/v2/server/stree/leaf.go | 30 +- .../nats-server/v2/server/stree/node.go | 8 +- .../nats-server/v2/server/stree/node10.go | 3 +- .../nats-server/v2/server/stree/node16.go | 3 +- .../nats-server/v2/server/stree/node4.go | 3 +- .../nats-server/v2/server/stree/node48.go | 3 +- .../nats-server/v2/server/stree/parts.go | 11 +- .../nats-server/v2/server/stree/stree.go | 43 +- .../nats-server/v2/server/stree/util.go | 10 - vendor/modules.txt | 4 +- 41 files changed, 2173 insertions(+), 1006 deletions(-) create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/dios.go create mode 100644 vendor/github.com/nats-io/nats-server/v2/server/raft_transport.go diff --git a/go.mod b/go.mod index 3334661b2c..9817bbc8e3 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/libregraph/lico v0.67.0 github.com/mna/pigeon v1.3.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 - github.com/nats-io/nats-server/v2 v2.14.3 + github.com/nats-io/nats-server/v2 v2.14.4 github.com/nats-io/nats.go v1.52.0 github.com/olekukonko/tablewriter v1.1.4 github.com/onsi/ginkgo v1.16.5 @@ -135,7 +135,7 @@ require ( github.com/ajg/form v1.5.1 // indirect github.com/alexedwards/argon2id v1.0.0 // indirect github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 // indirect - github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op // indirect + github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index 605cf4761b..daabd3d6b8 100644 --- a/go.sum +++ b/go.sum @@ -115,8 +115,8 @@ github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/ github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= -github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= +github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op h1:p2zFsAzvhIpFya8AIOHIbWf7NGvO34QpLGclyf7nXj8= +github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= @@ -894,8 +894,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt/v2 v2.8.2 h1:XXRgB60MSTnqsRwejQurVDs/hcv2dkt+86GjI+I/bMc= github.com/nats-io/jwt/v2 v2.8.2/go.mod h1:Ag/56sq9OblL4JgdYufDd16Egb17Kr/8WwwuO/forVc= -github.com/nats-io/nats-server/v2 v2.14.3 h1:+xjydPt7rkit67G+04TN0mcO2n+8nveZE7tK/PPV53A= -github.com/nats-io/nats-server/v2 v2.14.3/go.mod h1:5IlCtBzfwyzQzPMjmoJ9W2/LKmnJRtNyuOs/OT+NHDY= +github.com/nats-io/nats-server/v2 v2.14.4 h1:efgjZ8cdExAKRuqSg8UPJFprb+l7NlBtSDPhDlw3rO4= +github.com/nats-io/nats-server/v2 v2.14.4/go.mod h1:BltdpOYestjbtQSnVO2zGHdg5SGBZjt+GYTgB9LZq/I= github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc= github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno= github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg= diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go index eff6fa96bd..2d50c62460 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go @@ -6,7 +6,7 @@ // // These functions are no-ops with minimal performance overhead when called outside of the Antithesis environment. However, if the environment variable ANTITHESIS_SDK_LOCAL_OUTPUT is set, these functions will log to the file pointed to by that variable using a structured JSON format defined [here]. This allows you to make use of the Antithesis assertions package in your regular testing, or even in production. In particular, very few assertions frameworks offer a convenient way to define [Sometimes assertions], but they can be quite useful even outside Antithesis. // -// Each function in this package takes a parameter called message, which is a human readable identifier used to aggregate assertions. Antithesis generates one test property per unique message and this test property will be named "" in the [triage report]. +// Each function in this package takes a parameter called message, which is a human readable identifier used to aggregate assertions. Antithesis generates one test property per unique message and this test property will be named "" in the [triage report]. Message must be provided as a string literal. // // This test property either passes or fails, which depends upon the evaluation of every assertion that shares its message. Different assertions in different parts of the code should have different message, but the same assertion should always have the same message even if it is moved to a different file. // diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go index a932f5cf4a..9a0eda76b8 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go @@ -38,7 +38,7 @@ type libHandler interface { } const ( - errorLogLinePrefix = "[* antithesis-sdk-go *]" + errorLogLinePrefix = "[* antithesis-sdk-go *]" ) var handler libHandler diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/sdk_const.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/sdk_const.go index e520f15202..44f7b58457 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/sdk_const.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/sdk_const.go @@ -3,7 +3,7 @@ package internal // -------------------------------------------------------------------------------- // Versions // -------------------------------------------------------------------------------- -const SDK_Version = "0.7.0" +const SDK_Version = "0.7.2" const Protocol_Version = "1.1.0" // -------------------------------------------------------------------------------- diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go index eb410fd887..dab2363554 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go @@ -4,8 +4,8 @@ package internal import ( "fmt" - "unsafe" "os" + "unsafe" ) // -------------------------------------------------------------------------------- diff --git a/vendor/github.com/nats-io/nats-server/v2/server/auth.go b/vendor/github.com/nats-io/nats-server/v2/server/auth.go index 476667ae36..4d9598b7d1 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/auth.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/auth.go @@ -194,6 +194,53 @@ func (p *Permissions) clone() *Permissions { return clone } +// wsCanMapCerts returns true if the websocket listener collects client +// certificates, which mqtt and leafnode connections over it need to be mapped. +func (o *Options) wsCanMapCerts() bool { + return o.Websocket.Port != 0 && o.Websocket.TLSConfig != nil && + o.Websocket.TLSConfig.ClientAuth >= tls.RequestClientCert +} + +// canTLSMap returns true if verify_and_map on any listener that uses the users +// table could select this user, which makes it a certificate identity. Cluster +// and gateway map onto their own configured user, so they do not count. +func (o *Options) canTLSMap(u *User) bool { + // The mapping lookup skips users not allowed on the connection type. + allowed := func(cts ...string) bool { + if len(u.AllowedConnectionTypes) == 0 { + return true + } + for _, ct := range cts { + if _, ok := u.AllowedConnectionTypes[ct]; ok { + return true + } + } + return false + } + // Only a listener that is started can map, and in-process connections have + // no TLS state, so they never do. + if o.TLSMap && !o.DontListen && allowed(jwt.ConnectionTypeStandard) { + return true + } + if o.Websocket.TLSMap && o.Websocket.Port != 0 && allowed(jwt.ConnectionTypeWebsocket) { + return true + } + // Mqtt over websocket maps with the mqtt listener, not the websocket one, + // but takes its certificate from the websocket transport. + if o.MQTT.TLSMap && o.MQTT.Port != 0 && + (allowed(jwt.ConnectionTypeMqtt) || + o.wsCanMapCerts() && allowed(jwt.ConnectionTypeMqttWS)) { + return true + } + // Leafnodes only use the users table when the leafnode block sets no + // credentials of its own, see isLeafNodeAuthorized. + return o.LeafNode.TLSMap && o.LeafNode.Port != 0 && + o.LeafNode.Username == _EMPTY_ && o.LeafNode.Nkey == _EMPTY_ && + len(o.LeafNode.Users) == 0 && + (allowed(jwt.ConnectionTypeLeafnode) || + o.wsCanMapCerts() && allowed(jwt.ConnectionTypeLeafnodeWS)) +} + // checkAuthforWarnings will look for insecure settings and log concerns. // Lock is assumed held. func (s *Server) checkAuthforWarnings() { @@ -205,7 +252,7 @@ func (s *Server) checkAuthforWarnings() { for _, u := range s.users { // Skip warn if using TLS certs based auth // unless a password has been left in the config. - if u.Password == _EMPTY_ && opts.TLSMap { + if u.Password == _EMPTY_ && opts.canTLSMap(u) { continue } // Check if this is our internal sys client created on the fly. @@ -1164,6 +1211,13 @@ func (s *Server) processClientOrLeafAuthentication(c *client, opts *Options) (au if proxyRequired = user.ProxyRequired; proxyRequired && !trustedProxy { return setProxyAuthError(ErrAuthProxyRequired) } + // A user with no password is a certificate only identity when mapping is + // enabled on another listener, so do not let comparePasswords match two + // empty passwords. The no_auth_user needs no credentials either way. + if !tlsMap && user.Password == _EMPTY_ && user.Username != noAuthUser && opts.canTLSMap(user) { + c.Debugf("User %q requires a client certificate", user.Username) + return false + } ok = comparePasswords(user.Password, c.opts.Password) // If we are authorized, register the user which will properly setup any permissions // for pub/sub authorizations. @@ -1657,15 +1711,44 @@ func validateAuth(o *Options) error { if err := validateAllowedConnectionTypes(u.AllowedConnectionTypes); err != nil { return err } + if err := validatePermissionSubjects(u.Permissions); err != nil { + return fmt.Errorf("invalid permissions for user %q: %w", u.Username, err) + } } for _, u := range o.Nkeys { if err := validateAllowedConnectionTypes(u.AllowedConnectionTypes); err != nil { return err } + if err := validatePermissionSubjects(u.Permissions); err != nil { + return fmt.Errorf("invalid permissions for nkey %q: %w", u.Nkey, err) + } } return validateNoAuthUser(o, o.NoAuthUser) } +func validatePermissionSubjects(p *Permissions) error { + if p == nil { + return nil + } + if p.Publish != nil { + if err := checkPermSubjectArray(p.Publish.Allow, false); err != nil { + return fmt.Errorf("publish allow: %w", err) + } + if err := checkPermSubjectArray(p.Publish.Deny, false); err != nil { + return fmt.Errorf("publish deny: %w", err) + } + } + if p.Subscribe != nil { + if err := checkPermSubjectArray(p.Subscribe.Allow, true); err != nil { + return fmt.Errorf("subscribe allow: %w", err) + } + if err := checkPermSubjectArray(p.Subscribe.Deny, true); err != nil { + return fmt.Errorf("subscribe deny: %w", err) + } + } + return nil +} + func validateAllowedConnectionTypes(m map[string]struct{}) error { for ct := range m { ctuc := strings.ToUpper(ct) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/avl/seqset.go b/vendor/github.com/nats-io/nats-server/v2/server/avl/seqset.go index f4fa127df6..61f74c1693 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/avl/seqset.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/avl/seqset.go @@ -42,6 +42,22 @@ type SequenceSet struct { // Insert will insert the sequence into the set. // The tree will be balanced inline. func (ss *SequenceSet) Insert(seq uint64) { + // If a node covering seq already exists, setting a bit can not change the + // tree shape, so skip the recursive descent and rebalance checks. + for n := ss.root; n != nil; { + if seq < n.base { + n = n.l + } else if seq >= n.base+numEntries { + n = n.r + } else { + n.set(seq, &ss.changed) + if ss.changed { + ss.changed = false + ss.size++ + } + return + } + } if ss.root = ss.root.insert(seq, &ss.changed, &ss.nodes); ss.changed { ss.changed = false ss.size++ @@ -187,17 +203,32 @@ func (ss *SequenceSet) Clone() *SequenceSet { return css } +// Equal returns whether the two sets contain exactly the same sequences. +func (ss *SequenceSet) Equal(other *SequenceSet) bool { + if ss.IsEmpty() || other.IsEmpty() { + return ss.IsEmpty() && other.IsEmpty() + } + if ss.size != other.size { + return false + } + // Sizes are equal, so a one-way membership check suffices. + equal := true + ss.Range(func(seq uint64) bool { + equal = other.Exists(seq) + return equal + }) + return equal +} + // Union will union this SequenceSet with ssa. func (ss *SequenceSet) Union(ssa ...*SequenceSet) { for _, sa := range ssa { sa.root.nodeIter(func(n *node) { for nb, b := range n.bits { - for pos := uint64(0); b != 0; pos++ { - if b&1 == 1 { - seq := n.base + (uint64(nb) * uint64(bitsPerBucket)) + pos - ss.Insert(seq) - } - b >>= 1 + base := n.base + uint64(nb)*bitsPerBucket + for b != 0 { + ss.Insert(base + uint64(bits.TrailingZeros64(b))) + b &= b - 1 } } }) @@ -302,8 +333,11 @@ func decodev2(buf []byte) (*SequenceSet, int, error) { sz := int(le.Uint32(buf[index+4:])) index += 8 - expectedLen := minLen + (nn * ((numBuckets+1)*8 + 2)) - if len(buf) < expectedLen { + // nn is decoded as a uint32 but held in an int. On 32-bit builds a value + // above MaxInt32 turns negative and nn*perNode below overflows, so the + // length check would pass for a short buffer and the following make/reads + // run off the end. Compare with division so the bound holds on every arch. + if nn < 0 || nn > (len(buf)-minLen)/((numBuckets+1)*8+2) { return nil, -1, ErrBadEncoding } @@ -335,8 +369,9 @@ func decodev1(buf []byte) (*SequenceSet, int, error) { const v1NumBuckets = 64 - expectedLen := minLen + (nn * ((v1NumBuckets+1)*8 + 2)) - if len(buf) < expectedLen { + // See decodev2: guard the node count without overflowing the multiply so + // the bound stays correct on 32-bit builds too. + if nn < 0 || nn > (len(buf)-minLen)/((v1NumBuckets+1)*8+2) { return nil, -1, ErrBadEncoding } @@ -347,12 +382,9 @@ func decodev1(buf []byte) (*SequenceSet, int, error) { for nb := uint64(0); nb < v1NumBuckets; nb++ { n := le.Uint64(buf[index:]) // Walk all set bits and insert sequences manually for this decode from v1. - for pos := uint64(0); n != 0; pos++ { - if n&1 == 1 { - seq := base + (nb * uint64(bitsPerBucket)) + pos - ss.Insert(seq) - } - n >>= 1 + for n != 0 { + ss.Insert(base + (nb * uint64(bitsPerBucket)) + uint64(bits.TrailingZeros64(n))) + n &= n - 1 } index += 8 } @@ -527,10 +559,13 @@ func (n *node) clear(seq uint64, deleted *bool) bool { seq -= n.base i := seq / bitsPerBucket mask := uint64(1) << (seq % bitsPerBucket) - if (n.bits[i] & mask) != 0 { - n.bits[i] &^= mask - *deleted = true + if (n.bits[i] & mask) == 0 { + // Nothing cleared, and nodes in the tree are never empty, + // so no need to scan the buckets. + return false } + n.bits[i] &^= mask + *deleted = true for _, b := range n.bits { if b != 0 { return false @@ -663,11 +698,13 @@ func (n *node) iter(f func(uint64) bool) bool { if ok := n.l.iter(f); !ok { return false } - for num := n.base; num < n.base+numEntries; num++ { - if n.exists(num) { - if ok := f(num); !ok { + for i, b := range n.bits { + base := n.base + uint64(i)*bitsPerBucket + for b != 0 { + if ok := f(base + uint64(bits.TrailingZeros64(b))); !ok { return false } + b &= b - 1 } } if ok := n.r.iter(f); !ok { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/client.go b/vendor/github.com/nats-io/nats-server/v2/server/client.go index 12cc4db683..b5c5fcfaf7 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/client.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/client.go @@ -287,7 +287,7 @@ type client struct { subs map[string]*subscription replies map[string]*resp mperms *msgDeny - darray []string + darray []*subscription // Parsed subscribe deny entries used to build the delivery-time filter. pcd map[*client]struct{} atmr *time.Timer expires time.Time @@ -450,15 +450,22 @@ type resp struct { // succeed but no message sent on foo should be delivered. type msgDeny struct { deny *Sublist - dcache map[string]bool + dcache map[msgDenyKey]bool +} + +type msgDenyKey struct { + subject string + queue string } // routeTarget collects information regarding routes and queue groups for // sending information to a remote. type routeTarget struct { - sub *subscription - qs []byte - _qs [32]byte + sub *subscription + qs []byte + qsubs []*subscription + _qs [32]byte + _qsubs [4]*subscription } const ( @@ -1072,6 +1079,9 @@ func (c *client) updateDefaultPermissions(perms *Permissions) bool { func splitSubjectQueue(sq string) ([]byte, []byte, error) { vals := strings.Fields(strings.TrimSpace(sq)) + if len(vals) == 0 { + return nil, nil, fmt.Errorf("invalid subject-queue %q", sq) + } s := []byte(vals[0]) var q []byte if len(vals) == 2 { @@ -1079,6 +1089,9 @@ func splitSubjectQueue(sq string) ([]byte, []byte, error) { } else if len(vals) > 2 { return nil, nil, fmt.Errorf("invalid subject-queue %q", sq) } + if !IsValidSubject(vals[0]) || (len(q) > 0 && !IsValidSubject(vals[1])) { + return nil, nil, fmt.Errorf("invalid subject-queue %q", sq) + } return s, q, nil } @@ -1099,15 +1112,35 @@ func (c *client) setPermissions(perms *Permissions) { c.perms.pub.allow = NewSublist(slcache) } for _, pubSubject := range perms.Publish.Allow { + if !IsValidSubject(pubSubject) { + c.Errorf("invalid publish subject %q", pubSubject) + continue + } sub := &subscription{subject: []byte(pubSubject)} - c.perms.pub.allow.Insert(sub) + if err := c.perms.pub.allow.Insert(sub); err != nil { + c.Errorf("invalid publish subject %q", pubSubject) + } } if len(perms.Publish.Deny) > 0 { c.perms.pub.deny = NewSublist(slcache) } for _, pubSubject := range perms.Publish.Deny { - sub := &subscription{subject: []byte(pubSubject)} - c.perms.pub.deny.Insert(sub) + subject := []byte(pubSubject) + if !IsValidSubject(pubSubject) { + var err error + subject, _, err = splitSubjectQueue(pubSubject) + if err != nil { + c.Errorf("invalid publish deny subject %q", pubSubject) + continue + } + // Queue qualifiers have no meaning for publish permissions. If + // one reaches this defensive path, retain the deny's subject + // scope instead of silently creating an unreachable trie node. + c.Errorf("queue qualifier is not valid for publish deny subject %q", pubSubject) + } + if err := c.perms.pub.deny.Insert(&subscription{subject: subject}); err != nil { + c.Errorf("invalid publish deny subject %q", pubSubject) + } } } @@ -1131,12 +1164,12 @@ func (c *client) setPermissions(perms *Permissions) { c.Errorf("%s", err.Error()) continue } - c.perms.sub.allow.Insert(sub) + if err := c.perms.sub.allow.Insert(sub); err != nil { + c.Errorf("invalid subscribe allow subject %q", subSubject) + } } if len(perms.Subscribe.Deny) > 0 { c.perms.sub.deny = NewSublistNoCache() - // Also hold onto this array for later. - c.darray = perms.Subscribe.Deny } for _, subSubject := range perms.Subscribe.Deny { sub := &subscription{} @@ -1145,7 +1178,12 @@ func (c *client) setPermissions(perms *Permissions) { c.Errorf("%s", err.Error()) continue } - c.perms.sub.deny.Insert(sub) + if err := c.perms.sub.deny.Insert(sub); err != nil { + c.Errorf("invalid subscribe deny subject %q", subSubject) + continue + } + // Retain the parsed representation for delivery-time filtering. + c.darray = append(c.darray, sub) } } @@ -1195,14 +1233,14 @@ func (c *client) publicPermissions() *Permissions { subs := _subs[:0] c.perms.sub.allow.All(&subs) for _, sub := range subs { - perms.Subscribe.Allow = append(perms.Subscribe.Allow, string(sub.subject)) + perms.Subscribe.Allow = append(perms.Subscribe.Allow, subjectQueueString(sub)) } } if c.perms.sub.deny != nil { subs := _subs[:0] c.perms.sub.deny.All(&subs) for _, sub := range subs { - perms.Subscribe.Deny = append(perms.Subscribe.Deny, string(sub.subject)) + perms.Subscribe.Deny = append(perms.Subscribe.Deny, subjectQueueString(sub)) } } // Responses. @@ -1214,6 +1252,13 @@ func (c *client) publicPermissions() *Permissions { return perms } +func subjectQueueString(sub *subscription) string { + if len(sub.queue) == 0 { + return string(sub.subject) + } + return string(sub.subject) + " " + string(sub.queue) +} + type denyType int const ( @@ -1235,37 +1280,48 @@ func (c *client) mergeDenyPermissions(what denyType, denyPubs []string) { if c.perms.pub.deny == nil { c.perms.pub.deny = NewSublistForServer(c.srv) } - mergeDenyPerm(&c.perms.pub, denyPubs) + mergeDenyPerm(&c.perms.pub, denyPubs, false) } if what == sub || what == both { if c.perms.sub.deny == nil { // Avoid sublist cache contention in canSubscribe. c.perms.sub.deny = NewSublistNoCache() } - mergeDenyPerm(&c.perms.sub, denyPubs) + c.darray = append(c.darray, mergeDenyPerm(&c.perms.sub, denyPubs, true)...) } } // mergeDenyPerm inserts new deny permissions, skipping subjects that already exist. -func mergeDenyPerm(p *perm, denyPubs []string) { +func mergeDenyPerm(p *perm, denyPubs []string, allowQueue bool) []*subscription { + var inserted []*subscription FOR_DENY: - for _, subj := range denyPubs { - r := p.deny.Match(subj) + for _, deny := range denyPubs { + subject, queue, err := splitSubjectQueue(deny) + if err != nil { + continue + } + if !allowQueue { + queue = nil + } + r := p.deny.Match(string(subject)) for _, v := range r.qsubs { for _, s := range v { - if string(s.subject) == subj { + if bytes.Equal(s.subject, subject) && bytes.Equal(s.queue, queue) { continue FOR_DENY } } } for _, s := range r.psubs { - if string(s.subject) == subj { + if bytes.Equal(s.subject, subject) && len(queue) == 0 { continue FOR_DENY } } - sub := &subscription{subject: []byte(subj)} - p.deny.Insert(sub) + sub := &subscription{subject: subject, queue: queue} + if p.deny.Insert(sub) == nil { + inserted = append(inserted, sub) + } } + return inserted } // Merge client.perms structure with additional pub deny permissions @@ -1301,9 +1357,9 @@ func (c *client) setExpiration(claims *jwt.ClaimsData, validFor time.Duration) { // messages based on a deny clause for subscriptions. // Lock should be held. func (c *client) loadMsgDenyFilter() { - c.mperms = &msgDeny{NewSublistWithCache(), make(map[string]bool)} + c.mperms = &msgDeny{NewSublistWithCache(), make(map[msgDenyKey]bool)} for _, sub := range c.darray { - c.mperms.deny.Insert(&subscription{subject: []byte(sub)}) + c.mperms.deny.Insert(&subscription{subject: sub.subject, queue: sub.queue}) } } @@ -3339,21 +3395,28 @@ func (c *client) canSubscribe(subject string, optQueue ...string) bool { if !c.canSubscribeInternal(subject, optQueue...) { return false } + c.loadMsgDenyFilterIfNeeded(subject, len(optQueue) > 0 && optQueue[0] != _EMPTY_) + return true +} + +// Initializes the delivery-time deny filter when a wildcard subscription, or +// an exact queue subscription, can overlap a deny entry. Assumes caller is +// holding the write lock. +func (c *client) loadMsgDenyFilterIfNeeded(subject string, hasQueue bool) { // We use the actual subscription to signal us to spin up the deny mperms // and cache. We check if the subject is a wildcard that intersects any of // the deny clauses. // FIXME(dlc) - We could be smarter and track when these go away and remove. - if c.mperms == nil && subjectHasWildcard(subject) { + if c.mperms == nil && (hasQueue || subjectHasWildcard(subject)) { // Whip through the deny array and check if this wildcard subject can // overlap with any denied deliveries. for _, sub := range c.darray { - if SubjectsCollide(sub, subject) { + if SubjectsCollide(bytesToString(sub.subject), subject) { c.loadMsgDenyFilter() break } } } - return true } func queueMatches(queue string, qsubs [][]*subscription) bool { @@ -3496,19 +3559,21 @@ func (c *client) processUnsub(arg []byte) error { // presence of deny clauses for subscriptions. Deny clauses will not prevent // larger scoped wildcard subscriptions, so we need to check at delivery time. // Lock should be held. -func (c *client) checkDenySub(subject string) bool { - if denied, ok := c.mperms.dcache[subject]; ok { +func (c *client) checkDenySub(subject, queue string) bool { + key := msgDenyKey{subject, queue} + if denied, ok := c.mperms.dcache[key]; ok { return denied - } else if np, _ := c.mperms.deny.NumInterest(subject); np != 0 { - c.mperms.dcache[subject] = true - return true - } else { - c.mperms.dcache[subject] = false } + r := c.mperms.deny.Match(subject) + denied := len(r.psubs) != 0 + if !denied && queue != _EMPTY_ && len(r.qsubs) != 0 { + denied = queueMatches(queue, r.qsubs) + } + c.mperms.dcache[key] = denied if len(c.mperms.dcache) > maxDenyPermCacheSize { c.pruneDenyCache() } - return false + return denied } // Create a message header for routes or leafnodes. Header and origin cluster aware. @@ -3691,7 +3756,7 @@ func (c *client) deliverMsg(prodIsMQTT bool, sub *subscription, acc *Account, su client := sub.client // Check sub client and check echo. Only do this if not a service import. - if client == nil || (c == client && !client.echo && !sub.si) { + if client == nil || (c == client && !client.echo && !sub.si && !sub.rsi) { if client != nil && mt != nil { client.mu.Lock() mt.addEgressEvent(client, sub, errMsgTraceNoEcho) @@ -3704,7 +3769,7 @@ func (c *client) deliverMsg(prodIsMQTT bool, sub *subscription, acc *Account, su // Check if we have a subscribe deny clause. This will trigger us to check the subject // for a match against the denied subjects. - if client.mperms != nil && client.checkDenySub(string(subject)) { + if client.mperms != nil && client.checkDenySub(string(subject), bytesToString(sub.queue)) { mt.addEgressEvent(client, sub, errMsgTraceSubDeny) client.mu.Unlock() return false @@ -4067,8 +4132,8 @@ func (c *client) pruneReplyPerms() { // deliverMsg. func (c *client) pruneDenyCache() { r := 0 - for subject := range c.mperms.dcache { - delete(c.mperms.dcache, subject) + for key := range c.mperms.dcache { + delete(c.mperms.dcache, key) if r++; r > pruneSize { break } @@ -5083,6 +5148,7 @@ func (c *client) addSubToRouteTargets(sub *subscription) { if sub.queue != nil { rt.qs = append(rt.qs, sub.queue...) rt.qs = append(rt.qs, ' ') + rt.qsubs = append(rt.qsubs, sub) } return } @@ -5101,10 +5167,49 @@ func (c *client) addSubToRouteTargets(sub *subscription) { rt = &c.in.rts[lrts] rt.sub = sub rt.qs = rt._qs[:0] + rt.qsubs = rt._qsubs[:0] if sub.queue != nil { rt.qs = append(rt.qs, sub.queue...) rt.qs = append(rt.qs, ' ') + rt.qsubs = append(rt.qsubs, sub) + } +} + +// Filters queue groups in a coalesced route or leaf target against the +// destination connection's delivery-time subscription denies. +func filterRouteTargetDeny(subject []byte, rt *routeTarget) bool { + dc := rt.sub.client + dc.mu.Lock() + defer dc.mu.Unlock() + if dc.mperms == nil { + return true + } + + dsubject := string(subject) + if len(rt.sub.queue) == 0 { + if dc.checkDenySub(dsubject, _EMPTY_) { + return false + } + } else { + rt.sub = nil + } + + qs := rt.qs[:0] + qsubs := rt.qsubs[:0] + for _, qsub := range rt.qsubs { + if dc.checkDenySub(dsubject, bytesToString(qsub.queue)) { + continue + } + if rt.sub == nil { + rt.sub = qsub + } + qs = append(qs, qsub.queue...) + qs = append(qs, ' ') + qsubs = append(qsubs, qsub) } + rt.qs = qs + rt.qsubs = qsubs + return rt.sub != nil } // This processes the sublist results for a given message. @@ -5553,9 +5658,6 @@ func (c *client) processMsgResults(acc *Account, r *SublistResult, msg, deliver, // We are here if we have selected a leaf or route as the destination, // or if we tried to deliver to a local qsub but failed. c.addSubToRouteTargets(rsub) - if flags&pmrCollectQueueNames != 0 { - queues = append(queues, rsub.queue) - } } } @@ -5585,6 +5687,10 @@ sendToRoutesOrLeafs: // We have inline structs for memory layout and cache coherency. for i := range c.in.rts { rt := &c.in.rts[i] + if (len(rt.qsubs) > 1 || (len(rt.sub.queue) == 0 && len(rt.qsubs) > 0)) && + !filterRouteTargetDeny(subject, rt) { + continue + } dc := rt.sub.client dmsg, hset := msg, false @@ -5617,6 +5723,11 @@ sendToRoutesOrLeafs: mh := c.msgHeaderForRouteOrLeaf(subject, reply, rt, acc) if c.deliverMsg(prodIsMQTT, rt.sub, acc, subject, reply, mh, dmsg, false) { + if flags&pmrCollectQueueNames != 0 { + for _, qsub := range rt.qsubs { + queues = append(queues, qsub.queue) + } + } if rt.sub.icb == nil { dlvMsgs++ switch dc.kind { @@ -5999,10 +6110,13 @@ func (c *client) processSubsOnConfigReload(awcsti map[string]struct{}) { for _, sub := range c.subs { // Just checking to rebuild mperms under the lock, will collect removed though here. // Only collect under subs array of canSubscribe and checkAcc true. - canSub := c.canSubscribe(string(sub.subject)) - canQSub := sub.queue != nil && c.canSubscribe(string(sub.subject), string(sub.queue)) - - if !canSub && !canQSub { + var allowed bool + if len(sub.queue) > 0 { + allowed = c.canSubscribe(string(sub.subject), string(sub.queue)) + } else { + allowed = c.canSubscribe(string(sub.subject)) + } + if !allowed { removed = append(removed, sub) } else if checkAcc { subs = append(subs, sub) @@ -6025,8 +6139,13 @@ func (c *client) processSubsOnConfigReload(awcsti map[string]struct{}) { // Unsubscribe all that need to be removed and report back to client and logs. for _, sub := range removed { c.unsubscribe(acc, sub, true, true) - c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %q)", sub.subject, sub.sid)) - srv.Noticef("Removed sub %q (sid %q) for %s - not authorized", sub.subject, sub.sid, c.getAuthUser()) + if len(sub.queue) > 0 { + c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q using queue %q (sid %q)", sub.subject, sub.queue, sub.sid)) + srv.Noticef("Removed sub %q using queue %q (sid %q) for %s - not authorized", sub.subject, sub.queue, sub.sid, c.getAuthUser()) + } else { + c.sendErr(fmt.Sprintf("Permissions Violation for Subscription to %q (sid %q)", sub.subject, sub.sid)) + srv.Noticef("Removed sub %q (sid %q) for %s - not authorized", sub.subject, sub.sid, c.getAuthUser()) + } } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/const.go b/vendor/github.com/nats-io/nats-server/v2/server/const.go index 4fe5052ea5..d56085a0ca 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/const.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/const.go @@ -66,7 +66,7 @@ func init() { const ( // VERSION is the current version for the server. - VERSION = "2.14.3" + VERSION = "2.14.4" // PROTO is the currently supported protocol. // 0 was the original diff --git a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go index afb7ec03ab..760bd9dcb9 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/consumer.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/consumer.go @@ -502,6 +502,7 @@ type consumer struct { // Clustered. ca *consumerAssignment node RaftNode + term uint64 // Raft term, used to determine if we are still the leader for the current term (if applicable, 0 otherwise). infoSub *subscription lqsent time.Time prm map[string]struct{} @@ -1435,7 +1436,7 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri o.resetStartingSeq(0, _EMPTY_, false) } if config.Direct || standalone { - o.setLeader(true) + o.setLeader(true, 0) } // This is always true in single server mode. @@ -1602,18 +1603,112 @@ func (o *consumer) isLeader() bool { return o.leader.Load() } -func (o *consumer) setLeader(isLeader bool) error { - o.mu.RLock() +func (o *consumer) setLeader(isLeader bool, term uint64) error { + o.mu.Lock() mset, closed := o.mset, o.closed - movingToClustered := o.node != nil && o.pch == nil - movingToNonClustered := o.node == nil && o.pch != nil wasLeader := o.leader.Swap(isLeader) // For clustered new consumers, starting seq selection was deferred from // addConsumerWithAssignment so the scan wouldn't block the meta apply // goroutine, run it here on leader-elect instead. needsSelect := isLeader && !wasLeader && o.dseq == 0 && (o.store == nil || !o.store.HasState()) - o.mu.RUnlock() + + // We can skip the teardown if we were leader before and are still the leader now. + // But only at term 1, since that means scale up from or down to an unreplicated config. + // R1 assets have no raft node and use the coerced term 1. + if term < 1 { + term = 1 + } + skipTeardown := wasLeader && isLeader && term == 1 + o.term = term + + if skipTeardown { + movingToClustered := o.node != nil && o.pch == nil + movingToNonClustered := o.node == nil && o.pch != nil + + // If we detect we are scaling up, make sure to create clustered routines and channels. + if movingToClustered { + // We are moving from R1 to clustered. + o.pch = make(chan struct{}, 1) + go o.loopAndForwardProposals(o.node, o.qch, o.pch, term) + if o.phead != nil { + select { + case o.pch <- struct{}{}: + default: + } + } + } else if movingToNonClustered { + // We are moving from clustered to non-clustered now. + // Set pch to nil so if we scale back up we will recreate the loopAndForward from above. + pch := o.pch + o.pch = nil + select { + case pch <- struct{}{}: + default: + } + } + o.mu.Unlock() + return nil + } + + // Shutdown the go routines and the subscriptions. + if o.qch != nil { + close(o.qch) + o.qch = nil + } + // Stop any inactivity timers. Should only be running on leaders. + stopAndClearTimer(&o.dtmr) + // Stop any unpause timers. Should only be running on leaders. + stopAndClearTimer(&o.uptmr) + // Make sure to clear out any re-deliver queues + o.stopAndClearPtmr() + o.rdc = nil + o.rdq = nil + o.rdqi.Empty() + o.pending = nil + o.rsm = nil + o.resetPendingDeliveries() + // Reset num pending, these are only authoritative on the leader. + o.npc, o.npf = 0, 0 + // ok if they are nil, we protect inside unsubscribe() + o.unsubscribe(o.ackSubOld) + o.unsubscribe(o.ackSub) + o.unsubscribe(o.reqSub) + o.unsubscribe(o.resetSub) + o.unsubscribe(o.fcSubOld) + o.unsubscribe(o.fcSub) + o.ackSubOld, o.ackSub, o.reqSub, o.resetSub, o.fcSubOld, o.fcSub = nil, nil, nil, nil, nil, nil + if o.infoSub != nil { + o.srv.sysUnsubscribe(o.infoSub) + o.infoSub = nil + } + // Reset waiting if we are in pull mode. + if o.isPullMode() { + o.waiting = newWaitQueue(o.cfg.MaxWaiting) + o.nextMsgReqs.drain() + } else if o.srv.gateway.enabled { + stopAndClearTimer(&o.gwdtmr) + } + o.unassignPinId() + + // Make sure to drain queued up acks. + o.ackMsgs.drain() + // Reset amount of acks that need to be processed. + atomic.StoreInt64(&o.awl, 0) + // Also remove any pending replies since we should not be the one to respond at this point. + o.replies = nil + + // Set pch to nil, we will recreate the loopAndForwardProposals for the new term. + if pch := o.pch; pch != nil { + o.pch = nil + select { + case pch <- struct{}{}: + default: + } + } + // Clear proposals, none are valid anymore. + o.phead, o.ptail = nil, nil + o.mu.Unlock() // If we are here we have a change in leader status. if isLeader { @@ -1637,35 +1732,6 @@ func (o *consumer) setLeader(isLeader bool) error { o.mu.Unlock() } - if wasLeader { - // If we detect we are scaling up, make sure to create clustered routines and channels. - if movingToClustered { - o.mu.Lock() - // We are moving from R1 to clustered. - o.pch = make(chan struct{}, 1) - go o.loopAndForwardProposals(o.qch) - if o.phead != nil { - select { - case o.pch <- struct{}{}: - default: - } - } - o.mu.Unlock() - } else if movingToNonClustered { - // We are moving from clustered to non-clustered now. - // Set pch to nil so if we scale back up we will recreate the loopAndForward from above. - o.mu.Lock() - pch := o.pch - o.pch = nil - select { - case pch <- struct{}{}: - default: - } - o.mu.Unlock() - } - return nil - } - mset.mu.RLock() s, jsa, stream := mset.srv, mset.jsa, mset.getCfgName() mset.mu.RUnlock() @@ -1770,8 +1836,10 @@ func (o *consumer) setLeader(isLeader bool) error { o.qch = make(chan struct{}) qch := o.qch node := o.node + var pch chan struct{} if node != nil && o.pch == nil { o.pch = make(chan struct{}, 1) + pch = o.pch } pullMode := o.isPullMode() o.mu.Unlock() @@ -1811,67 +1879,16 @@ func (o *consumer) setLeader(isLeader bool) error { } // If we are R>1 spin up our proposal loop. - if node != nil { + if node != nil && pch != nil { // Determine if we can send pending requests info to the group. // They must be on server versions >= 2.7.1 o.checkAndSetPendingRequestsOk() o.checkPendingRequests() go func() { setGoRoutineLabels(labels) - o.loopAndForwardProposals(qch) + o.loopAndForwardProposals(node, qch, pch, term) }() } - - } else { - // Shutdown the go routines and the subscriptions. - o.mu.Lock() - if o.qch != nil { - close(o.qch) - o.qch = nil - } - // Stop any inactivity timers. Should only be running on leaders. - stopAndClearTimer(&o.dtmr) - // Stop any unpause timers. Should only be running on leaders. - stopAndClearTimer(&o.uptmr) - // Make sure to clear out any re-deliver queues - o.stopAndClearPtmr() - o.rdc = nil - o.rdq = nil - o.rdqi.Empty() - o.pending = nil - o.rsm = nil - o.resetPendingDeliveries() - // Reset num pending, these are only authoritative on the leader. - o.npc, o.npf = 0, 0 - // ok if they are nil, we protect inside unsubscribe() - o.unsubscribe(o.ackSubOld) - o.unsubscribe(o.ackSub) - o.unsubscribe(o.reqSub) - o.unsubscribe(o.resetSub) - o.unsubscribe(o.fcSubOld) - o.unsubscribe(o.fcSub) - o.ackSubOld, o.ackSub, o.reqSub, o.resetSub, o.fcSubOld, o.fcSub = nil, nil, nil, nil, nil, nil - if o.infoSub != nil { - o.srv.sysUnsubscribe(o.infoSub) - o.infoSub = nil - } - // Reset waiting if we are in pull mode. - if o.isPullMode() { - o.waiting = newWaitQueue(o.cfg.MaxWaiting) - o.nextMsgReqs.drain() - } else if o.srv.gateway.enabled { - stopAndClearTimer(&o.gwdtmr) - } - o.unassignPinId() - // If we were the leader make sure to drain queued up acks. - if wasLeader { - o.ackMsgs.drain() - // Reset amount of acks that need to be processed. - atomic.StoreInt64(&o.awl, 0) - // Also remove any pending replies since we should not be the one to respond at this point. - o.replies = nil - } - o.mu.Unlock() } return nil } @@ -2454,6 +2471,9 @@ func (acc *Account) checkNewConsumerConfig(cfg, ncfg *ConsumerConfig) error { if cfg.DeliverPolicy != ncfg.DeliverPolicy { return errors.New("deliver policy can not be updated") } + if cfg.MemoryStorage != ncfg.MemoryStorage { + return errors.New("storage type can not be updated") + } if cfg.OptStartSeq != ncfg.OptStartSeq { return errors.New("start sequence can not be updated") } @@ -2893,25 +2913,14 @@ func (o *consumer) resetLocalStartingSeq(seq uint64) { o.ldt, o.lat = time.Time{}, time.Time{} } -func (o *consumer) loopAndForwardProposals(qch chan struct{}) { - // On exit make sure we nil out pch. - defer func() { - o.mu.Lock() - o.pch = nil - o.mu.Unlock() - }() - - o.mu.RLock() - node, pch := o.node, o.pch - o.mu.RUnlock() - - if node == nil || pch == nil { +func (o *consumer) loopAndForwardProposals(node RaftNode, qch, pch chan struct{}, term uint64) { + if node == nil || qch == nil || pch == nil { return } forwardProposals := func() error { o.mu.Lock() - if o.node == nil || !o.node.Leader() { + if node == nil || !node.Leader() || o.term != term { o.mu.Unlock() return errors.New("no longer leader") } @@ -2925,14 +2934,14 @@ func (o *consumer) loopAndForwardProposals(qch chan struct{}) { entries = append(entries, newEntry(EntryNormal, proposal.data)) sz += len(proposal.data) if sz > maxBatch { - node.ProposeMulti(entries) + node.ProposeMulti(term, entries) // We need to re-create `entries` because there is a reference // to it in the node's pae map. sz, entries = 0, nil } } if len(entries) > 0 { - node.ProposeMulti(entries) + node.ProposeMulti(term, entries) } return nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/dios.go b/vendor/github.com/nats-io/nats-server/v2/server/dios.go new file mode 100644 index 0000000000..8ad3aff319 --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/dios.go @@ -0,0 +1,85 @@ +// Copyright 2026 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "sync/atomic" + "time" +) + +const defaultConcurrentIOs = 4096 +const minConcurrentIOs = 4 +const maxConcurrentIOs = 8192 + +// Used to limit number of disk IO calls in flight since they could all be blocking an OS thread. +// https://github.com/nats-io/nats-server/issues/2742 +type diskIOSemaphore struct { + ch chan struct{} + waiters atomic.Int64 + waits atomic.Uint64 + waitNanos atomic.Uint64 + maxWaitNanos atomic.Uint64 +} + +func newDiskIOSemaphore(n int) *diskIOSemaphore { + n = max(minConcurrentIOs, min(n, maxConcurrentIOs)) + d := &diskIOSemaphore{ch: make(chan struct{}, n)} + for range n { + d.ch <- struct{}{} + } + return d +} + +func defaultDiskIOSemaphore() *diskIOSemaphore { + // The disk IO semaphore used to be sized based on the number + // of CPU cores. That policy led to poor use of devices that + // can handle many requests in parallel, so simply cap the + // number of concurrent IO requests handed to the Go runtime. + return newDiskIOSemaphore(defaultConcurrentIOs) +} + +func (d *diskIOSemaphore) acquire() { + select { + case <-d.ch: + return + default: + // No slot available, count this + // waiter before blocking. + d.waiters.Add(1) + start := time.Now() + <-d.ch + waited := time.Since(start) + d.waiters.Add(-1) + d.countWait(uint64(waited.Nanoseconds())) + } +} + +func (d *diskIOSemaphore) countWait(ns uint64) { + d.waits.Add(1) + d.waitNanos.Add(ns) + for { + cur := d.maxWaitNanos.Load() + if ns <= cur || d.maxWaitNanos.CompareAndSwap(cur, ns) { + return + } + } +} + +func (d *diskIOSemaphore) release() { + d.ch <- struct{}{} +} + +func (d *diskIOSemaphore) cap() int { + return cap(d.ch) +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go index c860461c14..17f1a7b8c2 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/filestore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/filestore.go @@ -215,56 +215,58 @@ type fileStore struct { scheduling *MsgScheduling sdm *SDMMeta lpex time.Time // Last PurgeEx call. + dios *diskIOSemaphore } // Represents a message store block and its data. type msgBlock struct { // Here for 32bit systems and atomic. - first msgId - last msgId - mu sync.RWMutex - fs *fileStore - aek cipher.AEAD - bek cipher.Stream - seed []byte - nonce []byte - mfn string - mfd *os.File - cmp StoreCompression // Effective compression at the time of loading the block - liwsz int64 - index uint32 - bytes uint64 // User visible bytes count. - rbytes uint64 // Total bytes (raw) including deleted. Used for rolling to new blk. - cbytes uint64 // Bytes count after last compaction. 0 if no compaction happened yet. - msgs uint64 // User visible message count. - fss *stree.SubjectTree[SimpleState] - kfn string - lwts int64 - llts int64 - lrts int64 - lsts int64 - llseq uint64 - hh *highwayhash.Digest64 - ecache elastic.Pointer[cache] - cache *cache - cloads uint64 - cexp time.Duration - fexp time.Duration - ctmr *time.Timer - werr error - dmap avl.SequenceSet - fch chan struct{} - qch chan struct{} - lchk [8]byte - loading bool - flusher bool - noTrack bool - needSync bool - syncAlways bool - noCompact bool - closed bool - ttls uint64 // How many msgs have TTLs? - schedules uint64 // How many msgs have schedules? + first msgId + last msgId + mu sync.RWMutex + fs *fileStore + aek cipher.AEAD + bek cipher.Stream + seed []byte + nonce []byte + mfn string + mfd *os.File + cmp StoreCompression // Effective compression at the time of loading the block + liwsz int64 + index uint32 + bytes uint64 // User visible bytes count. + rbytes uint64 // Total bytes (raw) including deleted. Used for rolling to new blk. + cbytes uint64 // Bytes count after last compaction. 0 if no compaction happened yet. + msgs uint64 // User visible message count. + fss *stree.SubjectTree[SimpleState] + kfn string + lwts int64 + llts int64 + lrts int64 + lsts int64 + llseq uint64 + hh *highwayhash.Digest64 + ecache elastic.Pointer[cache] + cache *cache + cloads uint64 + cexp time.Duration + fexp time.Duration + ctmr *time.Timer + werr error + dmap avl.SequenceSet + fch chan struct{} + qch chan struct{} + lchk [8]byte + loading bool + flusher bool + noTrack bool + needSync bool + needKeySync bool // Key file is written once and immutable, cleared after its one sync. + syncAlways bool + noCompact bool + closed bool + ttls uint64 // How many msgs have TTLs? + schedules uint64 // How many msgs have schedules? // Used to mock write failures. mockWriteErr bool @@ -277,6 +279,9 @@ type cache struct { idx []uint32 fseq uint64 nra bool + // When GC collects this cache due to it being a weak pointer, + // recycle the buf back into the block buffer pools. + clean runtime.Cleanup } type msgId struct { @@ -411,6 +416,7 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim if fcfg.SyncInterval == 0 { fcfg.SyncInterval = defaultSyncInterval } + dios := fcfg.srv.diskIOSemaphore() // Check the directory if stat, err := os.Stat(fcfg.StoreDir); os.IsNotExist(err) { @@ -426,12 +432,13 @@ func newFileStoreWithCreated(fcfg FileStoreConfig, cfg StreamConfig, created tim } tmpfile.Close() - <-dios + dios.acquire() os.Remove(tmpfile.Name()) - dios <- struct{}{} + dios.release() fs = &fileStore{ fcfg: fcfg, + dios: dios, psim: stree.NewSubjectTree[psi](), bim: make(map[uint32]*msgBlock), cfg: FileStreamInfo{Created: created, StreamConfig: cfg}, @@ -1050,6 +1057,37 @@ func getMsgBlockBuf(sz int) (buf []byte) { } } +// registerRecycle arranges for the cache's buffer to be returned to the +// block buffer pools when the cache itself is garbage collected due to it +// remaining as a weak pointer. Without it, subsequent block loads would +// allocate fresh buffers, which could result in GC re-running, which is +// pathological under sustained block loads. +func (c *cache) registerRecycle() { + if c == nil { + return + } + // Any previous registration is canceled first. + c.stopRecycle() + // Skip if recycle isn't allowed or nothing to recycle. + if c.nra || cap(c.buf) == 0 { + return + } + // Don't make this cleanup conditional (e.g. skip if under memory pressure), GC + // will drain the block buffer pools separately, so this doesn't pin memory. + // Skipping would be worse, we'd delay freeing the buffer until the next GC cycle. + c.clean = runtime.AddCleanup(c, recycleMsgBlockBuf, c.buf) +} + +// stopRecycle cancels a pending recycle registration. Must be called before +// the buffer is recycled or handed off explicitly, otherwise the cleanup +// could return the same buffer to the pool a second time while it is in use. +func (c *cache) stopRecycle() { + if c.clean != (runtime.Cleanup{}) { + c.clean.Stop() + c.clean = runtime.Cleanup{} + } +} + // Recycle the msg block. func recycleMsgBlockBuf(buf []byte) { switch cap(buf) { @@ -1202,6 +1240,10 @@ func (fs *fileStore) recoverMsgBlock(index uint32) (*msgBlock, error) { // Make sure encryption loaded if needed. if err = fs.loadEncryptionForMsgBlock(mb); err != nil { + // If the encryption key is truncated or unrecoverable, return the block so it can be deleted. + if err == errBadKeySize || err == errKeyInvalid { + return mb, err + } return nil, err } @@ -1433,6 +1475,7 @@ func (mb *msgBlock) convertCipher() error { } // Reset the cache since we just read everything in. + mb.cache.stopRecycle() mb.cache = nil mb.ecache.Set(nil) @@ -1440,19 +1483,19 @@ func (mb *msgBlock) convertCipher() error { // the old keyfile back. if err := fs.genEncryptionKeysForBlock(mb); err != nil { keyFile := filepath.Join(mdir, fmt.Sprintf(keyScan, mb.index)) - fs.writeFileWithOptionalSync(keyFile, ekey, defaultFilePerms) + writeFileWithSync(fs.dios, keyFile, ekey, defaultFilePerms) return err } mb.bek.XORKeyStream(buf, buf) - <-dios + mb.fs.dios.acquire() err = os.WriteFile(mb.mfn, buf, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { return err } return nil } - return fmt.Errorf("unable to recover keys") + return errKeyInvalid } // Convert a plaintext block to encrypted. @@ -1476,6 +1519,7 @@ func (mb *msgBlock) convertToEncrypted() error { return err } // Undo cache from above for later. + mb.cache.stopRecycle() mb.cache = nil mb.ecache.Set(nil) // Regenerate mb.bek so that the keystream offset is at zero. This matches @@ -1486,9 +1530,9 @@ func (mb *msgBlock) convertToEncrypted() error { return err } mb.bek.XORKeyStream(buf, buf) - <-dios + mb.fs.dios.acquire() err = os.WriteFile(mb.mfn, buf, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { return err } @@ -1609,9 +1653,9 @@ func (mb *msgBlock) rebuildStateFromBufLocked(buf []byte, allowTruncate bool) (* if mb.mfd != nil { fd = mb.mfd } else { - <-dios + mb.fs.dios.acquire() fd, err = os.OpenFile(mb.mfn, os.O_RDWR, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err == nil { defer fd.Close() } @@ -1878,15 +1922,15 @@ func (fs *fileStore) recoverFullState() (rerr error) { defer fs.mu.Unlock() // Check for any left over purged messages. - <-dios + fs.dios.acquire() if err := fs.recoverPartialPurge(); err != nil { - dios <- struct{}{} + fs.dios.release() return err } // Grab our stream state file and load it in. fn := filepath.Join(fs.fcfg.StoreDir, msgDir, streamStreamStateFile) buf, err := os.ReadFile(fn) - dios <- struct{}{} + fs.dios.release() if err != nil { if !os.IsNotExist(err) { @@ -2115,12 +2159,12 @@ func (fs *fileStore) recoverFullState() (rerr error) { mdir := filepath.Join(fs.fcfg.StoreDir, msgDir) var dirs []os.DirEntry - <-dios + fs.dios.acquire() if f, err := os.Open(mdir); err == nil { dirs, _ = f.ReadDir(-1) f.Close() } - dios <- struct{}{} + fs.dios.release() var index uint32 for _, fi := range dirs { @@ -2166,10 +2210,10 @@ func (fs *fileStore) recoverFullState() (rerr error) { // Lock should be held. func (fs *fileStore) recoverTTLState() error { // See if we have a timed hash wheel for TTLs. - <-dios + fs.dios.acquire() fn := filepath.Join(fs.fcfg.StoreDir, msgDir, ttlStreamStateFile) buf, err := os.ReadFile(fn) - dios <- struct{}{} + fs.dios.release() if err != nil && !os.IsNotExist(err) { return err @@ -2253,10 +2297,10 @@ func (fs *fileStore) recoverTTLState() error { // Lock should be held. func (fs *fileStore) recoverMsgSchedulingState() error { // See if we have a timed hash wheel for TTLs. - <-dios + fs.dios.acquire() fn := filepath.Join(fs.fcfg.StoreDir, msgDir, msgSchedulingStreamStateFile) buf, err := os.ReadFile(fn) - dios <- struct{}{} + fs.dios.release() if err != nil && !os.IsNotExist(err) { return err @@ -2378,9 +2422,9 @@ func (fs *fileStore) cleanupOldMeta() { mdir := filepath.Join(fs.fcfg.StoreDir, msgDir) fs.mu.RUnlock() - <-dios + fs.dios.acquire() f, err := os.Open(mdir) - dios <- struct{}{} + fs.dios.release() if err != nil { return } @@ -2405,20 +2449,20 @@ func (fs *fileStore) recoverMsgs() error { defer fs.mu.Unlock() // Check for any left over purged messages. - <-dios + fs.dios.acquire() if err := fs.recoverPartialPurge(); err != nil { - dios <- struct{}{} + fs.dios.release() return err } mdir := filepath.Join(fs.fcfg.StoreDir, msgDir) f, err := os.Open(mdir) if err != nil { - dios <- struct{}{} + fs.dios.release() return errNotReadable } dirs, err := f.ReadDir(-1) f.Close() - dios <- struct{}{} + fs.dios.release() if err != nil { return errNotReadable @@ -2488,6 +2532,18 @@ func (fs *fileStore) recoverMsgs() error { mb.last.ts = fs.state.LastTime.UnixNano() } mb.mu.Unlock() + } else if (err == errBadKeySize || err == errKeyInvalid) && mb != nil { + // If we can't load the encryption key, we can't decrypt the block's data. + // We'll revert to deleting this block until there is peer-based recovery. This still + // catches up from the leader if it happened in the stream's tail. + mb.mu.Lock() + if err := mb.dirtyCloseWithRemove(true); err != nil { + mb.mu.Unlock() + return err + } + fs.removeMsgBlockFromList(mb) + mb.mu.Unlock() + continue } else { return err } @@ -3260,6 +3316,13 @@ func (mb *msgBlock) filteredPendingLocked(filter string, wc bool, sseq uint64) ( } } + needsCleanup := mb.cache == nil + defer func() { + if needsCleanup { + mb.finishedWithCache() + } + }() + if filter == _EMPTY_ { filter, wc = fwcs, true } @@ -3339,7 +3402,6 @@ func (mb *msgBlock) filteredPendingLocked(filter string, wc bool, sseq uint64) ( } shouldExpire = true } - defer mb.finishedWithCache() _tsa, _fsa := [32]string{}, [32]string{} tsa, fsa := _tsa[:0], _fsa[:0] @@ -3908,12 +3970,14 @@ func (fs *fileStore) MultiLastSeqs(filters []string, maxSeq uint64, maxAllowed i delete(subs, bytesToString(bsubj)) } else { // Need to search for the real last since recorded last is > maxSeq. - var didLoad bool + needsCleanup := mb.cache == nil if mb.cacheNotLoaded() { if ierr = mb.loadMsgsWithLock(); ierr != nil { + if needsCleanup { + mb.finishedWithCache() + } return false } - didLoad = true } var smv StoreMsg fseq := atomic.LoadUint64(&mb.first.seq) @@ -3928,7 +3992,7 @@ func (fs *fileStore) MultiLastSeqs(filters []string, maxSeq uint64, maxAllowed i delete(subs, ssubj) break } - if didLoad { + if needsCleanup { mb.finishedWithCache() } } @@ -4727,6 +4791,7 @@ func (mb *msgBlock) setupWriteCache(buf []byte) error { // Looks like there isn't an existing file on disk, mint a new cache. mb.cache = &cache{buf: buf} + mb.cache.registerRecycle() mb.ecache.Set(mb.cache) mb.llts = ats.AccessTime() mb.startCacheExpireTimer() @@ -4813,9 +4878,9 @@ func (fs *fileStore) newMsgBlockForWrite() (*msgBlock, error) { } mb.hh = hh - <-dios + fs.dios.acquire() mfd, err := os.OpenFile(mb.mfn, os.O_CREATE|os.O_RDWR, defaultFilePerms) - dios <- struct{}{} + fs.dios.release() if err != nil { if isPermissionError(err) { @@ -4859,11 +4924,14 @@ func (fs *fileStore) genEncryptionKeysForBlock(mb *msgBlock) error { if _, err := os.Stat(keyFile); err != nil && !os.IsNotExist(err) { return err } - err = fs.writeFileWithOptionalSync(keyFile, encrypted, defaultFilePerms) + sync := fs.syncAlways.Load() + err = writeAtomically(fs.dios, keyFile, encrypted, defaultFilePerms, sync) if err != nil { return err } mb.kfn = keyFile + // If we did not sync the key file above, mark it to be synced on the next syncBlocks pass. + mb.needKeySync = !sync return nil } @@ -4878,9 +4946,11 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts, t mmp := uint64(fs.cfg.MaxMsgsPer) var psmc uint64 psmax := mmp > 0 && len(subj) > 0 + var info *psi if psmax { - if info, ok := fs.psim.Find(stringToBytes(subj)); ok { - psmc = info.total + if info, _ = fs.psim.Find(stringToBytes(subj)); info != nil { + // Take current total, but add 1 for the message we are about to store. + psmc = info.total + 1 } } @@ -4890,7 +4960,7 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts, t // the message here since it could cause replicas to drift. if discardNewCheck && fs.cfg.Discard == DiscardNew { var asl bool - if psmax && psmc >= mmp { + if psmax && psmc > mmp { // If we are instructed to discard new per subject, this is an error. // However, allow rollup messages through since they will purge old // messages for the subject after storing, restoring the limit. @@ -4900,7 +4970,12 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts, t if fseq, err = fs.firstSeqForSubj(subj); err != nil { return err } - asl = true + // fs.firstSeqForSubj releases and re-acquires the lock, need to fetch the state again. + if info, _ = fs.psim.Find(stringToBytes(subj)); info != nil { + // Take current total, but add 1 for the message we are about to store. + psmc = info.total + 1 + } + asl = psmc > mmp } if fs.cfg.MaxMsgs > 0 && fs.state.Msgs >= uint64(fs.cfg.MaxMsgs) && !asl { return ErrMaxMsgs @@ -4940,11 +5015,12 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts, t } // Adjust top level tracking of per subject msg counts. - var info *psi - var ok bool if len(subj) > 0 && fs.psim != nil { index := fs.lmb.index - if info, ok = fs.psim.Find(stringToBytes(subj)); ok { + if info == nil { + info, _ = fs.psim.Find(stringToBytes(subj)) + } + if info != nil { info.total++ if index > info.lblk { info.lblk = index @@ -4968,41 +5044,33 @@ func (fs *fileStore) storeRawMsg(subj string, hdr, msg []byte, seq uint64, ts, t fs.state.LastTime = now // Enforce per message limits. - // We snapshotted psmc before our actual write, so >= comparison needed. - if psmax && psmc >= mmp { + for psmax && psmc > mmp { // We may have done this above. if fseq == 0 { fseq, err = fs.firstSeqForSubj(subj) if err != nil { return err + } else if fseq == 0 { + break + } + // fs.firstSeqForSubj releases and re-acquires the lock, need to fetch the state again. + if info, _ = fs.psim.Find(stringToBytes(subj)); info != nil { + psmc = info.total + } else { + break } + // Re-check if we're at the limit. + continue } - if ok, err := fs.removeMsgViaLimits(fseq); err != nil { + if _, err = fs.removeMsgViaLimits(fseq); err != nil && err != ErrStoreMsgNotFound { return err - } else if ok { - // Make sure we are below the limit. - if psmc--; psmc >= mmp { - bsubj := stringToBytes(subj) - for info, ok := fs.psim.Find(bsubj); ok && info.total > mmp; info, ok = fs.psim.Find(bsubj) { - if seq, err := fs.firstSeqForSubj(subj); err != nil { - return err - } else if seq == 0 { - break - } else if ok, err = fs.removeMsgViaLimits(seq); err != nil { - return err - } else if !ok { - break - } - } - } - } else if mb := fs.selectMsgBlock(fseq); mb != nil { - // If we are here we could not remove fseq from above, so rebuild. - var ld *LostStreamData - if ld, _, err = mb.rebuildState(); err != nil { - return err - } else if ld != nil { - fs.rebuildStateLocked(ld) - } + } + fseq = 0 + // fs.removeMsgViaLimits releases and re-acquires the lock, need to fetch the state again. + if info, _ = fs.psim.Find(stringToBytes(subj)); info != nil { + psmc = info.total + } else { + break } } // If we only ever store one/last message for a subject, can correct the first block to where we've just written. @@ -5418,10 +5486,20 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) { fs.mu.Unlock() mb.mu.Lock() + // If marked closed, the block is already gone. + if mb.closed { + mb.mu.Unlock() + fs.mu.Lock() + continue + } + needsCleanup := mb.cache == nil var shouldExpire bool if mb.fssNotLoaded() { // Make sure we have fss loaded. if err := mb.loadMsgsWithLock(); err != nil { + if needsCleanup { + mb.finishedWithCache() + } mb.mu.Unlock() // Re-acquire fs lock fs.mu.Lock() @@ -5438,6 +5516,9 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) { if ss.firstNeedsUpdate || ss.lastNeedsUpdate { err = mb.recalculateForSubj(subj, ss) } + if needsCleanup { + mb.finishedWithCache() + } mb.mu.Unlock() // Re-acquire fs lock fs.mu.Lock() @@ -5456,7 +5537,7 @@ func (fs *fileStore) firstSeqForSubj(subj string) (uint64, error) { if shouldExpire { // Expire this cache before moving on. mb.tryForceExpireCacheLocked() - } else { + } else if needsCleanup { mb.finishedWithCache() } mb.mu.Unlock() @@ -5790,16 +5871,18 @@ func (fs *fileStore) removeMsgFromBlock(mb *msgBlock, seq uint64, secure, viaLim // We used to not have to load in the messages except with callbacks or the filtered subject state (which is now always on). // Now just load regardless. // TODO(dlc) - Figure out a way not to have to load it in, we need subject tracking outside main data block. - var didLoad bool + needsCleanup := mb.cache == nil if mb.cacheNotLoaded() { if err := mb.loadMsgsWithLock(); err != nil { + if needsCleanup { + mb.finishedWithCache() + } mb.mu.Unlock() return false, err } - didLoad = true } finishedWithCache := func() { - if didLoad { + if needsCleanup { mb.finishedWithCache() } } @@ -6065,7 +6148,7 @@ func (mb *msgBlock) compact() error { // writing new messages. We will silently bail on any issues with the underlying block and let someone else detect. // if fseq > 0 we will attempt to cleanup stale tombstones. // Write lock needs to be held. -func (mb *msgBlock) compactWithFloor(floor uint64, fsDmap *avl.SequenceSet) error { +func (mb *msgBlock) compactWithFloor(floor uint64, fsDmap *interiorDeletes) error { wasLoaded := mb.cache != nil && mb.cacheAlreadyLoaded() if !wasLoaded { if err := mb.loadMsgsWithLock(); err != nil { @@ -6172,9 +6255,9 @@ func (mb *msgBlock) compactWithFloor(floor uint64, fsDmap *avl.SequenceSet) erro // We will write to a new file and mv/rename it in case of failure. mfn := filepath.Join(mb.fs.fcfg.StoreDir, msgDir, fmt.Sprintf(newScan, mb.index)) - <-dios + mb.fs.dios.acquire() err := os.WriteFile(mfn, nbuf, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { _ = os.Remove(mfn) return err @@ -6673,6 +6756,7 @@ func (mb *msgBlock) clearCache() { buf := mbcache.buf mb.cache = nil mb.ecache.Set(nil) + mbcache.stopRecycle() recycleMsgBlockBuf(buf) } @@ -6717,6 +6801,10 @@ func (mb *msgBlock) tryExpireWriteCache() []byte { mb.lwts = 0 return buf[:0] } + // The cache may have expired above without recycling the buffer. + if mb.cache == nil && !nra { + recycleMsgBlockBuf(buf) + } return nil } @@ -6771,6 +6859,7 @@ func (mb *msgBlock) tryExpireCacheLocked() { // If we are here we will at least expire the core msg buffer. // We need to capture offset in case we do a write next before a full load. if mb.cache != nil { + mb.cache.stopRecycle() if !mb.cache.nra { recycleMsgBlockBuf(mb.cache.buf) } @@ -7169,9 +7258,9 @@ func (mb *msgBlock) enableForWriting(fip bool) error { if mb.mfd != nil { return nil } - <-dios + mb.fs.dios.acquire() mfd, err := os.OpenFile(mb.mfn, os.O_CREATE|os.O_RDWR, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { return fmt.Errorf("error opening msg block file [%q]: %v", mb.mfn, err) } @@ -7279,11 +7368,13 @@ func (mb *msgBlock) writeMsgRecordLocked(rl, seq uint64, subj string, mhdr, msg // from the next pool size up to save us from reallocating in append() below. if nsz := len(mb.cache.buf) + int(rl); cap(mb.cache.buf) < nsz { prev := mb.cache.buf + mb.cache.stopRecycle() mb.cache.buf = getMsgBlockBuf(nsz) if prev != nil { mb.cache.buf = mb.cache.buf[:copy(mb.cache.buf[:nsz], prev)] recycleMsgBlockBuf(prev) } + mb.cache.registerRecycle() } // Indexing @@ -7508,7 +7599,7 @@ func (fs *fileStore) checkLastBlock(rl uint64) (lmb *msgBlock, err error) { func (fs *fileStore) writeMsgRecord(seq uint64, ts int64, subj string, hdr, msg []byte) (uint64, error) { // Get size for this message. rl := fileStoreMsgSize(subj, hdr, msg) - if rl&hbit != 0 || rl > rlBadThresh { + if isFileStoreMsgTooLarge(rl) { return 0, ErrMsgTooLarge } // Grab our current last message block. @@ -7564,9 +7655,9 @@ func (mb *msgBlock) recompressOnDiskIfNeeded() error { // header, in which case we do nothing. // 2. The block will be uncompressed, in which case we will compress it // and then write it back out to disk, re-encrypting if necessary. - <-dios + mb.fs.dios.acquire() origBuf, err := os.ReadFile(mb.mfn) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { return fmt.Errorf("failed to read original block from disk: %w", err) @@ -7620,9 +7711,9 @@ func (mb *msgBlock) atomicOverwriteFile(buf []byte, allowCompress bool) error { // operation if something goes wrong), create a new temporary file. We will // write out the new block here and then swap the files around afterwards // once everything else has succeeded correctly. - <-dios + mb.fs.dios.acquire() tmpFD, err := os.OpenFile(tmpFN, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, defaultFilePerms) - dios <- struct{}{} + mb.fs.dios.release() if err != nil { return fmt.Errorf("failed to create temporary file: %w", err) @@ -7764,8 +7855,7 @@ func (fs *fileStore) syncBlocks() { fs.setWriteErr(err) } - var fsDmapLoaded bool - var fsDmap avl.SequenceSet + var fsDmap *interiorDeletes var markDirty bool for _, mb := range blks { @@ -7810,10 +7900,12 @@ func (fs *fileStore) syncBlocks() { } // Check if we need to sync. We will not hold lock during actual sync. needSync := mb.needSync + needKeySync, kfn := mb.needKeySync, mb.kfn // Reset. Because we let go of the lock, we could write new data to this mb which might or // might not be synced later if we would've reset after letting go of the lock. mb.needSync = false + mb.needKeySync = false mb.mu.Unlock() // Check if we should compact here. @@ -7822,9 +7914,8 @@ func (fs *fileStore) syncBlocks() { // Load a delete map containing only interior deletes. // This is used when compacting to know if tombstones are still relevant, // and if not they can be compacted. - if !fsDmapLoaded { - fsDmapLoaded = true - fsDmap = fs.deleteMap() + if fsDmap == nil { + fsDmap = deleteMap(blks) } fs.mu.RLock() mb.mu.Lock() @@ -7834,7 +7925,7 @@ func (fs *fileStore) syncBlocks() { fs.mu.RUnlock() continue } - err := mb.compactWithFloor(firstSeq, &fsDmap) + err := mb.compactWithFloor(firstSeq, fsDmap) // If this compact removed all raw bytes due to tombstone cleanup, schedule to remove. shouldRemove := mb.rbytes == 0 mb.mu.Unlock() @@ -7860,6 +7951,14 @@ func (fs *fileStore) syncBlocks() { } } + // Check if we need to sync this block's key file. + if needKeySync && kfn != _EMPTY_ { + if err := fs.syncFileAndDir(kfn); err != nil { + storeFsWerr(err) + continue + } + } + // Check if we need to sync this block. if needSync { mb.mu.Lock() @@ -7869,9 +7968,9 @@ func (fs *fileStore) syncBlocks() { if mb.mfd != nil { fd = mb.mfd } else { - <-dios + fs.dios.acquire() fd, err = os.OpenFile(mb.mfn, os.O_RDWR, defaultFilePerms) - dios <- struct{}{} + fs.dios.release() didOpen = true if err != nil && !os.IsNotExist(err) { mb.mu.Unlock() @@ -7918,9 +8017,9 @@ func (fs *fileStore) syncBlocks() { fn := filepath.Join(fs.fcfg.StoreDir, msgDir, streamStreamStateFile) var fd *os.File var err error - <-dios + fs.dios.acquire() fd, err = os.OpenFile(fn, os.O_RDWR, defaultFilePerms) - dios <- struct{}{} + fs.dios.release() if err != nil && !os.IsNotExist(err) { fs.setWriteErr(err) return @@ -8052,6 +8151,7 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { // The buf arg already came from the pool probably, so there's // no point in reusing mb.cache.buf's underlying capacity here. // Just recycle it for the next block load. + mb.cache.stopRecycle() recycleMsgBlockBuf(mb.cache.buf) } if idx = mb.cache.idx; uint64(cap(idx)) >= idxSz { @@ -8202,6 +8302,7 @@ func (mb *msgBlock) indexCacheBuf(buf []byte) error { mb.cache.wp = int(lbuf) mb.ttls = ttls mb.schedules = schedules + mb.cache.registerRecycle() return nil } @@ -8231,9 +8332,9 @@ func (mb *msgBlock) writeAt(buf []byte, woff int64) (int, error) { mb.mockWriteErr = false return 0, errors.New("mock write error") } - <-dios + mb.fs.dios.acquire() n, err := mb.mfd.WriteAt(buf, woff) - dios <- struct{}{} + mb.fs.dios.release() return n, err } @@ -8393,9 +8494,9 @@ func (mb *msgBlock) fssNotLoaded() bool { // Lock should be held func (mb *msgBlock) openBlock() (*os.File, error) { // Gate with concurrent IO semaphore. - <-dios + mb.fs.dios.acquire() f, err := os.Open(mb.mfn) - dios <- struct{}{} + mb.fs.dios.release() return f, err } @@ -8440,9 +8541,9 @@ func (mb *msgBlock) loadBlock(buf []byte) ([]byte, error) { buf = getMsgBlockBuf(sz) } - <-dios + mb.fs.dios.acquire() n, err := io.ReadFull(f, buf[:sz]) - dios <- struct{}{} + mb.fs.dios.release() // On success capture raw bytes size. if err == nil { mb.rbytes = uint64(n) @@ -8609,6 +8710,7 @@ var ( errPendingData = errors.New("pending data still present") errNoEncryption = errors.New("encryption not enabled") errBadKeySize = errors.New("encryption bad key size") + errKeyInvalid = errors.New("unable to recover keys") errNoMsgBlk = errors.New("no message block") errMsgBlkTooBig = errors.New("message block size exceeded int capacity") errUnknownCipher = errors.New("unknown cipher") @@ -9066,18 +9168,20 @@ func (fs *fileStore) loadLastLocked(subj string, sm *StoreMsg) (lsm *StoreMsg, e return nil, err } } - var didLoad bool + needsCleanup := mb.cache == nil if l > 0 { if mb.cacheNotLoaded() { if err := mb.loadMsgsWithLock(); err != nil { + if needsCleanup { + mb.finishedWithCache() + } mb.mu.Unlock() return nil, err } - didLoad = true } lsm, err = mb.cacheLookup(l, sm) } - if didLoad { + if needsCleanup { mb.finishedWithCache() } mb.mu.Unlock() @@ -9596,6 +9700,12 @@ func fileStoreMsgSize(subj string, hdr, msg []byte) uint64 { return fileStoreMsgSizeRaw(len(subj), len(hdr), len(msg)) } +// isFileStoreMsgTooLarge reports whether a message record cannot be represented +// safely by the file store. +func isFileStoreMsgTooLarge(rl uint64) bool { + return rl&hbit != 0 || rl > rlBadThresh +} + func fileStoreMsgSizeEstimate(slen, maxPayload int) uint64 { return uint64(emptyRecordLen + slen + 4 + maxPayload) } @@ -10093,30 +10203,39 @@ func (fs *fileStore) Purge() (uint64, error) { return fs.purge(0) } -func (fs *fileStore) purge(fseq uint64) (purged uint64, rerr error) { +func (fs *fileStore) purge(fseq uint64) (uint64, error) { if fs.isClosed() { return 0, ErrStoreClosed } - // Persist any write errors. - defer func() { - if rerr != nil { - fs.mu.Lock() - fs.setWriteErr(rerr) - fs.mu.Unlock() - } - }() - fs.mu.Lock() + cb := fs.scb + purged, bytes, err := fs.purgeLocked(fseq) + if err != nil { + fs.setWriteErr(err) + fs.mu.Unlock() + return purged, err + } + fs.mu.Unlock() + + // Force a new index.db to be written. + if purged > 0 { + fs.forceWriteFullState() + } + if cb != nil { + cb(-int64(purged), -int64(bytes), 0, _EMPTY_) + } + return purged, nil +} +// Lock must be held. +func (fs *fileStore) purgeLocked(fseq uint64) (purged, bytes uint64, err error) { // Always return previous write errors. if err := fs.werr; err != nil { - fs.mu.Unlock() - return 0, err + return 0, 0, err } - purged = fs.state.Msgs - rbytes := int64(fs.state.Bytes) + purged, bytes = fs.state.Msgs, fs.state.Bytes fs.state.FirstSeq = fs.state.LastSeq + 1 fs.state.FirstTime = time.Time{} @@ -10140,8 +10259,7 @@ func (fs *fileStore) purge(fseq uint64) (purged uint64, rerr error) { // Make sure we have a lmb to write to. if _, err := fs.newMsgBlockForWrite(); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } lmb := fs.lmb @@ -10153,18 +10271,15 @@ func (fs *fileStore) purge(fseq uint64) (purged uint64, rerr error) { // Leave a tombstone so we can remember our starting sequence in case // full state becomes corrupted. if err := fs.writeTombstone(lseq, lmb.last.ts); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } } // Close FDs since we'll move the file. We re-enable the FD after the purge is complete. if err := lmb.flushPendingMsgs(); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } if err := lmb.closeFDs(); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } fs.blks = nil @@ -10181,37 +10296,32 @@ func (fs *fileStore) purge(fseq uint64) (purged uint64, rerr error) { mdir := filepath.Join(fs.fcfg.StoreDir, msgDir) ndir := filepath.Join(fs.fcfg.StoreDir, newMsgDir) pdir := filepath.Join(fs.fcfg.StoreDir, purgeDir) - <-dios + fs.dios.acquire() // If purge directory still exists then we need to wait // in place and remove since rename would fail. if _, err := os.Stat(ndir); err == nil { if err = os.RemoveAll(ndir); err != nil { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } } else if !os.IsNotExist(err) { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } if _, err := os.Stat(pdir); err == nil { if err = os.RemoveAll(pdir); err != nil { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } } else if !os.IsNotExist(err) { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } // Create directory to move the new tombstone to. if err := os.MkdirAll(ndir, defaultDirPerms); err != nil { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } // Move out the block containing the tombstone. Also move the key file if encrypted. // The block file itself MUST be moved last to ensure we can assume the prior renames @@ -10220,54 +10330,38 @@ func (fs *fileStore) purge(fseq uint64) (purged uint64, rerr error) { b := filepath.Join(mdir, mbf) a := filepath.Join(ndir, mbf) if err := os.Rename(b, a); err != nil && !os.IsNotExist(err) { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } } // Purge all remaining messages. if err := os.Rename(mdir, pdir); err != nil { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } // Rename the directory back to be left only with the tombstone. if err := os.Rename(ndir, mdir); err != nil { - dios <- struct{}{} - fs.mu.Unlock() - return purged, err + fs.dios.release() + return purged, bytes, err } - dios <- struct{}{} + fs.dios.release() // Remove the purged messages directory asynchronously. go func() { - <-dios + fs.dios.acquire() _ = os.RemoveAll(pdir) - dios <- struct{}{} + fs.dios.release() }() // Re-enable writing for the lmb. lmb.mu.Lock() - err := lmb.enableForWriting(fs.fip) + err = lmb.enableForWriting(fs.fip) lmb.mu.Unlock() if err != nil { - fs.mu.Unlock() - return purged, err - } - - cb := fs.scb - fs.mu.Unlock() - - // Force a new index.db to be written. - if purged > 0 { - fs.forceWriteFullState() - } - - if cb != nil { - cb(-int64(purged), -rbytes, 0, _EMPTY_) + return purged, bytes, err } - return purged, nil + return purged, bytes, nil } // Lock and dios should be held. @@ -10322,48 +10416,56 @@ func (fs *fileStore) Compact(seq uint64) (uint64, error) { return fs.compact(seq) } -func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { +func (fs *fileStore) compact(seq uint64) (uint64, error) { if fs.isClosed() { return 0, ErrStoreClosed } - if seq == 0 { - return fs.purge(seq) - } + var err error + var purged, bytes uint64 fs.mu.Lock() - // Always return previous write errors. - if err := fs.werr; err != nil { - fs.mu.Unlock() - return 0, err + if seq == 0 || seq > fs.state.LastSeq { + purged, bytes, err = fs.purgeLocked(seq) + } else { + purged, bytes, err = fs.compactLocked(seq) } - // Same as purge all. - if lseq := fs.state.LastSeq; seq > lseq { + if err != nil { + fs.setWriteErr(err) fs.mu.Unlock() - return fs.purge(seq) + return purged, err + } + cb := fs.scb + fs.mu.Unlock() + + // Force a new index.db to be written. + if purged > 0 { + fs.forceWriteFullState() + } + + if cb != nil && purged > 0 { + cb(-int64(purged), -int64(bytes), 0, _EMPTY_) + } + + return purged, nil +} + +// Lock must be held. +func (fs *fileStore) compactLocked(seq uint64) (purged, bytes uint64, err error) { + // Always return previous write errors. + if err := fs.werr; err != nil { + return 0, 0, err } + // Short-circuit if the store was already compacted past this point. if fs.state.FirstSeq > seq { - fs.mu.Unlock() - return purged, nil + return 0, 0, nil } // We have to delete interior messages. smb := fs.selectMsgBlock(seq) if smb == nil { - fs.mu.Unlock() - return 0, nil + return 0, 0, nil } - // Persist any write errors. - defer func() { - if rerr != nil { - fs.mu.Lock() - fs.setWriteErr(rerr) - fs.mu.Unlock() - } - }() - - var bytes uint64 - // All msgblocks up to this one can be thrown away. var deleted int for _, mb := range fs.blks { @@ -10376,8 +10478,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { // Make sure we do subject cleanup as well. if err := mb.ensurePerSubjectInfoLoaded(); err != nil { mb.mu.Unlock() - fs.mu.Unlock() - return 0, err + return 0, 0, err } mb.fss.IterOrdered(func(bsubj []byte, ss *SimpleState) bool { subj := bytesToString(bsubj) @@ -10390,14 +10491,12 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { err := mb.dirtyCloseWithRemove(true) mb.mu.Unlock() if err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } deleted++ } var smv StoreMsg - var err error var tombs []msgId smb.mu.Lock() @@ -10411,11 +10510,10 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { if smb.cacheNotLoaded() { if err = smb.loadMsgsWithLock(); err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } defer func() { - // The lock is released once we get here, so need to re-acquire. + // The block lock is released once we get here, so need to re-acquire. smb.mu.Lock() smb.finishedWithCache() smb.mu.Unlock() @@ -10442,8 +10540,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { // Update fss if _, err := smb.removeSeqPerSubject(sm.subj, mseq); err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } fs.removePerSubject(sm.subj) tombs = append(tombs, msgId{sm.seq, sm.ts}) @@ -10456,8 +10553,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { if smb != fs.lmb { if err = smb.dirtyCloseWithRemove(true); err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } deleted++ } else { @@ -10490,8 +10586,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { moff, _, _, err = smb.slotInfo(int(atomic.LoadUint64(&smb.first.seq) - smb.cache.fseq)) if err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } else if moff >= uint32(len(smb.cache.buf)) { goto SKIP } @@ -10507,8 +10602,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { originalSize := len(nbuf) if nbuf, err = smb.cmp.Compress(nbuf); err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } meta := &CompressionInfo{ Algorithm: smb.cmp, @@ -10522,8 +10616,7 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { bek, err := genBlockEncryptionKey(smb.fs.fcfg.Cipher, smb.seed, smb.nonce) if err != nil { smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } // For future writes make sure to set smb.bek to keep counter correct. smb.bek = bek @@ -10532,20 +10625,18 @@ func (fs *fileStore) compact(seq uint64) (purged uint64, rerr error) { // We will write to a new file and mv/rename it in case of failure. mfn := filepath.Join(smb.fs.fcfg.StoreDir, msgDir, fmt.Sprintf(newScan, smb.index)) - <-dios + fs.dios.acquire() err = os.WriteFile(mfn, nbuf, defaultFilePerms) - dios <- struct{}{} + fs.dios.release() if err != nil { _ = os.Remove(mfn) smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } if err = os.Rename(mfn, smb.mfn); err != nil { _ = os.Remove(mfn) smb.mu.Unlock() - fs.mu.Unlock() - return purged, err + return purged, bytes, err } // Make sure to remove fss state. @@ -10565,15 +10656,13 @@ SKIP: if len(tombs) > 0 { for _, tomb := range tombs { if err = fs.writeTombstoneNoFlush(tomb.seq, tomb.ts); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } } // Flush any pending. If we change blocks the newMsgBlockForWrite() will flush any pending for us. if lmb := fs.lmb; lmb != nil { if err = lmb.flushPendingMsgs(); err != nil { - fs.mu.Unlock() - return purged, err + return purged, bytes, err } } } @@ -10613,19 +10702,8 @@ SKIP: // after we release the lock. os.Remove(filepath.Join(fs.fcfg.StoreDir, msgDir, streamStreamStateFile)) fs.dirty++ - cb := fs.scb - fs.mu.Unlock() - - // Force a new index.db to be written. - if purged > 0 { - fs.forceWriteFullState() - } - - if cb != nil && purged > 0 { - cb(-int64(purged), -int64(bytes), 0, _EMPTY_) - } - return purged, err + return purged, bytes, nil } // Will completely reset our store. @@ -11271,10 +11349,13 @@ func (mb *msgBlock) removeSeqPerSubject(subj string, seq uint64) (uint64, error) // Will avoid slower path message lookups and scan the cache directly instead. func (mb *msgBlock) recalculateForSubj(subj string, ss *SimpleState) error { // Need to make sure messages are loaded. + needsCleanup := mb.cache == nil if mb.cacheNotLoaded() { if err := mb.loadMsgsWithLock(); err != nil { return err } + } + if needsCleanup { defer mb.finishedWithCache() } @@ -11411,16 +11492,22 @@ func (mb *msgBlock) generatePerSubjectInfo() error { return nil } + needsCleanup := mb.cache == nil if mb.cacheNotLoaded() { if err := mb.loadMsgsWithLock(); err != nil { + if needsCleanup { + mb.finishedWithCache() + } return err } - // indexCacheBuf can produce fss now, so if non-nil we are good. - if mb.fss != nil { - return nil - } + } + if needsCleanup { defer mb.finishedWithCache() } + // indexCacheBuf can produce fss now, so if non-nil we are good. + if mb.fss != nil { + return nil + } // Create new one regardless. mb.fss = mb.fss.Empty() @@ -11516,19 +11603,19 @@ func (fs *fileStore) populateGlobalPerSubjectInfo(mb *msgBlock) error { // Calls os.RemoveAll on the given `dir` directory, but if an error occurs, // retries up to one second. If that still fails, returns the last error // that os.RemoveAll returned. -func removeAllWithRetry(dir string) error { - <-dios +func removeAllWithRetry(dios *diskIOSemaphore, dir string) error { + dios.acquire() err := os.RemoveAll(dir) - dios <- struct{}{} + dios.release() if err == nil { return nil } ttl := time.Now().Add(time.Second) for time.Now().Before(ttl) { time.Sleep(10 * time.Millisecond) - <-dios + dios.acquire() err = os.RemoveAll(dir) - dios <- struct{}{} + dios.release() if err == nil { return nil } @@ -11637,11 +11724,11 @@ func (fs *fileStore) Delete(inline bool) error { // Do this in separate Go routine in case lots of blocks. // Purge above protects us as does the removal of meta artifacts above. if inline { - if err := removeAllWithRetry(ndir); err != nil { + if err := removeAllWithRetry(fs.dios, ndir); err != nil { return err } } else { - go removeAllWithRetry(ndir) + go removeAllWithRetry(fs.dios, ndir) } return nil } @@ -11937,10 +12024,10 @@ func (fs *fileStore) _writeFullState(force bool) error { // Write our update index.db // Protect with dios. - <-dios + fs.dios.acquire() err := os.WriteFile(fn, buf, defaultFilePerms) // if file system is not writable isPermissionError is set to true - dios <- struct{}{} + fs.dios.release() if err != nil { return err } @@ -12345,51 +12432,61 @@ func (fs *fileStore) EncodedStreamState(failed uint64) ([]byte, error) { } } - // Encoded is Msgs, Bytes, FirstSeq, LastSeq, Failed, NumDeleted and optional DeletedBlocks - var buf [1024]byte - buf[0], buf[1] = streamStateMagic, streamStateVersion - n := hdrLen - n += binary.PutUvarint(buf[n:], fs.state.Msgs) - n += binary.PutUvarint(buf[n:], fs.state.Bytes) - n += binary.PutUvarint(buf[n:], fs.state.FirstSeq) - n += binary.PutUvarint(buf[n:], fs.state.LastSeq) - n += binary.PutUvarint(buf[n:], failed) - n += binary.PutUvarint(buf[n:], uint64(numDeleted)) - - b := buf[0:n] + // Encoded is Msgs, Bytes, FirstSeq, LastSeq, Failed, NumDeleted and optional DeletedBlocks. + // Calculate the exact encoded size up front so the buffer is allocated once. + total := hdrLen + uvarintLen(fs.state.Msgs) + uvarintLen(fs.state.Bytes) + + uvarintLen(fs.state.FirstSeq) + uvarintLen(fs.state.LastSeq) + + uvarintLen(failed) + uvarintLen(uint64(numDeleted)) + var dbs DeleteBlocks if numDeleted > 0 { - var scratch [4 * 1024]byte - fs.readLockAllMsgBlocks() defer fs.readUnlockAllMsgBlocks() + var sz int + dbs, sz = fs.deleteBlocks() + total += sz + } - for _, db := range fs.deleteBlocks() { - switch db := db.(type) { - case *DeleteRange: - first, _, num := db.State() - scratch[0] = runLengthMagic - i := 1 - i += binary.PutUvarint(scratch[i:], first) - i += binary.PutUvarint(scratch[i:], num) - b = append(b, scratch[0:i]...) - case *avl.SequenceSet: - buf := db.Encode(scratch[:0]) - b = append(b, buf...) - default: - return nil, errors.New("no impl") + b := make([]byte, 0, total) + b = append(b, streamStateMagic, streamStateVersion) + b = binary.AppendUvarint(b, fs.state.Msgs) + b = binary.AppendUvarint(b, fs.state.Bytes) + b = binary.AppendUvarint(b, fs.state.FirstSeq) + b = binary.AppendUvarint(b, fs.state.LastSeq) + b = binary.AppendUvarint(b, failed) + b = binary.AppendUvarint(b, uint64(numDeleted)) + + for _, db := range dbs { + switch db := db.(type) { + case *DeleteRange: + b = appendRunLength(b, db.First, db.Num) + case *avl.SequenceSet: + enc := db.Encode(b[len(b):]) + if n := len(b) + len(enc); n <= cap(b) { + b = b[:n] + } else { + // Fallback if the buffer didn't have spare capacity. + b = append(b, enc...) } + default: + return nil, errors.New("no impl") } } + if len(b) != total { + assert.Unreachable("Filestore EncodedStreamState size accounting mismatch", map[string]any{ + "name": fs.cfg.Name, + "total": total, + "length": len(b), + }) + } return b, nil } // deleteBlocks returns DeleteBlocks representing interior deletes -// and gaps between blocks. +// and gaps between blocks, as well as their total binary encoded size. // All blocks should be at least read locked. -func (fs *fileStore) deleteBlocks() DeleteBlocks { - var dbs DeleteBlocks +func (fs *fileStore) deleteBlocks() (dbs DeleteBlocks, sz int) { var prevLast uint64 var prevRange *DeleteRange var msgsSinceGap bool @@ -12404,46 +12501,82 @@ func (fs *fileStore) deleteBlocks() DeleteBlocks { // blocks containing messages between the // two gaps. if prevRange != nil && !msgsSinceGap { + sz -= runLengthEncodeLen(prevRange.First, prevRange.Num) prevRange.Num += gapSize + sz += runLengthEncodeLen(prevRange.First, prevRange.Num) } else { prevRange = &DeleteRange{ First: prevLast + 1, Num: gapSize, } + sz += runLengthEncodeLen(prevRange.First, prevRange.Num) msgsSinceGap = false dbs = append(dbs, prevRange) } } if mb.dmap.Size() > 0 { dbs = append(dbs, &mb.dmap) + sz += mb.dmap.EncodeLen() prevRange = nil } prevLast = atomic.LoadUint64(&mb.last.seq) msgsSinceGap = msgsSinceGap || mb.msgs > 0 } - return dbs + return dbs, sz } -// deleteMap returns all interior deletes for each block based on the mb.dmap. -// Specifically, this will not contain any deletes for blocks that have been removed. -// This is useful to know whether a tombstone is still relevant and marked as deleted by an active block. -// No locks should be held. -func (fs *fileStore) deleteMap() (dmap avl.SequenceSet) { - fs.mu.RLock() - defer fs.mu.RUnlock() +// interiorDeletes is a point-in-time view of the interior deletes tracked by +// the live message blocks, held as per-block clones of each mb.dmap. Blocks +// own disjoint ascending sequence ranges, so a lookup binary searches for the +// owning clone. Reads require no locks. +type interiorDeletes struct { + sets []*avl.SequenceSet // Per-block dmap clones, ascending disjoint ranges. + maxs []uint64 // Last sequence of the block owning each clone. + last int // Clone index of the previous lookup. +} - fs.readLockAllMsgBlocks() - defer fs.readUnlockAllMsgBlocks() +// Exists returns whether the sequence was marked as an interior delete by a +// live block at the time the view was built. +// Not safe for concurrent use. +func (v *interiorDeletes) Exists(seq uint64) bool { + if v == nil { + return false + } + // Check the clone that answered the previous lookup first, sequences are + // mostly checked in ascending order and cluster per block. + if i := v.last; i < len(v.maxs) && seq <= v.maxs[i] && (i == 0 || v.maxs[i-1] < seq) { + return v.sets[i].Exists(seq) + } + // First clone whose max is >= seq is the only one that can contain it. + i, _ := slices.BinarySearch(v.maxs, seq) + if i == len(v.sets) { + return false + } + v.last = i + return v.sets[i].Exists(seq) +} - for _, mb := range fs.blks { - if mb.dmap.Size() > 0 { - mb.dmap.Range(func(seq uint64) bool { - dmap.Insert(seq) - return true - }) +// deleteMap returns a view of all interior deletes for each of the given blocks, +// based on the mb.dmap. Specifically, this will not contain any deletes for blocks +// that had already been removed. This is useful to know whether a tombstone is +// still relevant and marked as deleted by an active block. +// No locks should be held on entry. +func deleteMap(blks []*msgBlock) *interiorDeletes { + v := interiorDeletes{ + sets: make([]*avl.SequenceSet, 0, len(blks)), + maxs: make([]uint64, 0, len(blks)), + } + for _, mb := range blks { + mb.mu.RLock() + if !mb.closed && mb.dmap.Size() > 0 { + // The block's last sequence bounds all of its dmap entries and + // preserves the ascending disjoint ordering across clones. + v.sets = append(v.sets, mb.dmap.Clone()) + v.maxs = append(v.maxs, atomic.LoadUint64(&mb.last.seq)) } + mb.mu.RUnlock() } - return dmap + return &v } // SyncDeleted will make sure this stream has same deleted state as dbs. @@ -12467,7 +12600,7 @@ func (fs *fileStore) SyncDeleted(dbs DeleteBlocks) error { lseq := fs.state.LastSeq fs.readLockAllMsgBlocks() - mdbs := fs.deleteBlocks() + mdbs, _ := fs.deleteBlocks() // We'll release the locks below, so need to copy the ones that are references // which are only safe while the locks are still held. for i, db := range mdbs { @@ -12530,12 +12663,43 @@ func pruneDeleteBlock(db DeleteBlock, blocks DeleteBlocks) (bool, DeleteBlocks) } if aFirst == bFirst && aLast == bLast && aNum == bNum { - return true, blocks[1:] + // Matching state is only conclusive for a dense block; two sparse + // sequence sets can share the same state but differ in contents. + if aNum == aLast-aFirst+1 || deleteBlockContentsEqual(db, blocks[0]) { + return true, blocks[1:] + } } return false, blocks } +// deleteBlockContentsEqual reports whether two sparse delete blocks with +// identical State() contain the same sequences. If neither block is a +// sequence set we can't compare cheaply and safely report unequal. +func deleteBlockContentsEqual(a, b DeleteBlock) bool { + ssa, aIsSet := a.(*avl.SequenceSet) + ssb, bIsSet := b.(*avl.SequenceSet) + if aIsSet && bIsSet { + return ssa.Equal(ssb) + } + // Use whichever side is a SequenceSet for fast Exists lookups. + var ss *avl.SequenceSet + var other DeleteBlock + if bIsSet { + ss, other = ssb, a + } else if aIsSet { + ss, other = ssa, b + } else { + return false + } + equal := true + other.Range(func(seq uint64) bool { + equal = ss.Exists(seq) + return equal + }) + return equal +} + //////////////////////////////////////////////////////////////////////////////// // Consumers //////////////////////////////////////////////////////////////////////////////// @@ -13199,29 +13363,6 @@ func (o *consumerFileStore) encryptState(buf []byte) ([]byte, error) { return o.aek.Seal(nonce, nonce, buf, nil), nil } -// Used to limit number of disk IO calls in flight since they could all be blocking an OS thread. -// https://github.com/nats-io/nats-server/issues/2742 -var dios chan struct{} - -// Used to setup our simplistic counting semaphore using buffered channels. -// golang.org's semaphore seemed a bit heavy. -func init() { - // Limit ourselves to a sensible number of blocking I/O calls. Range between - // 4-16 concurrent disk I/Os based on CPU cores, or 50% of cores if greater - // than 32 cores. - mp := runtime.GOMAXPROCS(-1) - nIO := min(16, max(4, mp)) - if mp > 32 { - // If the system has more than 32 cores then limit dios to 50% of cores. - nIO = max(16, min(mp, mp/2)) - } - dios = make(chan struct{}, nIO) - // Fill it up to start. - for i := 0; i < nIO; i++ { - dios <- struct{}{} - } -} - func (o *consumerFileStore) writeState(buf []byte) error { // Check if we have the index file open. o.mu.Lock() @@ -13402,9 +13543,9 @@ func (o *consumerFileStore) stateWithCopyLocked(doCopy bool) (*ConsumerState, er } // Read the state in here from disk.. - <-dios + o.fs.dios.acquire() buf, err := os.ReadFile(o.ifn) - dios <- struct{}{} + o.fs.dios.release() if err != nil && !os.IsNotExist(err) { return nil, err @@ -13665,7 +13806,7 @@ func (o *consumerFileStore) delete(streamDeleted bool) error { // If our stream was not deleted this will remove the directories. if odir != _EMPTY_ && !streamDeleted { - if err := removeAllWithRetry(odir); err != nil { + if err := removeAllWithRetry(o.fs.dios, odir); err != nil { return err } } @@ -13806,27 +13947,25 @@ func (alg StoreCompression) Decompress(buf []byte) ([]byte, error) { // sets O_SYNC on the open file if SyncAlways is set. The dios semaphore is // handled automatically by this function, so don't wrap calls to it in dios. func (fs *fileStore) writeFileWithOptionalSync(name string, data []byte, perm fs.FileMode) error { - return writeAtomically(name, data, perm, fs.syncAlways.Load()) + return writeAtomically(fs.dios, name, data, perm, fs.syncAlways.Load()) } -func writeFileWithSync(name string, data []byte, perm fs.FileMode) error { - return writeAtomically(name, data, perm, true) +func writeFileWithSync(dios *diskIOSemaphore, name string, data []byte, perm fs.FileMode) error { + return writeAtomically(dios, name, data, perm, true) } // Windows does not support fsyncing directory metadata, it results in a panic, so // we need to skip doing this there. const canFsyncDirectories = runtime.GOOS != "windows" -func writeAtomically(name string, data []byte, perm fs.FileMode, sync bool) error { +func writeAtomically(dios *diskIOSemaphore, name string, data []byte, perm fs.FileMode, sync bool) error { tmp := name + ".tmp" flags := os.O_CREATE | os.O_WRONLY | os.O_TRUNC if sync { flags = flags | os.O_SYNC } - <-dios - defer func() { - dios <- struct{}{} - }() + dios.acquire() + defer dios.release() f, err := os.OpenFile(tmp, flags, perm) if err != nil { return err @@ -13848,18 +13987,53 @@ func writeAtomically(name string, data []byte, perm fs.FileMode, sync bool) erro if sync && canFsyncDirectories { // To ensure that the file rename was persisted on all filesystems, // also try to flush the directory metadata. - var d *os.File - if d, err = os.Open(filepath.Dir(name)); err != nil { + if err = syncDir(name); err != nil { return err } - if err = d.Sync(); err != nil { - // Close fd, but ignore its error since sync takes precedence. - _ = d.Close() - return err + } + return nil +} + +func (fs *fileStore) syncFileAndDir(name string) error { + fs.dios.acquire() + defer fs.dios.release() + f, err := os.OpenFile(name, os.O_RDWR, defaultFilePerms) + if err != nil { + if os.IsNotExist(err) { + return nil } - if err = d.Close(); err != nil { + return err + } + if err = f.Sync(); err != nil { + // Close fd, but ignore its error since sync takes precedence. + _ = f.Close() + return err + } + if err = f.Close(); err != nil { + return err + } + if canFsyncDirectories { + if err = syncDir(name); err != nil { return err } } return nil } + +// Dios should already be held. +func syncDir(name string) error { + var d *os.File + var err error + if d, err = os.Open(filepath.Dir(name)); err != nil { + return err + } + if err = d.Sync(); err != nil { + // Close fd, but ignore its error since sync takes precedence. + _ = d.Close() + return err + } + if err = d.Close(); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go index 73ae3ad802..80f5f197ab 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream.go @@ -716,7 +716,7 @@ func (s *Server) disableJetStream(deleteState bool) error { func (s *Server) enableJetStreamAccounts() error { // Reuse the same task workers across all accounts, so that we don't explode // with a large number of goroutines on multi-account systems. - tq := parallelTaskQueue(len(dios)) + tq := parallelTaskQueue(min(64, s.diskIOSemaphore().cap())) defer close(tq) // If we have no configured accounts setup then setup imports on global account. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go index 5d10e57fec..f19e636cd1 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_api.go @@ -2925,7 +2925,7 @@ func (s *Server) jsLeaderAccountPurgeRequest(sub *subscription, c *client, _ *Ac for osa := range js.streamAssignmentsOrInflightSeq(accName) { for oca := range js.consumerAssignmentsOrInflightSeq(accName, osa.Config.Name) { ca := &consumerAssignment{Group: oca.Group, Stream: oca.Stream, Name: oca.Name, Config: oca.Config, Subject: subject, Client: oca.Client, Created: oca.Created} - if err = meta.Propose(encodeDeleteConsumerAssignment(ca)); err != nil { + if err = meta.Propose(cc.term, encodeDeleteConsumerAssignment(ca)); err != nil { js.mu.Unlock() resp.Error = NewJSStreamGeneralError(err) s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp)) @@ -2935,7 +2935,7 @@ func (s *Server) jsLeaderAccountPurgeRequest(sub *subscription, c *client, _ *Ac nc++ } sa := &streamAssignment{Group: osa.Group, Config: osa.Config, Subject: subject, Client: osa.Client, Created: osa.Created} - if err = meta.Propose(encodeDeleteStreamAssignment(sa)); err != nil { + if err = meta.Propose(cc.term, encodeDeleteStreamAssignment(sa)); err != nil { js.mu.Unlock() resp.Error = NewJSStreamGeneralError(err) s.sendAPIErrResponse(ci, acc, subject, reply, string(msg), s.jsonResponse(&resp)) @@ -4186,7 +4186,7 @@ func (s *Server) jsStreamSnapshotRequest(sub *subscription, c *client, _ *Accoun s.sendAPIErrResponse(ci, acc, subject, reply, smsg, s.jsonResponse(&resp)) return } - if !IsValidSubject(req.DeliverSubject) { + if !IsValidPublishSubject(req.DeliverSubject) { resp.Error = NewJSSnapshotDeliverSubjectInvalidError() s.sendAPIErrResponse(ci, acc, subject, reply, smsg, s.jsonResponse(&resp)) return @@ -4230,7 +4230,10 @@ func (s *Server) jsStreamSnapshotRequest(sub *subscription, c *client, _ *Accoun }) // Now do the real streaming. - s.streamSnapshot(acc, mset, sr, &req) + if err := s.streamSnapshot(acc, mset, sr, &req); err != nil { + s.Warnf("Snapshot of stream '%s > %s' failed: %v", mset.jsa.account.Name, mset.name(), err) + return + } end := time.Now().UTC() @@ -4263,7 +4266,7 @@ const defaultSnapshotAckTimeout = 5 * time.Second var snapshotAckTimeout = defaultSnapshotAckTimeout // streamSnapshot will stream out our snapshot to the reply subject. -func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult, req *JSApiStreamSnapshotRequest) { +func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult, req *JSApiStreamSnapshotRequest) error { chunkSize, wndSize := req.ChunkSize, req.WindowSize if chunkSize == 0 { chunkSize = defaultSnapshotChunkSize @@ -4288,7 +4291,9 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult, // Check interest for the snapshot deliver subject. inch := make(chan bool, 1) - acc.sl.RegisterNotification(req.DeliverSubject, inch) + if err := acc.sl.RegisterNotification(req.DeliverSubject, inch); err != nil { + return fmt.Errorf("could not register snapshot delivery interest for %q: %w", req.DeliverSubject, err) + } defer acc.sl.ClearNotification(req.DeliverSubject, inch) hasInterest := <-inch if !hasInterest { @@ -4356,6 +4361,7 @@ func (s *Server) streamSnapshot(acc *Account, mset *stream, sr *SnapshotResult, done: mset.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0)) + return nil } // For determining consumer request type. @@ -5287,7 +5293,7 @@ func (s *Server) jsConsumerPauseRequest(sub *subscription, c *client, _ *Account setStaticConsumerMetadata(nca.Config) eca := encodeAddConsumerAssignment(nca) - if err = meta.Propose(eca); err != nil { + if err = meta.Propose(cc.term, eca); err != nil { js.mu.Unlock() return } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go index 1ac6636d3e..1968ce8a05 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_batching.go @@ -130,8 +130,8 @@ func getBatchStoreDir(storeDir, streamName, batchId string) (string, string) { func newBatchStore(mset *stream, batchId string, replicas int, storage StorageType, storeDir, streamName string) (StreamStore, error) { if replicas == 1 && storage == FileStorage { bname, storeDir := getBatchStoreDir(storeDir, streamName, batchId) - fcfg := FileStoreConfig{AsyncFlush: true, BlockSize: defaultLargeBlockSize, StoreDir: storeDir} s := mset.srv + fcfg := FileStoreConfig{AsyncFlush: true, BlockSize: defaultLargeBlockSize, StoreDir: storeDir, srv: s} prf := s.jsKeyGen(s.getOpts().JetStreamKey, mset.acc.Name) if prf != nil { // We are encrypted here, fill in correct cipher selection. @@ -546,6 +546,12 @@ func checkMsgHeadersPreClusteredProposal( var incr *big.Int var hasSchedule bool + // Do this before staging any proposal state. All clustered publish paths, + // including atomic and fast batches, use this helper. + if mset.store.Type() == FileStorage && isFileStoreMsgTooLarge(fileStoreMsgSize(subject, hdr, msg)) { + return hdr, msg, 0, NewJSStreamStoreFailedError(ErrMsgTooLarge), ErrMsgTooLarge + } + // Some header checks must be checked pre proposal. if len(hdr) > 0 { // Since we encode header len as u16 make sure we do not exceed. @@ -689,7 +695,7 @@ func checkMsgHeadersPreClusteredProposal( if sources == nil { sources = map[string]map[string]string{} } - if _, ok = sources[origStream]; !ok { + if sources[origStream] == nil { sources[origStream] = map[string]string{} } prevVal := sources[origStream][origSubj] @@ -758,7 +764,7 @@ func checkMsgHeadersPreClusteredProposal( // Allow override of the subject used for the check. seqSubj := subject if optSubj := getExpectedLastSeqPerSubjectForSubject(hdr); optSubj != _EMPTY_ { - seqSubj = optSubj + seqSubj = copyString(optSubj) } // The subject is already written to in this batch, we can't allow @@ -1071,11 +1077,11 @@ func recalculateClusteredSeq(mset *stream, needStreamLock bool) (lseq uint64) { // mset.clMu lock must be held. func commitSingleMsg( diff *batchStagedDiff, mset *stream, subject string, reply string, hdr []byte, msg []byte, name string, - jsa *jsAccount, mt *msgTrace, node RaftNode, replicas int, lseq uint64, + jsa *jsAccount, mt *msgTrace, node RaftNode, term uint64, replicas int, lseq uint64, ) error { // Do proposal. esm := encodeStreamMsgAllowCompress(subject, reply, hdr, msg, mset.clseq, time.Now().UnixNano(), false) - if err := node.Propose(esm); err != nil { + if err := node.Propose(term, esm); err != nil { return err } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go index b1d3ae19de..e97b36a992 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/jetstream_cluster.go @@ -60,6 +60,8 @@ type jetStreamCluster struct { // Holds a map of a peer ID to the reply subject, to only respond after gaining // quorum on the peer-remove action. peerRemoveReply map[string]peerRemoveInfo + // Raft term, used to determine if we are still the leader for the current term. + term uint64 // Signals meta-leader should check the stream assignments. streamsCheck bool // Server. @@ -1038,7 +1040,7 @@ func (js *jetStream) setupMetaGroup() error { cfg.Observer = s.canExtendOtherDomain() && s.getOpts().JetStreamExtHint != jsNoExtend var bootstrap bool - if ps, err := readPeerState(storeDir); err != nil { + if ps, err := readPeerState(s.diskIOSemaphore(), storeDir); err != nil { s.Noticef("JetStream cluster bootstrapping") bootstrap = true peers := s.ActivePeers() @@ -1072,7 +1074,7 @@ func (js *jetStream) setupMetaGroup() error { // To track possible configuration changes, responsible for an altered value of cfg.Observer, // set extension state to undetermined. ps.domainExt = extUndetermined - if err := writePeerState(storeDir, ps); err != nil { + if err := writePeerState(s.diskIOSemaphore(), storeDir, ps); err != nil { return err } } @@ -1916,9 +1918,10 @@ func (js *jetStream) monitorCluster() { } aq.recycle(&ces) - case isLeader = <-lch: + case lc := <-lch: + isLeader = lc.isLeader // Process the change. - js.processLeaderChange(isLeader) + js.processLeaderChange(isLeader, lc.term) if isLeader { s.sendInternalMsgLocked(serverStatsPingReqSubj, _EMPTY_, nil, nil) // Install a snapshot as we become leader. @@ -2013,10 +2016,12 @@ type writeableStreamAssignment struct { Consumers []*writeableConsumerAssignment } +// Returns the stream config as registered in the meta layer, from an inflight +// proposal that has not been applied yet, or from an applied assignment otherwise. func (js *jetStream) clusterStreamConfig(accName, streamName string) (StreamConfig, bool) { js.mu.RLock() defer js.mu.RUnlock() - if sa, ok := js.cluster.streams[accName][streamName]; ok { + if sa := js.streamAssignmentOrInflight(accName, streamName); sa != nil { return *sa.Config, true } return StreamConfig{}, false @@ -2044,7 +2049,12 @@ func (js *jetStream) applyMetaSnapshot(buf []byte, ru *recoveryUpdates, isRecove nasa := streams[account] for sn, sa := range asa { if nsa := nasa[sn]; nsa == nil { + // Stream was removed. saDel = append(saDel, sa) + } else if !nsa.Created.Equal(sa.Created) { + // Stream was recreated. + saDel = append(saDel, sa) + saAdd = append(saAdd, nsa) } else { saChk = append(saChk, nsa) } @@ -2069,10 +2079,10 @@ func (js *jetStream) applyMetaSnapshot(buf []byte, ru *recoveryUpdates, isRecove } if osa := js.streamAssignment(sa.Client.serviceAccount(), sa.Config.Name); osa != nil { for _, ca := range osa.consumers { - // Consumer was either removed, or recreated with a different raft group. + // Consumer was either removed or recreated. if nca := sa.consumers[ca.Name]; nca == nil { caDel = append(caDel, ca) - } else if nca.Group != nil && ca.Group != nil && nca.Group.Name != ca.Group.Name { + } else if !nca.Created.Equal(ca.Created) { caDel = append(caDel, ca) } } @@ -2330,6 +2340,9 @@ func (js *jetStream) collectStreamAndConsumerChanges(c RaftNodeCheckpoint, strea for _, e := range ae.entries { if e.Type == EntryNormal { buf := e.Data + if len(buf) == 0 { + return errBadEntryOp + } op := entryOp(buf[0]) switch op { case assignStreamOp, updateStreamOp, removeStreamOp: @@ -2497,7 +2510,7 @@ func (js *jetStream) processAddPeer(peer string) { csa := sa.copyGroup() csa.Group.Peers = append(csa.Group.Peers, peer) // Send our proposal for this csa. Also use same group definition for all the consumers as well. - if err := cc.meta.Propose(encodeAddStreamAssignment(csa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddStreamAssignment(csa)); err != nil { return } cc.trackInflightStreamProposal(accName, csa, false) @@ -2509,7 +2522,7 @@ func (js *jetStream) processAddPeer(peer string) { if ca.Config.Durable != _EMPTY_ || len(ca.Group.Peers) > 1 { cca := ca.copyGroup() cca.Group.Peers = csa.Group.Peers - if err := cc.meta.Propose(encodeAddConsumerAssignment(cca)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddConsumerAssignment(cca)); err != nil { return } cc.trackInflightConsumerProposal(accName, csa.Config.Name, cca, false) @@ -2596,7 +2609,7 @@ func (js *jetStream) removePeerFromStreamLocked(sa *streamAssignment, peer strin } // Send our proposal for this csa. Also use same group definition for all the consumers as well. - if err := cc.meta.Propose(encodeAddStreamAssignment(csa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddStreamAssignment(csa)); err != nil { return false } cc.trackInflightStreamProposal(accName, csa, false) @@ -2609,13 +2622,13 @@ func (js *jetStream) removePeerFromStreamLocked(sa *streamAssignment, peer strin if ca.Config.Durable != _EMPTY_ { cca := ca.copyGroup() cca.Group.Peers, cca.Group.Preferred = rg.Peers, _EMPTY_ - if err := cc.meta.Propose(encodeAddConsumerAssignment(cca)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddConsumerAssignment(cca)); err != nil { return false } cc.trackInflightConsumerProposal(accName, csa.Config.Name, cca, false) } else if ca.Group.isMember(peer) { // These are ephemerals. Check to see if we deleted this peer. - if err := cc.meta.Propose(encodeDeleteConsumerAssignment(ca)); err != nil { + if err := cc.meta.Propose(cc.term, encodeDeleteConsumerAssignment(ca)); err != nil { return false } cc.trackInflightConsumerProposal(accName, csa.Config.Name, ca, true) @@ -2715,6 +2728,9 @@ func (js *jetStream) applyMetaEntries(entries []*Entry, ru *recoveryUpdates) (bo } } else { buf := e.Data + if len(buf) == 0 { + return isRecovering, didSnap, errBadEntryOp + } switch entryOp(buf[0]) { case assignStreamOp: sa, err := decodeStreamAssignment(js.srv, buf[1:]) @@ -2986,7 +3002,7 @@ retry: cfg := &RaftConfig{Name: rgName, Store: storeDir, Log: store, Track: true, Recovering: recovering, ScaleUp: rgScaleUp} - if _, err := readPeerState(storeDir); err != nil { + if _, err := readPeerState(s.diskIOSemaphore(), storeDir); err != nil { s.bootstrapRaftNode(cfg, rgPeers, true) } @@ -3500,9 +3516,10 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps doSnapshot(false) } - case isLeader = <-lch: + case lc := <-lch: + isLeader = lc.isLeader // Process our leader change. - js.processStreamLeaderChange(mset, isLeader) + js.processStreamLeaderChange(mset, isLeader, lc.term) if isLeader { if mset != nil && n != nil && sendSnapshot && !isRecovering { @@ -3677,10 +3694,12 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps return } // Trigger the stream followers to catchup. + var term uint64 if n = mset.raftNode(); n != nil { n.SendSnapshot(mset.stateSnapshot()) + term = n.Term() } - js.processStreamLeaderChange(mset, isLeader) + js.processStreamLeaderChange(mset, isLeader, term) // Check to see if we have restored consumers here. // These are not currently assigned so we will need to do so here. @@ -3695,7 +3714,12 @@ func (js *jetStream) monitorStream(mset *stream, sa *streamAssignment, sendSnaps } for _, o := range consumers { name, cfg := o.String(), o.config() - rg := cc.createGroupForConsumer(&cfg, sa) + rg, err := cc.createGroupForConsumer(&cfg, sa) + if err != nil { + s.Warnf("Could not create group for consumer '%s > %s > %s': %v", + sa.Client.serviceAccount(), sa.Config.Name, name, err) + continue + } // Pick a preferred leader. rg.setPreferred(s) @@ -3986,6 +4010,9 @@ func (js *jetStream) applyStreamEntries(mset *stream, ce *CommittedEntry, isReco } if e.Type == EntryNormal { + if len(e.Data) == 0 { + return 0, errBadEntryOp + } buf, op := e.Data, entryOp(e.Data[0]) if op == batchMsgOp { batchId, batchSeq, _, _, err := decodeBatchMsg(buf[1:]) @@ -4648,7 +4675,7 @@ func (s *Server) replicas(node RaftNode) []*PeerInfo { } // Process a leader change for the clustered stream. -func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) { +func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool, term uint64) { if mset == nil { return } @@ -4719,7 +4746,7 @@ func (js *jetStream) processStreamLeaderChange(mset *stream, isLeader bool) { } // Tell stream to switch leader status. - mset.setLeader(isLeader) + mset.setLeader(isLeader, term) if !isLeader || hasResponded { return @@ -5303,7 +5330,7 @@ func (js *jetStream) processClusterUpdateStream(acc *Account, osa, sa *streamAss // If the stream is scaled down, there is a chance we weren't already the leader. if isLeader && numReplicas == 1 && oldNumReplicas > 1 { - js.processStreamLeaderChange(mset, true) + js.processStreamLeaderChange(mset, true, 0) } // Check for missing syncSubject bug. @@ -5564,7 +5591,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme s.sendInternalMsgLocked(streamAssignmentSubj, _EMPTY_, nil, b) return } - js.processStreamLeaderChange(mset, true) + js.processStreamLeaderChange(mset, true, 0) // Check to see if we have restored consumers here. // These are not currently assigned so we will need to do so here. @@ -5581,7 +5608,12 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme for _, o := range consumers { name, cfg := o.String(), o.config() - rg := cc.createGroupForConsumer(&cfg, sa) + rg, err := cc.createGroupForConsumer(&cfg, sa) + if err != nil { + s.Warnf("Could not create group for consumer '%s > %s > %s': %v", + sa.Client.serviceAccount(), sa.Config.Name, name, err) + continue + } // Place our initial state here as well for assignment distribution. ca := &consumerAssignment{ @@ -5623,7 +5655,7 @@ func (js *jetStream) processClusterCreateStream(acc *Account, sa *streamAssignme } }) } else { - js.processStreamLeaderChange(mset, true) + js.processStreamLeaderChange(mset, true, 0) } } } @@ -6220,7 +6252,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s func() { defer s.grWG.Done() defer o.clearMonitorRunning() - err = o.setLeader(true) + err = o.setLeader(true, 0) var resp = JSApiConsumerCreateResponse{ApiResponse: ApiResponse{Type: JSApiConsumerCreateResponseType}} if err != nil { resp.Error = NewJSConsumerCreateError(err, Unless(err)) @@ -6260,7 +6292,7 @@ func (js *jetStream) processClusterCreateConsumer(oca, ca *consumerAssignment, s func() { defer s.grWG.Done() defer o.clearMonitorRunning() - js.processConsumerLeaderChangeWithAssignment(o, cca, true) + js.processConsumerLeaderChangeWithAssignment(o, cca, true, 0) }, pprofLabels{ "type": "consumer", @@ -6695,13 +6727,14 @@ func (js *jetStream) monitorConsumer(o *consumer, ca *consumerAssignment) { } aq.recycle(&ces) - case isLeader = <-lch: + case lc := <-lch: + isLeader = lc.isLeader if recovering && !isLeader { js.setConsumerAssignmentRecovering(ca) } // Process the change. - if err := js.processConsumerLeaderChange(o, isLeader); err == nil { + if err := js.processConsumerLeaderChange(o, isLeader, lc.term); err == nil { // Check our state if we are under an interest based stream. if mset := o.getStream(); mset != nil { var ss StreamState @@ -6900,6 +6933,9 @@ func (js *jetStream) applyConsumerEntries(o *consumer, ce *CommittedEntry, isLea // Ignore for now. } else { buf := e.Data + if len(buf) == 0 { + return errBadEntryOp + } switch entryOp(buf[0]) { case updateDeliveredOp: dseq, sseq, dc, ts, err := decodeDeliveredUpdate(buf[1:]) @@ -6950,9 +6986,11 @@ func (js *jetStream) applyConsumerEntries(o *consumer, ce *CommittedEntry, isLea return err } case updateSkipOp: + sseq, err := decodeSkipUpdate(buf[1:]) + if err != nil { + return err + } o.mu.Lock() - var le = binary.LittleEndian - sseq := le.Uint64(buf[1:]) if !o.isLeader() && sseq > o.sseq { o.sseq = sseq } @@ -6964,10 +7002,11 @@ func (js *jetStream) applyConsumerEntries(o *consumer, ce *CommittedEntry, isLea } o.mu.Unlock() case resetSeqOp: + sseq, reply, err := decodeResetUpdate(buf[1:]) + if err != nil { + return err + } o.mu.Lock() - var le = binary.LittleEndian - sseq := le.Uint64(buf[1:9]) - reply := string(buf[9:]) o.resetLocalStartingSeq(sseq) if o.store != nil { o.store.Reset(sseq - 1) @@ -7086,16 +7125,19 @@ func (o *consumer) processReplicatedAck(dseq, sseq uint64) error { return nil } +var errBadEntryOp = errors.New("jetstream cluster bad replicated entry") var errBadAckUpdate = errors.New("jetstream cluster bad replicated ack update") var errBadDeliveredUpdate = errors.New("jetstream cluster bad replicated delivered update") +var errBadSkipUpdate = errors.New("jetstream cluster bad replicated skip update") +var errBadResetUpdate = errors.New("jetstream cluster bad replicated reset update") func decodeAckUpdate(buf []byte) (dseq, sseq uint64, err error) { var bi, n int - if dseq, n = binary.Uvarint(buf); n < 0 { + if dseq, n = binary.Uvarint(buf); n <= 0 { return 0, 0, errBadAckUpdate } bi += n - if sseq, n = binary.Uvarint(buf[bi:]); n < 0 { + if sseq, n = binary.Uvarint(buf[bi:]); n <= 0 { return 0, 0, errBadAckUpdate } return dseq, sseq, nil @@ -7103,29 +7145,43 @@ func decodeAckUpdate(buf []byte) (dseq, sseq uint64, err error) { func decodeDeliveredUpdate(buf []byte) (dseq, sseq, dc uint64, ts int64, err error) { var bi, n int - if dseq, n = binary.Uvarint(buf); n < 0 { + if dseq, n = binary.Uvarint(buf); n <= 0 { return 0, 0, 0, 0, errBadDeliveredUpdate } bi += n - if sseq, n = binary.Uvarint(buf[bi:]); n < 0 { + if sseq, n = binary.Uvarint(buf[bi:]); n <= 0 { return 0, 0, 0, 0, errBadDeliveredUpdate } bi += n - if dc, n = binary.Uvarint(buf[bi:]); n < 0 { + if dc, n = binary.Uvarint(buf[bi:]); n <= 0 { return 0, 0, 0, 0, errBadDeliveredUpdate } bi += n - if ts, n = binary.Varint(buf[bi:]); n < 0 { + if ts, n = binary.Varint(buf[bi:]); n <= 0 { return 0, 0, 0, 0, errBadDeliveredUpdate } return dseq, sseq, dc, ts, nil } -func (js *jetStream) processConsumerLeaderChange(o *consumer, isLeader bool) error { - return js.processConsumerLeaderChangeWithAssignment(o, nil, isLeader) +func decodeSkipUpdate(buf []byte) (sseq uint64, err error) { + if len(buf) < 8 { + return 0, errBadSkipUpdate + } + return binary.LittleEndian.Uint64(buf), nil +} + +func decodeResetUpdate(buf []byte) (sseq uint64, reply string, err error) { + if len(buf) < 8 { + return 0, _EMPTY_, errBadResetUpdate + } + return binary.LittleEndian.Uint64(buf[:8]), string(buf[8:]), nil +} + +func (js *jetStream) processConsumerLeaderChange(o *consumer, isLeader bool, term uint64) error { + return js.processConsumerLeaderChangeWithAssignment(o, nil, isLeader, term) } -func (js *jetStream) processConsumerLeaderChangeWithAssignment(o *consumer, ca *consumerAssignment, isLeader bool) error { +func (js *jetStream) processConsumerLeaderChangeWithAssignment(o *consumer, ca *consumerAssignment, isLeader bool, term uint64) error { stepDownIfLeader := func() error { if node := o.raftNode(); node != nil && isLeader { node.StepDown() @@ -7173,7 +7229,7 @@ func (js *jetStream) processConsumerLeaderChangeWithAssignment(o *consumer, ca * } // Tell consumer to switch leader status. - if lerr := o.setLeader(isLeader); lerr != nil && err == nil { + if lerr := o.setLeader(isLeader, term); lerr != nil && err == nil { err = lerr } @@ -7356,20 +7412,21 @@ func (js *jetStream) processStreamAssignmentResults(sub *subscription, c *client // Pick a new preferred leader. rg.setPreferred(s) // Get rid of previous attempt. - if err := cc.meta.Propose(encodeDeleteStreamAssignment(sa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeDeleteStreamAssignment(sa)); err != nil { return } cc.trackInflightStreamProposal(result.Account, sa, true) // Propose new. - sa.Group, sa.err = rg, nil - if err := cc.meta.Propose(encodeAddStreamAssignment(sa)); err != nil { + nsa := sa.copyGroup() + nsa.Group, nsa.err = rg, nil + if err := cc.meta.Propose(cc.term, encodeAddStreamAssignment(nsa)); err != nil { return } - cc.trackInflightStreamProposal(result.Account, sa, false) + cc.trackInflightStreamProposal(result.Account, nsa, false) // When the new stream assignment is processed, sa.reassigning will be // automatically set back to false. Until then, don't process any more // assignment results. - sa.reassigning = true + nsa.reassigning = true return } } @@ -7397,7 +7454,7 @@ func (js *jetStream) processStreamAssignmentResults(sub *subscription, c *client } s.Warnf("Stream assignment for '%s > %s' rejected by assigned member: %v", sa.Client.serviceAccount(), sa.Config.Name, apiErr) sa.err = NewJSClusterNotAssignedError() - if err := cc.meta.Propose(encodeDeleteStreamAssignment(sa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeDeleteStreamAssignment(sa)); err != nil { return } cc.trackInflightStreamProposal(result.Account, sa, true) @@ -7535,7 +7592,7 @@ func (s *Server) sendDomainLeaderElectAdvisory() { s.publishAdvisory(nil, JSAdvisoryDomainLeaderElected, adv) } -func (js *jetStream) processLeaderChange(isLeader bool) { +func (js *jetStream) processLeaderChange(isLeader bool, term uint64) { if js == nil { return } @@ -7543,8 +7600,6 @@ func (js *jetStream) processLeaderChange(isLeader bool) { if s == nil { return } - // Update our server atomic. - s.isMetaLeader.Store(isLeader) if isLeader { s.Noticef("Self is new JetStream cluster metadata leader") @@ -7568,6 +7623,10 @@ func (js *jetStream) processLeaderChange(isLeader bool) { js.mu.Lock() defer js.mu.Unlock() + // Update our server atomic, while holding the lock to not race with API requests. + s.isMetaLeader.Store(isLeader) + js.cluster.term = term + // Clear replies for peer-removes. js.cluster.peerRemoveReply = nil @@ -7601,7 +7660,7 @@ func (js *jetStream) processLeaderChange(isLeader bool) { s.Warnf("Stream assignment corrupt for stream '%s > %s'", acc, sa.Config.Name) nsa := &streamAssignment{Group: sa.Group, Config: sa.Config, Subject: sa.Subject, Reply: sa.Reply, Client: sa.Client, Created: sa.Created} nsa.Sync = syncSubjForStream() - if err := cc.meta.Propose(encodeUpdateStreamAssignment(nsa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeUpdateStreamAssignment(nsa)); err != nil { return } cc.trackInflightStreamProposal(acc, nsa, false) @@ -8239,7 +8298,7 @@ func (s *Server) jsClusteredStreamRequest(ci *ClientInfo, acc *Account, subject, } // Sync subject for post snapshot sync. sa := &streamAssignment{Group: rg, Sync: syncSubject, Config: cfg, Subject: subject, Reply: reply, Client: ci, Created: time.Now().UTC()} - if err := cc.meta.Propose(encodeAddStreamAssignment(sa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddStreamAssignment(sa)); err != nil { return } // On success, add this as an inflight proposal so we can apply limits @@ -8683,14 +8742,14 @@ func (s *Server) jsClusteredStreamUpdateRequest(ci *ClientInfo, acc *Account, su syncSubject = syncSubjForStream() } sa := &streamAssignment{Group: rg, Sync: syncSubject, Created: osa.Created, Config: newCfg, Subject: subject, Reply: reply, Client: ci} - if err := meta.Propose(encodeUpdateStreamAssignment(sa)); err != nil { + if err := meta.Propose(cc.term, encodeUpdateStreamAssignment(sa)); err != nil { return } cc.trackInflightStreamProposal(acc.Name, sa, false) // Process any staged consumers. for _, ca := range consumers { - if err := meta.Propose(encodeAddConsumerAssignment(ca)); err != nil { + if err := meta.Propose(cc.term, encodeAddConsumerAssignment(ca)); err != nil { return } cc.trackInflightConsumerProposal(acc.Name, sa.Config.Name, ca, false) @@ -8719,7 +8778,7 @@ func (s *Server) jsClusteredStreamDeleteRequest(ci *ClientInfo, acc *Account, st } sa := &streamAssignment{Group: osa.Group, Config: osa.Config, Subject: subject, Reply: reply, Client: ci, Created: osa.Created} - if err := cc.meta.Propose(encodeDeleteStreamAssignment(sa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeDeleteStreamAssignment(sa)); err != nil { return } cc.trackInflightStreamProposal(acc.Name, sa, true) @@ -8735,7 +8794,7 @@ func (s *Server) jsClusteredStreamPurgeRequest( preq *JSApiStreamPurgeRequest, ) { js, cc := s.getJetStreamCluster() - if js == nil || cc == nil { + if js == nil || cc == nil || mset == nil { return } @@ -8750,17 +8809,16 @@ func (s *Server) jsClusteredStreamPurgeRequest( } if n := sa.Group.node; n != nil { - sp := &streamPurge{Stream: stream, LastSeq: mset.state().LastSeq, Subject: subject, Reply: reply, Client: ci, Request: preq} - n.Propose(encodeStreamPurge(sp)) + sp := encodeStreamPurge(&streamPurge{Stream: stream, LastSeq: mset.state().LastSeq, Subject: subject, Reply: reply, Client: ci, Request: preq}) js.mu.Unlock() + mset.mu.RLock() + term := mset.term + mset.mu.RUnlock() + n.Propose(term, sp) return } js.mu.Unlock() - if mset == nil { - return - } - var resp = JSApiStreamPurgeResponse{ApiResponse: ApiResponse{Type: JSApiStreamPurgeResponseType}} purged, err := mset.purge(preq) if err != nil { @@ -8824,7 +8882,7 @@ func (s *Server) jsClusteredStreamRestoreRequest( sa := &streamAssignment{Group: rg, Sync: syncSubjForStream(), Config: &cfg, Subject: subject, Reply: reply, Client: ci, Created: time.Now().UTC()} // Now add in our restore state and pre-select a peer to handle the actual receipt of the snapshot. sa.Restore = &req.State - if err := cc.meta.Propose(encodeAddStreamAssignment(sa)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddStreamAssignment(sa)); err != nil { return } cc.trackInflightStreamProposal(ci.serviceAccount(), sa, false) @@ -9249,7 +9307,7 @@ func (s *Server) jsClusteredConsumerDeleteRequest(ci *ClientInfo, acc *Account, return } ca := &consumerAssignment{Group: oca.Group, Stream: stream, Name: consumer, Config: oca.Config, Subject: subject, Reply: reply, Client: ci, Created: oca.Created} - if err := cc.meta.Propose(encodeDeleteConsumerAssignment(ca)); err != nil { + if err := cc.meta.Propose(cc.term, encodeDeleteConsumerAssignment(ca)); err != nil { return } cc.trackInflightConsumerProposal(acc.Name, stream, ca, true) @@ -9284,9 +9342,12 @@ func (s *Server) jsClusteredMsgDeleteRequest(ci *ClientInfo, acc *Account, mset // Check for single replica items. if n := sa.Group.node; n != nil { - md := streamMsgDelete{Seq: req.Seq, NoErase: req.NoErase, Stream: stream, Subject: subject, Reply: reply, Client: ci} - n.Propose(encodeMsgDelete(&md)) + md := encodeMsgDelete(&streamMsgDelete{Seq: req.Seq, NoErase: req.NoErase, Stream: stream, Subject: subject, Reply: reply, Client: ci}) js.mu.Unlock() + mset.mu.RLock() + term := mset.term + mset.mu.RUnlock() + n.Propose(term, md) return } js.mu.Unlock() @@ -9393,9 +9454,9 @@ func decodeDeleteRange(buf []byte) (*DeleteRange, error) { } // createGroupForConsumer will create a new group from same peer set as the stream. -func (cc *jetStreamCluster) createGroupForConsumer(cfg *ConsumerConfig, sa *streamAssignment) *raftGroup { +func (cc *jetStreamCluster) createGroupForConsumer(cfg *ConsumerConfig, sa *streamAssignment) (*raftGroup, *selectPeerError) { if len(sa.Group.Peers) == 0 || cfg.Replicas > len(sa.Group.Peers) { - return nil + return nil, &selectPeerError{misc: true} } replicas := cfg.replicas(sa.Config) @@ -9413,14 +9474,14 @@ func (cc *jetStreamCluster) createGroupForConsumer(cfg *ConsumerConfig, sa *stre } if quorum := replicas/2 + 1; quorum > len(active) { // Not enough active to satisfy the request. - return nil + return nil, &selectPeerError{offline: true} } // If we want less then our parent stream, select from active. if replicas > 0 && replicas < len(peers) { // Pedantic in case stream is say R5 and consumer is R3 and 3 or more offline, etc. if len(active) < replicas { - return nil + return nil, &selectPeerError{offline: true} } // First shuffle the active peers and then select to account for replica = 1. rand.Shuffle(len(active), func(i, j int) { active[i], active[j] = active[j], active[i] }) @@ -9430,7 +9491,7 @@ func (cc *jetStreamCluster) createGroupForConsumer(cfg *ConsumerConfig, sa *stre if cfg.MemoryStorage { storage = MemoryStorage } - return &raftGroup{Name: groupNameForConsumer(peers, storage), Storage: storage, Peers: peers} + return &raftGroup{Name: groupNameForConsumer(peers, storage), Storage: storage, Peers: peers}, nil } // jsClusteredConsumerRequest is first point of entry to create a consumer in clustered mode. @@ -9615,8 +9676,8 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp)) return } - rg := cc.createGroupForConsumer(cfg, sa) - if rg == nil { + rg, err := cc.createGroupForConsumer(cfg, sa) + if err != nil { resp.Error = NewJSInsufficientResourcesError() s.sendAPIErrResponse(ci, acc, subject, reply, string(rmsg), s.jsonResponse(&resp)) return @@ -9792,7 +9853,7 @@ func (s *Server) jsClusteredConsumerRequest(ci *ClientInfo, acc *Account, subjec } // Do formal proposal. - if err := cc.meta.Propose(encodeAddConsumerAssignment(ca)); err != nil { + if err := cc.meta.Propose(cc.term, encodeAddConsumerAssignment(ca)); err != nil { return } cc.trackInflightConsumerProposal(acc.Name, stream, ca, false) @@ -9920,7 +9981,10 @@ func decodeStreamMsg(buf []byte) (subject, reply string, hdr, msg []byte, lseq u } ml := int(le.Uint32(buf)) buf = buf[4:] - if len(buf) < ml { + // ml is read as a uint32 but held in an int; on 32-bit builds a length with + // the high bit set becomes negative, which slips past len(buf) < ml and then + // panics on buf[:ml]. Reject a negative length so the bound holds everywhere. + if ml < 0 || len(buf) < ml { return _EMPTY_, _EMPTY_, nil, nil, 0, 0, false, errBadStreamMsg } if msg = buf[:ml]; len(msg) == 0 { @@ -10141,7 +10205,7 @@ func (mset *stream) processClusteredInboundMsg(subject, reply string, hdr, msg [ canRespond := !mset.cfg.NoAck && len(reply) > 0 name, stype := mset.cfg.Name, mset.cfg.Storage discard, discardNewPer, maxMsgs, maxMsgsPer, maxBytes := mset.cfg.Discard, mset.cfg.DiscardNewPer, mset.cfg.MaxMsgs, mset.cfg.MaxMsgsPer, mset.cfg.MaxBytes - s, js, jsa, st, r, tierName, outq, node := mset.srv, mset.js, mset.jsa, mset.cfg.Storage, mset.cfg.Replicas, mset.tier, mset.outq, mset.node + s, js, jsa, st, r, tierName, outq, node, term := mset.srv, mset.js, mset.jsa, mset.cfg.Storage, mset.cfg.Replicas, mset.tier, mset.outq, mset.node, mset.term maxMsgSize, lseq := int(mset.cfg.MaxMsgSize), mset.lseq isLeader, isSealed, allowRollup, denyPurge, allowTTL, allowMsgCounter, allowMsgSchedules := mset.isLeader(), mset.cfg.Sealed, mset.cfg.AllowRollup, mset.cfg.DenyPurge, mset.cfg.AllowMsgTTL, mset.cfg.AllowMsgCounter, mset.cfg.AllowMsgSchedules @@ -10266,7 +10330,7 @@ func (mset *stream) processClusteredInboundMsg(subject, reply string, hdr, msg [ return err } - err = commitSingleMsg(diff, mset, subject, reply, hdr, msg, name, jsa, mt, node, r, lseq) + err = commitSingleMsg(diff, mset, subject, reply, hdr, msg, name, jsa, mt, node, term, r, lseq) mset.clMu.Unlock() return err } @@ -10666,7 +10730,7 @@ RETRY: sreq = nil // Run our own select loop here. - for qch, lch := n.QuitC(), n.LeadChangeC(); ; { + for qch := n.QuitC(); ; { select { case <-msgsQ.ch: notActive.Reset(activityInterval) @@ -10754,16 +10818,16 @@ RETRY: msgsQ.recycle(&mrecs) } s.Warnf("Catchup for stream '%s > %s' stalled", mset.account(), mset.name()) + // Sanity check that we've not become leader. Shouldn't be possible + // since we haven't applied the snapshot yet. + if n.State() == Leader { + n.StepDown() + } goto RETRY case <-s.quitCh: return ErrServerNotRunning case <-qch: return errCatchupStreamStopped - case isLeader := <-lch: - if isLeader { - n.StepDown() - goto RETRY - } } } } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go index ee5f281d9b..6740b31e8b 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/leafnode.go @@ -216,6 +216,12 @@ func validateLeafNode(o *Options) error { if r.LocalAccount == _EMPTY_ { r.LocalAccount = globalAccountName } + if err := checkPermSubjectArray(r.DenyImports, false); err != nil { + return fmt.Errorf("invalid deny_imports for remote %s: %w", r.safeName(), err) + } + if err := checkPermSubjectArray(r.DenyExports, false); err != nil { + return fmt.Errorf("invalid deny_exports for remote %s: %w", r.safeName(), err) + } rn := r.name() if _, dup := names[rn]; dup { return fmt.Errorf("duplicate remote %s", r.safeName()) @@ -2049,7 +2055,7 @@ func (s *Server) addLeafNodeConnection(c *client, srvName, clusterName string, c meta.setObserver(false, extNotExtended) c.Debugf("Turning JetStream metadata controller Observer Mode off") // Take note that the domain was not extended to avoid this state from startup. - writePeerState(js.config.StoreDir, meta.currentPeerState()) + writePeerState(c.srv.diskIOSemaphore(), js.config.StoreDir, meta.currentPeerState()) // Meta controller can't be leader yet. // Yet it is possible that due to observer mode every server already stopped campaigning. // Therefore this server needs to be kicked into campaigning gear explicitly. @@ -2284,9 +2290,10 @@ func (c *client) processLeafNodeConnect(s *Server, arg []byte, lang string) erro if !c.isSolicitedLeafNode() && c.perms != nil { sp, pp := c.perms.sub, c.perms.pub c.perms.sub, c.perms.pub = pp, sp - if c.opts.Import != nil { - c.darray = c.opts.Import.Deny - } else { + // setPermissions populated darray from the subscribe permissions, + // which are the import permissions advertised to the spoke. Keep + // those parsed denies after reversing the live permission directions. + if c.opts.Import == nil { c.darray = nil } } @@ -2959,6 +2966,7 @@ func (c *client) processLeafSub(argo []byte) (err error) { c.Debugf(fmt.Sprintf("Permissions Violation for Subscription to %q", sub.subject)) return nil } + c.loadMsgDenyFilterIfNeeded(subj, len(sub.queue) > 0) } // Check if we have a maximum on the number of subscriptions. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go index ed841eafb5..d60db1d4d8 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/memstore.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/memstore.go @@ -22,6 +22,7 @@ import ( "sync" "time" + "github.com/antithesishq/antithesis-sdk-go/assert" "github.com/nats-io/nats-server/v2/server/ats" "github.com/nats-io/nats-server/v2/server/avl" "github.com/nats-io/nats-server/v2/server/gsl" @@ -2395,24 +2396,42 @@ func (ms *memStore) EncodedStreamState(failed uint64) ([]byte, error) { numDeleted = 0 } - // Encoded is Msgs, Bytes, FirstSeq, LastSeq, Failed, NumDeleted and optional DeletedBlocks - var buf [1024]byte - buf[0], buf[1] = streamStateMagic, streamStateVersion - n := hdrLen - n += binary.PutUvarint(buf[n:], ms.state.Msgs) - n += binary.PutUvarint(buf[n:], ms.state.Bytes) - n += binary.PutUvarint(buf[n:], ms.state.FirstSeq) - n += binary.PutUvarint(buf[n:], ms.state.LastSeq) - n += binary.PutUvarint(buf[n:], failed) - n += binary.PutUvarint(buf[n:], uint64(numDeleted)) + // Encoded is Msgs, Bytes, FirstSeq, LastSeq, Failed, NumDeleted and optional DeletedBlocks. + // Calculate the exact encoded size up front so the buffer is allocated once. + total := hdrLen + uvarintLen(ms.state.Msgs) + uvarintLen(ms.state.Bytes) + + uvarintLen(ms.state.FirstSeq) + uvarintLen(ms.state.LastSeq) + + uvarintLen(failed) + uvarintLen(uint64(numDeleted)) - b := buf[0:n] + if numDeleted > 0 { + total += ms.dmap.EncodeLen() + } + + b := make([]byte, 0, total) + b = append(b, streamStateMagic, streamStateVersion) + b = binary.AppendUvarint(b, ms.state.Msgs) + b = binary.AppendUvarint(b, ms.state.Bytes) + b = binary.AppendUvarint(b, ms.state.FirstSeq) + b = binary.AppendUvarint(b, ms.state.LastSeq) + b = binary.AppendUvarint(b, failed) + b = binary.AppendUvarint(b, uint64(numDeleted)) if numDeleted > 0 { - buf := ms.dmap.Encode(nil) - b = append(b, buf...) + enc := ms.dmap.Encode(b[len(b):]) + if n := len(b) + len(enc); n <= cap(b) { + b = b[:n] + } else { + // Fallback if the buffer didn't have spare capacity. + b = append(b, enc...) + } } + if len(b) != total { + assert.Unreachable("Memstore EncodedStreamState size accounting mismatch", map[string]any{ + "name": ms.cfg.Name, + "total": total, + "length": len(b), + }) + } return b, nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go index fbc79d56f6..c6412b4dc8 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/monitor.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/monitor.go @@ -21,6 +21,7 @@ import ( "crypto/x509" "encoding/hex" "encoding/json" + "errors" "expvar" "fmt" "maps" @@ -1290,6 +1291,7 @@ type Varz struct { OCSPResponseCache *OCSPResponseCacheVarz `json:"ocsp_peer_cache,omitempty"` // OCSPResponseCache is the state of the OCSP cache SlowConsumersStats *SlowConsumersStats `json:"slow_consumer_stats"` // SlowConsumersStats are statistics about all detected Slow Consumer StaleConnectionStats *StaleConnectionStats `json:"stale_connection_stats,omitempty"` // StaleConnectionStats are statistics about all detected Stale Connections + DiskIOWaitStats *DiskIOWaitStats `json:"disk_io_wait_stats"` // DiskIOWaitStats are statistics about disk I/O semaphore contention Proxies *ProxiesOptsVarz `json:"proxies,omitempty"` // Proxies hold information about network proxy devices TLSCertNotAfter time.Time `json:"tls_cert_not_after,omitzero"` // TLSCertNotAfter is the expiration date of the TLS certificate of this server } @@ -1449,6 +1451,14 @@ type StaleConnectionStats struct { Leafs uint64 `json:"leafs"` // Leafs is how many Leafnode connections became stale connections } +// DiskIOWaitStats contains information about disk I/O semaphore contention. +type DiskIOWaitStats struct { + Waiters int64 `json:"waiters"` // Waiters is the number of goroutines waiting on the dios + Waits uint64 `json:"waits"` // Waits is the number of dios acquires that had to wait + WaitTime uint64 `json:"wait_time"` // WaitTime is the cumulative time spent waiting for dios + MaxWaitTime uint64 `json:"max_wait_time"` // MaxWaitTime is the longest observed wait +} + func myUptime(d time.Duration) string { // Just use total seconds for uptime, and display days / years tsecs := d / time.Second @@ -1845,6 +1855,15 @@ func (s *Server) updateVarzConfigReloadableFields(v *Varz) { } else { v.Proxies = nil } + + if cfg := v.JetStream.Config; cfg != nil { + if opts.JetStreamMaxMemory > 0 { + cfg.MaxMemory = opts.JetStreamMaxMemory + } + if opts.JetStreamMaxStore > 0 { + cfg.MaxStore = opts.JetStreamMaxStore + } + } } func getPinnedCertsAsSlice(certs PinnedCertSet) []string { @@ -1969,6 +1988,19 @@ func (s *Server) updateVarzRuntimeFields(v *Varz, forceUpdate bool, pcpu float64 } } } + v.DiskIOWaitStats = diskIOWaitStats(s.dios) +} + +func diskIOWaitStats(d *diskIOSemaphore) *DiskIOWaitStats { + if d == nil { + return &DiskIOWaitStats{} + } + return &DiskIOWaitStats{ + Waiters: d.waiters.Load(), + Waits: d.waits.Load(), + WaitTime: d.waitNanos.Load(), + MaxWaitTime: d.maxWaitNanos.Load(), + } } // HandleVarz will process HTTP requests for server information. @@ -3691,6 +3723,29 @@ func (s *Server) healthz(opts *HealthzOptions) *HealthStatus { accFound = true } acc, err := s.LookupAccount(fi.Name()) + // Expired accounts are not a JetStream health problem; skip them when + // scanning all accounts. Still surface an error if this account was + // explicitly requested — including the err==nil + IsExpired() case. + expired := (err != nil && errors.Is(err, ErrAccountExpired)) || (err == nil && acc.IsExpired()) + if expired { + if opts.Account == _EMPTY_ { + continue + } + msg := fmt.Sprintf("JetStream account '%s' is expired", fi.Name()) + if !details { + health.Status = na + health.Error = msg + return health + } + health.Errors = append(health.Errors, HealthzError{ + Type: HealthzErrorAccount, + Account: fi.Name(), + Error: msg, + }) + // Return so later stream/consumer not-found checks do not + // replace this with a misleading 404 for assets we skipped. + return health + } if err != nil { if !details { health.Status = na @@ -3986,6 +4041,28 @@ func (s *Server) healthz(opts *HealthzOptions) *HealthStatus { // Use our copy to traverse so we do not need to hold the js lock. for accName, asa := range streams { acc, err := s.LookupAccount(accName) + // Expired accounts are not a JetStream health problem; skip them when + // scanning all accounts. Still surface an error if this account was + // explicitly requested — including the err==nil + IsExpired() case. + expired := (err != nil && errors.Is(err, ErrAccountExpired)) || (err == nil && acc.IsExpired()) + if expired { + if opts.Account == _EMPTY_ { + continue + } + msg := fmt.Sprintf("JetStream account %q is expired", accName) + if !details { + health.Status = na + health.Error = msg + return health + } + health.Errors = append(health.Errors, HealthzError{ + Type: HealthzErrorAccount, + Account: accName, + Error: msg, + }) + // Return so later health checks do not obscure the expired account. + return health + } if err != nil && len(asa) > 0 { if !details { health.Status = na @@ -4245,7 +4322,7 @@ func (s *Server) Raftz(opts *RaftzOptions) *RaftzStatus { PTerm: n.pterm, PIndex: n.pindex, SystemAcc: n.IsSystemAccount(), - TrafficAcc: n.acc.GetName(), + TrafficAcc: n.t.Account().GetName(), IPQPropLen: n.prop.len(), IPQEntryLen: n.entry.len(), IPQRespLen: n.resp.len(), diff --git a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go index 65004ccdaa..2175614690 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/mqtt.go @@ -72,6 +72,7 @@ const ( mqttPubFlagRetain = byte(0x01) mqttPubFlagQoS = byte(0x06) mqttPubFlagDup = byte(0x08) + mqttPubFlags = mqttPubFlagRetain | mqttPubFlagQoS | mqttPubFlagDup // 0x0f, the fixed-header flags nibble mqttPubQos1 = byte(0x1 << 1) mqttPubQoS2 = byte(0x2 << 1) @@ -242,6 +243,7 @@ var ( errMQTTUnsupportedCharacters = errors.New("character not supported for MQTT topics") errMQTTInvalidSession = errors.New("invalid MQTT session") errMQTTInvalidRetainFlags = errors.New("invalid retained message flags") + errMQTTInvalidRetainedMessage = errors.New("invalid retained message") errMQTTSessionCollision = errors.New("stored session does not match client ID") ) @@ -379,6 +381,12 @@ type mqttSub struct { qos byte jsDur string + // closed marks the subscription as torn down (QoS downgrade to 0, or + // unsubscribe) so QoS 1/2 delivery callbacks stop tracking new messages for + // it. Guarded like qos/jsDur (sess.mu or sess.subsMu). Unlike clearing + // sub.mqtt, this keeps the struct valid for an in-flight enqueue. + closed bool + // Pending serialization of retained messages to be sent when subscription // is registered. The sub's delivery callbacks must wait until `prm` is // ready (can block on sess.mu for that, too). @@ -475,6 +483,13 @@ const ( // NATS header that indicates that the message originated from MQTT and // stores the published message QOS. mqttNatsHeader = "Nmqtt-Pub" + // A staged QoS2 message's mqttNatsHeader value carries a second byte after + // the QoS: the MQTT PUBLISH flags nibble (mqttPubFlags) as one hex char, + // e.g. "25" for a retained QoS2 message (0x5 = retain|QoS2). The value is a + // persisted, cross-version contract: byte 0 stays the bare QoS forever + // (older servers read only it), a missing flags byte reads as no flags, and + // extensions may only append bytes (a second hex char = a full private + // byte), never change the meaning of existing ones. // NATS headers to store retained message metadata (along with the original // message as binary). @@ -493,9 +508,10 @@ const ( ) type mqttParsedPublishNATSHeader struct { - qos byte - subject []byte - mapped []byte + qos byte + retained bool + subject []byte + mapped []byte } func (s *Server) startMQTT() { @@ -1127,6 +1143,28 @@ func (s *Server) mqttStoreQoSMsgForAccountOnNewSubject(hdr int, msg []byte, acc, jsa.storeMsg(mqttStreamSubjectPrefix+subject, hdr, msg) } +// Encodes the MQTT PUBLISH flags nibble (mqttPubFlags) as one hex char, the +// flags byte that follows the QoS in a mqttNatsHeader value. +func mqttNatsHeaderEncodeFlags(ppFlags byte) byte { + return "0123456789abcdef"[ppFlags&mqttPubFlags] +} + +// Decodes the MQTT flags nibble carried after the QoS in a mqttNatsHeader +// value; values without the flags byte read as no flags set. Callers test the +// result with the mqttPubFlag* bits. +func mqttNatsHeaderDecodeFlags(value []byte) byte { + if len(value) < 2 { + return 0 + } + switch c := value[1]; { + case c >= '0' && c <= '9': + return c - '0' + case c >= 'a' && c <= 'f': + return c - 'a' + 10 + } + return 0 +} + func mqttParsePublishNATSHeader(headerBytes []byte) *mqttParsedPublishNATSHeader { if len(headerBytes) == 0 { return nil @@ -1137,9 +1175,10 @@ func mqttParsePublishNATSHeader(headerBytes []byte) *mqttParsedPublishNATSHeader return nil } return &mqttParsedPublishNATSHeader{ - qos: pubValue[0] - '0', - subject: getHeader(mqttNatsHeaderSubject, headerBytes), - mapped: getHeader(mqttNatsHeaderMapped, headerBytes), + qos: pubValue[0] - '0', + retained: mqttIsRetained(mqttNatsHeaderDecodeFlags(pubValue)), + subject: getHeader(mqttNatsHeaderSubject, headerBytes), + mapped: getHeader(mqttNatsHeaderMapped, headerBytes), } } @@ -1467,6 +1506,10 @@ func (s *Server) mqttCreateAccountSessionManager(acc *Account, quitCh chan struc default: needToTransfer = si.Config.MaxMsgsPer != 1 } + // Guard before dereferencing si.Config below. + if si == nil { + return nil, fmt.Errorf("could not look up or create the retained messages stream for account %q", accName) + } // Doing this check outside of above if/else due to possible race when // creating the stream. @@ -1503,6 +1546,10 @@ func (s *Server) mqttCreateAccountSessionManager(acc *Account, quitCh chan struc if err = transferRMS(); err != nil { return nil, err } + // Guard before dereferencing si.Config below. + if si == nil { + return nil, fmt.Errorf("could not look up the retained messages stream for account %q", accName) + } // Now, if the stream does not have MaxMsgsPer set to 1, and there are no // more messages on the single $MQTT.rmsgs subject, update the stream again. @@ -1764,7 +1811,13 @@ func (jsa *mqttJSA) createStream(cfg *StreamConfig) (*StreamInfo, bool, error) { return nil, false, err } scr := scri.(*JSApiStreamCreateResponse) - return scr.StreamInfo, scr.DidCreate, scr.ToError() + if err = scr.ToError(); err != nil { + return nil, false, err + } + if scr.StreamInfo == nil { + return nil, false, fmt.Errorf("invalid stream create response: missing stream info") + } + return scr.StreamInfo, scr.DidCreate, nil } func (jsa *mqttJSA) updateStream(cfg *StreamConfig) (*StreamInfo, error) { @@ -1777,7 +1830,13 @@ func (jsa *mqttJSA) updateStream(cfg *StreamConfig) (*StreamInfo, error) { return nil, err } scr := scri.(*JSApiStreamUpdateResponse) - return scr.StreamInfo, scr.ToError() + if err = scr.ToError(); err != nil { + return nil, err + } + if scr.StreamInfo == nil { + return nil, fmt.Errorf("invalid stream update response: missing stream info") + } + return scr.StreamInfo, nil } func (jsa *mqttJSA) lookupStream(name string) (*StreamInfo, error) { @@ -1786,7 +1845,13 @@ func (jsa *mqttJSA) lookupStream(name string) (*StreamInfo, error) { return nil, err } slr := slri.(*JSApiStreamInfoResponse) - return slr.StreamInfo, slr.ToError() + if err = slr.ToError(); err != nil { + return nil, err + } + if slr.StreamInfo == nil { + return nil, NewJSStreamNotFoundError() + } + return slr.StreamInfo, nil } func (jsa *mqttJSA) deleteStream(name string) (bool, error) { @@ -1809,7 +1874,13 @@ func (jsa *mqttJSA) loadLastMsgFor(streamName string, subject string) (*StoredMs return nil, err } lmr := lmri.(*JSApiMsgGetResponse) - return lmr.Message, lmr.ToError() + if err = lmr.ToError(); err != nil { + return nil, err + } + if lmr.Message == nil { + return nil, NewJSNoMessageFoundError() + } + return lmr.Message, nil } func (jsa *mqttJSA) loadLastMsgForMulti(streamName string, subjects []string) ([]*JSApiMsgGetResponse, error) { @@ -1847,7 +1918,13 @@ func (jsa *mqttJSA) loadNextMsgFor(streamName string, subject string) (*StoredMs return nil, err } lmr := lmri.(*JSApiMsgGetResponse) - return lmr.Message, lmr.ToError() + if err = lmr.ToError(); err != nil { + return nil, err + } + if lmr.Message == nil { + return nil, NewJSNoMessageFoundError() + } + return lmr.Message, nil } func (jsa *mqttJSA) loadMsg(streamName string, seq uint64) (*StoredMsg, error) { @@ -1861,7 +1938,13 @@ func (jsa *mqttJSA) loadMsg(streamName string, seq uint64) (*StoredMsg, error) { return nil, err } lmr := lmri.(*JSApiMsgGetResponse) - return lmr.Message, lmr.ToError() + if err := lmr.ToError(); err != nil { + return nil, err + } + if lmr.Message == nil { + return nil, NewJSNoMessageFoundError() + } + return lmr.Message, nil } func (jsa *mqttJSA) storeMsgNoWait(subject string, hdrLen int, msg []byte) { @@ -1963,13 +2046,13 @@ func (as *mqttAccountSessionManager) processJSAPIReplies(_ *subscription, pc *cl out(resp) case mqttJSAStreamLookup: var resp = &JSApiStreamInfoResponse{} - if err := json.Unmarshal(msg, &resp); err != nil { + if err := json.Unmarshal(msg, resp); err != nil { resp.Error = NewJSInvalidJSONError(err) } out(resp) case mqttJSAStreamDel: var resp = &JSApiStreamDeleteResponse{} - if err := json.Unmarshal(msg, &resp); err != nil { + if err := json.Unmarshal(msg, resp); err != nil { resp.Error = NewJSInvalidJSONError(err) } out(resp) @@ -1993,7 +2076,7 @@ func (as *mqttAccountSessionManager) processJSAPIReplies(_ *subscription, pc *cl out(resp) case mqttJSAMsgLoad: var resp = &JSApiMsgGetResponse{} - if err := json.Unmarshal(msg, &resp); err != nil { + if err := json.Unmarshal(msg, resp); err != nil { resp.Error = NewJSInvalidJSONError(err) } out(resp) @@ -2098,6 +2181,9 @@ func (as *mqttAccountSessionManager) processSessionPersist(_ *subscription, pc * if err := par.Error; err != nil { return } + if par.PubAck == nil { + return + } as.mu.RLock() // Note that as.domainTk includes a terminal '.', so strip to compare to PubAck.Domain. dl := len(as.domainTk) @@ -2484,6 +2570,8 @@ func (sess *mqttSession) processSub( // accessing it later requires a lock. ss.mqtt.qos = qos ss.mqtt.jsDur = jsDurName + // A (re)configured subscription is live; clear any prior teardown mark. + ss.mqtt.closed = false } if len(rms) > 0 { @@ -2538,13 +2626,10 @@ func (as *mqttAccountSessionManager) processSubs(sess *mqttSession, c *client, f.qos = 1 } - // Do not allow subscribing to our internal subjects. - // - // TODO: (levb: not sure why since one can subscribe to `#` and it'll - // include everything; I guess this would discourage? Otherwise another - // candidate for DO NOT DELIVER prefix list). - if strings.HasPrefix(f.filter, mqttSubPrefix) || - strings.HasPrefix(f.filter, mqttPubRelDeliverySubjectPrefix) { + // Do not allow MQTT clients to subscribe directly to internal subjects. + // Otherwise, subjects such as "$MQTT.msgs.*" could be used to bypass + // MQTT subscription permissions. + if strings.HasPrefix(f.filter, mqttPrefix) { f.qos = mqttSubAckFailure continue } @@ -2703,7 +2788,7 @@ func (as *mqttAccountSessionManager) serializeRetainedMsgsForSub(rms map[string] } // A broad wildcard subscription can overlap a subscribe deny clause. c.mu.Lock() - denied := c.mperms != nil && c.checkDenySub(string(subj)) + denied := c.mperms != nil && c.checkDenySub(string(subj), bytesToString(sub.queue)) c.mu.Unlock() if denied { return @@ -2715,7 +2800,7 @@ func (as *mqttAccountSessionManager) serializeRetainedMsgsForSub(rms map[string] return } if qos > 0 { - pi = sess.trackPublishRetained() + pi = sess.trackPublishRetained(string(sub.sid)) // If we failed to get a PI for this message, send it as a QoS0, the // best we can do? @@ -2817,6 +2902,10 @@ func (as *mqttAccountSessionManager) loadRetainedMessages(subjects map[string]ui w.Warnf("failed to load retained message for subject %q: %v", subj, err) continue } + // Guard before dereferencing below. + if result.Message == nil { + continue + } rm, err := mqttDecodeRetainedMessage(result.Message.Subject, result.Message.Header, result.Message.Data) if err != nil { // Unlikely that we can recover from that, so remove the message. @@ -2984,6 +3073,9 @@ func mqttDecodeRetainedMessage(subject string, h, m []byte) (*mqttRetainedMsg, e if err := json.Unmarshal(m, &rm); err != nil { return nil, err } + if rm == nil { + return nil, errMQTTInvalidRetainedMessage + } } // Now check that the values are correct. // @@ -3038,6 +3130,11 @@ func (as *mqttAccountSessionManager) createOrRestoreSession(clientID string, opt if ps.ID != clientID { return nil, false, errMQTTSessionCollision } + for sid, cc := range ps.Cons { + if cc == nil { + delete(ps.Cons, sid) + } + } // Restore this session (even if we don't own it), the caller will do the right thing. sess := mqttSessionCreate(jsa, clientID, hash, smsg.Sequence, opts) @@ -3291,6 +3388,10 @@ func (sess *mqttSession) save() error { if err != nil { return fmt.Errorf("unable to persist session %q (seq=%v): %v", ps.ID, seq, err) } + // Guard before dereferencing below. + if resp == nil || resp.PubAck == nil { + return fmt.Errorf("unable to persist session %q (seq=%v): invalid pub ack response", ps.ID, seq) + } sess.mu.Lock() sess.seq = resp.Sequence sess.mu.Unlock() @@ -3326,6 +3427,9 @@ func (sess *mqttSession) clear(noWait bool) error { sess.pubRelConsumer = nil sess.seq = 0 sess.tmaxack = 0 + // Discarded session: reset the PI counter too so a reused session object + // does not inherit the previous session's identifiers. Spec [MQTT-3.1.2-6]. + sess.last_pi = 0 sess.mu.Unlock() for _, dur := range durs { @@ -3410,14 +3514,23 @@ func (sess *mqttSession) bumpPI() uint16 { return sess.last_pi } +// mqttRetainedPendingDur returns the pseudo consumer-durable key under which a +// subscription's in-flight retained QoS deliveries are tracked in cpending. +// Retained deliveries have no JS consumer, but keying them per subscription +// lets unsubscribe/downgrade teardown purge them like consumer deliveries. +// Cannot collide with real durables (idHash+"_"+nuid, $MQTT_PUBREL_ prefix). +func mqttRetainedPendingDur(sid string) string { + return mqttRetainedMsgsStreamName + "/" + sid +} + // trackPublishRetained is invoked when a retained (QoS) message is published. -// It need a new PI to be allocated, so we add it to the pendingPublish map, -// with an empty value. Since cpending (not pending) is used to serialize the PI -// mappings, we need to add this PI there as well. Make a unique key by using -// mqttRetainedMsgsStreamName for the durable name, and PI for sseq. +// It needs a new PI to be allocated, so we add it to the pendingPublish map, +// and serialize it in cpending under the subscription's pseudo-durable key +// (with the PI as the sequence) so consumer teardown purges it; an ack removes +// both entries via untrackPublish. // // Lock held on entry -func (sess *mqttSession) trackPublishRetained() uint16 { +func (sess *mqttSession) trackPublishRetained(sid string) uint16 { // Make sure we initialize the tracking maps. if sess.pendingPublish == nil { sess.pendingPublish = make(map[uint16]*mqttPending) @@ -3430,7 +3543,14 @@ func (sess *mqttSession) trackPublishRetained() uint16 { if pi == 0 { return 0 } - sess.pendingPublish[pi] = &mqttPending{} + dur := mqttRetainedPendingDur(sid) + sseqToPi := sess.cpending[dur] + if sseqToPi == nil { + sseqToPi = make(map[uint64]uint16) + sess.cpending[dur] = sseqToPi + } + sseqToPi[uint64(pi)] = pi + sess.pendingPublish[pi] = &mqttPending{jsDur: dur, sseq: uint64(pi)} return pi } @@ -3527,9 +3647,9 @@ func (sess *mqttSession) untrackPublish(pi uint16) (jsAckSubject string) { } delete(sess.pendingPublish, pi) - if len(sess.pendingPublish) == 0 { - sess.last_pi = 0 - } + // Do NOT reset last_pi here: it is a monotonic rolling counter (see bumpPI) so + // a just-freed id is not reused within a delivery burst, which a client would + // read as a duplicate. Spec [MQTT-2.3.1-4]. if len(sess.cpending) != 0 && ack.jsDur != _EMPTY_ { if sseqToPi := sess.cpending[ack.jsDur]; sseqToPi != nil { @@ -4052,14 +4172,16 @@ func (s *Server) mqttHandleWill(c *client) { c.mu.Unlock() return } - pp := c.mqtt.pp - pp.topic = will.topic - pp.subject = will.subject - pp.mapped = will.mapped - pp.msg = will.message - pp.sz = len(will.message) - pp.pi = 0 - pp.flags = will.qos << 1 + // Create a synthetic PUBLISH packet to be delivered to the session's + // subscriptions, regardless of what is currently in c.mqtt.pp. + pp := &mqttPublish{ + topic: will.topic, + subject: will.subject, + mapped: will.mapped, + msg: will.message, + sz: len(will.message), + flags: will.qos << 1, + } if will.retain { pp.flags |= mqttPubFlagRetain } @@ -4197,6 +4319,7 @@ func mqttComputeNatsMsgSize(pp *mqttPublish, encodePP bool) int { 2 + // end-of-header CRLF pp.sz if encodePP { + size++ // for the flags byte size += len(mqttNatsHeaderSubject) + 1 + // +1 for ':' len(pp.subject) + 2 // 2 for CRLF @@ -4228,6 +4351,9 @@ func mqttNewDeliverableMessage(pp *mqttPublish, encodePP bool) (natsMsg []byte, buf.WriteString(mqttNatsHeader) buf.WriteByte(':') buf.WriteByte(qos + '0') + if encodePP { + buf.WriteByte(mqttNatsHeaderEncodeFlags(pp.flags)) + } buf.WriteString(_CRLF_) if encodePP { @@ -4330,7 +4456,13 @@ func (s *Server) mqttProcessPub(c *client, pp *mqttPublish, trace bool) error { func (s *Server) mqttInitiateMsgDelivery(c *client, pp *mqttPublish) error { natsMsg, headerLen := mqttNewDeliverableMessage(pp, false) - // Set the client's pubarg for processing. + // The delivered message becomes the client's current publish (it is not + // the last PARSED packet for a PUBREL- or will-initiated delivery), and + // c.pa carries its pubargs; one defer restores both. c.mqtt.pp is + // readLoop-owned, like c.pa. + prevPP := c.mqtt.pp + c.mqtt.pp = pp + c.pa.subject = pp.subject c.pa.mapped = pp.mapped c.pa.reply = nil @@ -4339,6 +4471,7 @@ func (s *Server) mqttInitiateMsgDelivery(c *client, pp *mqttPublish) error { c.pa.size = len(natsMsg) c.pa.szb = []byte(strconv.FormatInt(int64(c.pa.size), 10)) defer func() { + c.mqtt.pp = prevPP c.pa.subject = nil c.pa.mapped = nil c.pa.reply = nil @@ -4426,6 +4559,10 @@ func (s *Server) mqttProcessPubRel(c *client, pi uint16, trace bool) error { return errors.New("invalid message in QoS2 PUBREL stream") } + flags := h.qos << 1 + if h.retained { + flags |= mqttPubFlagRetain + } pp := &mqttPublish{ topic: natsSubjectToMQTTTopic(h.subject), subject: h.subject, @@ -4433,7 +4570,7 @@ func (s *Server) mqttProcessPubRel(c *client, pi uint16, trace bool) error { msg: stored.Data, sz: len(stored.Data), pi: pi, - flags: h.qos << 1, + flags: flags, } return s.mqttInitiateMsgDelivery(c, pp) @@ -5039,7 +5176,7 @@ func mqttDeliverMsgCbQoS12(sub *subscription, pc *client, _ *Account, subject, r // track of pending acks, etc. There is no need to acquire the subsMu RLock // since sess.Lock is overarching for modifying subscriptions. sess.mu.Lock() - if sess.c != cc || sub.mqtt == nil { + if sess.c != cc || sub.mqtt == nil || sub.mqtt.closed { sess.mu.Unlock() return } @@ -5070,7 +5207,7 @@ func mqttDeliverMsgCbQoS12(sub *subscription, pc *client, _ *Account, subject, r // A broad wildcard subscription can overlap a subscribe deny clause. cc.mu.Lock() - denied := cc.mperms != nil && cc.checkDenySub(strippedSubj) + denied := cc.mperms != nil && cc.checkDenySub(strippedSubj, bytesToString(sub.queue)) cc.mu.Unlock() if denied { sess.mu.Unlock() @@ -5436,8 +5573,32 @@ func (sess *mqttSession) processJSConsumer(c *client, subject, sid string, sub := c.subs[cc.DeliverSubject] c.mu.Unlock() + // Delete the consumer entry, mark its delivery subscription closed, + // and purge its pending QoS 1/2 deliveries — all under the session + // lock. Otherwise those packet identifiers leak and count against the + // in-flight cap for the life of the session (as mqttProcessUnsubs + // purges on unsubscribe). deleteConsumer is asynchronous, so an + // in-flight delivery callback could re-populate the maps after the + // purge; marking sub.mqtt.closed (guarded by sess.mu and sess.subsMu, + // same as delivery) makes mqttDeliverMsgCbQoS12 skip instead. The flag + // leaves sub.mqtt valid so an already-committed enqueue does not panic. sess.mu.Lock() delete(sess.cons, sid) + if sub != nil && sub.mqtt != nil { + sess.subsMu.Lock() + sub.mqtt.closed = true + sess.subsMu.Unlock() + } + // Also purge this sid's in-flight retained deliveries, tracked + // under a pseudo-durable key (no JS consumer, no redelivery). + for _, dur := range [...]string{cc.Durable, mqttRetainedPendingDur(sid)} { + if seqPis, ok := sess.cpending[dur]; ok { + delete(sess.cpending, dur) + for _, pi := range seqPis { + delete(sess.pendingPublish, pi) + } + } + } sess.mu.Unlock() sess.deleteConsumer(cc) @@ -5586,15 +5747,30 @@ func (c *client) mqttProcessUnsubs(filters []*mqttFilter) error { if ok { delete(sess.cons, sid) sess.deleteConsumer(cc) + + c.mu.Lock() + sub := c.subs[cc.DeliverSubject] + c.mu.Unlock() + // Need lock here since these are accessed by callbacks sess.mu.Lock() - if seqPis, ok := sess.cpending[cc.Durable]; ok { - delete(sess.cpending, cc.Durable) - for _, pi := range seqPis { - delete(sess.pendingPublish, pi) - } - if len(sess.pendingPublish) == 0 { - sess.last_pi = 0 + // Mark the delivery sub closed so an in-flight QoS 1/2 callback stops + // tracking new messages after the purge (deleteConsumer is async); + // same barrier as the QoS 0 downgrade path in processJSConsumer. + if sub != nil && sub.mqtt != nil { + sess.subsMu.Lock() + sub.mqtt.closed = true + sess.subsMu.Unlock() + } + // Purge both the consumer's deliveries and this sid's in-flight + // retained deliveries (tracked under a pseudo-durable key). + for _, dur := range [...]string{cc.Durable, mqttRetainedPendingDur(sid)} { + if seqPis, ok := sess.cpending[dur]; ok { + delete(sess.cpending, dur) + for _, pi := range seqPis { + delete(sess.pendingPublish, pi) + } + // last_pi stays monotonic (see untrackPublish); do not reset here. } } sess.mu.Unlock() diff --git a/vendor/github.com/nats-io/nats-server/v2/server/opts.go b/vendor/github.com/nats-io/nats-server/v2/server/opts.go index 3ef3a60d4c..46b18a56b4 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/opts.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/opts.go @@ -462,6 +462,7 @@ type Options struct { JetStreamMetaCompact uint64 JetStreamMetaCompactSize uint64 JetStreamMetaCompactSync bool + JetStreamConcurrentIOs int StreamMaxBufferedMsgs int `json:"-"` StreamMaxBufferedSize int64 `json:"-"` StoreDir string `json:"-"` @@ -2061,6 +2062,10 @@ func parseCluster(v any, opts *Options, errors *[]error, warnings *[]error) erro opts.Cluster.AuthTimeout = auth.timeout if auth.defaultPermissions != nil { + if err := checkClusterPermissionSubjects(auth.defaultPermissions); err != nil { + *errors = append(*errors, &configErr{tk, err.Error()}) + continue + } err := &configWarningErr{ field: mk, configErr: configErr{ @@ -2116,6 +2121,10 @@ func parseCluster(v any, opts *Options, errors *[]error, warnings *[]error) erro *errors = append(*errors, err) continue } + if err := checkClusterPermissionSubjects(perms); err != nil { + *errors = append(*errors, &configErr{tk, err.Error()}) + continue + } // This will possibly override permissions that were define in auth block setClusterPermissions(&opts.Cluster, perms) case "pool_size": @@ -2762,6 +2771,12 @@ func parseJetStream(v any, opts *Options, errors *[]error, warnings *[]error) er opts.JetStreamMetaCompactSize = uint64(s) case "meta_compact_sync": opts.JetStreamMetaCompactSync = mv.(bool) + case "max_concurrent_io": + dios, ok := mv.(int64) + if !ok || dios < minConcurrentIOs || dios > maxConcurrentIOs { + return &configErr{tk, fmt.Sprintf("Expected an absolute size for %q between 4 and 8192, got %v", mk, mv)} + } + opts.JetStreamConcurrentIOs = int(dios) default: if !tk.IsUsedVariable() { err := &unknownConfigFieldErr{ @@ -3127,14 +3142,14 @@ func parseRemoteLeafNodes(v any, errors *[]error, warnings *[]error) ([]*RemoteL case "hub": remote.Hub = v.(bool) case "deny_imports", "deny_import": - subjects, err := parsePermSubjects(tk, errors) + subjects, err := parsePermSubjects(tk, errors, false) if err != nil { *errors = append(*errors, err) continue } remote.DenyImports = subjects case "deny_exports", "deny_export": - subjects, err := parsePermSubjects(tk, errors) + subjects, err := parsePermSubjects(tk, errors, false) if err != nil { *errors = append(*errors, err) continue @@ -3346,6 +3361,29 @@ func setClusterPermissions(opts *ClusterOpts, perms *Permissions) { } } +func checkClusterPermissionSubjects(perms *Permissions) error { + if perms == nil { + return nil + } + if perms.Publish != nil { + if err := checkPermSubjectArray(perms.Publish.Allow, false); err != nil { + return fmt.Errorf("cluster import allow: %w", err) + } + if err := checkPermSubjectArray(perms.Publish.Deny, false); err != nil { + return fmt.Errorf("cluster import deny: %w", err) + } + } + if perms.Subscribe != nil { + if err := checkPermSubjectArray(perms.Subscribe.Allow, false); err != nil { + return fmt.Errorf("cluster export allow: %w", err) + } + if err := checkPermSubjectArray(perms.Subscribe.Deny, false); err != nil { + return fmt.Errorf("cluster export deny: %w", err) + } + } + return nil +} + // Temp structures to hold account import and export defintions since they need // to be processed after being parsed. type export struct { @@ -4787,14 +4825,14 @@ func parseUserPermissions(mv any, errors *[]error) (*Permissions, error) { // Import is Publish // Export is Subscribe case "pub", "publish", "import": - perms, err := parseVariablePermissions(mv, errors) + perms, err := parseVariablePermissions(mv, errors, false) if err != nil { *errors = append(*errors, err) continue } p.Publish = perms case "sub", "subscribe", "export": - perms, err := parseVariablePermissions(mv, errors) + perms, err := parseVariablePermissions(mv, errors, true) if err != nil { *errors = append(*errors, err) continue @@ -4834,19 +4872,19 @@ func parseUserPermissions(mv any, errors *[]error) (*Permissions, error) { } // Top level parser for authorization configurations. -func parseVariablePermissions(v any, errors *[]error) (*SubjectPermission, error) { +func parseVariablePermissions(v any, errors *[]error, allowQueue bool) (*SubjectPermission, error) { switch vv := v.(type) { case map[string]any: // New style with allow and/or deny properties. - return parseSubjectPermission(vv, errors) + return parseSubjectPermission(vv, errors, allowQueue) default: // Old style - return parseOldPermissionStyle(v, errors) + return parseOldPermissionStyle(v, errors, allowQueue) } } // Helper function to parse subject singletons and/or arrays -func parsePermSubjects(v any, errors *[]error) ([]string, error) { +func parsePermSubjects(v any, errors *[]error, allowQueue bool) ([]string, error) { var lt token defer convertPanicToErrorList(<, errors) @@ -4871,7 +4909,7 @@ func parsePermSubjects(v any, errors *[]error) ([]string, error) { default: return nil, &configErr{tk, fmt.Sprintf("Expected subject permissions to be a subject, or array of subjects, got %T", v)} } - if err := checkPermSubjectArray(subjects); err != nil { + if err := checkPermSubjectArray(subjects, allowQueue); err != nil { return nil, &configErr{tk, err.Error()} } return subjects, nil @@ -4936,8 +4974,8 @@ func parseAllowResponses(v any, errors *[]error) *ResponsePermission { } // Helper function to parse old style authorization configs. -func parseOldPermissionStyle(v any, errors *[]error) (*SubjectPermission, error) { - subjects, err := parsePermSubjects(v, errors) +func parseOldPermissionStyle(v any, errors *[]error, allowQueue bool) (*SubjectPermission, error) { + subjects, err := parsePermSubjects(v, errors, allowQueue) if err != nil { return nil, err } @@ -4945,7 +4983,7 @@ func parseOldPermissionStyle(v any, errors *[]error) (*SubjectPermission, error) } // Helper function to parse new style authorization into a SubjectPermission with Allow and Deny. -func parseSubjectPermission(v any, errors *[]error) (*SubjectPermission, error) { +func parseSubjectPermission(v any, errors *[]error, allowQueue bool) (*SubjectPermission, error) { var lt token defer convertPanicToErrorList(<, errors) @@ -4958,14 +4996,14 @@ func parseSubjectPermission(v any, errors *[]error) (*SubjectPermission, error) tk, _ := unwrapValue(v, <) switch strings.ToLower(k) { case "allow": - subjects, err := parsePermSubjects(tk, errors) + subjects, err := parsePermSubjects(tk, errors, allowQueue) if err != nil { *errors = append(*errors, err) continue } p.Allow = subjects case "deny": - subjects, err := parsePermSubjects(tk, errors) + subjects, err := parsePermSubjects(tk, errors, allowQueue) if err != nil { *errors = append(*errors, err) continue @@ -4982,15 +5020,20 @@ func parseSubjectPermission(v any, errors *[]error) (*SubjectPermission, error) } // Helper function to validate permissions subjects. -func checkPermSubjectArray(sa []string) error { +func checkPermSubjectArray(sa []string, allowQueue bool) error { for _, s := range sa { if !IsValidSubject(s) { + if !allowQueue { + return fmt.Errorf("subject %q is not a valid subject", s) + } // Check here if this is a queue group qualified subject. elements := strings.Fields(s) if len(elements) != 2 { return fmt.Errorf("subject %q is not a valid subject", s) } else if !IsValidSubject(elements[0]) { return fmt.Errorf("subject %q is not a valid subject", elements[0]) + } else if !IsValidSubject(elements[1]) { + return fmt.Errorf("queue %q is not a valid queue", elements[1]) } } } @@ -6122,6 +6165,9 @@ func setBaselineOptions(opts *Options) { if opts.JetStreamInfoQueueLimit <= 0 { opts.JetStreamInfoQueueLimit = opts.JetStreamRequestQueueLimit } + if opts.JetStreamConcurrentIOs <= 0 { + opts.JetStreamConcurrentIOs = defaultConcurrentIOs + } } func getDefaultAuthTimeout(tls *tls.Config, tlsTimeout float64) float64 { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/parser.go b/vendor/github.com/nats-io/nats-server/v2/server/parser.go index c052ebb166..011bf6d173 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/parser.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/parser.go @@ -181,14 +181,24 @@ func (c *client) parse(buf []byte) error { s.mu.Lock() user, exists := s.users[noAuthUser] s.mu.Unlock() - // Enforce the same connection restrictions as CONNECT before allowing. + // Run the same authentication pipeline as CONNECT. In addition to + // the connection restrictions checked here, this delegates the + // decision to auth callouts or custom authenticators. if exists && !user.ProxyRequired && c.connectionTypeAllowed(user.AllowedConnectionTypes) { - c.RegisterUser(user) + // Mirror processConnect: clear the auth-timeout timer and + // mark CONNECT received *before* authenticating. Auth may + // install a JWT/callout expiration timer into the same c.atmr + // slot, so clearing it afterwards would drop the expiration + // and leave the client connected past expiry. Setting + // connectReceived first also lets the expiration deadline be + // recorded on c.expires. c.mu.Lock() c.clearAuthTimer() c.flags.set(connectReceived) c.mu.Unlock() - authSet, ok = false, true + if s.checkAuthentication(c) { + authSet, ok = false, true + } } } case LEAF: diff --git a/vendor/github.com/nats-io/nats-server/v2/server/raft.go b/vendor/github.com/nats-io/nats-server/v2/server/raft.go index e164093e47..c27be8011d 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/raft.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/raft.go @@ -37,8 +37,8 @@ import ( ) type RaftNode interface { - Propose(entry []byte) error - ProposeMulti(entries []*Entry) error + Propose(term uint64, entry []byte) error + ProposeMulti(term uint64, entries []*Entry) error ForwardProposal(entry []byte) error InstallSnapshot(snap []byte, force bool) error CreateSnapshotCheckpoint(force bool) (RaftNodeCheckpoint, error) @@ -78,7 +78,7 @@ type RaftNode interface { PauseApply() error ResumeApply() DrainAndReplaySnapshot() bool - LeadChangeC() <-chan bool + LeadChangeC() <-chan leadChange QuitC() <-chan struct{} Created() time.Time Stop() @@ -154,12 +154,12 @@ type raft struct { created time.Time // Time that the group was created accName string // Account name of the asset this raft group is for - acc *Account // Account that NRG traffic will be sent/received in group string // Raft group sd string // Store directory id string // Node ID wg sync.WaitGroup // Wait for running goroutines to exit on shutdown + dios *diskIOSemaphore wal WAL // WAL store (filestore or memstore) wtype StorageType // WAL type, e.g. FileStorage or MemoryStorage bytes uint64 // Total amount of bytes stored in the WAL. (Saves us from needing to call wal.FastState very often) @@ -201,7 +201,6 @@ type raft struct { vote string // Our current vote state s *Server // Reference to top-level server - c *client // Internal client for subscriptions js *jetStream // JetStream, if running, to see if we are out of resources hasleader atomic.Bool // Is there a group leader right now? @@ -220,7 +219,7 @@ type raft struct { asubj string // Append entries subject areply string // Append entries responses subject - sq *sendq // Send queue for outbound RPC messages + t raftTransport // Transport that handles Raft messaging aesub *subscription // Subscription for handleAppendEntry callbacks wtv []byte // Term and vote to be written @@ -237,7 +236,7 @@ type raft struct { apply *ipQueue[*CommittedEntry] // Apply queue (committed entries to be passed to upper layer) reqs *ipQueue[*voteRequest] // Vote requests votes *ipQueue[*voteResponse] // Vote responses - leadc chan bool // Leader changes + leadc chan leadChange // Leader changes quit chan struct{} // Raft group shutdown lxfer bool // Are we doing a leadership transfer? @@ -323,6 +322,11 @@ type RaftConfig struct { // We need to protect against losing state due to the new peers starting with an empty log. // Therefore, these empty servers can't try to become leader until they at least have _some_ state. ScaleUp bool + + // NewTransport creates the transport used for Raft node communication. + // This is mainly for tests to inject a custom transport. + // If nil, the default transport is used. + NewTransport newTransportFunc } var ( @@ -410,13 +414,13 @@ func (s *Server) bootstrapRaftNode(cfg *RaftConfig, knownPeers []string, allPeer tmpfile.Close() os.Remove(tmpfile.Name()) - return writePeerState(cfg.Store, &peerState{knownPeers, expected, extUndetermined}) + return writePeerState(s.diskIOSemaphore(), cfg.Store, &peerState{knownPeers, expected, extUndetermined}) } // initRaftNode will initialize the raft node, to be used by startRaftNode or when testing to not run the Go routine. func (s *Server) initRaftNode(accName string, cfg *RaftConfig, labels pprofLabels) (*raft, error) { restorePeerState := func(n *raft) error { - ps, err := readPeerState(cfg.Store) + ps, err := readPeerState(s.diskIOSemaphore(), cfg.Store) if err != nil { return err } @@ -447,6 +451,7 @@ func (s *Server) initRaftNode(accName string, cfg *RaftConfig, labels pprofLabel sd: cfg.Store, wal: cfg.Log, wtype: cfg.Log.Type(), + dios: s.diskIOSemaphore(), track: cfg.Track, peers: make(map[string]*lps), acks: make(map[uint64]map[string]struct{}), @@ -461,10 +466,16 @@ func (s *Server) initRaftNode(accName string, cfg *RaftConfig, labels pprofLabel resp: newIPQueue[*appendEntryResponse](s, qpfx+"appendEntryResponse"), apply: newIPQueue[*CommittedEntry](s, qpfx+"committedEntry"), accName: accName, - leadc: make(chan bool, 32), + leadc: make(chan leadChange, 1), observer: cfg.Observer, } + if cfg.NewTransport != nil { + n.t = cfg.NewTransport(s, n) + } else { + n.t = defaultRaftTransport(s, n) + } + // Setup our internal subscriptions for proposals, votes and append entries. // If we fail to do this for some reason then this is fatal — we cannot // continue setting up or the Raft node may be partially/totally isolated. @@ -659,7 +670,10 @@ func (n *raft) IsSystemAccount() bool { func (n *raft) GetTrafficAccountName() string { n.RLock() defer n.RUnlock() - return n.acc.GetName() + if n.t == nil { + return (*Account)(nil).GetName() + } + return n.t.Account().GetName() } func (n *raft) RecreateInternalSubs() error { @@ -709,7 +723,7 @@ func (n *raft) recreateInternalSubsLocked() error { } } } - if n.aesub != nil && n.acc == nrgAcc { + if n.aesub != nil && n.t.Account() == nrgAcc { // Subscriptions already exist and the account NRG state // hasn't changed. return nil @@ -720,33 +734,11 @@ func (n *raft) recreateInternalSubsLocked() error { // the next step... n.cancelCatchup() - // If we have an existing client then tear down any existing - // subscriptions and close the internal client. - if c := n.c; c != nil { - c.mu.Lock() - subs := make([]*subscription, 0, len(c.subs)) - for _, sub := range c.subs { - subs = append(subs, sub) - } - c.mu.Unlock() - for _, sub := range subs { - n.unsubscribe(sub) - } - c.closeConnection(InternalClient) - } - - if n.acc != nrgAcc { + if n.t.Account() != nrgAcc { n.debug("Subscribing in '%s'", nrgAcc.GetName()) } - c := n.s.createInternalSystemClient() - c.registerWithAccount(nrgAcc) - if nrgAcc.sq == nil { - nrgAcc.sq = n.s.newSendQ(nrgAcc) - } - n.c = c - n.sq = nrgAcc.sq - n.acc = nrgAcc + n.t.Reset(nrgAcc) // Recreate any internal subscriptions for voting, append // entries etc in the new account. @@ -913,12 +905,16 @@ func (s *Server) transferRaftLeaders() bool { // Propose will propose a new entry to the group. // This should only be called on the leader. -func (n *raft) Propose(data []byte) error { +func (n *raft) Propose(term uint64, data []byte) error { n.Lock() defer n.Unlock() + return n.proposeLocked(term, data) +} + +func (n *raft) proposeLocked(term uint64, data []byte) error { // Check state under lock, we might not be leader anymore. - if state := n.State(); state != Leader { - n.debug("Proposal ignored, not leader (state: %v)", state) + if state := n.State(); state != Leader || term != n.term { + n.debug("Proposal ignored, not leader (state: %v, cterm: %d, term: %d)", state, term, n.term) return errNotLeader } @@ -942,12 +938,12 @@ func (n *raft) Propose(data []byte) error { // ProposeMulti will propose multiple entries at once. // This should only be called on the leader. -func (n *raft) ProposeMulti(entries []*Entry) error { +func (n *raft) ProposeMulti(term uint64, entries []*Entry) error { n.Lock() defer n.Unlock() // Check state under lock, we might not be leader anymore. - if state := n.State(); state != Leader { - n.debug("Multi proposal ignored, not leader (state: %v)", state) + if state := n.State(); state != Leader || term != n.term { + n.debug("Multi proposal ignored, not leader (state: %v, cterm: %d, term: %d)", state, term, n.term) return errNotLeader } @@ -992,7 +988,12 @@ func (n *raft) isLeaderOverrun() bool { // If we are the leader this is the same as calling propose. func (n *raft) ForwardProposal(entry []byte) error { if n.State() == Leader { - return n.Propose(entry) + n.Lock() + defer n.Unlock() + // We pass the node's term so the proposal goes through. This is unavoidable with forwarded + // proposals, normally the passed term MUST be that of the process triggering the proposals. + // So a stale process that is still running isn't allowed to make new proposals past its term. + return n.proposeLocked(n.term, entry) } // TODO: Currently we do not set a reply subject, even though we are @@ -1372,7 +1373,7 @@ func (n *raft) installSnapshot(snap *snapshot) error { sn := fmt.Sprintf(snapFileT, snap.lastTerm, snap.lastIndex) sfile := filepath.Join(snapDir, sn) - if err := writeFileWithSync(sfile, n.encodeSnapshot(snap), defaultFilePerms); err != nil { + if err := writeFileWithSync(n.dios, sfile, n.encodeSnapshot(snap), defaultFilePerms); err != nil { // We could set write err here, but if this is a temporary situation, too many open files etc. // we want to retry and snapshots are not fatal. return err @@ -1517,8 +1518,11 @@ func (c *checkpoint) AppendEntriesSeq() iter.Seq2[*appendEntry, error] { yield(nil, err) return } - yield(ae, nil) + hasMore := yield(ae, nil) ae.returnToPool() + if !hasMore { + return + } } } } @@ -1564,7 +1568,7 @@ func (c *checkpoint) InstallSnapshot(data []byte) (uint64, error) { // Unlock while writing. n.Unlock() - err := writeFileWithSync(c.snapFile, encoded, defaultFilePerms) + err := writeFileWithSync(n.dios, c.snapFile, encoded, defaultFilePerms) n.Lock() // On any failure path, drop the file we just wrote so it doesn't get // picked up by setupLastSnapshot on restart. Skip the remove if it's the @@ -1742,9 +1746,9 @@ func (n *raft) loadLastSnapshot() (*snapshot, error) { return nil, errNoSnapAvailable } - <-dios + n.dios.acquire() buf, err := os.ReadFile(n.snapfile) - dios <- struct{}{} + n.dios.release() if err != nil { n.warn("Error reading snapshot: %v", err) @@ -2208,7 +2212,7 @@ func (n *raft) ApplyQ() *ipQueue[*CommittedEntry] { return n.apply } // LeadChangeC returns the leader change channel, notifying when the Raft // leader role has moved. -func (n *raft) LeadChangeC() <-chan bool { return n.leadc } +func (n *raft) LeadChangeC() <-chan leadChange { return n.leadc } // QuitC returns the quit channel, notifying when the Raft group has shut down. func (n *raft) QuitC() <-chan struct{} { return n.quit } @@ -2351,17 +2355,12 @@ func (n *raft) newInbox() string { // Our internal subscribe. // Lock should be held. func (n *raft) subscribe(subject string, cb msgHandler) (*subscription, error) { - if n.c == nil { - return nil, errNoInternalClient - } - return n.s.systemSubscribe(subject, _EMPTY_, false, n.c, cb) + return n.t.Subscribe(subject, cb) } // Lock should be held. func (n *raft) unsubscribe(sub *subscription) { - if n.c != nil && sub != nil { - n.c.processUnsub(sub.sid) - } + n.t.Unsubscribe(sub) } // Lock should be held. @@ -2486,19 +2485,7 @@ runner: n.Lock() defer n.Unlock() - if c := n.c; c != nil { - var subs []*subscription - c.mu.Lock() - for _, sub := range c.subs { - subs = append(subs, sub) - } - c.mu.Unlock() - for _, sub := range subs { - n.unsubscribe(sub) - } - c.closeConnection(InternalClient) - n.c = nil - } + n.t.Close() // Unregistering ipQueues do not prevent them from push/pop // just will remove them from the central monitoring map @@ -3780,6 +3767,19 @@ func (n *raft) adjustClusterSizeAndQuorum() { } } +// Returns true if we should count vote responses from this peer. +// Lock should be held. +func (n *raft) shouldCountVoteFromPeer(peer string) bool { + if _, ok := n.peers[peer]; ok { + return true + } + // During bootstrap, we may know fewer peer ids + // than the declared cluster size. Initially, we + // may need to accept votes from peers that are + // not yet in the peer set. + return len(n.peers) < n.csz +} + // Track interactions with this peer. func (n *raft) trackPeer(peer string) error { n.Lock() @@ -3851,15 +3851,18 @@ func (n *raft) runAsCandidate() { n.RLock() nterm := n.term csz := n.csz + countVote := n.shouldCountVoteFromPeer(vresp.peer) n.RUnlock() if vresp.granted && nterm == vresp.term { // only track peers that would be our followers n.trackPeer(vresp.peer) - if !vresp.empty { - votes[vresp.peer] = struct{}{} - } else { - emptyVotes[vresp.peer] = struct{}{} + if countVote { + if !vresp.empty { + votes[vresp.peer] = struct{}{} + } else { + emptyVotes[vresp.peer] = struct{}{} + } } if n.wonElection(len(votes)) { // Become LEADER if we have won and gotten a quorum with everyone we should hear from. @@ -4970,25 +4973,25 @@ func (n *raft) writePeerState(ps *peerState) { } // Stamp latest and write the peer state file. n.wps = pse - if err := writePeerState(n.sd, ps); err != nil && !n.isClosed() { + if err := writePeerState(n.dios, n.sd, ps); err != nil && !n.isClosed() { n.setWriteErrLocked(err) n.warn("Error writing peer state file for %q: %v", n.group, err) } } // Writes out our peer state outside of a specific raft context. -func writePeerState(sd string, ps *peerState) error { +func writePeerState(dios *diskIOSemaphore, sd string, ps *peerState) error { psf := filepath.Join(sd, peerStateFile) if _, err := os.Stat(psf); err != nil && !os.IsNotExist(err) { return err } - return writeFileWithSync(psf, encodePeerState(ps), defaultFilePerms) + return writeFileWithSync(dios, psf, encodePeerState(ps), defaultFilePerms) } -func readPeerState(sd string) (ps *peerState, err error) { - <-dios +func readPeerState(dios *diskIOSemaphore, sd string) (ps *peerState, err error) { + dios.acquire() buf, err := os.ReadFile(filepath.Join(sd, peerStateFile)) - dios <- struct{}{} + dios.release() if err != nil { return nil, err @@ -5001,20 +5004,20 @@ const termLen = 8 // uint64 const termVoteLen = idLen + termLen // Writes out our term & vote outside of a specific raft context. -func writeTermVote(sd string, wtv []byte) error { +func writeTermVote(dios *diskIOSemaphore, sd string, wtv []byte) error { psf := filepath.Join(sd, termVoteFile) if _, err := os.Stat(psf); err != nil && !os.IsNotExist(err) { return err } - return writeFileWithSync(psf, wtv, defaultFilePerms) + return writeFileWithSync(dios, psf, wtv, defaultFilePerms) } // readTermVote will read the largest term and who we voted from to stable storage. // Lock should be held. func (n *raft) readTermVote() (term uint64, voted string, err error) { - <-dios + n.dios.acquire() buf, err := os.ReadFile(filepath.Join(n.sd, termVoteFile)) - dios <- struct{}{} + n.dios.release() if err != nil { return 0, noVote, err @@ -5118,7 +5121,7 @@ func (n *raft) writeTermVote() error { } // Stamp latest and write the term & vote file. n.wtv = b - if err := writeTermVote(n.sd, n.wtv); err != nil && !n.isClosed() { + if err := writeTermVote(n.dios, n.sd, n.wtv); err != nil && !n.isClosed() { // Clear wtv since we failed. n.wtv = nil n.setWriteErrLocked(err) @@ -5288,15 +5291,11 @@ func (n *raft) requestVote() { } func (n *raft) sendRPC(subject, reply string, msg []byte) { - if n.sq != nil { - n.sq.send(subject, reply, nil, msg) - } + n.t.Publish(subject, reply, msg) } func (n *raft) sendReply(subject string, msg []byte) { - if n.sq != nil { - n.sq.send(subject, _EMPTY_, nil, msg) - } + n.t.Publish(subject, _EMPTY_, msg) } func (n *raft) wonElection(votes int) bool { @@ -5311,13 +5310,21 @@ func (n *raft) quorumNeeded() int { return qn } +// leadChange signals a leadership change to the upper layer. The term +// identifies the leadership epoch the signal belongs to. +type leadChange struct { + isLeader bool + term uint64 +} + // Lock should be held. func (n *raft) updateLeadChange(isLeader bool) { + lc := leadChange{isLeader: isLeader, term: n.term} // We don't care about values that have not been consumed (transitory states), // so we dequeue any state that is pending and push the new one. for { select { - case n.leadc <- isLeader: + case n.leadc <- lc: return default: select { @@ -5347,23 +5354,23 @@ retry: // Reset the election timer. n.resetElectionTimeout() - var leadChange bool + var leadChanged bool if pstate == Leader && state != Leader { - leadChange = true + leadChanged = true n.updateLeadChange(false) // Drain the append entry response and proposal queues. n.resp.drain() n.prop.drain() } else if state == Leader && pstate != Leader { // Don't updateLeadChange here, it will be done in switchToLeader or after initial messages are applied. - leadChange = true + leadChanged = true if len(n.pae) > 0 { n.pae = make(map[uint64]*appendEntry) } } n.writeTermVote() - return leadChange + return leadChanged } const ( diff --git a/vendor/github.com/nats-io/nats-server/v2/server/raft_transport.go b/vendor/github.com/nats-io/nats-server/v2/server/raft_transport.go new file mode 100644 index 0000000000..f45316efdc --- /dev/null +++ b/vendor/github.com/nats-io/nats-server/v2/server/raft_transport.go @@ -0,0 +1,114 @@ +// Copyright 2026 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +// raftTransport is an interface that defines the communication +// mechanism for Raft nodes. +type raftTransport interface { + // Node returns the RaftNode associated with this transport. + Node() RaftNode + + // Account returns the NATS Account this transport operates within. + Account() *Account + + // Reset reconfigures the transport for a new account. + // This involves tearing down existing client resources and + // setting up new ones for the provided account. + Reset(acc *Account) + + // Close shuts down the transport, releasing any associated resources + // like internal clients and subscriptions. + Close() + + // Publish sends a message to the specified subject. + Publish(subject string, reply string, msg []byte) + + // Subscribe creates a subscription for to the specified subject. + Subscribe(subject string, cb msgHandler) (*subscription, error) + + // Unsubscribe removes a previously established subscription. + Unsubscribe(sub *subscription) +} + +type newTransportFunc func(*Server, RaftNode) raftTransport + +// defaultTransport is the default implementation of the raftTransport interface. +// It uses an internal NATS client to allow communication between Raft nodes. +type defaultTransport struct { + n RaftNode + s *Server + c *client + sq *sendq + acc *Account +} + +func defaultRaftTransport(server *Server, raft RaftNode) raftTransport { + return &defaultTransport{s: server, n: raft} +} + +func (t *defaultTransport) Node() RaftNode { + return t.n +} + +func (t *defaultTransport) Account() *Account { + return t.acc +} + +func (t *defaultTransport) Reset(acc *Account) { + t.Close() + + t.c = t.s.createInternalSystemClient() + t.c.registerWithAccount(acc) + if acc.sq == nil { + acc.sq = t.s.newSendQ(acc) + } + t.sq = acc.sq + t.acc = acc +} + +func (t *defaultTransport) Close() { + if c := t.c; c != nil { + c.mu.Lock() + subs := make([]*subscription, 0, len(c.subs)) + for _, sub := range c.subs { + subs = append(subs, sub) + } + c.mu.Unlock() + for _, sub := range subs { + t.Unsubscribe(sub) + } + c.closeConnection(InternalClient) + t.c = nil + } +} + +func (t *defaultTransport) Publish(subject string, reply string, msg []byte) { + if t.sq == nil { + return + } + t.sq.send(subject, reply, nil, msg) +} + +func (t *defaultTransport) Subscribe(subject string, cb msgHandler) (*subscription, error) { + if t.c == nil { + return nil, errNoInternalClient + } + return t.s.systemSubscribe(subject, _EMPTY_, false, t.c, cb) +} + +func (t *defaultTransport) Unsubscribe(sub *subscription) { + if t.c != nil && sub != nil { + t.c.processUnsub(sub.sid) + } +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/reload.go b/vendor/github.com/nats-io/nats-server/v2/server/reload.go index a8909596e8..53bcdd6dcc 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/reload.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/reload.go @@ -1864,6 +1864,15 @@ func (s *Server) diffOptions(newOpts *Options) ([]option, error) { } case "jetstreammetacompact", "jetstreammetacompactsize", "jetstreammetacompactsync": // Allowed at runtime but monitorCluster looks at s.opts directly, so no further work needed here. + case "jetstreamconcurrentios": + // Not reloadable at runtime; preserve the current value while JetStream is disabled, + // e.g. the entire jetstream{} block was deleted. + if newOpts.JetStream { + return nil, fmt.Errorf("config reload not supported for %s: old=%v, new=%v", + field.Name, oldValue, newValue) + } else { + newOpts.JetStreamConcurrentIOs = oldValue.(int) + } case "websocket": // Similar to gateways tmpOld := oldValue.(WebsocketOpts) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/server.go b/vendor/github.com/nats-io/nats-server/v2/server/server.go index 3a2d81a9d2..61dc434fe7 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/server.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/server.go @@ -190,6 +190,7 @@ type Server struct { sys *internal sysAcc atomic.Pointer[Account] js atomic.Pointer[jetStream] + dios *diskIOSemaphore isMetaLeader atomic.Bool jsClustered atomic.Bool accounts sync.Map @@ -773,6 +774,7 @@ func NewServer(opts *Options) (*Server, error) { rateLimitLoggingCh: make(chan time.Duration, 1), leafNodeEnabled: opts.LeafNode.Port != 0 || len(opts.LeafNode.Remotes) > 0, syncOutSem: make(chan struct{}, maxConcurrentSyncRequests), + dios: newDiskIOSemaphore(opts.JetStreamConcurrentIOs), } // Delayed API response queue. Create regardless if JetStream is configured @@ -1096,6 +1098,12 @@ func validateCluster(o *Options) error { if o.Cluster.Name != _EMPTY_ && strings.Contains(o.Cluster.Name, " ") { return ErrClusterNameHasSpaces } + if p := o.Cluster.Permissions; p != nil { + perms := &Permissions{Publish: p.Import, Subscribe: p.Export} + if err := checkClusterPermissionSubjects(perms); err != nil { + return err + } + } if o.Cluster.Compression.Mode != _EMPTY_ { if err := validateAndNormalizeCompressionOption(&o.Cluster.Compression, CompressionS2Fast); err != nil { return err @@ -2791,8 +2799,15 @@ func (s *Server) AcceptLoop(clr chan struct{}) { // Alert of TLS enabled. if opts.TLSConfig != nil { - s.Noticef("TLS required for client connections") - if opts.TLSHandshakeFirst && opts.TLSHandshakeFirstFallback == 0 { + // "TLS Handshake First" without a fallback delay always requires the + // handshake, which overrides "allow_non_tls". + tlsHandshakeFirstOnly := opts.TLSHandshakeFirst && opts.TLSHandshakeFirstFallback == 0 + if opts.AllowNonTLS && !tlsHandshakeFirstOnly { + s.Noticef("TLS available for client connections") + } else { + s.Noticef("TLS required for client connections") + } + if tlsHandshakeFirstOnly { s.Warnf("Clients that are not using \"TLS Handshake First\" option will fail to connect") } } @@ -4774,3 +4789,10 @@ func (s *Server) LDMClientByID(id uint64) error { return errors.New("client does not support Lame Duck Mode or is not ready to receive the notification") } } + +func (s *Server) diskIOSemaphore() *diskIOSemaphore { + if s == nil || s.dios == nil { + return defaultDiskIOSemaphore() + } + return s.dios +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/signal.go b/vendor/github.com/nats-io/nats-server/v2/server/signal.go index aef50a5b4b..f8e5dc74c9 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/signal.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/signal.go @@ -46,34 +46,7 @@ func (s *Server) handleSignals() { for { select { case sig := <-c: - s.Noticef("Trapped %q signal", sig) - switch sig { - case syscall.SIGINT: - s.Shutdown() - s.WaitForShutdown() - os.Exit(0) - case syscall.SIGTERM: - // Shutdown unless graceful shutdown already in progress. - s.mu.Lock() - ldm := s.ldm - s.mu.Unlock() - - if !ldm { - s.Shutdown() - s.WaitForShutdown() - os.Exit(0) - } - case syscall.SIGUSR1: - // File log re-open for rotating file logs. - s.ReOpenLogFile() - case syscall.SIGUSR2: - go s.lameDuckMode() - case syscall.SIGHUP: - // Config reload. - if err := s.Reload(); err != nil { - s.Errorf("Failed to reload server configuration: %s", err) - } - } + s.handleSignal(sig) case <-s.quitCh: return } @@ -81,6 +54,37 @@ func (s *Server) handleSignals() { }() } +func (s *Server) handleSignal(sig os.Signal) { + s.Noticef("Trapped %q signal", sig) + switch sig { + case syscall.SIGINT: + s.Shutdown() + s.WaitForShutdown() + os.Exit(0) + case syscall.SIGTERM: + // Shutdown unless graceful shutdown already in progress. + s.mu.Lock() + ldm := s.ldm + s.mu.Unlock() + + if !ldm { + s.Shutdown() + s.WaitForShutdown() + os.Exit(0) + } + case syscall.SIGUSR1: + // File log re-open for rotating file logs. + s.ReOpenLogFile() + case syscall.SIGUSR2: + go s.lameDuckMode() + case syscall.SIGHUP: + // Config reload. + if err := s.Reload(); err != nil { + s.Errorf("Failed to reload server configuration: %s", err) + } + } +} + // ProcessSignal sends the given signal command to the given process. If pidStr // is empty, this will send the signal to the single running instance of // nats-server. If multiple instances are running, pidStr can be a globular diff --git a/vendor/github.com/nats-io/nats-server/v2/server/signal_wasm.go b/vendor/github.com/nats-io/nats-server/v2/server/signal_wasm.go index 7ee34e4aba..140d5007aa 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/signal_wasm.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/signal_wasm.go @@ -15,10 +15,16 @@ package server +import "os" + func (s *Server) handleSignals() { } +func (s *Server) handleSignal(sig os.Signal) { + +} + func ProcessSignal(command Command, service string) error { return nil } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/store.go b/vendor/github.com/nats-io/nats-server/v2/server/store.go index a7c97439db..fe04ace0e8 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/store.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/store.go @@ -18,6 +18,7 @@ import ( "errors" "fmt" "io" + "math/bits" "os" "strings" "time" @@ -307,6 +308,27 @@ func DecodeStreamState(buf []byte) (*StreamReplicatedState, error) { return ss, nil } +// uvarintLen returns the number of bytes binary.PutUvarint/binary.AppendUvarint +// write for v: ceil(bits/7), with v=0 taking one byte. +func uvarintLen(v uint64) int { + return (bits.Len64(v|1) + 6) / 7 +} + +// runLengthEncodeLen returns the encoded size of a run-length delete record, +// exactly matching what appendRunLength writes. +func runLengthEncodeLen(first, num uint64) int { + return 1 + uvarintLen(first) + uvarintLen(num) +} + +// appendRunLength appends a run-length encoded delete record for num +// deleted sequences starting at first. +func appendRunLength(b []byte, first, num uint64) []byte { + b = append(b, runLengthMagic) + b = binary.AppendUvarint(b, first) + b = binary.AppendUvarint(b, num) + return b +} + // DeleteRange is a run length encoded delete range. type DeleteRange struct { First uint64 diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stream.go b/vendor/github.com/nats-io/nats-server/v2/server/stream.go index 108056d2e0..4ff6cb4e3a 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stream.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stream.go @@ -480,6 +480,10 @@ type stream struct { // Those subscriptions are for the subjects filters being listened to and captured by the stream. sid atomic.Uint64 + // Whether the stream layer has completed leader setup via setLeader. Unlike isLeader(), + // which reads the raft node's current state, this tracks our own processed leadership. + leader atomic.Bool + pubAck []byte // The template (prefix) to generate the pubAck responses for this stream quickly. outq *jsOutQ // Queue of *jsPubMsg for sending messages. msgs *ipQueue[*inMsg] // Intra-process queue for the ingress of messages. @@ -487,6 +491,7 @@ type stream struct { store StreamStore // The storage for this stream. ackq *ipQueue[uint64] // Intra-process queue for acks. lseq uint64 // The sequence number of the last message stored in the stream. + term uint64 // Raft term, used to determine if we are still the leader for the current term (if applicable, 0 otherwise). lmsgId string // The de-duplication message ID of the last message stored in the stream. consumers map[string]*consumer // The consumers for this stream. numFilter int // The number of filtered consumers. @@ -1043,7 +1048,7 @@ func (a *Account) addStreamWithAssignment(config *StreamConfig, fsConfig *FileSt // Call directly to set leader if not in clustered mode. // This can be called though before we actually setup clustering, so check both. if singleServerMode { - if err := mset.setLeader(true); err != nil { + if err := mset.setLeader(true, 0); err != nil { mset.stop(true, false) return nil, err } @@ -1252,8 +1257,37 @@ func (mset *stream) isLeaderNodeState() bool { } // TODO(dlc) - Check to see if we can accept being the leader or we should step down. -func (mset *stream) setLeader(isLeader bool) error { +func (mset *stream) setLeader(isLeader bool, term uint64) error { mset.mu.Lock() + wasLeader := mset.leader.Swap(isLeader) + + // We can skip the teardown if we were leader before and are still the leader now. + // But only at term 1, since that means scale up from or down to an unreplicated config. + // R1 assets have no raft node and use the coerced term 1. + if term < 1 { + term = 1 + } + skipTeardown := wasLeader && isLeader && term == 1 + mset.term = term + if !skipTeardown { + // cancel timer to create the source consumers if not fired yet + if mset.sourcesConsumerSetup != nil { + mset.sourcesConsumerSetup.Stop() + mset.sourcesConsumerSetup = nil + } else { + // Stop any source consumers + mset.stopSourceConsumers() + } + + // Stop responding to sync requests. + mset.stopClusterSubs() + // Unsubscribe from direct stream. + mset.unsubscribeToStream(false, false) + // Clear catchup state + mset.clearAllCatchupPeers() + mset.store.ResetState() + } + // If we are here we have a change in leader status. if isLeader { // Make sure we are listening for sync requests. @@ -1274,31 +1308,14 @@ func (mset *stream) setLeader(isLeader bool) error { // Reset any inflight fast batches. We were likely a follower before and need // to send an ack to the publishers so they know we're still there. - if mset.batches != nil { + if !skipTeardown && mset.batches != nil { mset.batches.mu.Lock() for batchId, b := range mset.batches.fast { mset.batches.fastBatchReset(mset, batchId, b) } mset.batches.mu.Unlock() } - } else { - // cancel timer to create the source consumers if not fired yet - if mset.sourcesConsumerSetup != nil { - mset.sourcesConsumerSetup.Stop() - mset.sourcesConsumerSetup = nil - } else { - // Stop any source consumers - mset.stopSourceConsumers() - } - - // Stop responding to sync requests. - mset.stopClusterSubs() - // Unsubscribe from direct stream. - mset.unsubscribeToStream(false, false) - // Clear catchup state - mset.clearAllCatchupPeers() } - mset.store.ResetState() mset.mu.Unlock() // If we are interest based make sure to check consumers. @@ -3274,16 +3291,18 @@ func (mset *stream) processInboundMirrorMsg(m *inMsg) bool { } s, js, stype := mset.srv, mset.js, mset.cfg.Storage - node := mset.node + node, term := mset.node, mset.term mset.mu.Unlock() var err error if node != nil { - if js.limitsExceeded(stype) { + if stype == FileStorage && isFileStoreMsgTooLarge(fileStoreMsgSize(m.subj, m.hdr, m.msg)) { + err = ErrMsgTooLarge + } else if js.limitsExceeded(stype) { s.resourcesExceededError(stype) err = ApiErrors[JSInsufficientResourcesErr] } else { - err = node.Propose(encodeStreamMsg(m.subj, _EMPTY_, m.hdr, m.msg, sseq-1, ts, true)) + err = node.Propose(term, encodeStreamMsg(m.subj, _EMPTY_, m.hdr, m.msg, sseq-1, ts, true)) } } else { err = mset.processJetStreamMsg(m.subj, _EMPTY_, m.hdr, m.msg, sseq-1, ts, nil, true, true) @@ -3364,7 +3383,7 @@ func (mset *stream) skipMsgs(start, end uint64) error { // Must only be enabled once every peer in the cluster supports receiving // deleteRangeOp in the normal apply path; older peers panic on unknown ops. if mset.srv.getOpts().getFeatureFlag(FeatureFlagJsRaftDeleteRange) { - return node.Propose(encodeDeleteRange(&DeleteRange{First: start, Num: end - start + 1})) + return node.Propose(mset.term, encodeDeleteRange(&DeleteRange{First: start, Num: end - start + 1})) } var entries []*Entry @@ -3372,7 +3391,7 @@ func (mset *stream) skipMsgs(start, end uint64) error { entries = append(entries, newEntry(EntryNormal, encodeStreamMsg(_EMPTY_, _EMPTY_, nil, nil, seq-1, 0, false))) // So a single message does not get too big. if len(entries) > 10_000 { - if err := node.ProposeMulti(entries); err != nil { + if err := node.ProposeMulti(mset.term, entries); err != nil { return err } // We need to re-create `entries` because there is a reference @@ -3382,7 +3401,7 @@ func (mset *stream) skipMsgs(start, end uint64) error { } // Send all at once. if len(entries) > 0 { - return node.ProposeMulti(entries) + return node.ProposeMulti(mset.term, entries) } return nil } @@ -3570,22 +3589,26 @@ func (mset *stream) setupMirrorConsumer() error { } } - respCh := make(chan *JSApiConsumerCreateResponse, 1) - reply := infoReplySubject() - crSub, err := mset.subscribeInternal(reply, func(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) { - _, msg := c.msgParts(rmsg) + newReplySubscription := func() (string, chan *JSApiConsumerCreateResponse, *subscription, error) { + respCh := make(chan *JSApiConsumerCreateResponse, 1) + reply := infoReplySubject() + crSub, err := mset.subscribeInternal(reply, func(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) { + _, msg := c.msgParts(rmsg) - var ccr JSApiConsumerCreateResponse - if err := json.Unmarshal(msg, &ccr); err != nil { - c.Warnf("JetStream bad mirror consumer create response: %q", msg) - mset.setMirrorErr(ApiErrors[JSInvalidJSONErr]) - return - } - select { - case respCh <- &ccr: - default: - } - }) + var ccr JSApiConsumerCreateResponse + if err := json.Unmarshal(msg, &ccr); err != nil { + c.Warnf("JetStream bad mirror consumer create response: %q", msg) + mset.setMirrorErr(ApiErrors[JSInvalidJSONErr]) + return + } + select { + case respCh <- &ccr: + default: + } + }) + return reply, respCh, crSub, err + } + reply, respCh, crSub, err := newReplySubscription() if err != nil { mirror.err = NewJSMirrorConsumerSetupFailedError(err, Unless(err)) mset.scheduleSetupMirrorConsumerRetry() @@ -3677,7 +3700,7 @@ func (mset *stream) setupMirrorConsumer() error { mirror := mset.mirror mirror.err = nil - if ccr.Error != nil || ccr.ConsumerInfo == nil { + if ccr.Error != nil || ccr.ConsumerInfo == nil || ccr.ConsumerInfo.Config == nil { // If the responding server doesn't support sourcing consumers, retry without it. if req.Config.Sourcing && ccr.Error != nil && (ccr.Error.ErrCode == uint16(JSRequiredApiLevelErr) || ccr.Error.ErrCode == uint16(JSInvalidJSONErr)) { @@ -3688,13 +3711,25 @@ func (mset *stream) setupMirrorConsumer() error { b, _ := json.Marshal(req) // Regenerate subject since the previous name could've been included in it. subject = generateSubject() + // Recreate the reply subscription so we don't get stale responses from other servers. + mset.unsubscribe(crSub) + if reply, respCh, crSub, err = newReplySubscription(); err != nil { + mirror.err = NewJSMirrorConsumerSetupFailedError(err, Unless(err)) + retry = true + mset.mu.Unlock() + return + } mset.outq.send(newJSPubMsg(subject, _EMPTY_, reply, nil, b, nil, 0)) mset.mu.Unlock() goto SELECT } mset.unsubscribe(crSub) - mset.srv.Warnf("JetStream error response for create mirror consumer: %+v", ccr.Error) - mirror.err = ccr.Error + cerr := ccr.Error + if cerr == nil { + cerr = NewJSMirrorConsumerSetupFailedError(errors.New("invalid consumer create response")) + } + mset.srv.Warnf("JetStream error response for create mirror consumer: %+v", cerr) + mirror.err = cerr // Let's retry as soon as possible, but we are gated by sourceConsumerRetryThreshold retry = true mset.mu.Unlock() @@ -4004,20 +4039,24 @@ func (mset *stream) trySetupSourceConsumer(iname string, seq uint64, startTime t } req.Config.FilterSubjects = filterSubjects - respCh := make(chan *JSApiConsumerCreateResponse, 1) - reply := infoReplySubject() - crSub, err := mset.subscribeInternal(reply, func(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) { - _, msg := c.msgParts(rmsg) - var ccr JSApiConsumerCreateResponse - if err := json.Unmarshal(msg, &ccr); err != nil { - c.Warnf("JetStream bad source consumer create response: %q", msg) - return - } - select { - case respCh <- &ccr: - default: - } - }) + newReplySubscription := func() (string, chan *JSApiConsumerCreateResponse, *subscription, error) { + respCh := make(chan *JSApiConsumerCreateResponse, 1) + reply := infoReplySubject() + crSub, err := mset.subscribeInternal(reply, func(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) { + _, msg := c.msgParts(rmsg) + var ccr JSApiConsumerCreateResponse + if err := json.Unmarshal(msg, &ccr); err != nil { + c.Warnf("JetStream bad source consumer create response: %q", msg) + return + } + select { + case respCh <- &ccr: + default: + } + }) + return reply, respCh, crSub, err + } + reply, respCh, crSub, err := newReplySubscription() if err != nil { si.err = NewJSSourceConsumerSetupFailedError(err, Unless(err)) mset.setupSourceConsumer(iname, seq, startTime) @@ -4098,7 +4137,7 @@ func (mset *stream) trySetupSourceConsumer(iname string, seq uint64, startTime t } else { si.err = nil - if ccr.Error != nil || ccr.ConsumerInfo == nil { + if ccr.Error != nil || ccr.ConsumerInfo == nil || ccr.ConsumerInfo.Config == nil { // If the responding server doesn't support sourcing consumers, retry without it. if req.Config.Sourcing && ccr.Error != nil && (ccr.Error.ErrCode == uint16(JSRequiredApiLevelErr) || ccr.Error.ErrCode == uint16(JSInvalidJSONErr)) { @@ -4109,6 +4148,14 @@ func (mset *stream) trySetupSourceConsumer(iname string, seq uint64, startTime t b, _ := json.Marshal(req) // Regenerate subject since the previous name could've been included in it. subject = generateSubject() + // Recreate the reply subscription so we don't get stale responses from other servers. + mset.unsubscribe(crSub) + if reply, respCh, crSub, err = newReplySubscription(); err != nil { + si.err = NewJSSourceConsumerSetupFailedError(err, Unless(err)) + retry = true + mset.mu.Unlock() + return + } mset.outq.send(newJSPubMsg(subject, _EMPTY_, reply, nil, b, nil, 0)) mset.mu.Unlock() goto SELECT @@ -4117,8 +4164,12 @@ func (mset *stream) trySetupSourceConsumer(iname string, seq uint64, startTime t // Note: this warning can happen a few times when starting up the server when sourcing streams are // defined, this is normal as the streams are re-created in no particular order and it is possible // that a stream sourcing another could come up before all of its sources have been recreated. - mset.srv.Warnf("JetStream error response for stream %s create source consumer %s: %+v", mset.cfg.Name, si.name, ccr.Error) - si.err = ccr.Error + cerr := ccr.Error + if cerr == nil { + cerr = NewJSSourceConsumerSetupFailedError(errors.New("invalid consumer create response")) + } + mset.srv.Warnf("JetStream error response for stream %s create source consumer %s: %+v", mset.cfg.Name, si.name, cerr) + si.err = cerr // Let's retry as soon as possible, but we are gated by sourceConsumerRetryThreshold retry = true mset.mu.Unlock() @@ -4313,7 +4364,7 @@ func (mset *stream) handleFlowControl(m *inMsg, dseq, sseq uint64) { // Append the current delivery and stream sequences, to be sent after replication. m.hdr = genHeader(m.hdr, JSLastConsumerSeq, strconv.FormatUint(dseq, 10)) m.hdr = genHeader(m.hdr, JSLastStreamSeq, strconv.FormatUint(sseq, 10)) - mset.node.Propose(encodeStreamMsg(_EMPTY_, m.rply, m.hdr, nil, 0, 0, false)) + mset.node.Propose(mset.term, encodeStreamMsg(_EMPTY_, m.rply, m.hdr, nil, 0, 0, false)) } else { const t = "NATS/1.0\r\n%s: %d\r\n%s: %d\r\n\r\n" hdr := fmt.Appendf(nil, t, JSLastConsumerSeq, dseq, JSLastStreamSeq, sseq) @@ -5176,12 +5227,12 @@ func (mset *stream) setupStore(fsCfg *FileStoreConfig) error { mset.store.RegisterStorageUpdates(mset.storeUpdates) mset.store.RegisterStorageRemoveMsg(func(seq uint64) { if mset.IsClustered() { - if mset.IsLeader() { - mset.mu.RLock() + mset.mu.RLock() + if mset.isLeader() { md := streamMsgDelete{Seq: seq, NoErase: true, Stream: mset.cfg.Name} - mset.node.Propose(encodeMsgDelete(&md)) - mset.mu.RUnlock() + mset.node.Propose(mset.term, encodeMsgDelete(&md)) } + mset.mu.RUnlock() } else { mset.removeMsg(seq) } @@ -6199,6 +6250,16 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg var resp = &JSPubAckResponse{} + if canConsistencyCheck && stype == FileStorage && isFileStoreMsgTooLarge(fileStoreMsgSize(subject, hdr, msg)) { + if canRespond { + resp.PubAck = &PubAck{Stream: name} + resp.Error = NewJSStreamStoreFailedError(ErrMsgTooLarge) + response, _ := json.Marshal(resp) + outq.sendMsg(reply, response) + } + return ErrMsgTooLarge + } + var ( batchId string batchSeq uint64 @@ -6755,7 +6816,7 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg if sources == nil { sources = map[string]map[string]string{} } - if _, ok := sources[origStream]; !ok { + if sources[origStream] == nil { sources[origStream] = map[string]string{} } prevVal := sources[origStream][origSubj] @@ -7016,7 +7077,7 @@ func (mset *stream) processJetStreamMsgWithBatch(subject, reply string, hdr, msg // If this proposal fails, we retry out-of-band. if isClustered && isLeader { md := streamMsgDelete{Seq: seq, NoErase: true, Stream: mset.cfg.Name} - _ = mset.node.Propose(encodeMsgDelete(&md)) + _ = mset.node.Propose(mset.term, encodeMsgDelete(&md)) } } @@ -7546,10 +7607,11 @@ func (mset *stream) processJetStreamAtomicBatchMsg(batchId, subject, reply strin } mset.mu.Unlock() } else { + term := mset.term mset.mu.Unlock() // Do a single multi proposal. This ensures we get to push all entries to the proposal queue in-order // and not interleaved with other proposals. - if err = node.ProposeMulti(entries); err == nil { + if err = node.ProposeMulti(term, entries); err == nil { diff.commit(mset) mset.trackReplicationTraffic(node, sz, r) @@ -7576,7 +7638,7 @@ func (mset *stream) processJetStreamFastBatchMsg(batch *FastBatch, subject, repl canRespond := !mset.cfg.NoAck && len(reply) > 0 name, stype := mset.cfg.Name, mset.cfg.Storage discard, discardNewPer, maxMsgs, maxMsgsPer, maxBytes := mset.cfg.Discard, mset.cfg.DiscardNewPer, mset.cfg.MaxMsgs, mset.cfg.MaxMsgsPer, mset.cfg.MaxBytes - s, js, jsa, st, r, tierName, outq, node := mset.srv, mset.js, mset.jsa, mset.cfg.Storage, mset.cfg.Replicas, mset.tier, mset.outq, mset.node + s, js, jsa, st, r, tierName, outq, node, term := mset.srv, mset.js, mset.jsa, mset.cfg.Storage, mset.cfg.Replicas, mset.tier, mset.outq, mset.node, mset.term maxMsgSize, lseq := int(mset.cfg.MaxMsgSize), mset.lseq isLeader, isClustered, isSealed, allowRollup, denyPurge, allowTTL, allowMsgCounter, allowMsgSchedules, allowBatchPublish := mset.isLeader(), mset.isClustered(), mset.cfg.Sealed, mset.cfg.AllowRollup, mset.cfg.DenyPurge, mset.cfg.AllowMsgTTL, mset.cfg.AllowMsgCounter, mset.cfg.AllowMsgSchedules, mset.cfg.AllowBatchPublish @@ -7903,7 +7965,7 @@ func (mset *stream) processJetStreamFastBatchMsg(batch *FastBatch, subject, repl mset.clMu.Unlock() return mset.processJetStreamMsgWithBatch(subject, reply, hdr, msg, 0, 0, mt, false, true, batch) } - err = commitSingleMsg(diff, mset, subject, reply, hdr, msg, name, jsa, mt, node, r, lseq) + err = commitSingleMsg(diff, mset, subject, reply, hdr, msg, name, jsa, mt, node, term, r, lseq) mset.clMu.Unlock() return err } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go index 119837ec26..b5281a5a3e 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/leaf.go @@ -13,10 +13,6 @@ package stree -import ( - "bytes" -) - // Leaf node // Order of struct fields for best memory alignment (as per govet/fieldalignment) type leaf[T any] struct { @@ -24,23 +20,25 @@ type leaf[T any] struct { // This could be the whole subject, but most likely just the suffix portion. // We will only store the suffix here and assume all prior prefix paths have // been checked once we arrive at this leafnode. - suffix []byte + suffix string } func newLeaf[T any](suffix []byte, value T) *leaf[T] { - return &leaf[T]{value, copyBytes(suffix)} + return &leaf[T]{value, string(suffix)} } -func (n *leaf[T]) isLeaf() bool { return true } -func (n *leaf[T]) base() *meta { return nil } -func (n *leaf[T]) match(subject []byte) bool { return bytes.Equal(subject, n.suffix) } -func (n *leaf[T]) setSuffix(suffix []byte) { n.suffix = copyBytes(suffix) } -func (n *leaf[T]) isFull() bool { return true } -func (n *leaf[T]) matchParts(parts [][]byte) ([][]byte, bool) { return matchParts(parts, n.suffix) } -func (n *leaf[T]) iter(f func(node) bool) {} -func (n *leaf[T]) children() []node { return nil } -func (n *leaf[T]) numChildren() uint16 { return 0 } -func (n *leaf[T]) path() []byte { return n.suffix } +func (n *leaf[T]) isLeaf() bool { return true } +func (n *leaf[T]) base() *meta { return nil } +func (n *leaf[T]) match(subject []byte) bool { return string(subject) == n.suffix } +func (n *leaf[T]) setSuffix(suffix []byte) { n.suffix = string(suffix) } +func (n *leaf[T]) isFull() bool { return true } +func (n *leaf[T]) matchParts(parts [][]byte) ([][]byte, bool) { + return matchParts(parts, n.suffix) +} +func (n *leaf[T]) iter(f func(node) bool) {} +func (n *leaf[T]) children() []node { return nil } +func (n *leaf[T]) numChildren() uint16 { return 0 } +func (n *leaf[T]) path() string { return n.suffix } // Not applicable to leafs and should not be called, so panic if we do. func (n *leaf[T]) setPrefix(pre []byte) { panic("setPrefix called on leaf") } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go index c8edfe3ea9..e5a7cb432e 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node.go @@ -29,11 +29,11 @@ type node interface { iter(f func(node) bool) children() []node numChildren() uint16 - path() []byte + path() string } type meta struct { - prefix []byte + prefix string size uint16 } @@ -41,11 +41,11 @@ func (n *meta) isLeaf() bool { return false } func (n *meta) base() *meta { return n } func (n *meta) setPrefix(pre []byte) { - n.prefix = append([]byte(nil), pre...) + n.prefix = string(pre) } func (n *meta) numChildren() uint16 { return n.size } -func (n *meta) path() []byte { return n.prefix } +func (n *meta) path() string { return n.prefix } // Will match parts against our prefix. func (n *meta) matchParts(parts [][]byte) ([][]byte, bool) { diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node10.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node10.go index 37cd2cc946..d359f26079 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/node10.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node10.go @@ -52,7 +52,8 @@ func (n *node10) findChild(c byte) *node { func (n *node10) isFull() bool { return n.size >= 10 } func (n *node10) grow() node { - nn := newNode16(n.prefix) + nn := &node16{} + nn.prefix = n.prefix for i := 0; i < 10; i++ { nn.addChild(n.key[i], n.child[i]) } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go index e2dc97908d..7950928a87 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node16.go @@ -50,7 +50,8 @@ func (n *node16) findChild(c byte) *node { func (n *node16) isFull() bool { return n.size >= 16 } func (n *node16) grow() node { - nn := newNode48(n.prefix) + nn := &node48{} + nn.prefix = n.prefix for i := 0; i < 16; i++ { nn.addChild(n.key[i], n.child[i]) } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go index 4eddf11b83..9e361c0fd3 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node4.go @@ -49,7 +49,8 @@ func (n *node4) findChild(c byte) *node { func (n *node4) isFull() bool { return n.size >= 4 } func (n *node4) grow() node { - nn := newNode10(n.prefix) + nn := &node10{} + nn.prefix = n.prefix for i := 0; i < 4; i++ { nn.addChild(n.key[i], n.child[i]) } diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/node48.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/node48.go index 7099edd58b..17a50c78a5 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/node48.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/node48.go @@ -50,7 +50,8 @@ func (n *node48) findChild(c byte) *node { func (n *node48) isFull() bool { return n.size >= 48 } func (n *node48) grow() node { - nn := newNode256(n.prefix) + nn := &node256{} + nn.prefix = n.prefix for c := 0; c < len(n.key); c++ { if i := n.key[byte(c)]; i > 0 { nn.addChild(byte(c), n.child[i-1]) diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go index af5dd9c176..520d0f24c5 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/parts.go @@ -14,7 +14,7 @@ package stree import ( - "bytes" + "strings" ) // genParts will break a filter subject up into parts. @@ -74,8 +74,9 @@ func genParts(filter []byte, parts [][]byte) [][]byte { return parts } -// Match our parts against a fragment, which could be prefix for nodes or a suffix for leafs. -func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { +// Match our parts against a stored fragment, which could be a prefix for nodes +// or a suffix for leaves. +func matchParts(parts [][]byte, frag string) ([][]byte, bool) { lf := len(frag) if lf == 0 { return parts, true @@ -92,7 +93,7 @@ func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { // Check for pwc or fwc place holders. if lp == 1 { if part[0] == pwc { - index := bytes.IndexByte(frag[si:], tsep) + index := strings.IndexByte(frag[si:], tsep) // We are trying to match pwc and did not find our tsep. // Will need to move to next node from caller. if index < 0 { @@ -114,7 +115,7 @@ func matchParts(parts [][]byte, frag []byte) ([][]byte, bool) { // Frag is smaller then part itself. part = part[:end-si] } - if !bytes.Equal(part, frag[si:end]) { + if string(part) != frag[si:end] { return parts, false } // If we still have a portion of the fragment left, update and continue. diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go index 0c257435dc..a2db3219ad 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/stree.go @@ -16,6 +16,7 @@ package stree import ( "bytes" "slices" + "strings" "unsafe" "github.com/nats-io/nats-server/v2/server/gsl" @@ -87,7 +88,7 @@ func (t *SubjectTree[T]) Find(subject []byte) (*T, bool) { // We are a node type here, grab meta portion. if bn := n.base(); len(bn.prefix) > 0 { end := min(si+len(bn.prefix), len(subject)) - if !bytes.Equal(subject[si:end], bn.prefix) { + if string(subject[si:end]) != bn.prefix { return nil, false } // Increment our subject index. @@ -181,12 +182,12 @@ func (t *SubjectTree[T]) insert(np *node, subject []byte, value T, si int) (*T, return &old, true } // Here we need to split this leaf. - cpi := commonPrefixLen(ln.suffix, subject[si:]) + cpi := commonPrefixLen(stringToBytes(ln.suffix), subject[si:]) nn := newNode4(subject[si : si+cpi]) - ln.setSuffix(ln.suffix[cpi:]) + ln.setSuffix(stringToBytes(ln.suffix[cpi:])) si += cpi // Make sure we have different pivot, normally this will be the case unless we have overflowing prefixes. - if p := pivot(ln.suffix, 0); cpi > 0 && si < len(subject) && p == subject[si] { + if p := pivot(stringToBytes(ln.suffix), 0); cpi > 0 && si < len(subject) && p == subject[si] { // We need to split the original leaf. Recursively call into insert. t.insert(np, subject, value, si) // Now add the update version of *np as a child to the new node4. @@ -194,9 +195,9 @@ func (t *SubjectTree[T]) insert(np *node, subject []byte, value T, si int) (*T, } else { // Can just add this new leaf as a sibling. nl := newLeaf(subject[si:], value) - nn.addChild(pivot(nl.suffix, 0), nl) + nn.addChild(pivot(stringToBytes(nl.suffix), 0), nl) // Add back original. - nn.addChild(pivot(ln.suffix, 0), ln) + nn.addChild(pivot(stringToBytes(ln.suffix), 0), ln) } *np = nn return nil, false @@ -205,7 +206,7 @@ func (t *SubjectTree[T]) insert(np *node, subject []byte, value T, si int) (*T, // Non-leaf nodes. bn := n.base() if len(bn.prefix) > 0 { - cpi := commonPrefixLen(bn.prefix, subject[si:]) + cpi := commonPrefixLen(stringToBytes(bn.prefix), subject[si:]) if pli := len(bn.prefix); cpi >= pli { // Move past this node. We look for an existing child node to recurse into. // If one does not exist we can create a new leaf node. @@ -227,8 +228,8 @@ func (t *SubjectTree[T]) insert(np *node, subject []byte, value T, si int) (*T, // We will insert a new node4 and attach our current node below after adjusting prefix. nn := newNode4(prefix) // Shift the prefix for our original node. - n.setPrefix(bn.prefix[cpi:]) - nn.addChild(pivot(bn.prefix[:], 0), n) + n.setPrefix(stringToBytes(bn.prefix[cpi:])) + nn.addChild(pivot(stringToBytes(bn.prefix), 0), n) // Add in our new leaf. nn.addChild(pivot(subject[si:], 0), newLeaf(subject[si:], value)) // Update our node reference. @@ -269,7 +270,7 @@ func (t *SubjectTree[T]) delete(np *node, subject []byte, si int) (*T, bool) { if len(subject) < si+len(bn.prefix) { return nil, false } - if !bytes.Equal(subject[si:si+len(bn.prefix)], bn.prefix) { + if string(subject[si:si+len(bn.prefix)]) != bn.prefix { return nil, false } // Increment our subject index. @@ -288,18 +289,16 @@ func (t *SubjectTree[T]) delete(np *node, subject []byte, si int) (*T, bool) { if sn := n.shrink(); sn != nil { bn := n.base() - // Make sure to set cap so we force an append to copy below. - pre := bn.prefix[:len(bn.prefix):len(bn.prefix)] + pre := bn.prefix // Need to fix up prefixes/suffixes. if sn.isLeaf() { ln := sn.(*leaf[T]) - // Make sure to set cap so we force an append to copy. - ln.suffix = append(pre, ln.suffix...) + ln.suffix = pre + ln.suffix } else { // We are a node here, we need to add in the old prefix. if len(pre) > 0 { bsn := sn.base() - sn.setPrefix(append(pre, bsn.prefix...)) + bsn.prefix = pre + bsn.prefix } } *np = sn @@ -367,7 +366,7 @@ func (t *SubjectTree[T]) match(n node, parts [][]byte, pre []byte, cb func(subje if !cb(append(pre, ln.suffix...), &ln.value) { return false } - } else if hasTermPWC && bytes.IndexByte(ln.suffix, tsep) < 0 { + } else if hasTermPWC && strings.IndexByte(ln.suffix, tsep) < 0 { if !cb(append(pre, ln.suffix...), &ln.value) { return false } @@ -446,7 +445,7 @@ func (t *SubjectTree[T]) iter(n node, pre []byte, ordered bool, cb func(subject } } // Now sort. - slices.SortStableFunc(nodes, func(a, b node) int { return bytes.Compare(a.path(), b.path()) }) + slices.SortStableFunc(nodes, func(a, b node) int { return strings.Compare(a.path(), b.path()) }) // Now walk the nodes in order and call into next iter. for i := range nodes { if !t.iter(nodes[i], pre, true, cb) { @@ -541,3 +540,13 @@ func bytesToString(b []byte) string { p := unsafe.SliceData(b) return unsafe.String(p, len(b)) } + +// Note this will avoid a copy of the string data, but the returned slice must +// only be used for reading since strings are immutable. +func stringToBytes(s string) []byte { + if len(s) == 0 { + return nil + } + p := unsafe.StringData(s) + return unsafe.Slice(p, len(s)) +} diff --git a/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go b/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go index 8cb6224fec..820b7d481d 100644 --- a/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go +++ b/vendor/github.com/nats-io/nats-server/v2/server/stree/util.go @@ -32,16 +32,6 @@ func commonPrefixLen(s1, s2 []byte) int { return i } -// Helper to copy bytes. -func copyBytes(src []byte) []byte { - if len(src) == 0 { - return nil - } - dst := make([]byte, len(src)) - copy(dst, src) - return dst -} - type position interface{ int | uint16 } // No pivot available. diff --git a/vendor/modules.txt b/vendor/modules.txt index 34862c0e50..c7fe23c00c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -93,7 +93,7 @@ github.com/alexedwards/argon2id # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 ## explicit github.com/amoghe/go-crypt -# github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op +# github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op ## explicit; go 1.24.0 github.com/antithesishq/antithesis-sdk-go/assert github.com/antithesishq/antithesis-sdk-go/internal @@ -1146,7 +1146,7 @@ github.com/munnerz/goautoneg # github.com/nats-io/jwt/v2 v2.8.2 ## explicit; go 1.25.0 github.com/nats-io/jwt/v2 -# github.com/nats-io/nats-server/v2 v2.14.3 +# github.com/nats-io/nats-server/v2 v2.14.4 ## explicit; go 1.25.0 github.com/nats-io/nats-server/v2/conf github.com/nats-io/nats-server/v2/internal/fastrand From 333604181913177dde52746cd0ca5a12a2fcfd4e Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Mon, 3 Aug 2026 13:18:43 +0200 Subject: [PATCH 11/27] chore(skills): add skill for bumping reva (#3224) * chore(skills): add skill for bumping reva * chore(skills): derive OpenCloud version from open release PR * chore: exclude .agents from codacy analysis --- .agents/skills/bumping-reva/SKILL.md | 153 +++++++++++++++++++++++++++ .codacy.yml | 1 + 2 files changed, 154 insertions(+) create mode 100644 .agents/skills/bumping-reva/SKILL.md diff --git a/.agents/skills/bumping-reva/SKILL.md b/.agents/skills/bumping-reva/SKILL.md new file mode 100644 index 0000000000..154c59b9ac --- /dev/null +++ b/.agents/skills/bumping-reva/SKILL.md @@ -0,0 +1,153 @@ +--- +name: bumping-reva +description: Use when the user asks to bump, update, or upgrade the OpenCloud reva dependency to a specific version (e.g. "reva bump to 2.48.0", "bump reva to v2.48.0"). Covers editing go.mod, re-vendoring, bumping the OpenCloud LatestTag, the single commit, and opening the PR against main. +--- + +# Bumping Reva + +## Overview + +Bumping reva updates the pinned [opencloud-eu/reva](https://github.com/opencloud-eu/reva) dependency (`github.com/opencloud-eu/reva/v2`) to a tagged release, re-vendors the module graph, and bumps the OpenCloud dev version. It touches `go.mod`, `go.sum`, `vendor/**` and `pkg/version/version.go`, lands as one commit, and ships as a PR whose body is the reva changelog for that version. + +Template PR: https://github.com/opencloud-eu/opencloud/pull/3127 + +## Prerequisites + +- **The reva release must already be tagged.** A reva release is cut by merging its release PR (title `🎉 Release X.Y.Z`, branch `next-release/main`). The **user merges that PR themselves** — this skill starts *after* the tag `vX.Y.Z` exists. Verify the tag before doing anything (step 1); if it's missing, stop and ask the user to merge the reva release PR first. +- **`gh` (GitHub CLI), authenticated** — tag lookup and PR creation go through `gh api` / `gh pr`. Verify with `gh auth status`; if it fails, ask the user to run `gh auth login` (suggest `! gh auth login` so it runs in-session). +- `gh` needs the system keyring, so run all `gh` commands with the sandbox disabled. +- **Go toolchain + network** — `go get` / `go mod tidy` / `go mod vendor` hit the Go module proxy. Run them with the sandbox disabled (network access). +- `git` and `base64` (decoding the changelog) — standard on macOS/Linux. + +## Inputs + +- `REVA_VERSION` — the new reva tag, always normalized to a leading `v` (e.g. `v2.48.0`). If the user didn't give it, ask (or take the latest reva release tag). +- `OC_VERSION` — the OpenCloud target for `LatestTag`, **without** the `+dev` suffix (e.g. `7.4.0`). **Do not ask or guess** — derive it from the open OpenCloud release PR (step 3). It can be a new major (e.g. `8.0.0`) when release-please picked up a breaking change. + +## What changes + +| File | What | +| ------------------------ | ------------------------------------------------------------------- | +| `go.mod` | `github.com/opencloud-eu/reva/v2` → `REVA_VERSION` (+ indirect deps pulled by `go mod tidy`) | +| `go.sum` | updated by `go get` / `go mod tidy` | +| `vendor/**` | re-vendored by `go mod vendor` (incl. `vendor/modules.txt`) | +| `pkg/version/version.go` | `LatestTag = "+dev"` | + +## Procedure + +### 1. Verify the reva tag exists + +```bash +gh api repos/opencloud-eu/reva/commits/$REVA_VERSION --jq '.sha' +``` + +- Success (a sha) → the release is tagged, continue. +- `422`/`404` → the tag does not exist yet. The reva release PR (`🎉 Release X.Y.Z`) is probably not merged. **Stop** and tell the user to merge it first. Helpful checks: + ```bash + gh api repos/opencloud-eu/reva/releases/latest --jq '.tag_name' # current latest tag + gh pr list --repo opencloud-eu/reva --search '🎉 Release in:title' --state open --json number,title + ``` + +### 2. Bump the dependency and re-vendor + +```bash +go get github.com/opencloud-eu/reva/v2@$REVA_VERSION +go mod tidy +go mod vendor +``` + +(Sandbox disabled — these need network.) Notes: +- Right after a fresh tag the module proxy can lag; if `go get` reports the version as unknown, retry, or use `GOPROXY=direct go get github.com/opencloud-eu/reva/v2@$REVA_VERSION`. +- `go mod tidy` will also bump indirect dependencies that reva pulled in — that is expected (the template PR did the same). + +### 3. Bump the OpenCloud dev version + +`OC_VERSION` is the version of the **open OpenCloud release PR** — the release-please PR from branch `next-release/main`, titled `🎉 Release X.Y.Z` (e.g. #3143). Derive it, don't ask: + +```bash +gh pr list --repo opencloud-eu/opencloud --head next-release/main --state open \ + --json title --jq '.[0].title' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 +``` + +This is the next release target and tracks breaking changes — it may be a new major (e.g. `8.0.0`), not just a minor bump. If no such PR is open, stop and ask the user. + +Edit `pkg/version/version.go`: + +```go +LatestTag = "+dev" // e.g. "7.4.0+dev" +``` + +### 4. Fetch the reva changelog for the PR body + +```bash +gh api "repos/opencloud-eu/reva/contents/CHANGELOG.md?ref=$REVA_VERSION" --jq '.content' | base64 -d +``` + +Take **only the section for this version** and trim it exactly like the web bump: start at the first content heading (`### 🐛 Bug Fixes` / `### 📈 Enhancement` / `### 💥 Breaking changes`), drop the `# Changelog` title, the `## [x.y.z] - date` header and the `### ❤️ Thanks to all contributors!` block, and stop before the next `## [...]` version header. + +Then **prepend two summary bullets** so the final PR body is: + +``` +- bump opencloud version to +- reva bump + +### 🐛 Bug Fixes +...trimmed reva changelog... +``` + +Write this to a file for `--body-file` (e.g. in the scratchpad). + +### 5. Confirm before committing + +The `vendor/` diff is huge — do **not** dump it. Show the user: + +```bash +git diff go.mod pkg/version/version.go # the meaningful edits +git diff --stat | tail -1 # vendor churn summary +``` + +Confirm the reva line in `go.mod` is exactly `github.com/opencloud-eu/reva/v2 REVA_VERSION`, show the target branch (`main`), and the PR body. **Do not commit until the user approves.** + +### 6. Commit, push, open PR + +- Create a branch (do not commit on `main`), e.g. `reva-bump-2.48.0`. +- Stage everything the bump touched: `git add go.mod go.sum vendor pkg/version/version.go`. +- One commit, conventional-commits format, **empty body**: + ``` + chore: reva bump -2.48.0 + ``` +- PR base: **`main`** (reva bumps always target main). +- PR title: `[full-ci] chore: reva bump -2.48.0` (the commit message prefixed with `[full-ci] `). +- PR body: the file from step 4. +- Add the label `Type:Maintenance`. + +```bash +gh pr create --base main \ + --title "[full-ci] chore: reva bump -$REVA_VERSION_NO_V" \ + --label "Type:Maintenance" \ + --body-file +``` + +(`gh` commands need the sandbox disabled — they require the system keyring.) + +## Quick reference + +```bash +REVA_VERSION=v2.48.0 +gh api repos/opencloud-eu/reva/commits/$REVA_VERSION --jq '.sha' # verify tag exists +OC_VERSION=$(gh pr list --repo opencloud-eu/opencloud --head next-release/main --state open \ + --json title --jq '.[0].title' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) # from open release PR +go get github.com/opencloud-eu/reva/v2@$REVA_VERSION && go mod tidy && go mod vendor # bump + re-vendor +# edit pkg/version/version.go -> LatestTag = "$OC_VERSION+dev" +gh api "repos/opencloud-eu/reva/contents/CHANGELOG.md?ref=$REVA_VERSION" --jq '.content' | base64 -d # changelog +``` + +## Common mistakes + +- Running the bump before the reva release PR is merged — the tag won't exist and `go get` will fail. Verify the tag first (step 1). +- Forgetting to re-run `go mod vendor` after `go mod tidy`, leaving `vendor/` out of sync with `go.mod`. +- Asking for or hardcoding `OC_VERSION` — always derive it from the open `next-release/main` release PR (step 3); it can even be a new major after a breaking change. +- Reading the changelog from reva `main` instead of the tag (`?ref=$REVA_VERSION`). Always pin to the tag. +- Missing the `[full-ci] ` prefix in the PR title, the `Type:Maintenance` label, or the two summary bullets at the top of the body. +- Dumping the full `vendor/` diff at the confirmation step instead of `go.mod` + `version.go` + a `--stat` summary. +- Committing before the user confirms the diff. diff --git a/.codacy.yml b/.codacy.yml index a3a7c00220..959f2f3cea 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -1,6 +1,7 @@ --- exclude_paths: - '.github/**' + - '.agents/**' - 'CHANGELOG.md' - '**/CHANGELOG.md' - 'changelog/**' From c92eaea3e66026390fedb4a689d35a103e33994f Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Mon, 3 Aug 2026 11:45:03 +0200 Subject: [PATCH 12/27] chore(idp): bump dependencies --- services/idp/package.json | 20 +- services/idp/pnpm-lock.yaml | 1526 +++++++++++++++--------------- services/idp/pnpm-workspace.yaml | 11 +- 3 files changed, 790 insertions(+), 767 deletions(-) diff --git a/services/idp/package.json b/services/idp/package.json index dea8b42731..adfcc2ea8f 100644 --- a/services/idp/package.json +++ b/services/idp/package.json @@ -30,19 +30,19 @@ }, "dependencies": { "@material-ui/core": "^4.12.4", - "@types/node": "^25.7.0", - "@types/react": "^17.0.91", + "@types/node": "^25.9.5", + "@types/react": "^17.0.93", "@types/react-dom": "^17.0.26", "@types/react-redux": "^7.1.34", "@types/redux-logger": "^3.0.13", - "axios": "^1.18.1", - "i18next": "^26.3.0", + "axios": "^1.19.0", + "i18next": "^26.3.6", "i18next-browser-languagedetector": "^8.2.1", - "i18next-resources-to-backend": "^1.2.1", - "query-string": "^9.3.1", + "i18next-resources-to-backend": "^1.2.3", + "query-string": "^9.4.1", "react": "^17.0.2", "react-dom": "^17.0.2", - "react-i18next": "^17.0.8", + "react-i18next": "^17.0.11", "react-redux": "^8.1.3", "react-router": "^5.3.4", "react-router-dom": "5.2.1", @@ -61,9 +61,9 @@ "css-minimizer-webpack-plugin": "^8.0.0", "dotenv": "17.4.2", "dotenv-expand": "^13.0.0", - "gettext-parser": "^9.0.2", - "html-webpack-plugin": "^5.6.7", - "i18next-cli": "^1.65.0", + "gettext-parser": "^9.1.1", + "html-webpack-plugin": "^5.6.8", + "i18next-cli": "^1.67.3", "i18next-conv": "^17.0.0", "license-checker-rseidelsohn": "5.0.1", "mini-css-extract-plugin": "2.9.2", diff --git a/services/idp/pnpm-lock.yaml b/services/idp/pnpm-lock.yaml index 08671f908b..97220918c9 100644 --- a/services/idp/pnpm-lock.yaml +++ b/services/idp/pnpm-lock.yaml @@ -5,11 +5,14 @@ settings: excludeLinksFromLockfile: false overrides: - fast-uri: '>=3.1.2' - undici: '>=7.28.0' - postcss: '>=8.5.10' + brace-expansion: '>=5.0.7' + fast-uri: '>=3.1.4' + js-yaml: '>=4.3.0' + postcss: '>=8.5.18' serialize-javascript@<7.0.3: '>=7.0.3' - shell-quote: '>=1.8.4' + shell-quote: '>=1.9.0' + svgo: '>=4.0.2' + undici: '>=7.28.0' '@babel/plugin-transform-modules-systemjs': '>=7.29.4' '@xmldom/xmldom': ^0.8.13 @@ -19,16 +22,16 @@ importers: dependencies: '@material-ui/core': specifier: ^4.12.4 - version: 4.12.4(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 4.12.4(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) '@types/node': - specifier: ^25.7.0 - version: 25.7.0 + specifier: ^25.9.5 + version: 25.9.5 '@types/react': - specifier: ^17.0.91 - version: 17.0.91 + specifier: ^17.0.93 + version: 17.0.93 '@types/react-dom': specifier: ^17.0.26 - version: 17.0.26(@types/react@17.0.91) + version: 17.0.26(@types/react@17.0.93) '@types/react-redux': specifier: ^7.1.34 version: 7.1.34 @@ -36,20 +39,20 @@ importers: specifier: ^3.0.13 version: 3.0.13 axios: - specifier: ^1.18.1 - version: 1.18.1 + specifier: ^1.19.0 + version: 1.19.0 i18next: - specifier: ^26.3.0 - version: 26.3.4(typescript@6.0.3) + specifier: ^26.3.6 + version: 26.3.6(typescript@6.0.3) i18next-browser-languagedetector: specifier: ^8.2.1 version: 8.2.1 i18next-resources-to-backend: - specifier: ^1.2.1 - version: 1.2.1 + specifier: ^1.2.3 + version: 1.2.3 query-string: - specifier: ^9.3.1 - version: 9.3.1 + specifier: ^9.4.1 + version: 9.4.1 react: specifier: ^17.0.2 version: 17.0.2 @@ -57,11 +60,11 @@ importers: specifier: ^17.0.2 version: 17.0.2(react@17.0.2) react-i18next: - specifier: ^17.0.8 - version: 17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@6.0.3) + specifier: ^17.0.11 + version: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@6.0.3) react-redux: specifier: ^8.1.3 - version: 8.1.3(@types/react-dom@17.0.26(@types/react@17.0.91))(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1) + version: 8.1.3(@types/react-dom@17.0.26(@types/react@17.0.93))(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1) react-router: specifier: ^5.3.4 version: 5.3.4(react@17.0.2) @@ -109,14 +112,14 @@ importers: specifier: ^13.0.0 version: 13.0.0 gettext-parser: - specifier: ^9.0.2 - version: 9.0.2 + specifier: ^9.1.1 + version: 9.1.1 html-webpack-plugin: - specifier: ^5.6.7 - version: 5.6.7(webpack@5.105.2) + specifier: ^5.6.8 + version: 5.6.8(webpack@5.105.2) i18next-cli: - specifier: ^1.65.0 - version: 1.65.0(@types/node@25.7.0)(react-dom@17.0.2(react@17.0.2))(typescript@6.0.3) + specifier: ^1.67.3 + version: 1.67.3(@types/node@25.9.5)(react-dom@17.0.2(react@17.0.2))(typescript@6.0.3) i18next-conv: specifier: ^17.0.0 version: 17.0.0 @@ -131,16 +134,16 @@ importers: version: 1.7.0(typescript@6.0.3) postcss-flexbugs-fixes: specifier: 5.0.2 - version: 5.0.2(postcss@8.5.15) + version: 5.0.2(postcss@8.5.25) postcss-loader: specifier: 8.2.1 - version: 8.2.1(postcss@8.5.15)(typescript@6.0.3)(webpack@5.105.2) + version: 8.2.1(postcss@8.5.25)(typescript@6.0.3)(webpack@5.105.2) postcss-normalize: specifier: 13.0.1 - version: 13.0.1(browserslist@4.28.2)(postcss@8.5.15) + version: 13.0.1(browserslist@4.28.2)(postcss@8.5.25) postcss-preset-env: specifier: 11.2.0 - version: 11.2.0(postcss@8.5.15) + version: 11.2.0(postcss@8.5.25) react-dev-utils: specifier: ^12.0.1 version: 12.0.1(typescript@6.0.3)(webpack@5.105.2) @@ -188,6 +191,10 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.3': resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} engines: {node: '>=6.9.0'} @@ -271,10 +278,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -283,8 +298,8 @@ packages: resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} '@babel/parser@7.29.3': @@ -292,6 +307,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -824,6 +844,10 @@ packages: resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} @@ -832,6 +856,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@colordx/core@5.0.3': resolution: {integrity: sha512-xBQ0MYRTNNxW3mS2sJtlQTT7C3Sasqgh1/PsHva7fyDb5uqYY+gv9V0utDdX8X80mqzbGz3u/IDJdn2d/uW09g==} @@ -890,253 +918,253 @@ packages: resolution: {integrity: sha512-fti7+GybzvfMrv5TSU6x8rWtXWOth5nLefT5w5AKJ3F3T0bZoxlRqajF0ZUgTtnytfMd4dQ8n5UiaNmsjFA65A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-cascade-layers@6.0.0': resolution: {integrity: sha512-WhsECqmrEZQGqaPlBA7JkmF/CJ2/+wetL4fkL9sOPccKd32PQ1qToFM6gqSI5rkpmYqubvbxjEJhyMTHYK0vZQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-color-function-display-p3-linear@2.0.3': resolution: {integrity: sha512-u8QNV2TKOxG6cqK4ZrJkpctnxdrwdNTMrkyokmCi+iuLpJegOraA0cqC7HoxF2tHhxjuXc+BxwY/Qd62SwvanQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-color-function@5.0.3': resolution: {integrity: sha512-BiBukIeQ7rPjx9A//9+qgJugBjX6FY9eWiojbnfIJCPulWrl8J07rCgQbFkloTXena+a6Aw5xa25weU+3MA75A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-color-mix-function@4.0.3': resolution: {integrity: sha512-M8ju3iqHRXtW1/5HYuOmi9WFR5rGGFgqkPh+kXkv/eG56oYK/WYtTeIwJgdcro7lRwjlo4Ut8xqbV3Iovkwfrw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-color-mix-variadic-function-arguments@2.0.3': resolution: {integrity: sha512-tL46UyFjIjz7mDywoPOe/JgOpvMic0rsTUfdMBB1OHrUcCtE8MQpBILzYl/cAOtinJGu+ZQLuDhqTgTBOoeg3g==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-content-alt-text@3.0.0': resolution: {integrity: sha512-OHa+4aCcrJtHpPWB3zptScHwpS1TUbeLR4uO0ntIz0Su/zw9SoWkVu+tDMSySSAsNtNSI3kut4fTliFwIsrHxA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-contrast-color-function@3.0.3': resolution: {integrity: sha512-YcohXq+/hfYeobKirg3oXGivDaaTfOPv568bE3jYQCn9ILpFz+RgyJR/kF7ZWh5560TTlTjeCqF4ZmVsj2zwnw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-exponential-functions@3.0.2': resolution: {integrity: sha512-WDrfdFJXF4M67+wniEGr/5XVzsmn1rt2lL1YAlTfE7x7XDlRstTc5e+HuFoGv6jkiMWTwPsiADJaLwsnGC3UjQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-font-format-keywords@5.0.0': resolution: {integrity: sha512-M1EjCe/J3u8fFhOZgRci74cQhJ7R0UFBX6T+WqoEvjrr8hVfMiV+HTYrzxLY5OW8YllvXYr5Q5t5OvJbsUSeDg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-font-width-property@1.0.0': resolution: {integrity: sha512-AvmySApdijbjYQuXXh95tb7iVnqZBbJrv3oajO927ksE/mDmJBiszm+psW8orL2lRGR8j6ZU5Uv9/ou2Z5KRKA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-gamut-mapping@3.0.3': resolution: {integrity: sha512-3v5ZvcVuynhFh5qCJX2LIJ9Iry8/SvxfOEj6vDngNxbH/3OKTZBFLgK+DgLuIbsP1DLA9LLH3Rn7jmRxXgEDLA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-gradients-interpolation-method@6.0.3': resolution: {integrity: sha512-wrRIaRv1dkq30a8nvYWtSAf41bwCl+sVzLBKGnqeOwk81aSktKN3NattJpkiPyoOtEoFqChisl3WH3Csj/rOsw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-hwb-function@5.0.3': resolution: {integrity: sha512-bHz0uc/PBg2wJEAlGinUf494nMyuXsVKH/fExc2xGkvL6WHOKlxzx/lkn+2AVCQACtWBLVRCBDgDnkYr4RSC9w==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-ic-unit@5.0.0': resolution: {integrity: sha512-/ws5d6c4uKqfM9zIL3ugcGI+3fvZEOOkJHNzAyTAGJIdZ+aSL9BVPNlHGV4QzmL0vqBSCOdU3+rhcMEj3+KzYw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-initial@3.0.0': resolution: {integrity: sha512-UVUrFmrTQyLomVepnjWlbBg7GoscLmXLwYFyjbcEnmpeGW7wde6lNpx5eM3eVwZI2M+7hCE3ykYnAsEPLcLa+Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-is-pseudo-class@6.0.0': resolution: {integrity: sha512-1Hdy/ykg9RDo8vU8RiM2o+RaXO39WpFPaIkHxlAEJFofle/lc33tdQMKhBk3jR/Fe+uZNLOs3HlowFafyFptVw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-light-dark-function@3.0.0': resolution: {integrity: sha512-s++V5/hYazeRUCYIn2lsBVzUsxdeC46gtwpgW6lu5U/GlPOS5UTDT14kkEyPgXmFbCvaWLREqV7YTMJq1K3G6w==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-logical-float-and-clear@4.0.0': resolution: {integrity: sha512-NGzdIRVj/VxOa/TjVdkHeyiJoDihONV0+uB0csUdgWbFFr8xndtfqK8iIGP9IKJzco+w0hvBF2SSk2sDSTAnOQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-logical-overflow@3.0.0': resolution: {integrity: sha512-5cRg93QXVskM0MNepHpPcL0WLSf5Hncky0DrFDQY/4ozbH5lH7SX5ejayVpNTGSX7IpOvu7ykQDLOdMMGYzwpA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-logical-overscroll-behavior@3.0.0': resolution: {integrity: sha512-82Jnl/5Wi5jb19nQE1XlBHrZcNL3PzOgcj268cDkfwf+xi10HBqufGo1Unwf5n8bbbEFhEKgyQW+vFsc9iY1jw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-logical-resize@4.0.0': resolution: {integrity: sha512-L0T3q0gei/tGetCGZU0c7VN77VTivRpz1YZRNxjXYmW+85PKeI6U9YnSvDqLU2vBT2uN4kLEzfgZ0ThIZpN18A==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-logical-viewport-units@4.0.0': resolution: {integrity: sha512-TA3AqVN/1IH3dKRC2UUWvprvwyOs2IeD7FDZk5Hz20w4q33yIuSg0i0gjyTUkcn90g8A4n7QpyZ2AgBrnYPnnA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-media-minmax@3.0.2': resolution: {integrity: sha512-+ABxs2ZhJDhy+B9PJg7pgkGq6/d3XPXsWl7+6yZfAk4b2ba6aQ1h2AiTn04XwS6rpMpZEF3tONli/ubfu4y8AQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0': resolution: {integrity: sha512-FDdC3lbrj8Vr0SkGIcSLTcRB7ApG6nlJFxOxkEF2C5hIZC1jtgjISFSGn/WjFdVkn8Dqe+Vx9QXI3axS2w1XHw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-mixins@1.0.0': resolution: {integrity: sha512-rz6qjT2w9L3k65jGc2dX+3oGiSrYQ70EZPDrINSmSVoVys7lLBFH0tvEa8DW2sr9cbRVD/W+1sy8+7bfu0JUfg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-nested-calc@5.0.0': resolution: {integrity: sha512-aPSw8P60e/i9BEfugauhikBqgjiwXcw3I9o4vXs+hktl4NSTgZRI0QHimxk9mst8N01A2TKDBxOln3mssRxiHQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-normalize-display-values@5.0.1': resolution: {integrity: sha512-FcbEmoxDEGYvm2W3rQzVzcuo66+dDJjzzVDs+QwRmZLHYofGmMGwIKPqzF86/YW+euMDa7sh1xjWDvz/fzByZQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-oklab-function@5.0.3': resolution: {integrity: sha512-vTMgJFMwMt9gnPvhKaDnMR7E/h9Nb+rPUv825SY5VUo4PWj+w0OH/N2NqgvjYeubaA3BVckbKDlvADATRpD4Hw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-position-area-property@2.0.0': resolution: {integrity: sha512-TeEfzsJGB23Syv7yCm8AHCD2XTFujdjr9YYu9ebH64vnfCEvY4BG319jXAYSlNlf3Yc9PNJ6WnkDkUF5XVgSKQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-progressive-custom-properties@5.0.0': resolution: {integrity: sha512-NsJoZ89rxmDrUsITf8QIk5w+lQZQ8Xw5K6cLFG+cfiffsLYHb3zcbOOrHLetGl1WIhjWWQ4Cr8MMrg46Q+oACg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-property-rule-prelude-list@2.0.0': resolution: {integrity: sha512-qcMAkc9AhpzHgmQCD8hoJgGYifcOAxd1exXjjxilMM6euwRE619xDa4UsKBCv/v4g+sS63sd6c29LPM8s2ylSQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-random-function@3.0.2': resolution: {integrity: sha512-iQ3vfX1LIqRXX7P1/ol45EpJ5CTWdQCAfdpTlHlsRPU4jMQeepmeNjQ0F60bj8RWTS1RkJ318fzzq4mUlyZ7hA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-relative-color-syntax@4.0.3': resolution: {integrity: sha512-SZSImz4KufmLi0dRwYivWXlza+7HF84SRApY8R48SyWgn+f0gDvmCn7D2Ie4CED7qU0JJK+YfCUC1HVlaQ10dg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-scope-pseudo-class@5.0.0': resolution: {integrity: sha512-kBrBFJcAji3MSHS4qQIihPvJfJC5xCabXLbejqDMiQi+86HD4eMBiTayAo46Urg7tlEmZZQFymFiJt+GH6nvXw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-sign-functions@2.0.2': resolution: {integrity: sha512-vOxkkMCMVnyaj7CW03uKR2R/zhJaCrptsXlm31HgI/dqC1lSIGnmu5W7N68x23XwcSgc8fE/fg0jKj4x1XFH4w==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-stepped-value-functions@5.0.2': resolution: {integrity: sha512-4PtqkRoBcMSxZG00gcDv+nq7cxVUua+Yd7TmG16qzJjdolyICHkx1RfhNL5mKSnWOLxUnk/IdxAoWN+KU7E/ng==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0': resolution: {integrity: sha512-elYcbdiBXAkPqvojB9kIBRuHY6htUhjSITtFQ+XiXnt6SvZCbNGxQmaaw6uZ7SPHu/+i/XVjzIt09/1k3SIerQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-system-ui-font-family@2.0.0': resolution: {integrity: sha512-FyGZCgchFImFyiHS2x3rD5trAqatf/x23veBLTIgbaqyFfna6RNBD+Qf8HRSjt6HGMXOLhAjxJ3OoZg0bbn7Qw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-text-decoration-shorthand@5.0.3': resolution: {integrity: sha512-62fjggvIM1YYfDJPcErMUDkEZB6CByG8neTJqexnZe1hRBgCjD4dnXDLoCSSurjs1LzjBq6irFDpDaOvDZfrlw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-trigonometric-functions@5.0.2': resolution: {integrity: sha512-hRansZmQk1HH11WGUNlWy8H/DCB9Wy6zDbRcyBfF2UUP+V2fubK+qwmq0q6LIDje5gRzxlKyWhgFYxPy1ohivA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/postcss-unset-value@5.0.0': resolution: {integrity: sha512-EoO54sS2KCIfesvHyFYAW99RtzwHdgaJzhl7cqKZSaMYKZv3fXSOehDjAQx8WZBKn1JrMd7xJJI1T1BxPF7/jA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@csstools/selector-resolve-nested@4.0.0': resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} @@ -1154,7 +1182,7 @@ packages: resolution: {integrity: sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' '@emotion/hash@0.8.0': resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} @@ -1817,8 +1845,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.7.0': - resolution: {integrity: sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==} + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1839,8 +1867,8 @@ packages: peerDependencies: '@types/react': '*' - '@types/react@17.0.91': - resolution: {integrity: sha512-xauZca6qMeCU3Moy0KxCM9jtf1vyk6qRYK39Ryf3afUqwgNUjRIGoDdS9BcGWgAMGSg1hvP4XcmlYrM66PtqeA==} + '@types/react@17.0.93': + resolution: {integrity: sha512-KM4Ty/ZTLZupiYxZVAlP+InNJS3De6uBMdq0ePa6/04+eG9Y7ftnWfst1xTLQ5rwAhgHwQ4momt/O4KepdGBTw==} '@types/redux-logger@3.0.13': resolution: {integrity: sha512-jylqZXQfMxahkuPcO8J12AKSSCQngdEWQrw7UiLUJzMBcv1r4Qg77P6mjGLjM27e5gFQDPD8vwUMJ9AyVxFSsg==} @@ -2039,14 +2067,14 @@ packages: engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.18.1: - resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + axios@1.19.0: + resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==} babel-loader@10.0.0: resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==} @@ -2090,9 +2118,6 @@ packages: babel-preset-react-app@10.1.0: resolution: {integrity: sha512-f9B1xMdnkCIqe+2dHrJsoQFRz7reChaAHE/65SdaykPklQqhme2WaC08oD3is77x9ff98/9EazAKFDZv5rFEQg==} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -2116,15 +2141,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -2277,9 +2296,6 @@ packages: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -2322,19 +2338,19 @@ packages: resolution: {integrity: sha512-C5B2e5hCM4llrQkUms+KnWEMVW8K1n2XvX9G7ppfMZJQ7KAS/4rNnkP1Cs+HhWriOz1mWWTMFD4j1J7s31Dgug==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' css-declaration-sorter@7.4.0: resolution: {integrity: sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==} engines: {node: ^14 || ^16 || >=18} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' css-has-pseudo@8.0.0: resolution: {integrity: sha512-Uz/bsHRbOeir/5Oeuz85tq/yLJLxX+3dpoRdjNTshs6jjqwUg8XaEZGDd0ci3fw7l53Srw0EkJ8mYan0eW5uGQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' css-loader@7.1.4: resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==} @@ -2377,7 +2393,7 @@ packages: resolution: {integrity: sha512-fv0mgtwUhh2m9iio3Kxc2CkrogjIaRdMFaaqyzSFdii17JF4cfPyMNX72B15ZW2Nrr/NZUpxI4dec1VMHYJvdw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} @@ -2412,19 +2428,19 @@ packages: resolution: {integrity: sha512-B3Eoouzw/sl2zANI0AL9KbacummJTCww+fkHaDBMZad/xuVx8bUduPLly6hKVQAlrmvYkS1jB1CVQEKm3gn0AA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' cssnano-utils@5.0.1: resolution: {integrity: sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' cssnano@7.1.4: resolution: {integrity: sha512-T9PNS7y+5Nc9Qmu9mRONqfxG1RVY7Vuvky0XN6MZ+9hqplesTEwnj9r0ROtVuSwUVfaDhVlavuzWIVLUgm4hkQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} @@ -2687,8 +2703,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@4.1.2: + resolution: {integrity: sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -2837,8 +2853,8 @@ packages: gettext-converter@1.3.3: resolution: {integrity: sha512-geN4Vc0T45a59rkUdjUYpu4PgpKdAcQ8bZPIAwNLw+ygYAahA7Ky/vwmVZ8v2szwQjHSRJDCuV8RG16PsE8a+Q==} - gettext-parser@9.0.2: - resolution: {integrity: sha512-dGvq3S1gpS6e9KzNkwgPED5xxfWk7mNYzzdi/fPdJF5qS7B+yo8El2ZQyyhJ79PyzTtHbwiqYOFsqBdzbQ0GPg==} + gettext-parser@9.1.1: + resolution: {integrity: sha512-ZLeqWPz9OMNrTgMuww0C22kkcNqis+e4059R94t7L7ERlZ2rUNpiDbaAUus+esBC6uBAQWbS9N+R5vJIJg//lw==} engines: {node: '>=20'} glob-parent@5.1.2: @@ -2938,14 +2954,14 @@ packages: engines: {node: '>=12'} hasBin: true - html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} + html-parse-stringify@4.0.1: + resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} - html-webpack-plugin@5.6.7: - resolution: {integrity: sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==} + html-webpack-plugin@5.6.8: + resolution: {integrity: sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==} engines: {node: '>=10.13.0'} peerDependencies: - '@rspack/core': 0.x || 1.x + '@rspack/core': 0.x || 1.x || 2.x webpack: ^5.20.0 peerDependenciesMeta: '@rspack/core': @@ -2981,8 +2997,8 @@ packages: i18next-browser-languagedetector@8.2.1: resolution: {integrity: sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==} - i18next-cli@1.65.0: - resolution: {integrity: sha512-sak+2Ry4P7wtl7xMAZg2sWG2vup1lRHFBKA7h5IeEqFUog51QEgeYUhHxd2x85+MvS4BhVOZZs833clnd1WgYA==} + i18next-cli@1.67.3: + resolution: {integrity: sha512-kW5Hq0kzOGoZQCxOLqtrcj4NqIGmQJkBr5c78XQz3WqBgZuJ1PzvLEEjoXdyDkX1IwPO19OTEv3sOeCCWU+Igg==} engines: {node: '>=22'} hasBin: true @@ -2995,13 +3011,13 @@ packages: resolution: {integrity: sha512-n5UexwEVt0OoIAhG2MWpSnAVJW1U8mQrQTmXyxc5DMAx+NLhcLZhSMJo/FnUsA5JQ3obTYqTgB7YIuZKWpDgow==} hasBin: true - i18next-resources-to-backend@1.2.1: - resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} + i18next-resources-to-backend@1.2.3: + resolution: {integrity: sha512-8Y/LLAm5fqZc2ckQxtTWbu75ndjNLzF7mcYl6rhc4g+WBopIsG3YhdYqF5qhJy64tlYhlU2KSSnEgW3gGHLqwg==} - i18next@26.3.4: - resolution: {integrity: sha512-pa7m0d7pBDqGHZxljT+WPFeyFgQ7P7SciPPo1tTqYuO0z4sqADYhwnBESmmGp/wEof1inwdls/k8ZgTg8rxFHA==} + i18next@26.3.6: + resolution: {integrity: sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==} peerDependencies: - typescript: ^5 || ^6 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: typescript: optional: true @@ -3018,7 +3034,7 @@ packages: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -3272,8 +3288,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@5.2.3: + resolution: {integrity: sha512-n+mUVyUX5bVv7G/G2zyIHOhdxfuU1dY2NOFzTQUWiMUbFss8b57NFlgCCaggU78wSw5KVS9cllzeLyzyR+n5nw==} hasBin: true jsesc@3.1.0: @@ -3554,8 +3570,8 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -3795,157 +3811,157 @@ packages: resolution: {integrity: sha512-fovIPEV35c2JzVXdmP+sp2xirbBMt54J+upU8u6TSj410kUU5+axgEzvBBSAX8KCybze8CFCelzFAw/FfWg2TA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-browser-comments@6.0.2: resolution: {integrity: sha512-bZFLM8UZupVsuZDR4zFbzrPtKN6Xqpgj+C+vaxlL8r5E0cyhSO4OD3z+MjKstoQsIaKiQS+/Xci5jBUGyo9HlA==} engines: {node: '>=18'} peerDependencies: browserslist: ^4.28.0 - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-calc@10.1.1: resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} engines: {node: ^18.12 || ^20.9 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-clamp@4.1.0: resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} engines: {node: '>=7.6.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-color-functional-notation@8.0.3: resolution: {integrity: sha512-MyaFK+3PusD7F2+qlMDP6+zfSgHWP17AtmvHQs44W3+Qbb39VptVDVRJ4Lf7gHSVffW5ekEy/XrsZ0S0t34hrA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-color-hex-alpha@11.0.0: resolution: {integrity: sha512-NCGa6vjIyrjosz9GqRxVKbONBklz5TeipYqTJp3IqbnBWlBq5e5EMtG6MaX4vqk9LzocPfMQkuRK9tfk+OQuKg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-color-rebeccapurple@11.0.0: resolution: {integrity: sha512-g9561mx7cbdqx7XeO/L+lJzVlzu7bICyXr72efBVKZGxIhvBBJf9fGXn3Cb6U4Bwh3LbzQO2e9NWBLVYdX5Eag==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-colormin@7.0.7: resolution: {integrity: sha512-sBQ628lSj3VQpDquQel8Pen5mmjFPsO4pH9lDLaHB1AVkMRHtkl0pRB5DCWznc9upWsxint/kV+AveSj7W1tew==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-convert-values@7.0.9: resolution: {integrity: sha512-l6uATQATZaCa0bckHV+r6dLXfWtUBKXxO3jK+AtxxJJtgMPD+VhhPCCx51I4/5w8U5uHV67g3w7PXj+V3wlMlg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-custom-media@12.0.1: resolution: {integrity: sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-custom-properties@15.0.1: resolution: {integrity: sha512-cuyq8sd8dLY0GLbelz1KB8IMIoDECo6RVXMeHeXY2Uw3Q05k/d1GVITdaKLsheqrHbnxlwxzSRZQQ5u+rNtbMg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-custom-selectors@9.0.1: resolution: {integrity: sha512-2XBELy4DmdVKimChfaZ2id9u9CSGYQhiJ53SvlfBvMTzLMW2VxuMb9rHsMSQw9kRq/zSbhT5x13EaK8JSmK8KQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-dir-pseudo-class@10.0.0: resolution: {integrity: sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-discard-comments@7.0.6: resolution: {integrity: sha512-Sq+Fzj1Eg5/CPf1ERb0wS1Im5cvE2gDXCE+si4HCn1sf+jpQZxDI4DXEp8t77B/ImzDceWE2ebJQFXdqZ6GRJw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-discard-duplicates@7.0.2: resolution: {integrity: sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-discard-empty@7.0.1: resolution: {integrity: sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-discard-overridden@7.0.1: resolution: {integrity: sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-double-position-gradients@7.0.0: resolution: {integrity: sha512-Msr/dxj8Os7KLJE5Hdhvprwm3K5Zrh1KTY0eFN3ngPKNkej/Usy4BM9JQmqE6CLAkDpHoQVsi4snbL72CPt6qg==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-flexbugs-fixes@5.0.2: resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-focus-visible@11.0.0: resolution: {integrity: sha512-VG1a9kBKizUBWS66t5xyB4uLONBnvZLCmZXxT40FALu8EF0QgVZBYy5ApC0KhmpHsv+pvHMJHB3agKHwmocWjw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-focus-within@10.0.0: resolution: {integrity: sha512-dvql0fzUTG+gcJYp+KTbag5vAjuo94LDYZHkqDV1rnf5gPGer1v/SrmIZBdvKU8moep3HbcbujqGjzSb3DL53Q==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-font-variant@5.0.0: resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-gap-properties@7.0.0: resolution: {integrity: sha512-PSDF2QoZMRUbsINvXObQgxx4HExRP85QTT8qS/YN9fBsCPWCqUuwqAD6E6PNp0BqL/jU1eyWUBORaOK/J/9LDA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-image-set-function@8.0.0: resolution: {integrity: sha512-rEGNkOkNusf4+IuMmfEoIdLuVmvbExGbmG+MIsyV6jR5UaWSoyPcAYHV/PxzVDCmudyF+2Nh/o6Ub2saqUdnuA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-lab-function@8.0.3: resolution: {integrity: sha512-rUa27RLVXjMn1aDkHEt5dRsK80+bAACPr8w5Ow0BkIlfH6gEk0Mh1I0REkYhtp4UhKFw1HLEk3AzvKBi6BGOqw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-loader@8.2.1: resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 - postcss: '>=8.5.10' + postcss: '>=8.5.18' webpack: ^5.0.0 peerDependenciesMeta: '@rspack/core': @@ -3957,198 +3973,198 @@ packages: resolution: {integrity: sha512-A4LNd9dk3q/juEUA9Gd8ALhBO3TeOeYurnyHLlf2aAToD94VHR8c5Uv7KNmf8YVRhTxvWsyug4c5fKtARzyIRQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-merge-longhand@7.0.5: resolution: {integrity: sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-merge-rules@7.0.8: resolution: {integrity: sha512-BOR1iAM8jnr7zoQSlpeBmCsWV5Uudi/+5j7k05D0O/WP3+OFMPD86c1j/20xiuRtyt45bhxw/7hnhZNhW2mNFA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-minify-font-values@7.0.1: resolution: {integrity: sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-minify-gradients@7.0.2: resolution: {integrity: sha512-fVY3AB8Um7SJR5usHqTY2Ngf9qh8IRN+FFzrBP0ONJy6yYXsP7xyjK2BvSAIrpgs1cST+H91V0TXi3diHLYJtw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-minify-params@7.0.6: resolution: {integrity: sha512-YOn02gC68JijlaXVuKvFSCvQOhTpblkcfDre2hb/Aaa58r2BIaK4AtE/cyZf2wV7YKAG+UlP9DT+By0ry1E4VQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-minify-selectors@7.0.6: resolution: {integrity: sha512-lIbC0jy3AAwDxEgciZlBullDiMBeBCT+fz5G8RcA9MWqh/hfUkpOI3vNDUNEZHgokaoiv0juB9Y8fGcON7rU/A==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-modules-extract-imports@3.1.0: resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-modules-local-by-default@4.2.0: resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-modules-scope@3.2.1: resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-modules-values@4.0.0: resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-nesting@14.0.0: resolution: {integrity: sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-charset@7.0.1: resolution: {integrity: sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-display-values@7.0.1: resolution: {integrity: sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-positions@7.0.1: resolution: {integrity: sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-repeat-style@7.0.1: resolution: {integrity: sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-string@7.0.1: resolution: {integrity: sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-timing-functions@7.0.1: resolution: {integrity: sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-unicode@7.0.6: resolution: {integrity: sha512-z6bwTV84YW6ZvvNoaNLuzRW4/uWxDKYI1iIDrzk6D2YTL7hICApy+Q1LP6vBEsljX8FM7YSuV9qI79XESd4ddQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-url@7.0.1: resolution: {integrity: sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize-whitespace@7.0.1: resolution: {integrity: sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-normalize@13.0.1: resolution: {integrity: sha512-oGfXG7IQ44FUIMFco2N2Uz71UotM+tZ9trEmT1bHIUR5gAplyG3RnHqpMDEcCx1r+1bwBJTrI5uhiQr4YOpqhQ==} engines: {node: '>= 18'} peerDependencies: browserslist: '>= 4' - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-opacity-percentage@3.0.0: resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} engines: {node: '>=18'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-ordered-values@7.0.2: resolution: {integrity: sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-overflow-shorthand@7.0.0: resolution: {integrity: sha512-9SLpjoUdGRoRrzoOdX66HbUs0+uDwfIAiXsRa7piKGOqPd6F4ZlON9oaDSP5r1Qpgmzw5L9Ht0undIK6igJPMA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-page-break@3.0.4: resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-place@11.0.0: resolution: {integrity: sha512-fAifpyjQ+fuDRp2nmF95WbotqbpjdazebedahXdfBxy5sHembOLpBQ1cHveZD9ZmjK26tYM8tikeNaUlp/KfHA==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-preset-env@11.2.0: resolution: {integrity: sha512-eNYpuj68cjGjvZMoSAbHilaCt3yIyzBL1cVuSGJfvJewsaBW/U6dI2bqCJl3iuZsL+yvBobcy4zJFA/3I68IHQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-pseudo-class-any-link@11.0.0: resolution: {integrity: sha512-DNFZ4GMa3C3pU5dM+UCTG1CEeLtS1ZqV5DKSqCTJQMn1G5jnd/30fS8+A7H4o5bSD3MOcnx+VgI+xPE9Z5Wvig==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-reduce-initial@7.0.6: resolution: {integrity: sha512-G6ZyK68AmrPdMB6wyeA37ejnnRG2S8xinJrZJnOv+IaRKf6koPAVbQsiC7MfkmXaGmF1UO+QCijb27wfpxuRNg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-reduce-transforms@7.0.1: resolution: {integrity: sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-replace-overflow-wrap@4.0.0: resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-selector-not@9.0.0: resolution: {integrity: sha512-xhAtTdHnVU2M/CrpYOPyRUvg3njhVlKmn2GNYXDaRJV9Ygx4d5OkSkc7NINzjUqnbDFtaKXlISOBeyMXU/zyFQ==} engines: {node: '>=20.19.0'} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} @@ -4158,19 +4174,19 @@ packages: resolution: {integrity: sha512-zU9H9oEDrUFKa0JB7w+IYL7Qs9ey1mZyjhbf0KLxwJDdDRtoPvCmaEfknzqfHj44QS9VD6c5sJnBAVYTLRg/Sg==} engines: {node: ^18.12.0 || ^20.9.0 || >= 18} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-unique-selectors@7.0.5: resolution: {integrity: sha512-3QoYmEt4qg/rUWDn6Tc8+ZVPmbp4G1hXDtCNWDx0st8SjtCbRcxRXDDM1QrEiXGG3A45zscSJFb4QH90LViyxg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} engines: {node: ^10 || ^12 || >=14} pretty-bytes@5.6.0: @@ -4213,8 +4229,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - query-string@9.3.1: - resolution: {integrity: sha512-5fBfMOcDi5SA9qj5jZhWAcTtDfKF5WFdd2uD9nVNlbxVv1baq65aALy6qofpNEGELHvisjjasxQp7BlM9gvMzw==} + query-string@9.4.1: + resolution: {integrity: sha512-lSyJeN3RuaG7DZGWThtYRhk96+kEyZ/+doZpERuWbjeFL+Ok3vEat/swU498rAI0NcVt5/RJp8UDuLz7FckxrA==} engines: {node: '>=18'} queue-microtask@1.2.3: @@ -4238,14 +4254,14 @@ packages: react-error-overlay@6.1.0: resolution: {integrity: sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ==} - react-i18next@17.0.8: - resolution: {integrity: sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==} + react-i18next@17.0.11: + resolution: {integrity: sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==} peerDependencies: i18next: '>= 26.2.0' react: '>= 16.8.0' react-dom: '*' react-native: '*' - typescript: ^5 || ^6 + typescript: ^5 || ^6 || ^7 peerDependenciesMeta: react-dom: optional: true @@ -4519,8 +4535,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.4: - resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} side-channel-list@1.0.1: @@ -4686,7 +4702,7 @@ packages: resolution: {integrity: sha512-I3f053GBLIiS5Fg6OMFhq/c+yW+5Hc2+1fgq7gElDMMSqwlRb3tBf2ef6ucLStYRpId4q//bQO1FjcyNyy4yDQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: - postcss: '>=8.5.10' + postcss: '>=8.5.18' supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -4700,8 +4716,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svgo@4.0.1: - resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} + svgo@4.0.2: + resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} engines: {node: '>=16'} hasBin: true @@ -4834,8 +4850,8 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@7.21.0: - resolution: {integrity: sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} undici@8.5.0: resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} @@ -4913,10 +4929,6 @@ packages: value-equal@1.0.1: resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==} - void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -5112,6 +5124,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.29.3': {} '@babel/core@7.26.10': @@ -5121,7 +5139,7 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.26.10) - '@babel/helpers': 7.29.2 + '@babel/helpers': 7.29.7 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 @@ -5243,8 +5261,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.6': @@ -5255,15 +5277,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helpers@7.29.2': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.3': dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -5929,6 +5955,12 @@ snapshots: '@babel/parser': 7.29.3 '@babel/types': 7.29.0 + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -5946,6 +5978,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@colordx/core@5.0.3': {} '@croct/json5-parser@0.2.2': @@ -5986,283 +6023,283 @@ snapshots: '@csstools/normalize.css@12.1.1': {} - '@csstools/postcss-alpha-function@2.0.4(postcss@8.5.15)': + '@csstools/postcss-alpha-function@2.0.4(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-cascade-layers@6.0.0(postcss@8.5.15)': + '@csstools/postcss-cascade-layers@6.0.0(postcss@8.5.25)': dependencies: '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - '@csstools/postcss-color-function-display-p3-linear@2.0.3(postcss@8.5.15)': + '@csstools/postcss-color-function-display-p3-linear@2.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-color-function@5.0.3(postcss@8.5.15)': + '@csstools/postcss-color-function@5.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-color-mix-function@4.0.3(postcss@8.5.15)': + '@csstools/postcss-color-mix-function@4.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-color-mix-variadic-function-arguments@2.0.3(postcss@8.5.15)': + '@csstools/postcss-color-mix-variadic-function-arguments@2.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-content-alt-text@3.0.0(postcss@8.5.15)': + '@csstools/postcss-content-alt-text@3.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-contrast-color-function@3.0.3(postcss@8.5.15)': + '@csstools/postcss-contrast-color-function@3.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-exponential-functions@3.0.2(postcss@8.5.15)': + '@csstools/postcss-exponential-functions@3.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-font-format-keywords@5.0.0(postcss@8.5.15)': + '@csstools/postcss-font-format-keywords@5.0.0(postcss@8.5.25)': dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-font-width-property@1.0.0(postcss@8.5.15)': + '@csstools/postcss-font-width-property@1.0.0(postcss@8.5.25)': dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-gamut-mapping@3.0.3(postcss@8.5.15)': + '@csstools/postcss-gamut-mapping@3.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-gradients-interpolation-method@6.0.3(postcss@8.5.15)': + '@csstools/postcss-gradients-interpolation-method@6.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-hwb-function@5.0.3(postcss@8.5.15)': + '@csstools/postcss-hwb-function@5.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-ic-unit@5.0.0(postcss@8.5.15)': + '@csstools/postcss-ic-unit@5.0.0(postcss@8.5.25)': dependencies: - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-initial@3.0.0(postcss@8.5.15)': + '@csstools/postcss-initial@3.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-is-pseudo-class@6.0.0(postcss@8.5.15)': + '@csstools/postcss-is-pseudo-class@6.0.0(postcss@8.5.25)': dependencies: '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - '@csstools/postcss-light-dark-function@3.0.0(postcss@8.5.15)': + '@csstools/postcss-light-dark-function@3.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-logical-float-and-clear@4.0.0(postcss@8.5.15)': + '@csstools/postcss-logical-float-and-clear@4.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-logical-overflow@3.0.0(postcss@8.5.15)': + '@csstools/postcss-logical-overflow@3.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-logical-overscroll-behavior@3.0.0(postcss@8.5.15)': + '@csstools/postcss-logical-overscroll-behavior@3.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-logical-resize@4.0.0(postcss@8.5.15)': + '@csstools/postcss-logical-resize@4.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-logical-viewport-units@4.0.0(postcss@8.5.15)': + '@csstools/postcss-logical-viewport-units@4.0.0(postcss@8.5.25)': dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-media-minmax@3.0.2(postcss@8.5.15)': + '@csstools/postcss-media-minmax@3.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0(postcss@8.5.15)': + '@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-mixins@1.0.0(postcss@8.5.15)': + '@csstools/postcss-mixins@1.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-nested-calc@5.0.0(postcss@8.5.15)': + '@csstools/postcss-nested-calc@5.0.0(postcss@8.5.25)': dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-normalize-display-values@5.0.1(postcss@8.5.15)': + '@csstools/postcss-normalize-display-values@5.0.1(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-oklab-function@5.0.3(postcss@8.5.15)': + '@csstools/postcss-oklab-function@5.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-position-area-property@2.0.0(postcss@8.5.15)': + '@csstools/postcss-position-area-property@2.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-progressive-custom-properties@5.0.0(postcss@8.5.15)': + '@csstools/postcss-progressive-custom-properties@5.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-property-rule-prelude-list@2.0.0(postcss@8.5.15)': + '@csstools/postcss-property-rule-prelude-list@2.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-random-function@3.0.2(postcss@8.5.15)': + '@csstools/postcss-random-function@3.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-relative-color-syntax@4.0.3(postcss@8.5.15)': + '@csstools/postcss-relative-color-syntax@4.0.3(postcss@8.5.25)': dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - '@csstools/postcss-scope-pseudo-class@5.0.0(postcss@8.5.15)': + '@csstools/postcss-scope-pseudo-class@5.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - '@csstools/postcss-sign-functions@2.0.2(postcss@8.5.15)': + '@csstools/postcss-sign-functions@2.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-stepped-value-functions@5.0.2(postcss@8.5.15)': + '@csstools/postcss-stepped-value-functions@5.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0(postcss@8.5.15)': + '@csstools/postcss-syntax-descriptor-syntax-production@2.0.0(postcss@8.5.25)': dependencies: '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-system-ui-font-family@2.0.0(postcss@8.5.15)': + '@csstools/postcss-system-ui-font-family@2.0.0(postcss@8.5.25)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-text-decoration-shorthand@5.0.3(postcss@8.5.15)': + '@csstools/postcss-text-decoration-shorthand@5.0.3(postcss@8.5.25)': dependencies: '@csstools/color-helpers': 6.0.2 - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - '@csstools/postcss-trigonometric-functions@5.0.2(postcss@8.5.15)': + '@csstools/postcss-trigonometric-functions@5.0.2(postcss@8.5.25)': dependencies: '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - '@csstools/postcss-unset-value@5.0.0(postcss@8.5.15)': + '@csstools/postcss-unset-value@5.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 '@csstools/selector-resolve-nested@4.0.0(postcss-selector-parser@7.1.1)': dependencies: @@ -6272,9 +6309,9 @@ snapshots: dependencies: postcss-selector-parser: 7.1.1 - '@csstools/utilities@3.0.0(postcss@8.5.15)': + '@csstools/utilities@3.0.0(postcss@8.5.25)': dependencies: - postcss: 8.5.15 + postcss: 8.5.25 '@emotion/hash@0.8.0': {} @@ -6282,122 +6319,122 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@5.2.1(@types/node@25.7.0)': + '@inquirer/checkbox@5.2.1(@types/node@25.9.5)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/confirm@6.1.1(@types/node@25.7.0)': + '@inquirer/confirm@6.1.1(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/core@11.2.1(@types/node@25.7.0)': + '@inquirer/core@11.2.1(@types/node@25.9.5)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/type': 4.0.7(@types/node@25.9.5) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/editor@5.2.2(@types/node@25.7.0)': + '@inquirer/editor@5.2.2(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/external-editor': 3.0.3(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/external-editor': 3.0.3(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/expand@5.1.1(@types/node@25.7.0)': + '@inquirer/expand@5.1.1(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/external-editor@3.0.3(@types/node@25.7.0)': + '@inquirer/external-editor@3.0.3(@types/node@25.9.5)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 '@inquirer/figures@2.0.7': {} - '@inquirer/input@5.1.2(@types/node@25.7.0)': + '@inquirer/input@5.1.2(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/number@4.1.1(@types/node@25.7.0)': + '@inquirer/number@4.1.1(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/password@5.1.1(@types/node@25.7.0)': + '@inquirer/password@5.1.1(@types/node@25.9.5)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 - - '@inquirer/prompts@8.5.2(@types/node@25.7.0)': - dependencies: - '@inquirer/checkbox': 5.2.1(@types/node@25.7.0) - '@inquirer/confirm': 6.1.1(@types/node@25.7.0) - '@inquirer/editor': 5.2.2(@types/node@25.7.0) - '@inquirer/expand': 5.1.1(@types/node@25.7.0) - '@inquirer/input': 5.1.2(@types/node@25.7.0) - '@inquirer/number': 4.1.1(@types/node@25.7.0) - '@inquirer/password': 5.1.1(@types/node@25.7.0) - '@inquirer/rawlist': 5.3.1(@types/node@25.7.0) - '@inquirer/search': 4.2.1(@types/node@25.7.0) - '@inquirer/select': 5.2.1(@types/node@25.7.0) + '@types/node': 25.9.5 + + '@inquirer/prompts@8.5.2(@types/node@25.9.5)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@25.9.5) + '@inquirer/confirm': 6.1.1(@types/node@25.9.5) + '@inquirer/editor': 5.2.2(@types/node@25.9.5) + '@inquirer/expand': 5.1.1(@types/node@25.9.5) + '@inquirer/input': 5.1.2(@types/node@25.9.5) + '@inquirer/number': 4.1.1(@types/node@25.9.5) + '@inquirer/password': 5.1.1(@types/node@25.9.5) + '@inquirer/rawlist': 5.3.1(@types/node@25.9.5) + '@inquirer/search': 4.2.1(@types/node@25.9.5) + '@inquirer/select': 5.2.1(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/rawlist@5.3.1(@types/node@25.7.0)': + '@inquirer/rawlist@5.3.1(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/search@4.2.1(@types/node@25.7.0)': + '@inquirer/search@4.2.1(@types/node@25.9.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/select@5.2.1(@types/node@25.7.0)': + '@inquirer/select@5.2.1(@types/node@25.9.5)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/type': 4.0.7(@types/node@25.9.5) optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 - '@inquirer/type@4.0.7(@types/node@25.7.0)': + '@inquirer/type@4.0.7(@types/node@25.9.5)': optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 '@isaacs/cliui@9.0.0': {} @@ -6409,7 +6446,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 jest-regex-util: 30.0.1 '@jest/schemas@30.0.5': @@ -6422,7 +6459,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.7.0 + '@types/node': 25.9.5 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6445,14 +6482,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@material-ui/core@4.12.4(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@material-ui/core@4.12.4(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.29.7 - '@material-ui/styles': 4.11.5(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@material-ui/system': 4.12.2(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@material-ui/types': 5.1.0(@types/react@17.0.91) + '@material-ui/styles': 4.11.5(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@material-ui/system': 4.12.2(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + '@material-ui/types': 5.1.0(@types/react@17.0.93) '@material-ui/utils': 4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) - '@types/react-transition-group': 4.4.12(@types/react@17.0.91) + '@types/react-transition-group': 4.4.12(@types/react@17.0.93) clsx: 1.2.1 hoist-non-react-statics: 3.3.2 popper.js: 1.16.1-lts @@ -6462,13 +6499,13 @@ snapshots: react-is: 17.0.2 react-transition-group: 4.4.5(react-dom@17.0.2(react@17.0.2))(react@17.0.2) optionalDependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 - '@material-ui/styles@4.11.5(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@material-ui/styles@4.11.5(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.29.7 '@emotion/hash': 0.8.0 - '@material-ui/types': 5.1.0(@types/react@17.0.91) + '@material-ui/types': 5.1.0(@types/react@17.0.93) '@material-ui/utils': 4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) clsx: 1.2.1 csstype: 2.6.21 @@ -6485,9 +6522,9 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) optionalDependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 - '@material-ui/system@4.12.2(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': + '@material-ui/system@4.12.2(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: '@babel/runtime': 7.29.7 '@material-ui/utils': 4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2) @@ -6496,11 +6533,11 @@ snapshots: react: 17.0.2 react-dom: 17.0.2(react@17.0.2) optionalDependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 - '@material-ui/types@5.1.0(@types/react@17.0.91)': + '@material-ui/types@5.1.0(@types/react@17.0.93)': optionalDependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 '@material-ui/utils@4.11.3(react-dom@17.0.2(react@17.0.2))(react@17.0.2)': dependencies: @@ -6886,9 +6923,9 @@ snapshots: '@types/estree@1.0.9': {} - '@types/hoist-non-react-statics@3.3.7(@types/react@17.0.91)': + '@types/hoist-non-react-statics@3.3.7(@types/react@17.0.93)': dependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 hoist-non-react-statics: 3.3.2 '@types/html-minifier-terser@6.1.0': {} @@ -6905,30 +6942,30 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.7.0': + '@types/node@25.9.5': dependencies: - undici-types: 7.21.0 + undici-types: 7.24.6 '@types/parse-json@4.0.2': {} '@types/prop-types@15.7.15': {} - '@types/react-dom@17.0.26(@types/react@17.0.91)': + '@types/react-dom@17.0.26(@types/react@17.0.93)': dependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 '@types/react-redux@7.1.34': dependencies: - '@types/hoist-non-react-statics': 3.3.7(@types/react@17.0.91) - '@types/react': 17.0.91 + '@types/hoist-non-react-statics': 3.3.7(@types/react@17.0.93) + '@types/react': 17.0.93 hoist-non-react-statics: 3.3.2 redux: 4.2.1 - '@types/react-transition-group@4.4.12(@types/react@17.0.91)': + '@types/react-transition-group@4.4.12(@types/react@17.0.93)': dependencies: - '@types/react': 17.0.91 + '@types/react': 17.0.93 - '@types/react@17.0.91': + '@types/react@17.0.93': dependencies: '@types/prop-types': 15.7.15 '@types/scheduler': 0.16.8 @@ -7084,7 +7121,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 4.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -7132,20 +7169,20 @@ snapshots: at-least-node@1.0.0: {} - autoprefixer@10.4.27(postcss@8.5.15): + autoprefixer@10.4.27(postcss@8.5.25): dependencies: browserslist: 4.28.2 caniuse-lite: 1.0.30001787 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - axios@1.18.1: + axios@1.19.0: dependencies: follow-redirects: 1.16.0 form-data: 4.0.6 @@ -7227,8 +7264,6 @@ snapshots: transitivePeerDependencies: - supports-color - balanced-match@1.0.2: {} - balanced-match@4.0.4: {} baseline-browser-mapping@2.10.18: {} @@ -7247,16 +7282,7 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.14: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.1.1: - dependencies: - balanced-match: 1.0.2 - - brace-expansion@5.0.6: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -7417,8 +7443,6 @@ snapshots: common-tags@1.8.2: {} - concat-map@0.0.1: {} - content-type@1.0.5: {} convert-source-map@1.9.0: {} @@ -7449,7 +7473,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 5.2.3 parse-json: 5.2.0 optionalDependencies: typescript: 6.0.3 @@ -7462,30 +7486,30 @@ snapshots: crypto-random-string@2.0.0: {} - css-blank-pseudo@8.0.1(postcss@8.5.15): + css-blank-pseudo@8.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - css-declaration-sorter@7.4.0(postcss@8.5.15): + css-declaration-sorter@7.4.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - css-has-pseudo@8.0.0(postcss@8.5.15): + css-has-pseudo@8.0.0(postcss@8.5.25): dependencies: '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 css-loader@7.1.4(webpack@5.105.2): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) - postcss-modules-scope: 3.2.1(postcss@8.5.15) - postcss-modules-values: 4.0.0(postcss@8.5.15) + icss-utils: 5.1.0(postcss@8.5.25) + postcss: 8.5.25 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.25) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.25) + postcss-modules-scope: 3.2.1(postcss@8.5.25) + postcss-modules-values: 4.0.0(postcss@8.5.25) postcss-value-parser: 4.2.0 semver: 7.8.1 optionalDependencies: @@ -7494,16 +7518,16 @@ snapshots: css-minimizer-webpack-plugin@8.0.0(webpack@5.105.2): dependencies: '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.4(postcss@8.5.15) + cssnano: 7.1.4(postcss@8.5.25) jest-worker: 30.3.0 - postcss: 8.5.15 + postcss: 8.5.25 schema-utils: 4.3.3 serialize-javascript: 7.0.5 webpack: 5.105.2 - css-prefers-color-scheme@11.0.0(postcss@8.5.15): + css-prefers-color-scheme@11.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 css-select@4.3.0: dependencies: @@ -7542,49 +7566,49 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@7.0.12(postcss@8.5.15): + cssnano-preset-default@7.0.12(postcss@8.5.25): dependencies: browserslist: 4.28.2 - css-declaration-sorter: 7.4.0(postcss@8.5.15) - cssnano-utils: 5.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-calc: 10.1.1(postcss@8.5.15) - postcss-colormin: 7.0.7(postcss@8.5.15) - postcss-convert-values: 7.0.9(postcss@8.5.15) - postcss-discard-comments: 7.0.6(postcss@8.5.15) - postcss-discard-duplicates: 7.0.2(postcss@8.5.15) - postcss-discard-empty: 7.0.1(postcss@8.5.15) - postcss-discard-overridden: 7.0.1(postcss@8.5.15) - postcss-merge-longhand: 7.0.5(postcss@8.5.15) - postcss-merge-rules: 7.0.8(postcss@8.5.15) - postcss-minify-font-values: 7.0.1(postcss@8.5.15) - postcss-minify-gradients: 7.0.2(postcss@8.5.15) - postcss-minify-params: 7.0.6(postcss@8.5.15) - postcss-minify-selectors: 7.0.6(postcss@8.5.15) - postcss-normalize-charset: 7.0.1(postcss@8.5.15) - postcss-normalize-display-values: 7.0.1(postcss@8.5.15) - postcss-normalize-positions: 7.0.1(postcss@8.5.15) - postcss-normalize-repeat-style: 7.0.1(postcss@8.5.15) - postcss-normalize-string: 7.0.1(postcss@8.5.15) - postcss-normalize-timing-functions: 7.0.1(postcss@8.5.15) - postcss-normalize-unicode: 7.0.6(postcss@8.5.15) - postcss-normalize-url: 7.0.1(postcss@8.5.15) - postcss-normalize-whitespace: 7.0.1(postcss@8.5.15) - postcss-ordered-values: 7.0.2(postcss@8.5.15) - postcss-reduce-initial: 7.0.6(postcss@8.5.15) - postcss-reduce-transforms: 7.0.1(postcss@8.5.15) - postcss-svgo: 7.1.1(postcss@8.5.15) - postcss-unique-selectors: 7.0.5(postcss@8.5.15) - - cssnano-utils@5.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - - cssnano@7.1.4(postcss@8.5.15): - dependencies: - cssnano-preset-default: 7.0.12(postcss@8.5.15) + css-declaration-sorter: 7.4.0(postcss@8.5.25) + cssnano-utils: 5.0.1(postcss@8.5.25) + postcss: 8.5.25 + postcss-calc: 10.1.1(postcss@8.5.25) + postcss-colormin: 7.0.7(postcss@8.5.25) + postcss-convert-values: 7.0.9(postcss@8.5.25) + postcss-discard-comments: 7.0.6(postcss@8.5.25) + postcss-discard-duplicates: 7.0.2(postcss@8.5.25) + postcss-discard-empty: 7.0.1(postcss@8.5.25) + postcss-discard-overridden: 7.0.1(postcss@8.5.25) + postcss-merge-longhand: 7.0.5(postcss@8.5.25) + postcss-merge-rules: 7.0.8(postcss@8.5.25) + postcss-minify-font-values: 7.0.1(postcss@8.5.25) + postcss-minify-gradients: 7.0.2(postcss@8.5.25) + postcss-minify-params: 7.0.6(postcss@8.5.25) + postcss-minify-selectors: 7.0.6(postcss@8.5.25) + postcss-normalize-charset: 7.0.1(postcss@8.5.25) + postcss-normalize-display-values: 7.0.1(postcss@8.5.25) + postcss-normalize-positions: 7.0.1(postcss@8.5.25) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.25) + postcss-normalize-string: 7.0.1(postcss@8.5.25) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.25) + postcss-normalize-unicode: 7.0.6(postcss@8.5.25) + postcss-normalize-url: 7.0.1(postcss@8.5.25) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.25) + postcss-ordered-values: 7.0.2(postcss@8.5.25) + postcss-reduce-initial: 7.0.6(postcss@8.5.25) + postcss-reduce-transforms: 7.0.1(postcss@8.5.25) + postcss-svgo: 7.1.1(postcss@8.5.25) + postcss-unique-selectors: 7.0.5(postcss@8.5.25) + + cssnano-utils@5.0.1(postcss@8.5.25): + dependencies: + postcss: 8.5.25 + + cssnano@7.1.4(postcss@8.5.25): + dependencies: + cssnano-preset-default: 7.0.12(postcss@8.5.25) lilconfig: 3.1.3 - postcss: 8.5.15 + postcss: 8.5.25 csso@5.0.5: dependencies: @@ -7896,7 +7920,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.2: {} + fast-uri@4.1.2: {} fast-wrap-ansi@0.2.2: dependencies: @@ -8050,7 +8074,7 @@ snapshots: content-type: 1.0.5 encoding: 0.1.13 - gettext-parser@9.0.2: + gettext-parser@9.1.1: dependencies: content-type: 1.0.5 encoding: 0.1.13 @@ -8172,11 +8196,9 @@ snapshots: relateurl: 0.2.7 terser: 5.47.1 - html-parse-stringify@3.0.1: - dependencies: - void-elements: 3.1.0 + html-parse-stringify@4.0.1: {} - html-webpack-plugin@5.6.7(webpack@5.105.2): + html-webpack-plugin@5.6.8(webpack@5.105.2): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -8224,7 +8246,7 @@ snapshots: dependencies: '@babel/runtime': 7.29.7 - i18next-cli@1.65.0(@types/node@25.7.0)(react-dom@17.0.2(react@17.0.2))(typescript@6.0.3): + i18next-cli@1.67.3(@types/node@25.9.5)(react-dom@17.0.2(react@17.0.2))(typescript@6.0.3): dependencies: '@croct/json5-parser': 0.2.2 '@swc/core': 1.15.43 @@ -8232,16 +8254,16 @@ snapshots: commander: 14.0.3 execa: 9.6.1 glob: 13.0.6 - i18next: 26.3.4(typescript@6.0.3) + i18next: 26.3.6(typescript@6.0.3) i18next-resources-for-ts: 2.1.0 - inquirer: 14.0.2(@types/node@25.7.0) + inquirer: 14.0.2(@types/node@25.9.5) jiti: 2.7.0 jsonc-parser: 3.3.1 magic-string: 0.30.21 minimatch: 10.2.5 ora: 9.4.1 react: 19.2.7 - react-i18next: 17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@19.2.7)(typescript@6.0.3) + react-i18next: 17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@19.2.7)(typescript@6.0.3) yaml: 2.9.0 transitivePeerDependencies: - '@swc/helpers' @@ -8256,7 +8278,7 @@ snapshots: colorette: 2.0.20 commander: 14.0.3 gettext-converter: 1.3.3 - gettext-parser: 9.0.2 + gettext-parser: 9.1.1 p-from-callback: 3.0.0 i18next-resources-for-ts@2.1.0: @@ -8268,11 +8290,11 @@ snapshots: transitivePeerDependencies: - '@swc/helpers' - i18next-resources-to-backend@1.2.1: + i18next-resources-to-backend@1.2.3: dependencies: '@babel/runtime': 7.29.7 - i18next@26.3.4(typescript@6.0.3): + i18next@26.3.6(typescript@6.0.3): optionalDependencies: typescript: 6.0.3 @@ -8284,9 +8306,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.15): + icss-utils@5.1.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 idb@7.1.1: {} @@ -8314,16 +8336,16 @@ snapshots: ini@6.0.0: {} - inquirer@14.0.2(@types/node@25.7.0): + inquirer@14.0.2(@types/node@25.9.5): dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@25.7.0) - '@inquirer/prompts': 8.5.2(@types/node@25.7.0) - '@inquirer/type': 4.0.7(@types/node@25.7.0) + '@inquirer/core': 11.2.1(@types/node@25.9.5) + '@inquirer/prompts': 8.5.2(@types/node@25.9.5) + '@inquirer/type': 4.0.7(@types/node@25.9.5) mute-stream: 3.0.0 run-async: 4.0.6 optionalDependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 internal-slot@1.1.0: dependencies: @@ -8498,7 +8520,7 @@ snapshots: jest-util@30.3.0: dependencies: '@jest/types': 30.3.0 - '@types/node': 25.7.0 + '@types/node': 25.9.5 chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 @@ -8506,13 +8528,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@30.3.0: dependencies: - '@types/node': 25.7.0 + '@types/node': 25.9.5 '@ungap/structured-clone': 1.3.0 jest-util: 30.3.0 merge-stream: 2.0.0 @@ -8522,7 +8544,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@5.2.3: dependencies: argparse: 2.0.1 @@ -8749,15 +8771,15 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.9 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 5.0.9 minimatch@5.1.9: dependencies: - brace-expansion: 2.1.1 + brace-expansion: 5.0.9 minimist@1.2.8: {} @@ -8807,7 +8829,7 @@ snapshots: mute-stream@3.0.0: {} - nanoid@3.3.12: {} + nanoid@3.3.16: {} negotiator@1.0.0: {} @@ -9067,413 +9089,413 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-attribute-case-insensitive@8.0.0(postcss@8.5.15): + postcss-attribute-case-insensitive@8.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-browser-comments@6.0.2(browserslist@4.28.2)(postcss@8.5.15): + postcss-browser-comments@6.0.2(browserslist@4.28.2)(postcss@8.5.25): dependencies: browserslist: 4.28.2 - postcss: 8.5.15 + postcss: 8.5.25 - postcss-calc@10.1.1(postcss@8.5.15): + postcss-calc@10.1.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-clamp@4.1.0(postcss@8.5.15): + postcss-clamp@4.1.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-color-functional-notation@8.0.3(postcss@8.5.15): + postcss-color-functional-notation@8.0.3(postcss@8.5.25): dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - postcss-color-hex-alpha@11.0.0(postcss@8.5.15): + postcss-color-hex-alpha@11.0.0(postcss@8.5.25): dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-color-rebeccapurple@11.0.0(postcss@8.5.15): + postcss-color-rebeccapurple@11.0.0(postcss@8.5.25): dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-colormin@7.0.7(postcss@8.5.15): + postcss-colormin@7.0.7(postcss@8.5.25): dependencies: '@colordx/core': 5.0.3 browserslist: 4.28.2 caniuse-api: 3.0.0 - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-convert-values@7.0.9(postcss@8.5.15): + postcss-convert-values@7.0.9(postcss@8.5.25): dependencies: browserslist: 4.28.2 - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-custom-media@12.0.1(postcss@8.5.15): + postcss-custom-media@12.0.1(postcss@8.5.25): dependencies: '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - postcss: 8.5.15 + postcss: 8.5.25 - postcss-custom-properties@15.0.1(postcss@8.5.15): + postcss-custom-properties@15.0.1(postcss@8.5.25): dependencies: '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-custom-selectors@9.0.1(postcss@8.5.15): + postcss-custom-selectors@9.0.1(postcss@8.5.25): dependencies: '@csstools/cascade-layer-name-parser': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-dir-pseudo-class@10.0.0(postcss@8.5.15): + postcss-dir-pseudo-class@10.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-discard-comments@7.0.6(postcss@8.5.15): + postcss-discard-comments@7.0.6(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-discard-duplicates@7.0.2(postcss@8.5.15): + postcss-discard-duplicates@7.0.2(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-discard-empty@7.0.1(postcss@8.5.15): + postcss-discard-empty@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-discard-overridden@7.0.1(postcss@8.5.15): + postcss-discard-overridden@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-double-position-gradients@7.0.0(postcss@8.5.15): + postcss-double-position-gradients@7.0.0(postcss@8.5.25): dependencies: - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-flexbugs-fixes@5.0.2(postcss@8.5.15): + postcss-flexbugs-fixes@5.0.2(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-focus-visible@11.0.0(postcss@8.5.15): + postcss-focus-visible@11.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-focus-within@10.0.0(postcss@8.5.15): + postcss-focus-within@10.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-font-variant@5.0.0(postcss@8.5.15): + postcss-font-variant@5.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-gap-properties@7.0.0(postcss@8.5.15): + postcss-gap-properties@7.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-image-set-function@8.0.0(postcss@8.5.15): + postcss-image-set-function@8.0.0(postcss@8.5.25): dependencies: - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-lab-function@8.0.3(postcss@8.5.15): + postcss-lab-function@8.0.3(postcss@8.5.25): dependencies: '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/utilities': 3.0.0(postcss@8.5.15) - postcss: 8.5.15 + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/utilities': 3.0.0(postcss@8.5.25) + postcss: 8.5.25 - postcss-loader@8.2.1(postcss@8.5.15)(typescript@6.0.3)(webpack@5.105.2): + postcss-loader@8.2.1(postcss@8.5.25)(typescript@6.0.3)(webpack@5.105.2): dependencies: cosmiconfig: 9.0.1(typescript@6.0.3) jiti: 2.7.0 - postcss: 8.5.15 + postcss: 8.5.25 semver: 7.8.1 optionalDependencies: webpack: 5.105.2 transitivePeerDependencies: - typescript - postcss-logical@9.0.0(postcss@8.5.15): + postcss-logical@9.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-merge-longhand@7.0.5(postcss@8.5.15): + postcss-merge-longhand@7.0.5(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - stylehacks: 7.0.8(postcss@8.5.15) + stylehacks: 7.0.8(postcss@8.5.25) - postcss-merge-rules@7.0.8(postcss@8.5.15): + postcss-merge-rules@7.0.8(postcss@8.5.25): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 - cssnano-utils: 5.0.1(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 5.0.1(postcss@8.5.25) + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-minify-font-values@7.0.1(postcss@8.5.15): + postcss-minify-font-values@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-minify-gradients@7.0.2(postcss@8.5.15): + postcss-minify-gradients@7.0.2(postcss@8.5.25): dependencies: '@colordx/core': 5.0.3 - cssnano-utils: 5.0.1(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 5.0.1(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-minify-params@7.0.6(postcss@8.5.15): + postcss-minify-params@7.0.6(postcss@8.5.25): dependencies: browserslist: 4.28.2 - cssnano-utils: 5.0.1(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 5.0.1(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-minify-selectors@7.0.6(postcss@8.5.15): + postcss-minify-selectors@7.0.6(postcss@8.5.25): dependencies: cssesc: 3.0.0 - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-modules-extract-imports@3.1.0(postcss@8.5.15): + postcss-modules-extract-imports@3.1.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-modules-local-by-default@4.2.0(postcss@8.5.15): + postcss-modules-local-by-default@4.2.0(postcss@8.5.25): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 + icss-utils: 5.1.0(postcss@8.5.25) + postcss: 8.5.25 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.15): + postcss-modules-scope@3.2.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-modules-values@4.0.0(postcss@8.5.15): + postcss-modules-values@4.0.0(postcss@8.5.25): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 + icss-utils: 5.1.0(postcss@8.5.25) + postcss: 8.5.25 - postcss-nesting@14.0.0(postcss@8.5.15): + postcss-nesting@14.0.0(postcss@8.5.25): dependencies: '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-normalize-charset@7.0.1(postcss@8.5.15): + postcss-normalize-charset@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-normalize-display-values@7.0.1(postcss@8.5.15): + postcss-normalize-display-values@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-positions@7.0.1(postcss@8.5.15): + postcss-normalize-positions@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@7.0.1(postcss@8.5.15): + postcss-normalize-repeat-style@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-string@7.0.1(postcss@8.5.15): + postcss-normalize-string@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@7.0.1(postcss@8.5.15): + postcss-normalize-timing-functions@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@7.0.6(postcss@8.5.15): + postcss-normalize-unicode@7.0.6(postcss@8.5.25): dependencies: browserslist: 4.28.2 - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-url@7.0.1(postcss@8.5.15): + postcss-normalize-url@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@7.0.1(postcss@8.5.15): + postcss-normalize-whitespace@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-normalize@13.0.1(browserslist@4.28.2)(postcss@8.5.15): + postcss-normalize@13.0.1(browserslist@4.28.2)(postcss@8.5.25): dependencies: '@csstools/normalize.css': 12.1.1 browserslist: 4.28.2 - postcss: 8.5.15 - postcss-browser-comments: 6.0.2(browserslist@4.28.2)(postcss@8.5.15) + postcss: 8.5.25 + postcss-browser-comments: 6.0.2(browserslist@4.28.2)(postcss@8.5.25) sanitize.css: 13.0.0 - postcss-opacity-percentage@3.0.0(postcss@8.5.15): + postcss-opacity-percentage@3.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-ordered-values@7.0.2(postcss@8.5.15): + postcss-ordered-values@7.0.2(postcss@8.5.25): dependencies: - cssnano-utils: 5.0.1(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 5.0.1(postcss@8.5.25) + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-overflow-shorthand@7.0.0(postcss@8.5.15): + postcss-overflow-shorthand@7.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-page-break@3.0.4(postcss@8.5.15): + postcss-page-break@3.0.4(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-place@11.0.0(postcss@8.5.15): + postcss-place@11.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-preset-env@11.2.0(postcss@8.5.15): - dependencies: - '@csstools/postcss-alpha-function': 2.0.4(postcss@8.5.15) - '@csstools/postcss-cascade-layers': 6.0.0(postcss@8.5.15) - '@csstools/postcss-color-function': 5.0.3(postcss@8.5.15) - '@csstools/postcss-color-function-display-p3-linear': 2.0.3(postcss@8.5.15) - '@csstools/postcss-color-mix-function': 4.0.3(postcss@8.5.15) - '@csstools/postcss-color-mix-variadic-function-arguments': 2.0.3(postcss@8.5.15) - '@csstools/postcss-content-alt-text': 3.0.0(postcss@8.5.15) - '@csstools/postcss-contrast-color-function': 3.0.3(postcss@8.5.15) - '@csstools/postcss-exponential-functions': 3.0.2(postcss@8.5.15) - '@csstools/postcss-font-format-keywords': 5.0.0(postcss@8.5.15) - '@csstools/postcss-font-width-property': 1.0.0(postcss@8.5.15) - '@csstools/postcss-gamut-mapping': 3.0.3(postcss@8.5.15) - '@csstools/postcss-gradients-interpolation-method': 6.0.3(postcss@8.5.15) - '@csstools/postcss-hwb-function': 5.0.3(postcss@8.5.15) - '@csstools/postcss-ic-unit': 5.0.0(postcss@8.5.15) - '@csstools/postcss-initial': 3.0.0(postcss@8.5.15) - '@csstools/postcss-is-pseudo-class': 6.0.0(postcss@8.5.15) - '@csstools/postcss-light-dark-function': 3.0.0(postcss@8.5.15) - '@csstools/postcss-logical-float-and-clear': 4.0.0(postcss@8.5.15) - '@csstools/postcss-logical-overflow': 3.0.0(postcss@8.5.15) - '@csstools/postcss-logical-overscroll-behavior': 3.0.0(postcss@8.5.15) - '@csstools/postcss-logical-resize': 4.0.0(postcss@8.5.15) - '@csstools/postcss-logical-viewport-units': 4.0.0(postcss@8.5.15) - '@csstools/postcss-media-minmax': 3.0.2(postcss@8.5.15) - '@csstools/postcss-media-queries-aspect-ratio-number-values': 4.0.0(postcss@8.5.15) - '@csstools/postcss-mixins': 1.0.0(postcss@8.5.15) - '@csstools/postcss-nested-calc': 5.0.0(postcss@8.5.15) - '@csstools/postcss-normalize-display-values': 5.0.1(postcss@8.5.15) - '@csstools/postcss-oklab-function': 5.0.3(postcss@8.5.15) - '@csstools/postcss-position-area-property': 2.0.0(postcss@8.5.15) - '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.15) - '@csstools/postcss-property-rule-prelude-list': 2.0.0(postcss@8.5.15) - '@csstools/postcss-random-function': 3.0.2(postcss@8.5.15) - '@csstools/postcss-relative-color-syntax': 4.0.3(postcss@8.5.15) - '@csstools/postcss-scope-pseudo-class': 5.0.0(postcss@8.5.15) - '@csstools/postcss-sign-functions': 2.0.2(postcss@8.5.15) - '@csstools/postcss-stepped-value-functions': 5.0.2(postcss@8.5.15) - '@csstools/postcss-syntax-descriptor-syntax-production': 2.0.0(postcss@8.5.15) - '@csstools/postcss-system-ui-font-family': 2.0.0(postcss@8.5.15) - '@csstools/postcss-text-decoration-shorthand': 5.0.3(postcss@8.5.15) - '@csstools/postcss-trigonometric-functions': 5.0.2(postcss@8.5.15) - '@csstools/postcss-unset-value': 5.0.0(postcss@8.5.15) - autoprefixer: 10.4.27(postcss@8.5.15) + postcss-preset-env@11.2.0(postcss@8.5.25): + dependencies: + '@csstools/postcss-alpha-function': 2.0.4(postcss@8.5.25) + '@csstools/postcss-cascade-layers': 6.0.0(postcss@8.5.25) + '@csstools/postcss-color-function': 5.0.3(postcss@8.5.25) + '@csstools/postcss-color-function-display-p3-linear': 2.0.3(postcss@8.5.25) + '@csstools/postcss-color-mix-function': 4.0.3(postcss@8.5.25) + '@csstools/postcss-color-mix-variadic-function-arguments': 2.0.3(postcss@8.5.25) + '@csstools/postcss-content-alt-text': 3.0.0(postcss@8.5.25) + '@csstools/postcss-contrast-color-function': 3.0.3(postcss@8.5.25) + '@csstools/postcss-exponential-functions': 3.0.2(postcss@8.5.25) + '@csstools/postcss-font-format-keywords': 5.0.0(postcss@8.5.25) + '@csstools/postcss-font-width-property': 1.0.0(postcss@8.5.25) + '@csstools/postcss-gamut-mapping': 3.0.3(postcss@8.5.25) + '@csstools/postcss-gradients-interpolation-method': 6.0.3(postcss@8.5.25) + '@csstools/postcss-hwb-function': 5.0.3(postcss@8.5.25) + '@csstools/postcss-ic-unit': 5.0.0(postcss@8.5.25) + '@csstools/postcss-initial': 3.0.0(postcss@8.5.25) + '@csstools/postcss-is-pseudo-class': 6.0.0(postcss@8.5.25) + '@csstools/postcss-light-dark-function': 3.0.0(postcss@8.5.25) + '@csstools/postcss-logical-float-and-clear': 4.0.0(postcss@8.5.25) + '@csstools/postcss-logical-overflow': 3.0.0(postcss@8.5.25) + '@csstools/postcss-logical-overscroll-behavior': 3.0.0(postcss@8.5.25) + '@csstools/postcss-logical-resize': 4.0.0(postcss@8.5.25) + '@csstools/postcss-logical-viewport-units': 4.0.0(postcss@8.5.25) + '@csstools/postcss-media-minmax': 3.0.2(postcss@8.5.25) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 4.0.0(postcss@8.5.25) + '@csstools/postcss-mixins': 1.0.0(postcss@8.5.25) + '@csstools/postcss-nested-calc': 5.0.0(postcss@8.5.25) + '@csstools/postcss-normalize-display-values': 5.0.1(postcss@8.5.25) + '@csstools/postcss-oklab-function': 5.0.3(postcss@8.5.25) + '@csstools/postcss-position-area-property': 2.0.0(postcss@8.5.25) + '@csstools/postcss-progressive-custom-properties': 5.0.0(postcss@8.5.25) + '@csstools/postcss-property-rule-prelude-list': 2.0.0(postcss@8.5.25) + '@csstools/postcss-random-function': 3.0.2(postcss@8.5.25) + '@csstools/postcss-relative-color-syntax': 4.0.3(postcss@8.5.25) + '@csstools/postcss-scope-pseudo-class': 5.0.0(postcss@8.5.25) + '@csstools/postcss-sign-functions': 2.0.2(postcss@8.5.25) + '@csstools/postcss-stepped-value-functions': 5.0.2(postcss@8.5.25) + '@csstools/postcss-syntax-descriptor-syntax-production': 2.0.0(postcss@8.5.25) + '@csstools/postcss-system-ui-font-family': 2.0.0(postcss@8.5.25) + '@csstools/postcss-text-decoration-shorthand': 5.0.3(postcss@8.5.25) + '@csstools/postcss-trigonometric-functions': 5.0.2(postcss@8.5.25) + '@csstools/postcss-unset-value': 5.0.0(postcss@8.5.25) + autoprefixer: 10.4.27(postcss@8.5.25) browserslist: 4.28.2 - css-blank-pseudo: 8.0.1(postcss@8.5.15) - css-has-pseudo: 8.0.0(postcss@8.5.15) - css-prefers-color-scheme: 11.0.0(postcss@8.5.15) + css-blank-pseudo: 8.0.1(postcss@8.5.25) + css-has-pseudo: 8.0.0(postcss@8.5.25) + css-prefers-color-scheme: 11.0.0(postcss@8.5.25) cssdb: 8.8.0 - postcss: 8.5.15 - postcss-attribute-case-insensitive: 8.0.0(postcss@8.5.15) - postcss-clamp: 4.1.0(postcss@8.5.15) - postcss-color-functional-notation: 8.0.3(postcss@8.5.15) - postcss-color-hex-alpha: 11.0.0(postcss@8.5.15) - postcss-color-rebeccapurple: 11.0.0(postcss@8.5.15) - postcss-custom-media: 12.0.1(postcss@8.5.15) - postcss-custom-properties: 15.0.1(postcss@8.5.15) - postcss-custom-selectors: 9.0.1(postcss@8.5.15) - postcss-dir-pseudo-class: 10.0.0(postcss@8.5.15) - postcss-double-position-gradients: 7.0.0(postcss@8.5.15) - postcss-focus-visible: 11.0.0(postcss@8.5.15) - postcss-focus-within: 10.0.0(postcss@8.5.15) - postcss-font-variant: 5.0.0(postcss@8.5.15) - postcss-gap-properties: 7.0.0(postcss@8.5.15) - postcss-image-set-function: 8.0.0(postcss@8.5.15) - postcss-lab-function: 8.0.3(postcss@8.5.15) - postcss-logical: 9.0.0(postcss@8.5.15) - postcss-nesting: 14.0.0(postcss@8.5.15) - postcss-opacity-percentage: 3.0.0(postcss@8.5.15) - postcss-overflow-shorthand: 7.0.0(postcss@8.5.15) - postcss-page-break: 3.0.4(postcss@8.5.15) - postcss-place: 11.0.0(postcss@8.5.15) - postcss-pseudo-class-any-link: 11.0.0(postcss@8.5.15) - postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.15) - postcss-selector-not: 9.0.0(postcss@8.5.15) - - postcss-pseudo-class-any-link@11.0.0(postcss@8.5.15): - dependencies: - postcss: 8.5.15 + postcss: 8.5.25 + postcss-attribute-case-insensitive: 8.0.0(postcss@8.5.25) + postcss-clamp: 4.1.0(postcss@8.5.25) + postcss-color-functional-notation: 8.0.3(postcss@8.5.25) + postcss-color-hex-alpha: 11.0.0(postcss@8.5.25) + postcss-color-rebeccapurple: 11.0.0(postcss@8.5.25) + postcss-custom-media: 12.0.1(postcss@8.5.25) + postcss-custom-properties: 15.0.1(postcss@8.5.25) + postcss-custom-selectors: 9.0.1(postcss@8.5.25) + postcss-dir-pseudo-class: 10.0.0(postcss@8.5.25) + postcss-double-position-gradients: 7.0.0(postcss@8.5.25) + postcss-focus-visible: 11.0.0(postcss@8.5.25) + postcss-focus-within: 10.0.0(postcss@8.5.25) + postcss-font-variant: 5.0.0(postcss@8.5.25) + postcss-gap-properties: 7.0.0(postcss@8.5.25) + postcss-image-set-function: 8.0.0(postcss@8.5.25) + postcss-lab-function: 8.0.3(postcss@8.5.25) + postcss-logical: 9.0.0(postcss@8.5.25) + postcss-nesting: 14.0.0(postcss@8.5.25) + postcss-opacity-percentage: 3.0.0(postcss@8.5.25) + postcss-overflow-shorthand: 7.0.0(postcss@8.5.25) + postcss-page-break: 3.0.4(postcss@8.5.25) + postcss-place: 11.0.0(postcss@8.5.25) + postcss-pseudo-class-any-link: 11.0.0(postcss@8.5.25) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.25) + postcss-selector-not: 9.0.0(postcss@8.5.25) + + postcss-pseudo-class-any-link@11.0.0(postcss@8.5.25): + dependencies: + postcss: 8.5.25 postcss-selector-parser: 7.1.1 - postcss-reduce-initial@7.0.6(postcss@8.5.15): + postcss-reduce-initial@7.0.6(postcss@8.5.25): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 - postcss: 8.5.15 + postcss: 8.5.25 - postcss-reduce-transforms@7.0.1(postcss@8.5.15): + postcss-reduce-transforms@7.0.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - postcss-replace-overflow-wrap@4.0.0(postcss@8.5.15): + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 - postcss-selector-not@9.0.0(postcss@8.5.15): + postcss-selector-not@9.0.0(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 postcss-selector-parser@7.1.1: @@ -9481,22 +9503,22 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@7.1.1(postcss@8.5.15): + postcss-svgo@7.1.1(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-value-parser: 4.2.0 - svgo: 4.0.1 + svgo: 4.0.2 - postcss-unique-selectors@7.0.5(postcss@8.5.15): + postcss-unique-selectors@7.0.5(postcss@8.5.25): dependencies: - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 postcss-value-parser@4.2.0: {} - postcss@8.5.15: + postcss@8.5.25: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -9534,7 +9556,7 @@ snapshots: punycode@2.3.1: {} - query-string@9.3.1: + query-string@9.4.1: dependencies: decode-uri-component: 0.4.1 filter-obj: 5.1.0 @@ -9565,7 +9587,7 @@ snapshots: prompts: 2.4.2 react-error-overlay: 6.1.0 recursive-readdir: 2.2.3 - shell-quote: 1.8.4 + shell-quote: 1.10.0 strip-ansi: 6.0.1 text-table: 0.2.0 webpack: 5.105.2 @@ -9585,22 +9607,22 @@ snapshots: react-error-overlay@6.1.0: {} - react-i18next@17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@6.0.3): + react-i18next@17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 - html-parse-stringify: 3.0.1 - i18next: 26.3.4(typescript@6.0.3) + html-parse-stringify: 4.0.1 + i18next: 26.3.6(typescript@6.0.3) react: 17.0.2 use-sync-external-store: 1.6.0(react@17.0.2) optionalDependencies: react-dom: 17.0.2(react@17.0.2) typescript: 6.0.3 - react-i18next@17.0.8(i18next@26.3.4(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@19.2.7)(typescript@6.0.3): + react-i18next@17.0.11(i18next@26.3.6(typescript@6.0.3))(react-dom@17.0.2(react@17.0.2))(react@19.2.7)(typescript@6.0.3): dependencies: '@babel/runtime': 7.29.7 - html-parse-stringify: 3.0.1 - i18next: 26.3.4(typescript@6.0.3) + html-parse-stringify: 4.0.1 + i18next: 26.3.6(typescript@6.0.3) react: 19.2.7 use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: @@ -9613,18 +9635,18 @@ snapshots: react-is@18.3.1: {} - react-redux@8.1.3(@types/react-dom@17.0.26(@types/react@17.0.91))(@types/react@17.0.91)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1): + react-redux@8.1.3(@types/react-dom@17.0.26(@types/react@17.0.93))(@types/react@17.0.93)(react-dom@17.0.2(react@17.0.2))(react@17.0.2)(redux@4.2.1): dependencies: '@babel/runtime': 7.29.7 - '@types/hoist-non-react-statics': 3.3.7(@types/react@17.0.91) + '@types/hoist-non-react-statics': 3.3.7(@types/react@17.0.93) '@types/use-sync-external-store': 0.0.3 hoist-non-react-statics: 3.3.2 react: 17.0.2 react-is: 18.3.1 use-sync-external-store: 1.6.0(react@17.0.2) optionalDependencies: - '@types/react': 17.0.91 - '@types/react-dom': 17.0.26(@types/react@17.0.91) + '@types/react': 17.0.93 + '@types/react-dom': 17.0.26(@types/react@17.0.93) react-dom: 17.0.2(react@17.0.2) redux: 4.2.1 @@ -9774,7 +9796,7 @@ snapshots: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.5.15 + postcss: 8.5.25 source-map: 0.6.1 resolve@1.22.12: @@ -9924,7 +9946,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.4: {} + shell-quote@1.10.0: {} side-channel-list@1.0.1: dependencies: @@ -10134,10 +10156,10 @@ snapshots: strip-final-newline@4.0.0: {} - stylehacks@7.0.8(postcss@8.5.15): + stylehacks@7.0.8(postcss@8.5.25): dependencies: browserslist: 4.28.2 - postcss: 8.5.15 + postcss: 8.5.25 postcss-selector-parser: 7.1.1 supports-color@7.2.0: @@ -10150,7 +10172,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svgo@4.0.1: + svgo@4.0.2: dependencies: commander: 11.1.0 css-select: 5.2.2 @@ -10301,7 +10323,7 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@7.21.0: {} + undici-types@7.24.6: {} undici@8.5.0: {} @@ -10361,8 +10383,6 @@ snapshots: value-equal@1.0.1: {} - void-elements@3.1.0: {} - walk-up-path@4.0.0: {} watchpack@2.5.1: diff --git a/services/idp/pnpm-workspace.yaml b/services/idp/pnpm-workspace.yaml index 06e30a5b9c..9684a135d3 100644 --- a/services/idp/pnpm-workspace.yaml +++ b/services/idp/pnpm-workspace.yaml @@ -20,11 +20,14 @@ supportedArchitectures: - musl overrides: - fast-uri: ">=3.1.2" - undici: ">=7.28.0" - postcss: ">=8.5.10" + brace-expansion: ">=5.0.7" + fast-uri: ">=3.1.4" + js-yaml: ">=4.3.0" + postcss: ">=8.5.18" "serialize-javascript@<7.0.3": ">=7.0.3" - shell-quote: ">=1.8.4" + shell-quote: ">=1.9.0" + svgo: ">=4.0.2" + undici: ">=7.28.0" "@babel/plugin-transform-modules-systemjs": ">=7.29.4" "@xmldom/xmldom": "^0.8.13" From d9c5796d9137bbdbec23c7adf67b8497f091b1cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Wed, 29 Jul 2026 11:21:55 +0200 Subject: [PATCH 13/27] Simplify output, remove spinner lib --- opencloud/pkg/command/posixfs.go | 52 ++++---------------------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index e38a400afb..00cd687bee 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -25,7 +25,6 @@ import ( "github.com/pkg/xattr" "github.com/rs/zerolog" "github.com/spf13/cobra" - "github.com/theckman/yacspin" "github.com/vmihailenco/msgpack/v5" ) @@ -39,7 +38,6 @@ const ( ) var ( - spinner *yacspin.Spinner restartRequired = false ignorer *ignore.Ignorer ) @@ -221,30 +219,11 @@ func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error { return fmt.Errorf("error accessing '%s': %w", indexesPath, err) } - spinnerCfg := yacspin.Config{ - Frequency: 100 * time.Millisecond, - CharSet: yacspin.CharSets[11], - StopCharacter: "✓", - StopColors: []string{"fgGreen"}, - StopFailCharacter: "✗", - StopFailColors: []string{"fgRed"}, - } - - spinner, err = yacspin.New(spinnerCfg) - err = spinner.Start() - if err != nil { - return fmt.Errorf("error creating spinner: %w", err) - } - + fmt.Println("Checking personal spaces...") checkSpaces(filepath.Join(rootPath, "users")) - spinner.Suffix(" Personal spaces check ") - spinner.StopMessage("completed\n") - spinner.Stop() + fmt.Println("Checking project spaces...") checkSpaces(filepath.Join(rootPath, "projects")) - spinner.Suffix(" Project spaces check ") - spinner.StopMessage("completed") - spinner.Stop() if restartRequired { fmt.Println("\n\n ⚠️ Please restart your openCloud instance to apply changes.") @@ -255,8 +234,7 @@ func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error { func checkSpaces(basePath string) { dirEntries, err := os.ReadDir(basePath) if err != nil { - spinner.Message(fmt.Sprintf("Error reading spaces directory '%s'\n", basePath)) - spinner.StopFail() + logFailure("Error reading spaces directory '%s': %v", basePath, err) return } @@ -269,9 +247,6 @@ func checkSpaces(basePath string) { } func checkSpace(spacePath string) { - spinner.Message("") - spinner.Suffix(fmt.Sprintf(" Checking space '%s'", spacePath)) - info, err := os.Stat(spacePath) if err != nil { logFailure("Error accessing path '%s': %v", spacePath, err) @@ -289,12 +264,10 @@ func checkSpace(spacePath string) { } checkSpaceID(spacePath) - checkNodeIDs(spacePath) + checkNodes(spacePath) } func checkSpaceID(spacePath string) { - spinner.Message(" - checking space ID uniqueness") - entries, uniqueIDs, oldestEntry, err := gatherAttributes(spacePath) if err != nil { logFailure("Failed to gather attributes: %v", err) @@ -306,7 +279,6 @@ func checkSpaceID(spacePath string) { } if len(uniqueIDs) > 1 { - spinner.Pause() fmt.Println("\n ⚠ Multiple space IDs found:") for id := range uniqueIDs { fmt.Printf(" - %s\n", id) @@ -325,7 +297,6 @@ func checkSpaceID(spacePath string) { input = strings.TrimSpace(strings.ToLower(input)) if input != "y" { - spinner.Unpause() logFailure("Operation cancelled by user.") return } @@ -338,7 +309,6 @@ func checkSpaceID(spacePath string) { } } fixSpaceID(spacePath, obsoleteIDs, targetID, entries) - spinner.Unpause() } } @@ -364,9 +334,7 @@ func walkNodes(dir string, parentID string) int { if err != nil { logFailure("Failed to fix parent ID for '%s': %v", fullPath, err) } else { - spinner.Pause() fmt.Printf(" + Fixed parent ID for '%s'", fullPath) - spinner.Unpause() fixes++ restartRequired = true } @@ -379,9 +347,7 @@ func walkNodes(dir string, parentID string) int { if err != nil { logFailure("Failed to fix name attribute for '%s': %v", fullPath, err) } else { - spinner.Pause() fmt.Printf(" + Fixed name attribute for '%s'", fullPath) - spinner.Unpause() fixes++ restartRequired = true } @@ -399,9 +365,7 @@ func walkNodes(dir string, parentID string) int { return fixes } -func checkNodeIDs(spacePath string) { - spinner.Message(" - checking nodes") - +func checkNodes(spacePath string) { rootID, err := xattr.Get(spacePath, idAttrName) if err != nil || len(rootID) == 0 { logFailure("Space root '%s' missing '%s' attribute", spacePath, idAttrName) @@ -411,9 +375,7 @@ func checkNodeIDs(spacePath string) { fixes := walkNodes(spacePath, string(rootID)) if fixes > 0 { - spinner.Pause() fmt.Printf("\n ✓ Fixed %d incorrect node attributes in %s\n", fixes, filepath.Base(spacePath)) - spinner.Unpause() } } @@ -584,7 +546,5 @@ func removeAttributes(path string) error { } func logFailure(message string, args ...any) { - spinner.StopFailMessage(fmt.Sprintf("\n"+message, args...)) - spinner.StopFail() - spinner.Start() + fmt.Fprintf(os.Stderr, message+"\n", args...) } From fd0c206a7c4b2501a9690ecefde99d071755f10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Wed, 29 Jul 2026 12:21:43 +0200 Subject: [PATCH 14/27] Also check blobsize and checksums (when --fix-checkums is set) --- opencloud/pkg/command/posixfs.go | 115 +++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 27 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 00cd687bee..1150a8c473 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -2,9 +2,12 @@ package command import ( "bufio" + "bytes" + "context" "fmt" "os" "path/filepath" + "strconv" "strings" "time" @@ -21,6 +24,8 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore" "github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options" "github.com/opencloud-eu/reva/v2/pkg/storage/fs/registry" + "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes" + "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node" "github.com/pkg/xattr" "github.com/rs/zerolog" @@ -28,18 +33,10 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -// Define the names of the extended attributes we are working with. -const ( - parentIDAttrName = "user.oc.parentid" - idAttrName = "user.oc.id" - nameAttrName = "user.oc.name" - spaceIDAttrName = "user.oc.space.id" - ownerIDAttrName = "user.oc.owner.id" -) - var ( - restartRequired = false - ignorer *ignore.Ignorer + restartRequired = false + recalculateChecksums = false + ignorer *ignore.Ignorer ) type IDCacher interface { @@ -196,6 +193,7 @@ func consistencyCmd(cfg *config.Config) *cobra.Command { } consCmd.Flags().StringP("root", "r", "", "Path to the root directory of the posixfs storage") _ = consCmd.MarkFlagRequired("root") + consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") return consCmd } @@ -203,6 +201,7 @@ func consistencyCmd(cfg *config.Config) *cobra.Command { // checkPosixfsConsistency checks the consistency of the posixfs storage. func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error { rootPath, _ := cmd.Flags().GetString("root") + recalculateChecksums, _ = cmd.Flags().GetBool("fix-checksums") indexesPath := filepath.Join(rootPath, "indexes") opt, _ := options.New(map[string]interface{}{ @@ -257,9 +256,9 @@ func checkSpace(spacePath string) { return } - spaceID, err := xattr.Get(spacePath, spaceIDAttrName) + spaceID, err := xattr.Get(spacePath, prefixes.SpaceIDAttr) if err != nil || len(spaceID) == 0 { - logFailure("Error: The directory '%s' does not seem to be a space root, it's missing the '%s' attribute\n", spacePath, spaceIDAttrName) + logFailure("Error: The directory '%s' does not seem to be a space root, it's missing the '%s' attribute\n", spacePath, prefixes.SpaceIDAttr) return } @@ -328,9 +327,9 @@ func walkNodes(dir string, parentID string) int { } // Check if the parent ID attribute matches the expected parent ID, if not, fix it. - actualParentID, err := xattr.Get(fullPath, parentIDAttrName) + actualParentID, err := xattr.Get(fullPath, prefixes.ParentidAttr) if err != nil || string(actualParentID) != parentID { - err = xattr.Set(fullPath, parentIDAttrName, []byte(parentID)) + err = xattr.Set(fullPath, prefixes.ParentidAttr, []byte(parentID)) if err != nil { logFailure("Failed to fix parent ID for '%s': %v", fullPath, err) } else { @@ -341,9 +340,9 @@ func walkNodes(dir string, parentID string) int { } // Check that the name attribute matches the actual name of the file/directory, if not, fix it. - nameAttr, err := xattr.Get(fullPath, nameAttrName) + nameAttr, err := xattr.Get(fullPath, prefixes.NameAttr) if err != nil || string(nameAttr) != entry.Name() { - err = xattr.Set(fullPath, nameAttrName, []byte(entry.Name())) + err = xattr.Set(fullPath, prefixes.NameAttr, []byte(entry.Name())) if err != nil { logFailure("Failed to fix name attribute for '%s': %v", fullPath, err) } else { @@ -354,21 +353,83 @@ func walkNodes(dir string, parentID string) int { } if entry.IsDir() { - nodeID, err := xattr.Get(fullPath, idAttrName) + nodeID, err := xattr.Get(fullPath, prefixes.IDAttr) if err != nil || len(nodeID) == 0 { - logFailure("Directory '%s' missing '%s', skipping its children", fullPath, idAttrName) + logFailure("Directory '%s' missing '%s', skipping its children", fullPath, prefixes.IDAttr) continue } - walkNodes(fullPath, string(nodeID)) + fixes += walkNodes(fullPath, string(nodeID)) + } else { + fixes += checkBlobsize(fullPath) + if recalculateChecksums { + fixes += fixChecksums(fullPath) + } + } + } + return fixes +} + +// checkBlobsize verifies that the stored blobsize attribute matches the actual +// file size and fixes it if it doesn't. It returns the number of fixes applied. +func checkBlobsize(path string) int { + info, err := os.Stat(path) + if err != nil { + logFailure("Error accessing file '%s': %v", path, err) + return 0 + } + + expectedSize := strconv.FormatInt(info.Size(), 10) + blobsize, err := xattr.Get(path, prefixes.BlobsizeAttr) + if err == nil && string(blobsize) == expectedSize { + return 0 + } + + if err := xattr.Set(path, prefixes.BlobsizeAttr, []byte(expectedSize)); err != nil { + logFailure("Failed to fix blobsize for '%s': %v", path, err) + return 0 + } + + fmt.Printf(" + Fixed blobsize for '%s'\n", path) + restartRequired = true + return 1 +} + +// fixChecksums recalculates the sha1, md5 and adler32 checksums of the file and +// updates the stored attributes if they differ. It returns the number of fixes applied. +func fixChecksums(path string) int { + sha1h, md5h, adler32h, err := node.CalculateChecksums(context.Background(), path) + if err != nil { + logFailure("Failed to calculate checksums for '%s': %v", path, err) + return 0 + } + + checksums := map[string][]byte{ + prefixes.ChecksumPrefix + "sha1": sha1h.Sum(nil), + prefixes.ChecksumPrefix + "md5": md5h.Sum(nil), + prefixes.ChecksumPrefix + "adler32": adler32h.Sum(nil), + } + + fixes := 0 + for attrName, sum := range checksums { + current, err := xattr.Get(path, attrName) + if err == nil && bytes.Equal(current, sum) { + continue + } + if err := xattr.Set(path, attrName, sum); err != nil { + logFailure("Failed to fix checksum '%s' for '%s': %v", attrName, path, err) + continue } + fmt.Printf(" + Fixed checksum '%s' for '%s'\n", attrName, path) + restartRequired = true + fixes++ } return fixes } func checkNodes(spacePath string) { - rootID, err := xattr.Get(spacePath, idAttrName) + rootID, err := xattr.Get(spacePath, prefixes.IDAttr) if err != nil || len(rootID) == 0 { - logFailure("Space root '%s' missing '%s' attribute", spacePath, idAttrName) + logFailure("Space root '%s' missing '%s' attribute", spacePath, prefixes.IDAttr) return } @@ -388,13 +449,13 @@ func fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries } // Update space ID itself - fmt.Printf(" Updating directory '%s' with attribute '%s' -> %s\n", filepath.Base(spacePath), idAttrName, targetID) - err = xattr.Set(spacePath, idAttrName, []byte(targetID)) + fmt.Printf(" Updating directory '%s' with attribute '%s' -> %s\n", filepath.Base(spacePath), prefixes.IDAttr, targetID) + err = xattr.Set(spacePath, prefixes.IDAttr, []byte(targetID)) if err != nil { logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) return } - err = xattr.Set(spacePath, spaceIDAttrName, []byte(targetID)) + err = xattr.Set(spacePath, prefixes.SpaceIDAttr, []byte(targetID)) if err != nil { logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) return @@ -429,7 +490,7 @@ func gatherAttributes(path string) ([]EntryInfo, map[string]struct{}, EntryInfo, continue } - parentID, err := xattr.Get(fullPath, parentIDAttrName) + parentID, err := xattr.Get(fullPath, prefixes.ParentidAttr) if err != nil { continue // Skip if attribute doesn't exist or can't be read } @@ -481,7 +542,7 @@ func setAllParentIDAttributes(entries []EntryInfo, targetID string) error { func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { fmt.Printf(" Rewriting index file '%s'\n", basePath) - ownerID, err := xattr.Get(basePath, ownerIDAttrName) + ownerID, err := xattr.Get(basePath, prefixes.OwnerIDAttr) if err != nil { return fmt.Errorf("could not get owner ID from oldest entry '%s' to find index: %w", basePath, err) } From 738f077d0b5269b1cd022773c981f4133953d628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Thu, 30 Jul 2026 08:46:35 +0200 Subject: [PATCH 15/27] Allow for checking specific spaces and files as well --- opencloud/pkg/command/posixfs.go | 216 +++++++++++++++++++++++-------- 1 file changed, 162 insertions(+), 54 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 1150a8c473..fa049d8731 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -17,6 +17,7 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config/parser" oclog "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" + storageUsersConfig "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config" storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/event" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig" @@ -28,7 +29,6 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node" "github.com/pkg/xattr" - "github.com/rs/zerolog" "github.com/spf13/cobra" "github.com/vmihailenco/msgpack/v5" ) @@ -183,46 +183,77 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { } // consistencyCmd returns a command to check the consistency of the posixfs storage. -func consistencyCmd(cfg *config.Config) *cobra.Command { +func consistencyCmd(ocCfg *config.Config) *cobra.Command { consCmd := &cobra.Command{ - Use: "consistency", + Use: "consistency ", Short: "check the consistency of the posixfs storage", + Long: `check the consistency of the posixfs storage. + +The argument determines the scope of the check: + - a storage root: the whole storage (all personal and project spaces) is checked + - a space root: only that space is checked + - a file or folder: only that single entity is checked (and its children, if it is a folder)`, + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + + if err := parser.ParseConfig(ocCfg, true); err != nil { + return configlog.ReturnError(err) + } + + // Parse storage users config + ocCfg.StorageUsers.Commons = ocCfg.Commons + + return configlog.ReturnFatal(storageUsersParser.ParseConfig(ocCfg.StorageUsers)) + }, RunE: func(cmd *cobra.Command, args []string) error { - return checkPosixfsConsistency(cmd, cfg) + cfg := ocCfg.StorageUsers + return checkPosixfsConsistency(cfg, cmd, args[0]) }, } - consCmd.Flags().StringP("root", "r", "", "Path to the root directory of the posixfs storage") - _ = consCmd.MarkFlagRequired("root") consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") return consCmd } -// checkPosixfsConsistency checks the consistency of the posixfs storage. -func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error { - rootPath, _ := cmd.Flags().GetString("root") +// checkPosixfsConsistency checks the consistency of the posixfs storage. The +// given path determines the scope of the check: the whole storage, a single +// space or a single entity within a space. +func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, path string) error { recalculateChecksums, _ = cmd.Flags().GetBool("fix-checksums") - indexesPath := filepath.Join(rootPath, "indexes") - opt, _ := options.New(map[string]interface{}{ - "root": rootPath, - }) - log := zerolog.Nop() - ignorer = ignore.NewIgnorer(opt, &log) + path = filepath.Clean(path) + if _, err := os.Stat(path); err != nil { + return fmt.Errorf("error accessing '%s': %w", path, err) + } - _, err := os.Stat(indexesPath) + rootPath, err := findStorageRoot(path) if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("consistency check failed: '%s' is not a posixfs root", rootPath) - } - return fmt.Errorf("error accessing '%s': %w", indexesPath, err) + return err + } + + drivers := revaconfig.StorageProviderDrivers(cfg) + drivers["posix"] = revaconfig.Posix(cfg, false, false) + opts, err := options.New(drivers["posix"].(map[string]any)) + if err != nil { + return err } - fmt.Println("Checking personal spaces...") - checkSpaces(filepath.Join(rootPath, "users")) + ignorer = ignore.NewIgnorer(opts, nil) + + switch { + case path == rootPath: + fmt.Println("Checking personal spaces...") + checkSpaces(filepath.Join(path, "users")) - fmt.Println("Checking project spaces...") - checkSpaces(filepath.Join(rootPath, "projects")) + fmt.Println("Checking project spaces...") + checkSpaces(filepath.Join(path, "projects")) + case isSpaceRoot(path): + fmt.Printf("Checking space '%s'...\n", path) + checkSpace(path) + default: + fmt.Printf("Checking '%s'...\n", path) + checkEntity(path) + } if restartRequired { fmt.Println("\n\n ⚠️ Please restart your openCloud instance to apply changes.") @@ -230,6 +261,38 @@ func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error { return nil } +// findStorageRoot walks up the directory tree starting at path until it finds a +// directory that contains an "indexes" subdirectory which marks the root of a +// posixfs storage. A user folder inside a space might also be named "indexes", +// so to disambiguate we require that the "indexes" directory is an internal +// directory: the storage's own indexes directory is skipped during assimilation +// and therefore never receives a node ID attribute, whereas a regular user +// folder would have one. +func findStorageRoot(path string) (string, error) { + current := path + for { + indexesPath := filepath.Join(current, "indexes") + if info, err := os.Stat(indexesPath); err == nil && info.IsDir() { + if id, err := xattr.Get(indexesPath, prefixes.IDAttr); err != nil || len(id) == 0 { + return current, nil + } + } + + parent := filepath.Dir(current) + if parent == current { + return "", fmt.Errorf("'%s' does not appear to be inside a posixfs storage (no 'indexes' directory found)", path) + } + current = parent + } +} + +// isSpaceRoot reports whether the given path is a space root, which is +// identified by the presence of the space ID attribute. +func isSpaceRoot(path string) bool { + spaceID, err := xattr.Get(path, prefixes.SpaceIDAttr) + return err == nil && len(spaceID) > 0 +} + func checkSpaces(basePath string) { dirEntries, err := os.ReadDir(basePath) if err != nil { @@ -326,31 +389,7 @@ func walkNodes(dir string, parentID string) int { continue } - // Check if the parent ID attribute matches the expected parent ID, if not, fix it. - actualParentID, err := xattr.Get(fullPath, prefixes.ParentidAttr) - if err != nil || string(actualParentID) != parentID { - err = xattr.Set(fullPath, prefixes.ParentidAttr, []byte(parentID)) - if err != nil { - logFailure("Failed to fix parent ID for '%s': %v", fullPath, err) - } else { - fmt.Printf(" + Fixed parent ID for '%s'", fullPath) - fixes++ - restartRequired = true - } - } - - // Check that the name attribute matches the actual name of the file/directory, if not, fix it. - nameAttr, err := xattr.Get(fullPath, prefixes.NameAttr) - if err != nil || string(nameAttr) != entry.Name() { - err = xattr.Set(fullPath, prefixes.NameAttr, []byte(entry.Name())) - if err != nil { - logFailure("Failed to fix name attribute for '%s': %v", fullPath, err) - } else { - fmt.Printf(" + Fixed name attribute for '%s'", fullPath) - fixes++ - restartRequired = true - } - } + fixes += checkNodeAttributes(fullPath, entry.Name(), parentID, entry.IsDir()) if entry.IsDir() { nodeID, err := xattr.Get(fullPath, prefixes.IDAttr) @@ -359,16 +398,85 @@ func walkNodes(dir string, parentID string) int { continue } fixes += walkNodes(fullPath, string(nodeID)) + } + } + return fixes +} + +// checkNodeAttributes checks and fixes the parent ID and name attributes of a +// single node. For files it additionally checks the blobsize and, when +// requested, the checksums. It returns the number of fixes applied. +func checkNodeAttributes(path, name, parentID string, isDir bool) int { + fixes := 0 + + // Check if the parent ID attribute matches the expected parent ID, if not, fix it. + actualParentID, err := xattr.Get(path, prefixes.ParentidAttr) + if err != nil || string(actualParentID) != parentID { + if err := xattr.Set(path, prefixes.ParentidAttr, []byte(parentID)); err != nil { + logFailure("Failed to fix parent ID for '%s': %v", path, err) } else { - fixes += checkBlobsize(fullPath) - if recalculateChecksums { - fixes += fixChecksums(fullPath) - } + fmt.Printf(" + Fixed parent ID for '%s'\n", path) + fixes++ + restartRequired = true + } + } + + // Check that the name attribute matches the actual name of the file/directory, if not, fix it. + nameAttr, err := xattr.Get(path, prefixes.NameAttr) + if err != nil || string(nameAttr) != name { + if err := xattr.Set(path, prefixes.NameAttr, []byte(name)); err != nil { + logFailure("Failed to fix name attribute for '%s': %v", path, err) + } else { + fmt.Printf(" + Fixed name attribute for '%s'\n", path) + fixes++ + restartRequired = true + } + } + + if !isDir { + fixes += checkBlobsize(path) + if recalculateChecksums { + fixes += fixChecksums(path) } } + return fixes } +// checkEntity checks a single file or folder within a space, including its own +// parent ID, name and (for files) blobsize/checksums. If the entity is a folder +// its children are checked recursively. +func checkEntity(path string) { + info, err := os.Stat(path) + if err != nil { + logFailure("Error accessing path '%s': %v", path, err) + return + } + + // The expected parent ID is the ID attribute of the containing directory. + parentDir := filepath.Dir(path) + parentID, err := xattr.Get(parentDir, prefixes.IDAttr) + if err != nil || len(parentID) == 0 { + logFailure("Parent directory '%s' is missing the '%s' attribute", parentDir, prefixes.IDAttr) + return + } + + fixes := checkNodeAttributes(path, info.Name(), string(parentID), info.IsDir()) + + if info.IsDir() { + nodeID, err := xattr.Get(path, prefixes.IDAttr) + if err != nil || len(nodeID) == 0 { + logFailure("Directory '%s' missing '%s' attribute", path, prefixes.IDAttr) + } else { + fixes += walkNodes(path, string(nodeID)) + } + } + + if fixes > 0 { + fmt.Printf(" ✓ Fixed %d incorrect node attributes for %s\n", fixes, filepath.Base(path)) + } +} + // checkBlobsize verifies that the stored blobsize attribute matches the actual // file size and fixes it if it doesn't. It returns the number of fixes applied. func checkBlobsize(path string) int { @@ -436,7 +544,7 @@ func checkNodes(spacePath string) { fixes := walkNodes(spacePath, string(rootID)) if fixes > 0 { - fmt.Printf("\n ✓ Fixed %d incorrect node attributes in %s\n", fixes, filepath.Base(spacePath)) + fmt.Printf(" ✓ Fixed %d incorrect node attributes in %s\n", fixes, filepath.Base(spacePath)) } } From f51272deb967b15040c9e2bce7c88e5435a7af7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 08:58:19 +0200 Subject: [PATCH 16/27] Streamline logging in the commands --- opencloud/pkg/command/posixfs.go | 10 +++------- opencloud/pkg/command/root.go | 14 ++++++++++++-- opencloud/pkg/command/shares.go | 14 ++------------ 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index fa049d8731..5b36d7fd93 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -15,7 +15,6 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config" "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/config/parser" - oclog "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" storageUsersConfig "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config" storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser" @@ -135,11 +134,7 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { fmt.Fprintf(os.Stderr, "Failed to create event stream for posix driver: %v\n", err) os.Exit(1) } - log := oclog.NewLogger( - oclog.Name("posixfs scan"), - oclog.Level("error"), - oclog.Pretty(true), - oclog.Color(false)).Logger + log := logger("posixfs") if !defaultRoot { log = log.With().Str("basepath", root).Logger() @@ -219,6 +214,7 @@ The argument determines the scope of the check: // given path determines the scope of the check: the whole storage, a single // space or a single entity within a space. func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, path string) error { + log := logger("posixfs") recalculateChecksums, _ = cmd.Flags().GetBool("fix-checksums") path = filepath.Clean(path) @@ -238,7 +234,7 @@ func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, return err } - ignorer = ignore.NewIgnorer(opts, nil) + ignorer = ignore.NewIgnorer(opts, &log) switch { case path == rootPath: diff --git a/opencloud/pkg/command/root.go b/opencloud/pkg/command/root.go index 834bc68e02..01ff6a77a7 100644 --- a/opencloud/pkg/command/root.go +++ b/opencloud/pkg/command/root.go @@ -6,11 +6,13 @@ import ( "os/signal" "syscall" + "github.com/rs/zerolog" + "github.com/spf13/cobra" + "github.com/opencloud-eu/opencloud/opencloud/pkg/register" "github.com/opencloud-eu/opencloud/pkg/clihelper" "github.com/opencloud-eu/opencloud/pkg/config" - - "github.com/spf13/cobra" + oclog "github.com/opencloud-eu/opencloud/pkg/log" ) // Execute is the entry point for the opencloud command. @@ -38,3 +40,11 @@ func Execute() error { ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP) return app.ExecuteContext(ctx) } + +func logger(name string) zerolog.Logger { + return oclog.NewLogger( + oclog.Name(name), + oclog.Level("info"), + oclog.Pretty(true), + oclog.Color(true)).Logger +} diff --git a/opencloud/pkg/command/shares.go b/opencloud/pkg/command/shares.go index 98cee2da4a..0cdfeea5d1 100644 --- a/opencloud/pkg/command/shares.go +++ b/opencloud/pkg/command/shares.go @@ -9,7 +9,6 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config" "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/config/parser" - oclog "github.com/opencloud-eu/opencloud/pkg/log" mregistry "github.com/opencloud-eu/opencloud/pkg/registry" sharing "github.com/opencloud-eu/opencloud/services/sharing/pkg/config" sharingparser "github.com/opencloud-eu/opencloud/services/sharing/pkg/config/parser" @@ -85,7 +84,7 @@ func cleanup(_ *cobra.Command, cfg *config.Config) error { return configlog.ReturnError(errors.New("cleanup is only implemented for the jsoncs3 share manager")) } - l := logger() + l := logger("migrate") zerolog.SetGlobalLevel(zerolog.InfoLevel) @@ -94,7 +93,7 @@ func cleanup(_ *cobra.Command, cfg *config.Config) error { if !ok { return configlog.ReturnError(errors.New("Unknown share manager type '" + driver + "'")) } - mgr, err := f(rcfg[driver].(map[string]any), l) + mgr, err := f(rcfg[driver].(map[string]any), &l) if err != nil { return configlog.ReturnError(err) } @@ -167,12 +166,3 @@ func revaShareConfig(cfg *sharing.Config) map[string]any { }, } } - -func logger() *zerolog.Logger { - log := oclog.NewLogger( - oclog.Name("migrate"), - oclog.Level("info"), - oclog.Pretty(true), - oclog.Color(true)).Logger - return &log -} From d26cf954567d783071dc2983ee94437d5f4f502e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 09:49:10 +0200 Subject: [PATCH 17/27] Check if the given path is part of the storage --- opencloud/pkg/command/posixfs.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 5b36d7fd93..54e8e688c0 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -188,7 +188,7 @@ The argument determines the scope of the check: - a storage root: the whole storage (all personal and project spaces) is checked - a space root: only that space is checked - a file or folder: only that single entity is checked (and its children, if it is a folder)`, - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { if err := parser.ParseConfig(ocCfg, true); err != nil { @@ -202,7 +202,11 @@ The argument determines the scope of the check: }, RunE: func(cmd *cobra.Command, args []string) error { cfg := ocCfg.StorageUsers - return checkPosixfsConsistency(cfg, cmd, args[0]) + path := cfg.Drivers.Posix.Root + if len(args) > 0 { + path = args[0] + } + return checkPosixfsConsistency(cfg, cmd, path) }, } consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") @@ -235,6 +239,7 @@ func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, } ignorer = ignore.NewIgnorer(opts, &log) + contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) switch { case path == rootPath: @@ -246,9 +251,11 @@ func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, case isSpaceRoot(path): fmt.Printf("Checking space '%s'...\n", path) checkSpace(path) - default: + case contained: fmt.Printf("Checking '%s'...\n", path) checkEntity(path) + default: + return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) } if restartRequired { From 31ad7e5e2f3692e0bb54a992c7b92e4d1d72637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 10:58:53 +0200 Subject: [PATCH 18/27] Allow for providing multiple paths to check --- opencloud/pkg/command/posixfs.go | 76 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 54e8e688c0..37ce00abe8 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -180,15 +180,18 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { // consistencyCmd returns a command to check the consistency of the posixfs storage. func consistencyCmd(ocCfg *config.Config) *cobra.Command { consCmd := &cobra.Command{ - Use: "consistency ", + Use: "consistency [path ...]", Short: "check the consistency of the posixfs storage", Long: `check the consistency of the posixfs storage. -The argument determines the scope of the check: +You can specify one or more paths to limit the scope of the check. +If no path is provided, the whole storage is checked. + +The provided arguments determines the scope of the check: - a storage root: the whole storage (all personal and project spaces) is checked - a space root: only that space is checked - a file or folder: only that single entity is checked (and its children, if it is a folder)`, - Args: cobra.MaximumNArgs(1), + Args: cobra.ArbitraryArgs, PreRunE: func(cmd *cobra.Command, args []string) error { if err := parser.ParseConfig(ocCfg, true); err != nil { @@ -202,11 +205,7 @@ The argument determines the scope of the check: }, RunE: func(cmd *cobra.Command, args []string) error { cfg := ocCfg.StorageUsers - path := cfg.Drivers.Posix.Root - if len(args) > 0 { - path = args[0] - } - return checkPosixfsConsistency(cfg, cmd, path) + return checkPosixfsConsistency(cfg, cmd, args) }, } consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") @@ -217,45 +216,48 @@ The argument determines the scope of the check: // checkPosixfsConsistency checks the consistency of the posixfs storage. The // given path determines the scope of the check: the whole storage, a single // space or a single entity within a space. -func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, path string) error { +func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, paths []string) error { + if len(paths) == 0 { + paths = []string{cfg.Drivers.Posix.Root} + } log := logger("posixfs") recalculateChecksums, _ = cmd.Flags().GetBool("fix-checksums") - path = filepath.Clean(path) - if _, err := os.Stat(path); err != nil { - return fmt.Errorf("error accessing '%s': %w", path, err) - } - - rootPath, err := findStorageRoot(path) - if err != nil { - return err - } - drivers := revaconfig.StorageProviderDrivers(cfg) drivers["posix"] = revaconfig.Posix(cfg, false, false) opts, err := options.New(drivers["posix"].(map[string]any)) if err != nil { return err } - ignorer = ignore.NewIgnorer(opts, &log) - contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) - - switch { - case path == rootPath: - fmt.Println("Checking personal spaces...") - checkSpaces(filepath.Join(path, "users")) - - fmt.Println("Checking project spaces...") - checkSpaces(filepath.Join(path, "projects")) - case isSpaceRoot(path): - fmt.Printf("Checking space '%s'...\n", path) - checkSpace(path) - case contained: - fmt.Printf("Checking '%s'...\n", path) - checkEntity(path) - default: - return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) + + for _, path := range paths { + rootPath, err := findStorageRoot(path) + if err != nil { + return err + } + path = filepath.Clean(path) + if _, err := os.Stat(path); err != nil { + return fmt.Errorf("error accessing '%s': %w", path, err) + } + contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) + + switch { + case path == rootPath: + fmt.Println("Checking personal spaces...") + checkSpaces(filepath.Join(path, "users")) + + fmt.Println("Checking project spaces...") + checkSpaces(filepath.Join(path, "projects")) + case isSpaceRoot(path): + fmt.Printf("Checking space '%s'...\n", path) + checkSpace(path) + case contained: + fmt.Printf("Checking '%s'...\n", path) + checkEntity(path) + default: + return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) + } } if restartRequired { From e0b28660349c5a960db18950334806b71d697675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 11:03:45 +0200 Subject: [PATCH 19/27] Fix typos --- opencloud/pkg/command/posixfs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 37ce00abe8..94d90abc56 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -181,8 +181,8 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { func consistencyCmd(ocCfg *config.Config) *cobra.Command { consCmd := &cobra.Command{ Use: "consistency [path ...]", - Short: "check the consistency of the posixfs storage", - Long: `check the consistency of the posixfs storage. + Short: "Check the consistency of the posixfs storage", + Long: `Check the consistency of the posixfs storage. You can specify one or more paths to limit the scope of the check. If no path is provided, the whole storage is checked. From ba2a11ed1b6316bb67e4c9e0a0bdf29c2f43480c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 11:54:36 +0200 Subject: [PATCH 20/27] Improve wording --- opencloud/pkg/command/posixfs.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 94d90abc56..567d726a8f 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -190,7 +190,7 @@ If no path is provided, the whole storage is checked. The provided arguments determines the scope of the check: - a storage root: the whole storage (all personal and project spaces) is checked - a space root: only that space is checked - - a file or folder: only that single entity is checked (and its children, if it is a folder)`, + - a file or directory: only that single entity is checked (and its children, if it is a directory)`, Args: cobra.ArbitraryArgs, PreRunE: func(cmd *cobra.Command, args []string) error { @@ -268,11 +268,11 @@ func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, // findStorageRoot walks up the directory tree starting at path until it finds a // directory that contains an "indexes" subdirectory which marks the root of a -// posixfs storage. A user folder inside a space might also be named "indexes", +// posixfs storage. A user directory inside a space might also be named "indexes", // so to disambiguate we require that the "indexes" directory is an internal // directory: the storage's own indexes directory is skipped during assimilation // and therefore never receives a node ID attribute, whereas a regular user -// folder would have one. +// directory would have one. func findStorageRoot(path string) (string, error) { current := path for { @@ -448,8 +448,8 @@ func checkNodeAttributes(path, name, parentID string, isDir bool) int { return fixes } -// checkEntity checks a single file or folder within a space, including its own -// parent ID, name and (for files) blobsize/checksums. If the entity is a folder +// checkEntity checks a single file or directory within a space, including its own +// parent ID, name and (for files) blobsize/checksums. If the entity is a directory // its children are checked recursively. func checkEntity(path string) { info, err := os.Stat(path) From 1393cbf970e627d0b41fc683805629a6d8d5ff98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 12:17:19 +0200 Subject: [PATCH 21/27] Move consistency check logic into a checker struct This makes it possible to get rid of the globals without passing multiple state vars around. --- opencloud/pkg/command/posixfs.go | 514 +------------------ opencloud/pkg/command/posixfs_consistency.go | 492 ++++++++++++++++++ 2 files changed, 515 insertions(+), 491 deletions(-) create mode 100644 opencloud/pkg/command/posixfs_consistency.go diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 567d726a8f..751e3db1be 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -1,14 +1,9 @@ package command import ( - "bufio" - "bytes" - "context" "fmt" "os" "path/filepath" - "strconv" - "strings" "time" "github.com/opencloud-eu/opencloud/opencloud/pkg/register" @@ -16,7 +11,6 @@ import ( "github.com/opencloud-eu/opencloud/pkg/config/configlog" "github.com/opencloud-eu/opencloud/pkg/config/parser" "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" - storageUsersConfig "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config" storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/event" "github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig" @@ -25,17 +19,9 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options" "github.com/opencloud-eu/reva/v2/pkg/storage/fs/registry" "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes" - "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node" "github.com/pkg/xattr" "github.com/spf13/cobra" - "github.com/vmihailenco/msgpack/v5" -) - -var ( - restartRequired = false - recalculateChecksums = false - ignorer *ignore.Ignorer ) type IDCacher interface { @@ -205,7 +191,27 @@ The provided arguments determines the scope of the check: }, RunE: func(cmd *cobra.Command, args []string) error { cfg := ocCfg.StorageUsers - return checkPosixfsConsistency(cfg, cmd, args) + if len(args) == 0 { + args = []string{cfg.Drivers.Posix.Root} + } + log := logger("posixfs") + recalculateChecksums, _ := cmd.Flags().GetBool("fix-checksums") + + drivers := revaconfig.StorageProviderDrivers(cfg) + drivers["posix"] = revaconfig.Posix(cfg, false, false) + opts, err := options.New(drivers["posix"].(map[string]any)) + if err != nil { + return err + } + ignorer := ignore.NewIgnorer(opts, &log) + + checker := &consistencyChecker{ + cfg: cfg, + ignorer: ignorer, + recalculateChecksums: recalculateChecksums, + } + + return checker.Check(args) }, } consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") @@ -213,57 +219,8 @@ The provided arguments determines the scope of the check: return consCmd } -// checkPosixfsConsistency checks the consistency of the posixfs storage. The -// given path determines the scope of the check: the whole storage, a single -// space or a single entity within a space. -func checkPosixfsConsistency(cfg *storageUsersConfig.Config, cmd *cobra.Command, paths []string) error { - if len(paths) == 0 { - paths = []string{cfg.Drivers.Posix.Root} - } - log := logger("posixfs") - recalculateChecksums, _ = cmd.Flags().GetBool("fix-checksums") - - drivers := revaconfig.StorageProviderDrivers(cfg) - drivers["posix"] = revaconfig.Posix(cfg, false, false) - opts, err := options.New(drivers["posix"].(map[string]any)) - if err != nil { - return err - } - ignorer = ignore.NewIgnorer(opts, &log) - - for _, path := range paths { - rootPath, err := findStorageRoot(path) - if err != nil { - return err - } - path = filepath.Clean(path) - if _, err := os.Stat(path); err != nil { - return fmt.Errorf("error accessing '%s': %w", path, err) - } - contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) - - switch { - case path == rootPath: - fmt.Println("Checking personal spaces...") - checkSpaces(filepath.Join(path, "users")) - - fmt.Println("Checking project spaces...") - checkSpaces(filepath.Join(path, "projects")) - case isSpaceRoot(path): - fmt.Printf("Checking space '%s'...\n", path) - checkSpace(path) - case contained: - fmt.Printf("Checking '%s'...\n", path) - checkEntity(path) - default: - return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) - } - } - - if restartRequired { - fmt.Println("\n\n ⚠️ Please restart your openCloud instance to apply changes.") - } - return nil +func logFailure(message string, args ...any) { + fmt.Fprintf(os.Stderr, message+"\n", args...) } // findStorageRoot walks up the directory tree starting at path until it finds a @@ -297,428 +254,3 @@ func isSpaceRoot(path string) bool { spaceID, err := xattr.Get(path, prefixes.SpaceIDAttr) return err == nil && len(spaceID) > 0 } - -func checkSpaces(basePath string) { - dirEntries, err := os.ReadDir(basePath) - if err != nil { - logFailure("Error reading spaces directory '%s': %v", basePath, err) - return - } - - for _, entry := range dirEntries { - if entry.IsDir() { - fullPath := filepath.Join(basePath, entry.Name()) - checkSpace(fullPath) - } - } -} - -func checkSpace(spacePath string) { - info, err := os.Stat(spacePath) - if err != nil { - logFailure("Error accessing path '%s': %v", spacePath, err) - return - } - if !info.IsDir() { - logFailure("Error: The provided path '%s' is not a directory\n", spacePath) - return - } - - spaceID, err := xattr.Get(spacePath, prefixes.SpaceIDAttr) - if err != nil || len(spaceID) == 0 { - logFailure("Error: The directory '%s' does not seem to be a space root, it's missing the '%s' attribute\n", spacePath, prefixes.SpaceIDAttr) - return - } - - checkSpaceID(spacePath) - checkNodes(spacePath) -} - -func checkSpaceID(spacePath string) { - entries, uniqueIDs, oldestEntry, err := gatherAttributes(spacePath) - if err != nil { - logFailure("Failed to gather attributes: %v", err) - return - } - - if len(entries) == 0 { - return - } - - if len(uniqueIDs) > 1 { - fmt.Println("\n ⚠ Multiple space IDs found:") - for id := range uniqueIDs { - fmt.Printf(" - %s\n", id) - } - - fmt.Printf("\n ⏳ Oldest entry is '%s' (modified on %s).\n", - filepath.Base(oldestEntry.Path), oldestEntry.ModTime.Format(time.RFC1123)) - - targetID := oldestEntry.ParentID - fmt.Printf(" ✅ Proposed target Parent ID: %s\n", targetID) - - fmt.Printf("\n Do you want to unify all parent IDs to '%s'? This will modify %d entries, the directory, and the user index. (y/N): ", targetID, len(entries)) - - reader := bufio.NewReader(os.Stdin) - input, _ := reader.ReadString('\n') - input = strings.TrimSpace(strings.ToLower(input)) - - if input != "y" { - logFailure("Operation cancelled by user.") - return - } - restartRequired = true - - obsoleteIDs := []string{} - for id := range uniqueIDs { - if id != targetID { - obsoleteIDs = append(obsoleteIDs, id) - } - } - fixSpaceID(spacePath, obsoleteIDs, targetID, entries) - } -} - -func walkNodes(dir string, parentID string) int { - fixes := 0 - entries, err := os.ReadDir(dir) - if err != nil { - logFailure("Error reading directory '%s': %v", dir, err) - return 0 - } - - for _, entry := range entries { - fullPath := filepath.Join(dir, entry.Name()) - - if ignorer.IsIgnored(fullPath) { - continue - } - - fixes += checkNodeAttributes(fullPath, entry.Name(), parentID, entry.IsDir()) - - if entry.IsDir() { - nodeID, err := xattr.Get(fullPath, prefixes.IDAttr) - if err != nil || len(nodeID) == 0 { - logFailure("Directory '%s' missing '%s', skipping its children", fullPath, prefixes.IDAttr) - continue - } - fixes += walkNodes(fullPath, string(nodeID)) - } - } - return fixes -} - -// checkNodeAttributes checks and fixes the parent ID and name attributes of a -// single node. For files it additionally checks the blobsize and, when -// requested, the checksums. It returns the number of fixes applied. -func checkNodeAttributes(path, name, parentID string, isDir bool) int { - fixes := 0 - - // Check if the parent ID attribute matches the expected parent ID, if not, fix it. - actualParentID, err := xattr.Get(path, prefixes.ParentidAttr) - if err != nil || string(actualParentID) != parentID { - if err := xattr.Set(path, prefixes.ParentidAttr, []byte(parentID)); err != nil { - logFailure("Failed to fix parent ID for '%s': %v", path, err) - } else { - fmt.Printf(" + Fixed parent ID for '%s'\n", path) - fixes++ - restartRequired = true - } - } - - // Check that the name attribute matches the actual name of the file/directory, if not, fix it. - nameAttr, err := xattr.Get(path, prefixes.NameAttr) - if err != nil || string(nameAttr) != name { - if err := xattr.Set(path, prefixes.NameAttr, []byte(name)); err != nil { - logFailure("Failed to fix name attribute for '%s': %v", path, err) - } else { - fmt.Printf(" + Fixed name attribute for '%s'\n", path) - fixes++ - restartRequired = true - } - } - - if !isDir { - fixes += checkBlobsize(path) - if recalculateChecksums { - fixes += fixChecksums(path) - } - } - - return fixes -} - -// checkEntity checks a single file or directory within a space, including its own -// parent ID, name and (for files) blobsize/checksums. If the entity is a directory -// its children are checked recursively. -func checkEntity(path string) { - info, err := os.Stat(path) - if err != nil { - logFailure("Error accessing path '%s': %v", path, err) - return - } - - // The expected parent ID is the ID attribute of the containing directory. - parentDir := filepath.Dir(path) - parentID, err := xattr.Get(parentDir, prefixes.IDAttr) - if err != nil || len(parentID) == 0 { - logFailure("Parent directory '%s' is missing the '%s' attribute", parentDir, prefixes.IDAttr) - return - } - - fixes := checkNodeAttributes(path, info.Name(), string(parentID), info.IsDir()) - - if info.IsDir() { - nodeID, err := xattr.Get(path, prefixes.IDAttr) - if err != nil || len(nodeID) == 0 { - logFailure("Directory '%s' missing '%s' attribute", path, prefixes.IDAttr) - } else { - fixes += walkNodes(path, string(nodeID)) - } - } - - if fixes > 0 { - fmt.Printf(" ✓ Fixed %d incorrect node attributes for %s\n", fixes, filepath.Base(path)) - } -} - -// checkBlobsize verifies that the stored blobsize attribute matches the actual -// file size and fixes it if it doesn't. It returns the number of fixes applied. -func checkBlobsize(path string) int { - info, err := os.Stat(path) - if err != nil { - logFailure("Error accessing file '%s': %v", path, err) - return 0 - } - - expectedSize := strconv.FormatInt(info.Size(), 10) - blobsize, err := xattr.Get(path, prefixes.BlobsizeAttr) - if err == nil && string(blobsize) == expectedSize { - return 0 - } - - if err := xattr.Set(path, prefixes.BlobsizeAttr, []byte(expectedSize)); err != nil { - logFailure("Failed to fix blobsize for '%s': %v", path, err) - return 0 - } - - fmt.Printf(" + Fixed blobsize for '%s'\n", path) - restartRequired = true - return 1 -} - -// fixChecksums recalculates the sha1, md5 and adler32 checksums of the file and -// updates the stored attributes if they differ. It returns the number of fixes applied. -func fixChecksums(path string) int { - sha1h, md5h, adler32h, err := node.CalculateChecksums(context.Background(), path) - if err != nil { - logFailure("Failed to calculate checksums for '%s': %v", path, err) - return 0 - } - - checksums := map[string][]byte{ - prefixes.ChecksumPrefix + "sha1": sha1h.Sum(nil), - prefixes.ChecksumPrefix + "md5": md5h.Sum(nil), - prefixes.ChecksumPrefix + "adler32": adler32h.Sum(nil), - } - - fixes := 0 - for attrName, sum := range checksums { - current, err := xattr.Get(path, attrName) - if err == nil && bytes.Equal(current, sum) { - continue - } - if err := xattr.Set(path, attrName, sum); err != nil { - logFailure("Failed to fix checksum '%s' for '%s': %v", attrName, path, err) - continue - } - fmt.Printf(" + Fixed checksum '%s' for '%s'\n", attrName, path) - restartRequired = true - fixes++ - } - return fixes -} - -func checkNodes(spacePath string) { - rootID, err := xattr.Get(spacePath, prefixes.IDAttr) - if err != nil || len(rootID) == 0 { - logFailure("Space root '%s' missing '%s' attribute", spacePath, prefixes.IDAttr) - return - } - - fixes := walkNodes(spacePath, string(rootID)) - - if fixes > 0 { - fmt.Printf(" ✓ Fixed %d incorrect node attributes in %s\n", fixes, filepath.Base(spacePath)) - } -} - -func fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries []EntryInfo) { - // Set all parentid attributes to the proper space ID - err := setAllParentIDAttributes(entries, targetID) - if err != nil { - logFailure("an error occurred during file attribute update: %v", err) - return - } - - // Update space ID itself - fmt.Printf(" Updating directory '%s' with attribute '%s' -> %s\n", filepath.Base(spacePath), prefixes.IDAttr, targetID) - err = xattr.Set(spacePath, prefixes.IDAttr, []byte(targetID)) - if err != nil { - logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) - return - } - err = xattr.Set(spacePath, prefixes.SpaceIDAttr, []byte(targetID)) - if err != nil { - logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) - return - } - - // update the index - err = updateOwnerIndexFile(spacePath, obsoleteIDs) - if err != nil { - logFailure("Could not update the owner index file: %v", err) - } -} - -func gatherAttributes(path string) ([]EntryInfo, map[string]struct{}, EntryInfo, error) { - dirEntries, err := os.ReadDir(path) - if err != nil { - return nil, nil, EntryInfo{}, fmt.Errorf("failed to read directory: %w", err) - } - - var allEntries []EntryInfo - uniqueIDs := make(map[string]struct{}) - var oldestEntry EntryInfo - oldestTime := time.Now().Add(100 * 365 * 24 * time.Hour) // Set to a future date to find the oldest entry - - for _, entry := range dirEntries { - fullPath := filepath.Join(path, entry.Name()) - if ignorer.IsIgnored(fullPath) { - continue - } - info, err := os.Stat(fullPath) - if err != nil { - fmt.Printf(" - Warning: could not stat %s: %v\n", entry.Name(), err) - continue - } - - parentID, err := xattr.Get(fullPath, prefixes.ParentidAttr) - if err != nil { - continue // Skip if attribute doesn't exist or can't be read - } - - entryInfo := EntryInfo{ - Path: fullPath, - ModTime: info.ModTime(), - ParentID: string(parentID), - } - - allEntries = append(allEntries, entryInfo) - uniqueIDs[string(parentID)] = struct{}{} - - if entryInfo.ModTime.Before(oldestTime) { - oldestTime = entryInfo.ModTime - oldestEntry = entryInfo - } - } - - return allEntries, uniqueIDs, oldestEntry, nil -} - -func setAllParentIDAttributes(entries []EntryInfo, targetID string) error { - fmt.Printf(" Setting all parent IDs to '%s':\n", targetID) - - for _, entry := range entries { - if entry.ParentID == targetID { - fmt.Printf(" - Skipping '%s' (already has target ID).\n", filepath.Base(entry.Path)) - continue - } - - fmt.Printf(" - Removing all attributes from '%s'. It will be re-assimilated\n", filepath.Base(entry.Path)) - filepath.WalkDir(entry.Path, func(path string, d os.DirEntry, err error) error { - if err != nil { - return fmt.Errorf("error walking path '%s': %w", path, err) - } - - // Remove all attributes from the file. - if err := removeAttributes(path); err != nil { - fmt.Printf("failed to remove attributes from '%s': %v", path, err) - } - return nil - }) - } - return nil -} - -// updateOwnerIndexFile handles the logic of reading, modifying, and writing the MessagePack index file. -func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { - fmt.Printf(" Rewriting index file '%s'\n", basePath) - - ownerID, err := xattr.Get(basePath, prefixes.OwnerIDAttr) - if err != nil { - return fmt.Errorf("could not get owner ID from oldest entry '%s' to find index: %w", basePath, err) - } - - indexPath := filepath.Join(basePath, "../../indexes/by-user-id", string(ownerID)+".mpk") - indexPath = filepath.Clean(indexPath) - - // Read the MessagePack file - fileData, err := os.ReadFile(indexPath) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("index file does not exist, skipping update") - } - return fmt.Errorf("could not read index file: %w", err) - } - var indexMap map[string]string - if err := msgpack.Unmarshal(fileData, &indexMap); err != nil { - return fmt.Errorf("failed to parse MessagePack index file (is it corrupt?): %w", err) - } - - // Remove obsolete IDs from the map - itemsRemoved := 0 - for _, id := range obsoleteIDs { - if _, exists := indexMap[id]; exists { - fmt.Printf(" - Removing obsolete ID '%s' from index.\n", id) - delete(indexMap, id) - itemsRemoved++ - } else { - fmt.Printf(" - Obsolete ID '%s' not found in index\n", id) - } - } - - if itemsRemoved == 0 { - return nil - } - - // Write the data back to the file - updatedData, err := msgpack.Marshal(&indexMap) - if err != nil { - return fmt.Errorf("failed to marshal updated index map: %w", err) - } - if err := os.WriteFile(indexPath, updatedData, 0644); err != nil { - return fmt.Errorf("failed to write updated index file: %w", err) - } - - fmt.Printf(" ✓ Successfully removed %d item(s) and saved index file.\n", itemsRemoved) - return nil -} - -func removeAttributes(path string) error { - attrNames, err := xattr.List(path) - if err != nil { - return fmt.Errorf("failed to list attributes for '%s': %w", path, err) - } - - for _, attrName := range attrNames { - if err := xattr.Remove(path, attrName); err != nil { - return fmt.Errorf("failed to remove attribute '%s' from '%s': %w", attrName, path, err) - } - } - return nil -} - -func logFailure(message string, args ...any) { - fmt.Fprintf(os.Stderr, message+"\n", args...) -} diff --git a/opencloud/pkg/command/posixfs_consistency.go b/opencloud/pkg/command/posixfs_consistency.go new file mode 100644 index 0000000000..c56677c65e --- /dev/null +++ b/opencloud/pkg/command/posixfs_consistency.go @@ -0,0 +1,492 @@ +// Copyright 2026 OpenCloud GmbH +// SPDX-License-Identifier: Apache-2.0 + +package command + +import ( + "bufio" + "bytes" + "context" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" + storageUsersConfig "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config" + "github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore" + "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes" + "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node" + "github.com/pkg/xattr" + "github.com/shamaton/msgpack/v2" +) + +type consistencyChecker struct { + cfg *storageUsersConfig.Config + ignorer *ignore.Ignorer + recalculateChecksums bool + + restartRequired bool +} + +// checkPosixfsConsistency checks the consistency of the posixfs storage. The +// given path determines the scope of the check: the whole storage, a single +// space or a single entity within a space. +func (c *consistencyChecker) Check(paths []string) error { + for _, path := range paths { + rootPath, err := findStorageRoot(path) + if err != nil { + return err + } + path = filepath.Clean(path) + if _, err := os.Stat(path); err != nil { + return fmt.Errorf("error accessing '%s': %w", path, err) + } + contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) + + switch { + case path == rootPath: + fmt.Println("Checking personal spaces...") + c.checkSpaces(filepath.Join(path, "users")) + + fmt.Println("Checking project spaces...") + c.checkSpaces(filepath.Join(path, "projects")) + case isSpaceRoot(path): + fmt.Printf("Checking space '%s'...\n", path) + c.checkSpace(path) + case contained: + fmt.Printf("Checking '%s'...\n", path) + c.checkEntity(path) + default: + return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) + } + } + + if c.restartRequired { + fmt.Println("\n\n ⚠️ Please restart your openCloud instance to apply changes.") + } + return nil +} + +func (c *consistencyChecker) checkSpaces(basePath string) { + dirEntries, err := os.ReadDir(basePath) + if err != nil { + logFailure("Error reading spaces directory '%s': %v", basePath, err) + return + } + + for _, entry := range dirEntries { + if entry.IsDir() { + fullPath := filepath.Join(basePath, entry.Name()) + c.checkSpace(fullPath) + } + } +} + +func (c *consistencyChecker) checkSpace(spacePath string) { + info, err := os.Stat(spacePath) + if err != nil { + logFailure("Error accessing path '%s': %v", spacePath, err) + return + } + if !info.IsDir() { + logFailure("Error: The provided path '%s' is not a directory\n", spacePath) + return + } + + spaceID, err := xattr.Get(spacePath, prefixes.SpaceIDAttr) + if err != nil || len(spaceID) == 0 { + logFailure("Error: The directory '%s' does not seem to be a space root, it's missing the '%s' attribute\n", spacePath, prefixes.SpaceIDAttr) + return + } + + c.checkSpaceID(spacePath) + c.checkNodes(spacePath) +} + +func (c *consistencyChecker) checkSpaceID(spacePath string) { + entries, uniqueIDs, oldestEntry, err := c.gatherAttributes(spacePath) + if err != nil { + logFailure("Failed to gather attributes: %v", err) + return + } + + if len(entries) == 0 { + return + } + + if len(uniqueIDs) > 1 { + fmt.Println("\n ⚠ Multiple space IDs found:") + for id := range uniqueIDs { + fmt.Printf(" - %s\n", id) + } + + fmt.Printf("\n ⏳ Oldest entry is '%s' (modified on %s).\n", + filepath.Base(oldestEntry.Path), oldestEntry.ModTime.Format(time.RFC1123)) + + targetID := oldestEntry.ParentID + fmt.Printf(" ✅ Proposed target Parent ID: %s\n", targetID) + + fmt.Printf("\n Do you want to unify all parent IDs to '%s'? This will modify %d entries, the directory, and the user index. (y/N): ", targetID, len(entries)) + + reader := bufio.NewReader(os.Stdin) + input, _ := reader.ReadString('\n') + input = strings.TrimSpace(strings.ToLower(input)) + + if input != "y" { + logFailure("Operation cancelled by user.") + return + } + c.restartRequired = true + + obsoleteIDs := []string{} + for id := range uniqueIDs { + if id != targetID { + obsoleteIDs = append(obsoleteIDs, id) + } + } + fixSpaceID(spacePath, obsoleteIDs, targetID, entries) + } +} + +func (c *consistencyChecker) walkNodes(dir string, parentID string) int { + fixes := 0 + entries, err := os.ReadDir(dir) + if err != nil { + logFailure("Error reading directory '%s': %v", dir, err) + return 0 + } + + for _, entry := range entries { + fullPath := filepath.Join(dir, entry.Name()) + + if c.ignorer.IsIgnored(fullPath) { + continue + } + + fixes += c.checkNodeAttributes(fullPath, entry.Name(), parentID, entry.IsDir()) + + if entry.IsDir() { + nodeID, err := xattr.Get(fullPath, prefixes.IDAttr) + if err != nil || len(nodeID) == 0 { + logFailure("Directory '%s' missing '%s', skipping its children", fullPath, prefixes.IDAttr) + continue + } + fixes += c.walkNodes(fullPath, string(nodeID)) + } + } + return fixes +} + +// checkNodeAttributes checks and fixes the parent ID and name attributes of a +// single node. For files it additionally checks the blobsize and, when +// requested, the checksums. It returns the number of fixes applied. +func (c *consistencyChecker) checkNodeAttributes(path, name, parentID string, isDir bool) int { + fixes := 0 + + // Check if the parent ID attribute matches the expected parent ID, if not, fix it. + actualParentID, err := xattr.Get(path, prefixes.ParentidAttr) + if err != nil || string(actualParentID) != parentID { + if err := xattr.Set(path, prefixes.ParentidAttr, []byte(parentID)); err != nil { + logFailure("Failed to fix parent ID for '%s': %v", path, err) + } else { + fmt.Printf(" + Fixed parent ID for '%s'\n", path) + fixes++ + c.restartRequired = true + } + } + + // Check that the name attribute matches the actual name of the file/directory, if not, fix it. + nameAttr, err := xattr.Get(path, prefixes.NameAttr) + if err != nil || string(nameAttr) != name { + if err := xattr.Set(path, prefixes.NameAttr, []byte(name)); err != nil { + logFailure("Failed to fix name attribute for '%s': %v", path, err) + } else { + fmt.Printf(" + Fixed name attribute for '%s'\n", path) + fixes++ + c.restartRequired = true + } + } + + if !isDir { + fixes += c.checkBlobsize(path) + if c.recalculateChecksums { + fixes += c.fixChecksums(path) + } + } + + return fixes +} + +// checkEntity checks a single file or directory within a space, including its own +// parent ID, name and (for files) blobsize/checksums. If the entity is a directory +// its children are checked recursively. +func (c *consistencyChecker) checkEntity(path string) { + info, err := os.Stat(path) + if err != nil { + logFailure("Error accessing path '%s': %v", path, err) + return + } + + // The expected parent ID is the ID attribute of the containing directory. + parentDir := filepath.Dir(path) + parentID, err := xattr.Get(parentDir, prefixes.IDAttr) + if err != nil || len(parentID) == 0 { + logFailure("Parent directory '%s' is missing the '%s' attribute", parentDir, prefixes.IDAttr) + return + } + + fixes := c.checkNodeAttributes(path, info.Name(), string(parentID), info.IsDir()) + + if info.IsDir() { + nodeID, err := xattr.Get(path, prefixes.IDAttr) + if err != nil || len(nodeID) == 0 { + logFailure("Directory '%s' missing '%s' attribute", path, prefixes.IDAttr) + } else { + fixes += c.walkNodes(path, string(nodeID)) + } + } + + if fixes > 0 { + fmt.Printf(" ✓ Fixed %d incorrect node attributes for %s\n", fixes, filepath.Base(path)) + } +} + +// checkBlobsize verifies that the stored blobsize attribute matches the actual +// file size and fixes it if it doesn't. It returns the number of fixes applied. +func (c *consistencyChecker) checkBlobsize(path string) int { + info, err := os.Stat(path) + if err != nil { + logFailure("Error accessing file '%s': %v", path, err) + return 0 + } + + expectedSize := strconv.FormatInt(info.Size(), 10) + blobsize, err := xattr.Get(path, prefixes.BlobsizeAttr) + if err == nil && string(blobsize) == expectedSize { + return 0 + } + + if err := xattr.Set(path, prefixes.BlobsizeAttr, []byte(expectedSize)); err != nil { + logFailure("Failed to fix blobsize for '%s': %v", path, err) + return 0 + } + + fmt.Printf(" + Fixed blobsize for '%s'\n", path) + c.restartRequired = true + return 1 +} + +// fixChecksums recalculates the sha1, md5 and adler32 checksums of the file and +// updates the stored attributes if they differ. It returns the number of fixes applied. +func (c *consistencyChecker) fixChecksums(path string) int { + sha1h, md5h, adler32h, err := node.CalculateChecksums(context.Background(), path) + if err != nil { + logFailure("Failed to calculate checksums for '%s': %v", path, err) + return 0 + } + + checksums := map[string][]byte{ + prefixes.ChecksumPrefix + "sha1": sha1h.Sum(nil), + prefixes.ChecksumPrefix + "md5": md5h.Sum(nil), + prefixes.ChecksumPrefix + "adler32": adler32h.Sum(nil), + } + + fixes := 0 + for attrName, sum := range checksums { + current, err := xattr.Get(path, attrName) + if err == nil && bytes.Equal(current, sum) { + continue + } + if err := xattr.Set(path, attrName, sum); err != nil { + logFailure("Failed to fix checksum '%s' for '%s': %v", attrName, path, err) + continue + } + fmt.Printf(" + Fixed checksum '%s' for '%s'\n", attrName, path) + c.restartRequired = true + fixes++ + } + return fixes +} + +func (c *consistencyChecker) checkNodes(spacePath string) { + rootID, err := xattr.Get(spacePath, prefixes.IDAttr) + if err != nil || len(rootID) == 0 { + logFailure("Space root '%s' missing '%s' attribute", spacePath, prefixes.IDAttr) + return + } + + fixes := c.walkNodes(spacePath, string(rootID)) + + if fixes > 0 { + fmt.Printf(" ✓ Fixed %d incorrect node attributes in %s\n", fixes, filepath.Base(spacePath)) + } +} + +func fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries []EntryInfo) { + // Set all parentid attributes to the proper space ID + err := setAllParentIDAttributes(entries, targetID) + if err != nil { + logFailure("an error occurred during file attribute update: %v", err) + return + } + + // Update space ID itself + fmt.Printf(" Updating directory '%s' with attribute '%s' -> %s\n", filepath.Base(spacePath), prefixes.IDAttr, targetID) + err = xattr.Set(spacePath, prefixes.IDAttr, []byte(targetID)) + if err != nil { + logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) + return + } + err = xattr.Set(spacePath, prefixes.SpaceIDAttr, []byte(targetID)) + if err != nil { + logFailure("Failed to set attribute on directory '%s': %v", spacePath, err) + return + } + + // update the index + err = updateOwnerIndexFile(spacePath, obsoleteIDs) + if err != nil { + logFailure("Could not update the owner index file: %v", err) + } +} + +func (c *consistencyChecker) gatherAttributes(path string) ([]EntryInfo, map[string]struct{}, EntryInfo, error) { + dirEntries, err := os.ReadDir(path) + if err != nil { + return nil, nil, EntryInfo{}, fmt.Errorf("failed to read directory: %w", err) + } + + var allEntries []EntryInfo + uniqueIDs := make(map[string]struct{}) + var oldestEntry EntryInfo + oldestTime := time.Now().Add(100 * 365 * 24 * time.Hour) // Set to a future date to find the oldest entry + + for _, entry := range dirEntries { + fullPath := filepath.Join(path, entry.Name()) + if c.ignorer.IsIgnored(fullPath) { + continue + } + info, err := os.Stat(fullPath) + if err != nil { + fmt.Printf(" - Warning: could not stat %s: %v\n", entry.Name(), err) + continue + } + + parentID, err := xattr.Get(fullPath, prefixes.ParentidAttr) + if err != nil { + continue // Skip if attribute doesn't exist or can't be read + } + + entryInfo := EntryInfo{ + Path: fullPath, + ModTime: info.ModTime(), + ParentID: string(parentID), + } + + allEntries = append(allEntries, entryInfo) + uniqueIDs[string(parentID)] = struct{}{} + + if entryInfo.ModTime.Before(oldestTime) { + oldestTime = entryInfo.ModTime + oldestEntry = entryInfo + } + } + + return allEntries, uniqueIDs, oldestEntry, nil +} + +func setAllParentIDAttributes(entries []EntryInfo, targetID string) error { + fmt.Printf(" Setting all parent IDs to '%s':\n", targetID) + + for _, entry := range entries { + if entry.ParentID == targetID { + fmt.Printf(" - Skipping '%s' (already has target ID).\n", filepath.Base(entry.Path)) + continue + } + + fmt.Printf(" - Removing all attributes from '%s'. It will be re-assimilated\n", filepath.Base(entry.Path)) + filepath.WalkDir(entry.Path, func(path string, d os.DirEntry, err error) error { + if err != nil { + return fmt.Errorf("error walking path '%s': %w", path, err) + } + + // Remove all attributes from the file. + if err := removeAttributes(path); err != nil { + fmt.Printf("failed to remove attributes from '%s': %v", path, err) + } + return nil + }) + } + return nil +} + +// updateOwnerIndexFile handles the logic of reading, modifying, and writing the MessagePack index file. +func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { + fmt.Printf(" Rewriting index file '%s'\n", basePath) + + ownerID, err := xattr.Get(basePath, prefixes.OwnerIDAttr) + if err != nil { + return fmt.Errorf("could not get owner ID from oldest entry '%s' to find index: %w", basePath, err) + } + + indexPath := filepath.Join(basePath, "../../indexes/by-user-id", string(ownerID)+".mpk") + indexPath = filepath.Clean(indexPath) + + // Read the MessagePack file + fileData, err := os.ReadFile(indexPath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("index file does not exist, skipping update") + } + return fmt.Errorf("could not read index file: %w", err) + } + var indexMap map[string]string + if err := msgpack.Unmarshal(fileData, &indexMap); err != nil { + return fmt.Errorf("failed to parse MessagePack index file (is it corrupt?): %w", err) + } + + // Remove obsolete IDs from the map + itemsRemoved := 0 + for _, id := range obsoleteIDs { + if _, exists := indexMap[id]; exists { + fmt.Printf(" - Removing obsolete ID '%s' from index.\n", id) + delete(indexMap, id) + itemsRemoved++ + } else { + fmt.Printf(" - Obsolete ID '%s' not found in index\n", id) + } + } + + if itemsRemoved == 0 { + return nil + } + + // Write the data back to the file + updatedData, err := msgpack.Marshal(&indexMap) + if err != nil { + return fmt.Errorf("failed to marshal updated index map: %w", err) + } + if err := os.WriteFile(indexPath, updatedData, 0644); err != nil { + return fmt.Errorf("failed to write updated index file: %w", err) + } + + fmt.Printf(" ✓ Successfully removed %d item(s) and saved index file.\n", itemsRemoved) + return nil +} + +func removeAttributes(path string) error { + attrNames, err := xattr.List(path) + if err != nil { + return fmt.Errorf("failed to list attributes for '%s': %w", path, err) + } + + for _, attrName := range attrNames { + if err := xattr.Remove(path, attrName); err != nil { + return fmt.Errorf("failed to remove attribute '%s' from '%s': %w", attrName, path, err) + } + } + return nil +} From 16b9088d2d41326ebd5c966b3d58be0cad3c3649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 13:24:23 +0200 Subject: [PATCH 22/27] Infer the indexes path from the config instead of calculating it --- opencloud/pkg/command/posixfs_consistency.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/opencloud/pkg/command/posixfs_consistency.go b/opencloud/pkg/command/posixfs_consistency.go index c56677c65e..f822143bbc 100644 --- a/opencloud/pkg/command/posixfs_consistency.go +++ b/opencloud/pkg/command/posixfs_consistency.go @@ -147,7 +147,7 @@ func (c *consistencyChecker) checkSpaceID(spacePath string) { obsoleteIDs = append(obsoleteIDs, id) } } - fixSpaceID(spacePath, obsoleteIDs, targetID, entries) + c.fixSpaceID(spacePath, obsoleteIDs, targetID, entries) } } @@ -325,7 +325,9 @@ func (c *consistencyChecker) checkNodes(spacePath string) { } } -func fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries []EntryInfo) { +// fixSpaceID updates the parentid attributes of all entries in a space to a new target ID, +// updates the space's own ID attributes, and removes obsolete IDs from the user index file. +func (c *consistencyChecker) fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries []EntryInfo) { // Set all parentid attributes to the proper space ID err := setAllParentIDAttributes(entries, targetID) if err != nil { @@ -347,7 +349,7 @@ func fixSpaceID(spacePath string, obsoleteIDs []string, targetID string, entries } // update the index - err = updateOwnerIndexFile(spacePath, obsoleteIDs) + err = c.updateOwnerIndexFile(spacePath, obsoleteIDs) if err != nil { logFailure("Could not update the owner index file: %v", err) } @@ -424,7 +426,7 @@ func setAllParentIDAttributes(entries []EntryInfo, targetID string) error { } // updateOwnerIndexFile handles the logic of reading, modifying, and writing the MessagePack index file. -func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { +func (c *consistencyChecker) updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { fmt.Printf(" Rewriting index file '%s'\n", basePath) ownerID, err := xattr.Get(basePath, prefixes.OwnerIDAttr) @@ -432,7 +434,7 @@ func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error { return fmt.Errorf("could not get owner ID from oldest entry '%s' to find index: %w", basePath, err) } - indexPath := filepath.Join(basePath, "../../indexes/by-user-id", string(ownerID)+".mpk") + indexPath := filepath.Join(c.cfg.Drivers.Posix.Root, "indexes", "by-user-id", string(ownerID)+".mpk") indexPath = filepath.Clean(indexPath) // Read the MessagePack file From 6076c66e193390e1c1f0dcf0c2a6311aaa93e428 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:26:55 +0000 Subject: [PATCH 23/27] build(deps): bump google.golang.org/grpc from 1.82.0 to 1.83.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.0 to 1.83.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.82.0...v1.83.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.83.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 5 +- go.sum | 6 +- vendor/github.com/theckman/yacspin/.gitignore | 25 - .../theckman/yacspin/.golangci.yaml | 71 - vendor/github.com/theckman/yacspin/LICENSE | 201 --- vendor/github.com/theckman/yacspin/README.md | 274 ---- .../theckman/yacspin/character_sets.go | 121 -- vendor/github.com/theckman/yacspin/colors.go | 174 --- vendor/github.com/theckman/yacspin/spinner.go | 1191 ----------------- vendor/google.golang.org/grpc/clientconn.go | 23 +- .../clientconn_disconnect_reason_noplan9.go | 48 + .../clientconn_disconnect_reason_plan9.go | 39 + .../grpc/internal/envconfig/envconfig.go | 11 + .../grpc/internal/envconfig/xds.go | 7 +- .../internal/grpcsync/callback_serializer.go | 26 + .../grpc/internal/resolver/config_selector.go | 78 +- .../grpc/internal/transport/client_stream.go | 5 +- .../grpc/internal/transport/controlbuf.go | 172 ++- .../grpc/internal/transport/http2_client.go | 16 +- .../grpc/internal/transport/http2_server.go | 13 +- .../grpc/internal/transport/transport.go | 2 - vendor/google.golang.org/grpc/stream.go | 35 +- vendor/google.golang.org/grpc/version.go | 2 +- vendor/modules.txt | 5 +- 24 files changed, 264 insertions(+), 2286 deletions(-) delete mode 100644 vendor/github.com/theckman/yacspin/.gitignore delete mode 100644 vendor/github.com/theckman/yacspin/.golangci.yaml delete mode 100644 vendor/github.com/theckman/yacspin/LICENSE delete mode 100644 vendor/github.com/theckman/yacspin/README.md delete mode 100644 vendor/github.com/theckman/yacspin/character_sets.go delete mode 100644 vendor/github.com/theckman/yacspin/colors.go delete mode 100644 vendor/github.com/theckman/yacspin/spinner.go create mode 100644 vendor/google.golang.org/grpc/clientconn_disconnect_reason_noplan9.go create mode 100644 vendor/google.golang.org/grpc/clientconn_disconnect_reason_plan9.go diff --git a/go.mod b/go.mod index 9817bbc8e3..53f5f8a0ab 100644 --- a/go.mod +++ b/go.mod @@ -75,6 +75,7 @@ require ( github.com/rogpeppe/go-internal v1.15.0 github.com/rs/cors v1.11.1 github.com/rs/zerolog v1.35.1 + github.com/shamaton/msgpack/v2 v2.4.1 github.com/sirupsen/logrus v1.9.4 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 @@ -84,7 +85,6 @@ require ( github.com/test-go/testify v1.1.4 github.com/testcontainers/testcontainers-go v0.43.0 github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0 - github.com/theckman/yacspin v0.13.12 github.com/thejerf/suture/v4 v4.0.6 github.com/tidwall/gjson v1.19.0 github.com/tidwall/sjson v1.2.5 @@ -111,7 +111,7 @@ require ( golang.org/x/term v0.45.0 golang.org/x/text v0.40.0 google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa - google.golang.org/grpc v1.82.0 + google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -344,7 +344,6 @@ require ( github.com/sergi/go-diff v1.4.0 // indirect github.com/sethvargo/go-diceware v0.5.0 // indirect github.com/sethvargo/go-password v0.3.1 // indirect - github.com/shamaton/msgpack/v2 v2.4.1 // indirect github.com/shirou/gopsutil/v4 v4.26.5 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect diff --git a/go.sum b/go.sum index daabd3d6b8..c026746fe3 100644 --- a/go.sum +++ b/go.sum @@ -1191,8 +1191,6 @@ github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0 h1:a1ipjF github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0/go.mod h1:OWSeUDiGMUy30iMsAltIJIo9uh/CleLv6KyxjYOsgR8= github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= -github.com/theckman/yacspin v0.13.12 h1:CdZ57+n0U6JMuh2xqjnjRq5Haj6v1ner2djtLQRzJr4= -github.com/theckman/yacspin v0.13.12/go.mod h1:Rd2+oG2LmQi5f3zC3yeZAOl245z8QOvrH4OPOJNZxLg= github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8= github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1722,8 +1720,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU= -google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= +google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU= google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/vendor/github.com/theckman/yacspin/.gitignore b/vendor/github.com/theckman/yacspin/.gitignore deleted file mode 100644 index fa3fbdf514..0000000000 --- a/vendor/github.com/theckman/yacspin/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool -*.out -coverage.txt - -# Dependency directories (remove the comment below to include it) -# vendor/ - -# README GIF generation files -*.mov -*.mp4 -*.gif -*.prproj - -# OS files -.DS_Store diff --git a/vendor/github.com/theckman/yacspin/.golangci.yaml b/vendor/github.com/theckman/yacspin/.golangci.yaml deleted file mode 100644 index 31b1123180..0000000000 --- a/vendor/github.com/theckman/yacspin/.golangci.yaml +++ /dev/null @@ -1,71 +0,0 @@ -run: - tests: true - -# all available settings of specific linters -linters-settings: - govet: - # report about shadowed variables - check-shadowing: true - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - dupl: - # tokens count to trigger issue, 150 by default - threshold: 100 - goconst: - # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 - misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - locale: US - staticcheck: - checks: [ "all" ] - revive: - confidence: 0.8 - ignore-generated-header: true - rules: - - name: context-keys-type - - name: time-naming - - name: var-declaration - - name: unexported-return - - name: errorf - - name: blank-imports - - name: context-as-argument - - name: dot-imports - - name: error-return - - name: error-strings - - name: error-naming - - name: exported - - name: increment-decrement - - name: var-naming - - name: package-comments - - name: range - - name: receiver-naming - - name: indent-error-flow - - name: superfluous-else - - name: struct-tag - - name: modifies-value-receiver - - name: range-val-in-closure - - name: range-val-address - - name: atomic - - name: empty-lines - - name: early-return - - name: useless-break - -linters: - enable: - - revive - - govet - - gosec - - staticcheck - - typecheck - fast: false - -issues: - exclude-use-default: false - exclude: - - G104 diff --git a/vendor/github.com/theckman/yacspin/LICENSE b/vendor/github.com/theckman/yacspin/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/vendor/github.com/theckman/yacspin/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/theckman/yacspin/README.md b/vendor/github.com/theckman/yacspin/README.md deleted file mode 100644 index c7c21b1f02..0000000000 --- a/vendor/github.com/theckman/yacspin/README.md +++ /dev/null @@ -1,274 +0,0 @@ -# Yet Another CLi Spinner (for Go) -[![License](https://img.shields.io/github/license/theckman/yacspin.svg)](https://github.com/theckman/yacspin/blob/master/LICENSE) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/theckman/yacspin) -[![Latest Git Tag](https://img.shields.io/github/tag/theckman/yacspin.svg)](https://github.com/theckman/yacspin/releases) -[![GitHub Actions master Build Status](https://github.com/theckman/yacspin/actions/workflows/tests.yaml/badge.svg?branch=master)](https://github.com/theckman/yacspin/actions/workflows/tests.yaml) -[![Go Report Card](https://goreportcard.com/badge/github.com/theckman/yacspin)](https://goreportcard.com/report/github.com/theckman/yacspin) -[![Codecov](https://img.shields.io/codecov/c/github/theckman/yacspin)](https://codecov.io/gh/theckman/yacspin) - -Package `yacspin` provides yet another CLi spinner for Go, taking inspiration -(and some utility code) from the https://github.com/briandowns/spinner project. -Specifically `yacspin` borrows the default character sets, and color mappings to -github.com/fatih/color colors, from that project. - -## License -Because this package adopts the spinner character sets from https://github.com/briandowns/spinner, -this package is released under the Apache 2.0 License. - -## Yet Another CLi Spinner? -This project was created after it was realized that the most popular spinner -library for Go had some limitations, that couldn't be fixed without a massive -overhaul of the API. - -The other spinner ties the ability to show updated messages to the spinner's -animation, meaning you can't always show all the information you want to the end -user without changing the animation speed. This means you need to trade off -animation aesthetics to show "realtime" information. It was a goal to avoid this -problem. - -In addition, there were also some API design choices that have made it unsafe -for concurrent use, which presents challenges when trying to update the text in -the spinner while it's animating. This could result in undefined behavior due to -data races. - -There were also some variable-width spinners in that other project that did -not render correctly. Because the width of the spinner animation would change, -so would the position of the message on the screen. `yacspin` uses a dynamic -width when animating, so your message should appear static relative to the -animating spinner. - -Finally, there was an interest in the spinner being able to represent a task, and to -indicate whether it failed or was successful. This would have further compounded -the API changes needed above to support in an intuitive way. - -This project takes inspiration from that other project, and takes a new approach -to address the challenges above. - -## Features -#### Provided Spinners -There are over 90 spinners available in the `CharSets` package variable. They -were borrowed from [github.com/briandowns/spinner](https://github.com/briandowns/spinner). -There is a table with most of the spinners [at the bottom of this README](#Spinners). - -#### Dynamic Width of Animation -Because of how some spinners are animated, they may have different widths are -different times in the animation. `yacspin` calculates the maximum width, and -pads the animation to ensure the text's position on the screen doesn't change. -This results in a smoother looking animation. - -##### yacspin -![yacspin animation with dynamic width](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/width_good.gif) - -##### other spinners -![other spinners' animation with dynamic width](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/width_bad.gif) - -#### Success and Failure Results -The spinner has both a `Stop()` and `StopFail()` method, which allows the -spinner to result in a success message or a failure message. The messages, -colors, and even the character used to denote success or failure are -customizable in either the initial config or via the spinner's methods. - -By doing this you can use a single `yacspin` spinner to display the status of a -list of tasks being executed serially. - -##### Stop -![Animation with Success](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop.gif) - -##### StopFail -![Animation with Failure](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop_fail.gif) - -#### Animation At End of Line -The `SpinnerAtEnd` field of the `Config` struct allows you to specify whether -the spinner is rendered at the end of the line instead of the beginning. The -default value (`false`) results in the spinner being rendered at the beginning -of the line. - -#### Concurrency -The spinner is safe for concurrent use, so you can update any of its settings -via methods whether the spinner is stopped or is currently animating. - -#### Live Updates -Most spinners tie the ability to show new messages with the animation of the -spinner. So if the spinner animates every 200ms, you can only show updated -information every 200ms. If you wanted more frequent updates, you'd need to -tradeoff the asthetics of the animation to display more data. - -This spinner updates the printed information of the spinner immediately on -change, without the animation updating. This allows you to use an animation -speed that looks astheticaly pleasing, while also knowing the data presented to -the user will be updated live. - -You can see this in action in the following gif, where the filenames being -uploaded are rendered independent of the spinner being animated: - -![Animation with Success](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/features/stop.gif) - -#### Pausing for Updates -Sometimes you want to change a few settings, and don't want the spinner to -render your partially applied configuration. If your spinner is running, and you -want to change a few configuration items via method calls, you can `Pause()` the -spinner first. After making the changes you can call `Unpause()`, and it will -continue rendering like normal with the newly applied configuration. - -#### Supporting Non-Interactive (TTY) Output Targets -`yacspin` also has native support for non-interactive (TTY) output targets. By -default this is detected in the constructor, or can be overriden via the -`TerminalMode` `Config` struct field. When detecting the application is not -running withn a TTY session, the behavior of the spinner is different. - -Specifically, when this is automatically detected the spinner no longer uses -colors, disables the automatic spinner animation, and instead only animates the -spinner when updating the message. In addition, each animation is rendered on a -new line instead of overwriting the current line. - -This should result in human-readable output without any changes needed by -consumers, even when the system is writing to a non-TTY destination. - -#### Manually Stepping Animation -If you'd like to manually animate the spinner, you can do so by setting the -`TerminalMode` to `ForceNoTTYMode | ForceSmartTerminalMode`. In this mode the -spinner will still use colors and other text stylings, but the animation only -happens when data is updated and on individual lines. You can accomplish this by -calling the `Message()` method with the same used previously. - -## Usage -``` -go get github.com/theckman/yacspin -``` - -Within the `yacspin` package there are some default spinners stored in the -`yacspin.CharSets` variable, and you can also provide your own. There is also a -list of known colors in the `yacspin.ValidColors` variable. - -### Example - -There are runnable examples in the [examples/](https://github.com/theckman/yacspin/tree/master/examples) -directory, with one simple example and one more advanced one. Here is a quick -snippet showing usage from a very high level, with error handling omitted: - -```Go -cfg := yacspin.Config{ - Frequency: 100 * time.Millisecond, - CharSet: yacspin.CharSets[59], - Suffix: " backing up database to S3", - SuffixAutoColon: true, - Message: "exporting data", - StopCharacter: "✓", - StopColors: []string{"fgGreen"}, -} - -spinner, err := yacspin.New(cfg) -// handle the error - -err = spinner.Start() - -// doing some work -time.Sleep(2 * time.Second) - -spinner.Message("uploading data") - -// upload... -time.Sleep(2 * time.Second) - -err = spinner.Stop() -``` - -## Spinners - -The spinner animations below are recorded at a refresh frequency of 200ms. Some -animations may look better at a different speed, so play around with the -frequency until you find a value you find aesthetically pleasing. - -yacspin.CharSets index | sample gif (Frequency: 200ms) ------------------------|------------------------------ -0 | ![0 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/0.gif) -1 | ![1 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/1.gif) -2 | ![2 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/2.gif) -3 | ![3 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/3.gif) -4 | ![4 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/4.gif) -5 | ![5 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/5.gif) -6 | ![6 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/6.gif) -7 | ![7 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/7.gif) -8 | ![8 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/8.gif) -9 | ![9 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/9.gif) -10 | ![10 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/10.gif) -11 | ![11 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/11.gif) -12 | ![12 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/12.gif) -13 | ![13 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/13.gif) -14 | ![14 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/14.gif) -15 | ![15 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/15.gif) -16 | ![16 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/16.gif) -17 | ![17 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/17.gif) -18 | ![18 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/18.gif) -19 | ![19 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/19.gif) -20 | ![20 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/20.gif) -21 | ![21 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/21.gif) -22 | ![22 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/22.gif) -23 | ![23 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/23.gif) -24 | ![24 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/24.gif) -25 | ![25 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/25.gif) -26 | ![26 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/26.gif) -27 | ![27 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/27.gif) -28 | ![28 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/28.gif) -29 | ![29 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/29.gif) -30 | ![30 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/30.gif) -31 | ![31 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/31.gif) -32 | ![32 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/32.gif) -33 | ![33 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/33.gif) -34 | ![34 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/34.gif) -35 | ![35 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/35.gif) -36 | ![36 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/36.gif) -37 | ![37 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/37.gif) -38 | ![38 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/38.gif) -39 | ![39 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/39.gif) -40 | ![40 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/40.gif) -41 | ![41 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/41.gif) -42 | ![42 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/42.gif) -43 | ![43 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/43.gif) -44 | ![44 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/44.gif) -45 | ![45 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/45.gif) -46 | ![46 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/46.gif) -47 | ![47 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/47.gif) -48 | ![48 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/48.gif) -49 | ![49 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/49.gif) -50 | ![50 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/50.gif) -51 | ![51 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/51.gif) -52 | ![52 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/52.gif) -53 | ![53 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/53.gif) -54 | ![54 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/54.gif) -55 | ![55 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/55.gif) -56 | ![56 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/56.gif) -57 | ![57 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/57.gif) -58 | ![58 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/58.gif) -59 | ![59 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/59.gif) -60 | ![60 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/60.gif) -61 | ![61 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/61.gif) -62 | ![62 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/62.gif) -63 | ![63 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/63.gif) -64 | ![64 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/64.gif) -65 | ![65 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/65.gif) -66 | ![66 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/66.gif) -67 | ![67 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/67.gif) -68 | ![68 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/68.gif) -69 | ![69 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/69.gif) -70 | ![70 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/70.gif) -71 | ![71 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/71.gif) -72 | ![72 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/72.gif) -73 | ![73 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/73.gif) -74 | ![74 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/74.gif) -75 | ![75 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/75.gif) -76 | ![76 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/76.gif) -77 | ![77 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/77.gif) -78 | ![78 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/78.gif) -79 | ![79 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/79.gif) -80 | ![80 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/80.gif) -81 | ![81 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/81.gif) -82 | ![82 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/82.gif) -83 | ![83 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/83.gif) -84 | ![84 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/84.gif) -85 | ![85 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/85.gif) -86 | ![86 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/86.gif) -87 | ![87 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/87.gif) -88 | ![88 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/88.gif) -89 | ![89 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/89.gif) -90 | ![90 gif](https://raw.githubusercontent.com/theckman/yacspin-gifs/11953a4f12560eaf4a27054d3adad471eb19193c/spinners/90.gif) diff --git a/vendor/github.com/theckman/yacspin/character_sets.go b/vendor/github.com/theckman/yacspin/character_sets.go deleted file mode 100644 index 7fb5990f7f..0000000000 --- a/vendor/github.com/theckman/yacspin/character_sets.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2021 Brian J. Downs -// Copyright (c) 2019-2021 Tim Heckman -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Please see the LICENSE file for the copy of the Apache 2.0 License. -// -// This file was copied from: https://github.com/briandowns/spinner -// -// Modifications: -// -// - removed runtime generation of CharSets 37 and 38; made them literals -// - fixed pipe spinner (32) animation, by adding missing frame - -package yacspin - -// CharSets contains the default character sets from -// https://github.com/briandowns/spinner. -var CharSets = map[int][]string{ - 0: {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"}, - 1: {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"}, - 2: {"▖", "▘", "▝", "▗"}, - 3: {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"}, - 4: {"◢", "◣", "◤", "◥"}, - 5: {"◰", "◳", "◲", "◱"}, - 6: {"◴", "◷", "◶", "◵"}, - 7: {"◐", "◓", "◑", "◒"}, - 8: {".", "o", "O", "@", "*"}, - 9: {"|", "/", "-", "\\"}, - 10: {"◡◡", "⊙⊙", "◠◠"}, - 11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"}, - 12: {">))'>", " >))'>", " >))'>", " >))'>", " >))'>", " <'((<", " <'((<", " <'((<"}, - 13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"}, - 14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, - 15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}, - 16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"}, - 17: {"■", "□", "▪", "▫"}, - 18: {"←", "↑", "→", "↓"}, - 19: {"╫", "╪"}, - 20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"}, - 21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"}, - 22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"}, - 23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"}, - 24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"}, - 25: {"ヲ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ン"}, - 26: {".", "..", "..."}, - 27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"}, - 28: {".", "o", "O", "°", "O", "o", "."}, - 29: {"+", "x"}, - 30: {"v", "<", "^", ">"}, - 31: {">>--->", " >>--->", " >>--->", " >>--->", " >>--->", " <---<<", " <---<<", " <---<<", " <---<<", "<---<<"}, - 32: {"|", "||", "|||", "||||", "|||||", "||||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"}, - 33: {"[ ]", "[= ]", "[== ]", "[=== ]", "[==== ]", "[===== ]", "[====== ]", "[======= ]", "[======== ]", "[========= ]", "[==========]"}, - 34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"}, - 35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"}, - 36: {"[ ]", "[=> ]", "[===> ]", "[=====> ]", "[======> ]", "[========> ]", "[==========> ]", "[============> ]", "[==============> ]", "[================> ]", "[==================> ]", "[===================>]"}, - 37: {"🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚", "🕛"}, // clock emoji: one per hour for hours 1~12 - 38: {"🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡", "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦", "🕛", "🕧"}, // clock emoji: one per half hour for hours 1~12 - 39: {"🌍", "🌎", "🌏"}, - 40: {"◜", "◝", "◞", "◟"}, - 41: {"⬒", "⬔", "⬓", "⬕"}, - 42: {"⬖", "⬘", "⬗", "⬙"}, - 43: {"[>>> >]", "[]>>>> []", "[] >>>> []", "[] >>>> []", "[] >>>> []", "[] >>>>[]", "[>> >>]"}, - 44: {"♠", "♣", "♥", "♦"}, - 45: {"➞", "➟", "➠", "➡", "➠", "➟"}, - 46: {" | ", ` \ `, "_ ", ` \ `, " | ", " / ", " _", " / "}, - 47: {" . . . .", ". . . .", ". . . .", ". . . .", ". . . . ", ". . . . ."}, - 48: {" | ", " / ", " _ ", ` \ `, " | ", ` \ `, " _ ", " / "}, - 49: {"⎺", "⎻", "⎼", "⎽", "⎼", "⎻"}, - 50: {"▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"}, - 51: {"[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]"}, - 52: {"( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )", "( ● )"}, - 53: {"✶", "✸", "✹", "✺", "✹", "✷"}, - 54: {"▐|\\____________▌", "▐_|\\___________▌", "▐__|\\__________▌", "▐___|\\_________▌", "▐____|\\________▌", "▐_____|\\_______▌", "▐______|\\______▌", "▐_______|\\_____▌", "▐________|\\____▌", "▐_________|\\___▌", "▐__________|\\__▌", "▐___________|\\_▌", "▐____________|\\▌", "▐____________/|▌", "▐___________/|_▌", "▐__________/|__▌", "▐_________/|___▌", "▐________/|____▌", "▐_______/|_____▌", "▐______/|______▌", "▐_____/|_______▌", "▐____/|________▌", "▐___/|_________▌", "▐__/|__________▌", "▐_/|___________▌", "▐/|____________▌"}, - 55: {"▐⠂ ▌", "▐⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂▌", "▐ ⠠▌", "▐ ⡀▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐ ⠠ ▌", "▐ ⠂ ▌", "▐ ⠈ ▌", "▐ ⠂ ▌", "▐ ⠠ ▌", "▐ ⡀ ▌", "▐⠠ ▌"}, - 56: {"¿", "?"}, - 57: {"⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏"}, - 58: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠"}, - 59: {". ", ".. ", "...", " ..", " .", " "}, - 60: {".", "o", "O", "°", "O", "o", "."}, - 61: {"▓", "▒", "░"}, - 62: {"▌", "▀", "▐", "▄"}, - 63: {"⊶", "⊷"}, - 64: {"▪", "▫"}, - 65: {"□", "■"}, - 66: {"▮", "▯"}, - 67: {"-", "=", "≡"}, - 68: {"d", "q", "p", "b"}, - 69: {"∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"}, - 70: {"🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "}, - 71: {"☗", "☖"}, - 72: {"⧇", "⧆"}, - 73: {"◉", "◎"}, - 74: {"㊂", "㊀", "㊁"}, - 75: {"⦾", "⦿"}, - 76: {"ဝ", "၀"}, - 77: {"▌", "▀", "▐▄"}, - 78: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢀⡱", "⢄⡱", "⢄⡱", "⢆⡱", "⢎⡱", "⢎⡰", "⢎⡠", "⢎⡀", "⢎⠁", "⠎⠁", "⠊⠁"}, - 79: {"________", "-_______", "_-______", "__-_____", "___-____", "____-___", "_____-__", "______-_", "_______-", "________", "_______-", "______-_", "_____-__", "____-___", "___-____", "__-_____", "_-______", "-_______", "________"}, - 80: {"|_______", "_/______", "__-_____", "___\\____", "____|___", "_____/__", "______-_", "_______\\", "_______|", "______\\_", "_____-__", "____/___", "___|____", "__\\_____", "_-______"}, - 81: {"□", "◱", "◧", "▣", "■"}, - 82: {"□", "◱", "▨", "▩", "■"}, - 83: {"░", "▒", "▓", "█"}, - 84: {"░", "█"}, - 85: {"⚪", "⚫"}, - 86: {"◯", "⬤"}, - 87: {"▱", "▰"}, - 88: {"➊", "➋", "➌", "➍", "➎", "➏", "➐", "➑", "➒", "➓"}, - 89: {"½", "⅓", "⅔", "¼", "¾", "⅛", "⅜", "⅝", "⅞"}, - 90: {"↞", "↟", "↠", "↡"}, -} diff --git a/vendor/github.com/theckman/yacspin/colors.go b/vendor/github.com/theckman/yacspin/colors.go deleted file mode 100644 index 1a0bc8a620..0000000000 --- a/vendor/github.com/theckman/yacspin/colors.go +++ /dev/null @@ -1,174 +0,0 @@ -// This file is available under the Apache 2.0 License -// This file was copied from: https://github.com/briandowns/spinner -// -// Please see the LICENSE file for the copy of the Apache 2.0 License. -// -// Modifications: -// -// - made validColors set map more idiomatic with an empty struct value -// - added a function for creating color functions from color list - -package yacspin - -import ( - "fmt" - - "github.com/fatih/color" -) - -// ValidColors holds the list of the strings that are mapped to -// github.com/fatih/color color attributes. Any of these colors / attributes can -// be used with the *Spinner type, and it should be reflected in the output. -var ValidColors = map[string]struct{}{ - // default colors for backwards compatibility - "black": {}, - "red": {}, - "green": {}, - "yellow": {}, - "blue": {}, - "magenta": {}, - "cyan": {}, - "white": {}, - - // attributes - "reset": {}, - "bold": {}, - "faint": {}, - "italic": {}, - "underline": {}, - "blinkslow": {}, - "blinkrapid": {}, - "reversevideo": {}, - "concealed": {}, - "crossedout": {}, - - // foreground text - "fgBlack": {}, - "fgRed": {}, - "fgGreen": {}, - "fgYellow": {}, - "fgBlue": {}, - "fgMagenta": {}, - "fgCyan": {}, - "fgWhite": {}, - - // foreground Hi-Intensity text - "fgHiBlack": {}, - "fgHiRed": {}, - "fgHiGreen": {}, - "fgHiYellow": {}, - "fgHiBlue": {}, - "fgHiMagenta": {}, - "fgHiCyan": {}, - "fgHiWhite": {}, - - // background text - "bgBlack": {}, - "bgRed": {}, - "bgGreen": {}, - "bgYellow": {}, - "bgBlue": {}, - "bgMagenta": {}, - "bgCyan": {}, - "bgWhite": {}, - - // background Hi-Intensity text - "bgHiBlack": {}, - "bgHiRed": {}, - "bgHiGreen": {}, - "bgHiYellow": {}, - "bgHiBlue": {}, - "bgHiMagenta": {}, - "bgHiCyan": {}, - "bgHiWhite": {}, -} - -// returns a valid color's foreground text color attribute -var colorAttributeMap = map[string]color.Attribute{ - // default colors for backwards compatibility - "black": color.FgBlack, - "red": color.FgRed, - "green": color.FgGreen, - "yellow": color.FgYellow, - "blue": color.FgBlue, - "magenta": color.FgMagenta, - "cyan": color.FgCyan, - "white": color.FgWhite, - - // attributes - "reset": color.Reset, - "bold": color.Bold, - "faint": color.Faint, - "italic": color.Italic, - "underline": color.Underline, - "blinkslow": color.BlinkSlow, - "blinkrapid": color.BlinkRapid, - "reversevideo": color.ReverseVideo, - "concealed": color.Concealed, - "crossedout": color.CrossedOut, - - // foreground text colors - "fgBlack": color.FgBlack, - "fgRed": color.FgRed, - "fgGreen": color.FgGreen, - "fgYellow": color.FgYellow, - "fgBlue": color.FgBlue, - "fgMagenta": color.FgMagenta, - "fgCyan": color.FgCyan, - "fgWhite": color.FgWhite, - - // foreground Hi-Intensity text colors - "fgHiBlack": color.FgHiBlack, - "fgHiRed": color.FgHiRed, - "fgHiGreen": color.FgHiGreen, - "fgHiYellow": color.FgHiYellow, - "fgHiBlue": color.FgHiBlue, - "fgHiMagenta": color.FgHiMagenta, - "fgHiCyan": color.FgHiCyan, - "fgHiWhite": color.FgHiWhite, - - // background text colors - "bgBlack": color.BgBlack, - "bgRed": color.BgRed, - "bgGreen": color.BgGreen, - "bgYellow": color.BgYellow, - "bgBlue": color.BgBlue, - "bgMagenta": color.BgMagenta, - "bgCyan": color.BgCyan, - "bgWhite": color.BgWhite, - - // background Hi-Intensity text colors - "bgHiBlack": color.BgHiBlack, - "bgHiRed": color.BgHiRed, - "bgHiGreen": color.BgHiGreen, - "bgHiYellow": color.BgHiYellow, - "bgHiBlue": color.BgHiBlue, - "bgHiMagenta": color.BgHiMagenta, - "bgHiCyan": color.BgHiCyan, - "bgHiWhite": color.BgHiWhite, -} - -// validColor will make sure the given color is actually allowed -func validColor(c string) bool { - _, ok := ValidColors[c] - - return ok -} - -func colorFunc(colors ...string) (func(format string, a ...interface{}) string, error) { - if len(colors) == 0 { - return fmt.Sprintf, nil - } - - attrib := make([]color.Attribute, len(colors)) - - for i, color := range colors { - if !validColor(color) { - return nil, fmt.Errorf("%s is not a valid color", color) - } - - attrib[i] = colorAttributeMap[color] - } - - return color.New(attrib...).SprintfFunc(), nil -} diff --git a/vendor/github.com/theckman/yacspin/spinner.go b/vendor/github.com/theckman/yacspin/spinner.go deleted file mode 100644 index dd96e5f3fa..0000000000 --- a/vendor/github.com/theckman/yacspin/spinner.go +++ /dev/null @@ -1,1191 +0,0 @@ -// Package yacspin provides Yet Another CLi Spinner for Go, taking inspiration -// (and some utility code) from the https://github.com/briandowns/spinner -// project. Specifically this project borrows the default character sets, and -// color mappings to github.com/fatih/color colors, from that project. -// -// This spinner should support all major operating systems, and is tested -// against Linux, MacOS, and Windows. -// -// This spinner also supports an alternate mode of operation when the TERM -// environment variable is set to "dumb". This is discovered automatically when -// constructing the spinner. -// -// Within the yacspin package there are some default spinners stored in the -// yacspin.CharSets variable, and you can also provide your own. There is also a -// list of known colors in the yacspin.ValidColors variable, if you'd like to -// see what's supported. If you've used github.com/fatih/color before, they -// should look familiar. -// -// cfg := yacspin.Config{ -// Frequency: 100 * time.Millisecond, -// CharSet: yacspin.CharSets[59], -// Suffix: " backing up database to S3", -// Message: "exporting data", -// StopCharacter: "✓", -// StopColors: []string{"fgGreen"}, -// } -// -// spinner, err := yacspin.New(cfg) -// // handle the error -// -// spinner.Start() -// -// // doing some work -// time.Sleep(2 * time.Second) -// -// spinner.Message("uploading data") -// -// // upload... -// time.Sleep(2 * time.Second) -// -// spinner.Stop() -// -// Check out the Config struct to see all of the possible configuration options -// supported by the Spinner. -package yacspin - -import ( - "bytes" - "errors" - "fmt" - "io" - "math" - "os" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" - "github.com/mattn/go-runewidth" -) - -type character struct { - Value string - Size int -} - -func setToCharSlice(ss []string) ([]character, int) { - if len(ss) == 0 { - return nil, 0 - } - - var maxWidth int - c := make([]character, len(ss)) - - for i, s := range ss { - n := runewidth.StringWidth(s) - if n > maxWidth { - maxWidth = n - } - - c[i] = character{ - Value: s, - Size: n, - } - } - - return c, maxWidth -} - -// TerminalMode is a type to represent the bit flag controlling the terminal -// mode of the spinner, accepted as a field on the Config struct. See the -// comments on the exported constants for more info. -type TerminalMode uint32 - -const ( - // AutomaticMode configures the constructor function to try and determine if - // the application using yacspin is being executed within a interactive - // (teletype [TTY]) session. - AutomaticMode TerminalMode = 1 << iota - - // ForceTTYMode configures the spinner to operate as if it's running within - // a TTY session. - ForceTTYMode - - // ForceNoTTYMode configures the spinner to operate as if it's not running - // within a TTY session. This mode causes the spinner to only animate when - // data is being updated. Each animation is rendered on a new line. You can - // trigger an animation by calling the Message() method, including with the - // last value it was called with. - ForceNoTTYMode - - // ForceDumbTerminalMode configures the spinner to operate as if it's - // running within a dumb terminal. This means the spinner will not use ANSI - // escape sequences to print colors or to erase each line. Line erasure to - // animate the spinner is accomplished by overwriting the line with space - // characters. - ForceDumbTerminalMode - - // ForceSmartTerminalMode configures the spinner to operate as if it's - // running within a terminal that supports ANSI escape sequences (VT100). - // This includes printing of stylized text, and more better line erasure to - // animate the spinner. - ForceSmartTerminalMode -) - -func termModeAuto(t TerminalMode) bool { return t&AutomaticMode > 0 } -func termModeForceTTY(t TerminalMode) bool { return t&ForceTTYMode > 0 } -func termModeForceNoTTY(t TerminalMode) bool { return t&ForceNoTTYMode > 0 } -func termModeForceDumb(t TerminalMode) bool { return t&ForceDumbTerminalMode > 0 } -func termModeForceSmart(t TerminalMode) bool { return t&ForceSmartTerminalMode > 0 } - -// Config is the configuration structure for the Spinner type, which you provide -// to the New() function. Some of the fields can be updated after the *Spinner -// is constructed, others can only be set when calling the constructor. Please -// read the comments for those details. -type Config struct { - // Frequency specifies how often to animate the spinner. Optimal value - // depends on the character set you use. - Frequency time.Duration - - // Writer is the place where we are outputting the spinner, and can't be - // changed after the *Spinner has been constructed. If omitted (nil), this - // defaults to os.Stdout. - Writer io.Writer - - // ShowCursor specifies that the cursor should be shown by the spinner while - // animating. If it is not shown, the cursor will be restored when the - // spinner stops. This can't be changed after the *Spinner has been - // constructed. - // - // Please note, if you do not set this to true and the program crashes or is - // killed, you may need to reset your terminal for the cursor to appear - // again. - ShowCursor bool - - // HideCursor describes whether the cursor should be hidden by the spinner - // while animating. If it is hidden, it will be restored when the spinner - // stops. This can't be changed after the *Spinner has been constructed. - // - // Please note, if the program crashes or is killed you may need to reset - // your terminal for the cursor to appear again. - // - // Deprecated: use ShowCursor instead. - HideCursor bool - - // SpinnerAtEnd configures the spinner to render the animation at the end of - // the line instead of the beginning. The default behavior is to render the - // animated spinner at the beginning of the line. - SpinnerAtEnd bool - - // ColorAll describes whether to color everything (all) or just the spinner - // character(s). This cannot be changed after the *Spinner has been - // constructed. - ColorAll bool - - // Colors are the colors used for the different printed messages. This - // respects the ColorAll field. - Colors []string - - // CharSet is the list of characters to iterate through to draw the spinner. - CharSet []string - - // Prefix is the string printed immediately before the spinner. - // - // If SpinnerAtEnd is set to true, it's recommended that this string start - // with a space character (` `). - Prefix string - - // Suffix is the string printed immediately after the spinner and before the - // message. - // - // If SpinnerAtEnd is set to false, it's recommended that this string starts - // with an space character (` `). - Suffix string - - // SuffixAutoColon configures whether the spinner adds a colon after the - // suffix automatically. If there is a message, a colon followed by a space - // is added to the suffix. Otherwise, if there is no message, or the suffix - // is only space characters, the colon is omitted. - // - // If SpinnerAtEnd is set to true, this option is ignored. - SuffixAutoColon bool - - // Message is the message string printed by the spinner. If SpinnerAtEnd is - // set to false and SuffixAutoColon is set to true, the printed line will - // look like: - // - // : - // - // If SpinnerAtEnd is set to true, the printed line will instead look like - // this: - // - // - // - // In this case, it may be preferred to set the Prefix to empty space (` `). - Message string - - // StopMessage is the message used when Stop() is called. - StopMessage string - - // StopCharacter is spinner character used when Stop() is called. - // Recommended character is ✓, and can be more than just one character. - StopCharacter string - - // StopColors are the colors used for the Stop() printed line. This respects - // the ColorAll field. - StopColors []string - - // StopFailMessage is the message used when StopFail() is called. - StopFailMessage string - - // StopFailCharacter is the spinner character used when StopFail() is - // called. Recommended character is ✗, and can be more than just one - // character. - StopFailCharacter string - - // StopFailColors are the colors used for the StopFail() printed line. This - // respects the ColorAll field. - StopFailColors []string - - // TerminalMode is a bitflag field to control how the internal TTY / "dumb - // terminal" detection works, to allow consumers to override the internal - // behaviors. To set this value, it's recommended to use the TerminalMode - // constants exported by this package. - // - // If not set, the New() function implicitly sets it to AutomaticMode. The - // New() function also returns an error if you have conflicting flags, such - // as setting ForceTTYMode and ForceNoTTYMode, or if you set AutomaticMode - // and any other flags set. - // - // When in AutomaticMode, the New() function attempts to determine if the - // current application is running within an interactive (teletype [TTY]) - // session. If it does not appear to be within a TTY, it sets this field - // value to ForceNoTTYMode | ForceDumbTerminalMode. - // - // If this does appear to be a TTY, the ForceTTYMode bitflag will bet set. - // Similarly, if it's a TTY and the TERM environment variable isn't set to - // "dumb" the ForceSmartTerminalMode bitflag will also be set. - // - // If the deprecated NoTTY Config struct field is set to true, and this - // field is AutomaticMode, the New() function sets field to the value of - // ForceNoTTYMode | ForceDumbTerminalMode. - TerminalMode TerminalMode - - // NotTTY tells the spinner that the Writer should not be treated as a TTY. - // This results in the animation being disabled, with the animation only - // happening whenever the data is updated. This mode also renders each - // update on new line, versus reusing the current line. - // - // Deprecated: use TerminalMode field instead by setting it to: - // ForceNoTTYMode | ForceDumbTerminalMode. This will be removed in a future - // release. - NotTTY bool -} - -// Spinner is a type representing an animated CLi terminal spinner. The Spinner -// is constructed by the New() function of this package, which accepts a Config -// struct as the only argument. Some of the configuration values cannot be -// changed after the spinner is constructed, so be sure to read the comments -// within the Config type. -// -// Please note, by default the spinner will hide the terminal cursor when -// animating the spinner. If you do not set Config.ShowCursor to true, you need -// to make sure to call the Stop() or StopFail() method to reset the cursor in -// the terminal. Otherwise, after the program exits the cursor will be hidden -// and the user will need to `reset` their terminal. -type Spinner struct { - writer io.Writer - buffer *bytes.Buffer - colorAll bool - cursorHidden bool - suffixAutoColon bool - termMode TerminalMode - spinnerAtEnd bool - - status *uint32 - lastPrintLen int - cancelCh chan struct{} // send: Stop(), close: StopFail(); both stop painter - doneCh chan struct{} - pauseCh chan struct{} - unpauseCh chan struct{} - unpausedCh chan struct{} - - // mutex hat and the fields wearing it - mu *sync.Mutex - frequency time.Duration - chars []character - maxWidth int - index int - prefix string - suffix string - message string - colorFn func(format string, a ...interface{}) string - stopMsg string - stopChar character - stopColorFn func(format string, a ...interface{}) string - stopFailMsg string - stopFailChar character - stopFailColorFn func(format string, a ...interface{}) string - frequencyUpdateCh chan time.Duration - dataUpdateCh chan struct{} -} - -const ( - statusStopped uint32 = iota - statusStarting - statusRunning - statusStopping - statusPausing - statusPaused - statusUnpausing -) - -// New creates a new unstarted spinner. If stdout does not appear to be a TTY, -// this constructor implicitly sets cfg.NotTTY to true. -func New(cfg Config) (*Spinner, error) { - if cfg.ShowCursor && cfg.HideCursor { - return nil, errors.New("cfg.ShowCursor and cfg.HideCursor cannot be true") - } - - if cfg.TerminalMode == 0 { - cfg.TerminalMode = AutomaticMode - } - - // AutomaticMode flag has been set, but so have others - if termModeAuto(cfg.TerminalMode) && cfg.TerminalMode != AutomaticMode { - return nil, errors.New("cfg.TerminalMode cannot have AutomaticMode flag set if others are set") - } - - if termModeForceTTY(cfg.TerminalMode) && termModeForceNoTTY(cfg.TerminalMode) { - return nil, errors.New("cfg.TerminalMode cannot have both ForceTTYMode and ForceNoTTYMode flags set") - } - - if termModeForceDumb(cfg.TerminalMode) && termModeForceSmart(cfg.TerminalMode) { - return nil, errors.New("cfg.TerminalMode cannot have both ForceDumbTerminalMode and ForceSmartTerminalMode flags set") - } - - if cfg.HideCursor { - cfg.ShowCursor = false - } - - // cfg.NotTTY compatibility - if cfg.TerminalMode == AutomaticMode && cfg.NotTTY { - cfg.TerminalMode = ForceNoTTYMode | ForceDumbTerminalMode - } - - // is this a dumb terminal / not a TTY? - if cfg.TerminalMode == AutomaticMode && !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) { - cfg.TerminalMode = ForceNoTTYMode | ForceDumbTerminalMode - } - - // if cfg.TerminalMode is still equal to AutomaticMode, this is a TTY - if cfg.TerminalMode == AutomaticMode { - cfg.TerminalMode = ForceTTYMode - - if os.Getenv("TERM") == "dumb" { - cfg.TerminalMode |= ForceDumbTerminalMode - } else { - cfg.TerminalMode |= ForceSmartTerminalMode - } - } - - buf := bytes.NewBuffer(make([]byte, 2048)) - buf.Reset() - - s := &Spinner{ - buffer: buf, - mu: &sync.Mutex{}, - frequency: cfg.Frequency, - status: uint32Ptr(0), - frequencyUpdateCh: make(chan time.Duration), // use unbuffered for now to avoid .Frequency() panic - dataUpdateCh: make(chan struct{}), - - colorAll: cfg.ColorAll, - cursorHidden: !cfg.ShowCursor, - spinnerAtEnd: cfg.SpinnerAtEnd, - suffixAutoColon: cfg.SuffixAutoColon, - termMode: cfg.TerminalMode, - colorFn: fmt.Sprintf, - stopColorFn: fmt.Sprintf, - stopFailColorFn: fmt.Sprintf, - } - - if err := s.Colors(cfg.Colors...); err != nil { - return nil, err - } - - if err := s.StopColors(cfg.StopColors...); err != nil { - return nil, err - } - - if err := s.StopFailColors(cfg.StopFailColors...); err != nil { - return nil, err - } - - if len(cfg.CharSet) == 0 { - cfg.CharSet = CharSets[9] - } - - // can only error if the charset is empty, and we prevent that above - _ = s.CharSet(cfg.CharSet) - - if termModeForceNoTTY(s.termMode) { - // hack to prevent the animation from running if not a TTY - s.frequency = time.Duration(math.MaxInt64) - } - - if cfg.Writer == nil { - cfg.Writer = colorable.NewColorableStdout() - } - - s.writer = cfg.Writer - - if len(cfg.Prefix) > 0 { - s.Prefix(cfg.Prefix) - } - - if len(cfg.Suffix) > 0 { - s.Suffix(cfg.Suffix) - } - - if len(cfg.Message) > 0 { - s.Message(cfg.Message) - } - - if len(cfg.StopMessage) > 0 { - s.StopMessage(cfg.StopMessage) - } - - if len(cfg.StopCharacter) > 0 { - s.StopCharacter(cfg.StopCharacter) - } - - if len(cfg.StopFailMessage) > 0 { - s.StopFailMessage(cfg.StopFailMessage) - } - - if len(cfg.StopFailCharacter) > 0 { - s.StopFailCharacter(cfg.StopFailCharacter) - } - - return s, nil -} - -func (s *Spinner) notifyDataChange() { - // non-blocking notification - select { - case s.dataUpdateCh <- struct{}{}: - default: - } -} - -// SpinnerStatus describes the status of the spinner. See the package constants -// for the list of all possible statuses -type SpinnerStatus uint32 - -const ( - // SpinnerStopped is a stopped spinner - SpinnerStopped SpinnerStatus = iota - - // SpinnerStarting is a starting spinner - SpinnerStarting - - // SpinnerRunning is a running spinner - SpinnerRunning - - // SpinnerStopping is a stopping spinner - SpinnerStopping - - // SpinnerPausing is a pausing spinner - SpinnerPausing - - // SpinnerPaused is a paused spinner - SpinnerPaused - - // SpinnerUnpausing is an unpausing spinner - SpinnerUnpausing -) - -func (s SpinnerStatus) String() string { - switch s { - case SpinnerStopped: - return "stopped" - case SpinnerStarting: - return "starting" - case SpinnerRunning: - return "running" - case SpinnerStopping: - return "stopping" - case SpinnerPausing: - return "pausing" - case SpinnerPaused: - return "paused" - case SpinnerUnpausing: - return "unpausing" - default: - return fmt.Sprintf("unknown (%d)", s) - } -} - -// Status returns the current status of the spinner. The returned value is of -// type SpinnerStatus, which can be compared against the exported Spinner* -// package-level constants (e.g., SpinnerRunning). -func (s *Spinner) Status() SpinnerStatus { - return SpinnerStatus(atomic.LoadUint32(s.status)) -} - -// Start begins the spinner on the Writer in the Config provided to New(). Only -// possible error is if the spinner is already runninng. -func (s *Spinner) Start() error { - // move us to the starting state - if !atomic.CompareAndSwapUint32(s.status, statusStopped, statusStarting) { - return errors.New("spinner already running or shutting down") - } - - // we now have atomic guarantees of no other goroutines starting or running - - s.mu.Lock() - - if s.frequency < 1 && termModeForceTTY(s.termMode) { - return errors.New("spinner Frequency duration must be greater than 0 when used within a TTY") - } - - if len(s.chars) == 0 { - s.mu.Unlock() - - // move us to the stopped state - if !atomic.CompareAndSwapUint32(s.status, statusStarting, statusStopped) { - panic("atomic invariant encountered") - } - - return errors.New("before starting the spinner a CharSet must be set") - } - - s.frequencyUpdateCh = make(chan time.Duration, 4) - s.dataUpdateCh, s.cancelCh = make(chan struct{}, 1), make(chan struct{}, 1) - - s.mu.Unlock() - - // because of the atomic swap above, we know it's safe to mutate these - // values outside of mutex - s.doneCh = make(chan struct{}) - s.pauseCh = make(chan struct{}) // unbuffered since we want this to be synchronous - - go s.painter(s.cancelCh, s.dataUpdateCh, s.pauseCh, s.doneCh, s.frequencyUpdateCh) - - // move us to the running state - if !atomic.CompareAndSwapUint32(s.status, statusStarting, statusRunning) { - panic("atomic invariant encountered") - } - - return nil -} - -// Pause puts the spinner in a state where it no longer animates or renders -// updates to data. This function blocks until the spinner's internal painting -// goroutine enters a paused state. -// -// If you want to make a few configuration changes and have them to appear at -// the same time, like changing the suffix, message, and color, you can Pause() -// the spinner first and then Unpause() after making the changes. -// -// If the spinner is not running (stopped, paused, or in transition to another -// state) this returns an error. -func (s *Spinner) Pause() error { - if !atomic.CompareAndSwapUint32(s.status, statusRunning, statusPausing) { - return errors.New("spinner not running") - } - - // set up the channels the painter will use - s.unpauseCh, s.unpausedCh = make(chan struct{}), make(chan struct{}) - - // inform the painter to pause as a blocking send - s.pauseCh <- struct{}{} - - if !atomic.CompareAndSwapUint32(s.status, statusPausing, statusPaused) { - panic("atomic invariant encountered") - } - - return nil -} - -// Unpause returns the spinner back to a running state after pausing. See -// Pause() documentation for more detail. This function blocks until the -// spinner's internal painting goroutine acknowledges the request to unpause. -// -// If the spinner is not paused this returns an error. -func (s *Spinner) Unpause() error { - if !atomic.CompareAndSwapUint32(s.status, statusPaused, statusUnpausing) { - return errors.New("spinner not paused") - } - - s.unpause() - - if !atomic.CompareAndSwapUint32(s.status, statusUnpausing, statusRunning) { - panic("atomic invariant encountered") - } - - return nil -} - -func (s *Spinner) unpause() { - // tell the painter to unpause - close(s.unpauseCh) - - // wait for the painter to signal it will continue - <-s.unpausedCh - - // clear the no longer needed channels - s.unpauseCh = nil - s.unpausedCh = nil -} - -// Stop disables the spinner, and prints the StopCharacter with the StopMessage -// using the StopColors. This blocks until the stopped message is printed. Only -// possible error is if the spinner is not running. -func (s *Spinner) Stop() error { - return s.stop(false) -} - -// StopFail disables the spinner, and prints the StopFailCharacter with the -// StopFailMessage using the StopFailColors. This blocks until the stopped -// message is printed. Only possible error is if the spinner is not running. -func (s *Spinner) StopFail() error { - return s.stop(true) -} - -func (s *Spinner) stop(fail bool) error { - // move us to a stopping state to protect against concurrent Stop() calls - wasRunning := atomic.CompareAndSwapUint32(s.status, statusRunning, statusStopping) - wasPaused := atomic.CompareAndSwapUint32(s.status, statusPaused, statusStopping) - - if !wasRunning && !wasPaused { - return errors.New("spinner not running or paused") - } - - // we now have an atomic guarantees of no other threads invoking state changes - - if !fail { - // this tells the painter to print the StopMessage and not the - // StopFailMessage - s.cancelCh <- struct{}{} - } - - close(s.cancelCh) - - if wasPaused { - s.unpause() - } - - // wait for the painter to stop - <-s.doneCh - - s.mu.Lock() - - s.dataUpdateCh = make(chan struct{}) // prevent panic() in various setter methods - s.frequencyUpdateCh = make(chan time.Duration) // prevent panic() in .Frequency() - - s.mu.Unlock() - - // because of atomic swaps and channel receive above we know it's - // safe to mutate these fields outside of the mutex - s.index = 0 - s.cancelCh = nil - s.doneCh = nil - s.pauseCh = nil - - // move us to the stopped state - if !atomic.CompareAndSwapUint32(s.status, statusStopping, statusStopped) { - panic("atomic invariant encountered") - } - - return nil -} - -// handleFrequencyUpdate is for when the frequency was changed. This tries to -// see if we should fire the timer now, or change its current duration to match -// the new duration. -func handleFrequencyUpdate(newFrequency time.Duration, timer *time.Timer, lastTick time.Time) { - // if timer fired, drain the channel - if !timer.Stop() { - timerLoop: - for { - select { - case <-timer.C: - default: - break timerLoop - } - } - } - - timeSince := time.Since(lastTick) - - // if we've exceeded the new delay trigger timer immediately - if timeSince >= newFrequency { - timer.Reset(0) - return - } - - timer.Reset(newFrequency - timeSince) -} - -func (s *Spinner) painter(cancel, dataUpdate, pause <-chan struct{}, done chan<- struct{}, frequencyUpdate <-chan time.Duration) { - timer := time.NewTimer(0) - var lastTick time.Time - - for { - select { - case <-timer.C: - lastTick = time.Now() - - s.paintUpdate(timer, true) - - case <-pause: - <-s.unpauseCh - close(s.unpausedCh) - - case <-dataUpdate: - // if this is not a TTY: animate the spinner on the data update - s.paintUpdate(timer, termModeForceNoTTY(s.termMode)) - - case frequency := <-frequencyUpdate: - handleFrequencyUpdate(frequency, timer, lastTick) - - case _, ok := <-cancel: - defer close(done) - - timer.Stop() - - s.paintStop(ok) - - return - } - } -} - -func (s *Spinner) paintUpdate(timer *time.Timer, animate bool) { - s.mu.Lock() - - p := s.prefix - m := s.message - suf := s.suffix - mw := s.maxWidth - cFn := s.colorFn - d := s.frequency - index := s.index - - if animate { - s.index++ - - if s.index == len(s.chars) { - s.index = 0 - } - } else { - // for data updates use the last spinner char - index-- - - if index < 0 { - index = len(s.chars) - 1 - } - } - - c := s.chars[index] - - s.mu.Unlock() - - defer s.buffer.Reset() - - if termModeForceSmart(s.termMode) { - if err := erase(s.buffer); err != nil { - panic(fmt.Sprintf("failed to erase line: %v", err)) - } - - if s.cursorHidden { - if err := hideCursor(s.buffer); err != nil { - panic(fmt.Sprintf("failed to hide cursor: %v", err)) - } - } - - if _, err := paint(s.buffer, mw, c, p, m, suf, s.suffixAutoColon, s.colorAll, s.spinnerAtEnd, false, termModeForceNoTTY(s.termMode), cFn); err != nil { - panic(fmt.Sprintf("failed to paint line: %v", err)) - } - } else { - if err := s.eraseDumbTerm(s.buffer); err != nil { - panic(fmt.Sprintf("failed to erase line: %v", err)) - } - - n, err := paint(s.buffer, mw, c, p, m, suf, s.suffixAutoColon, false, s.spinnerAtEnd, false, termModeForceNoTTY(s.termMode), fmt.Sprintf) - if err != nil { - panic(fmt.Sprintf("failed to paint line: %v", err)) - } - - s.lastPrintLen = n - } - - if s.buffer.Len() > 0 { - if _, err := s.writer.Write(s.buffer.Bytes()); err != nil { - panic(fmt.Sprintf("failed to output buffer to writer: %v", err)) - } - } - - if animate { - timer.Reset(d) - } -} - -func (s *Spinner) paintStop(chanOk bool) { - var m string - var c character - var cFn func(format string, a ...interface{}) string - - s.mu.Lock() - - if chanOk { - c = s.stopChar - cFn = s.stopColorFn - m = s.stopMsg - } else { - c = s.stopFailChar - cFn = s.stopFailColorFn - m = s.stopFailMsg - } - - p := s.prefix - suf := s.suffix - mw := s.maxWidth - - s.mu.Unlock() - - defer s.buffer.Reset() - - if termModeForceSmart(s.termMode) { - if err := erase(s.buffer); err != nil { - panic(fmt.Sprintf("failed to erase line: %v", err)) - } - - if s.cursorHidden { - if err := unhideCursor(s.buffer); err != nil { - panic(fmt.Sprintf("failed to hide cursor: %v", err)) - } - } - - if c.Size > 0 || len(m) > 0 { - // paint the line with a newline as it's the final line - if _, err := paint(s.buffer, mw, c, p, m, suf, s.suffixAutoColon, s.colorAll, s.spinnerAtEnd, true, termModeForceNoTTY(s.termMode), cFn); err != nil { - panic(fmt.Sprintf("failed to paint line: %v", err)) - } - } - } else { - if err := s.eraseDumbTerm(s.buffer); err != nil { - panic(fmt.Sprintf("failed to erase line: %v", err)) - } - - if c.Size > 0 || len(m) > 0 { - if _, err := paint(s.buffer, mw, c, p, m, suf, s.suffixAutoColon, false, s.spinnerAtEnd, true, termModeForceNoTTY(s.termMode), fmt.Sprintf); err != nil { - panic(fmt.Sprintf("failed to paint line: %v", err)) - } - } - - s.lastPrintLen = 0 - } - - if s.buffer.Len() > 0 { - if _, err := s.writer.Write(s.buffer.Bytes()); err != nil { - panic(fmt.Sprintf("failed to output buffer to writer: %v", err)) - } - } -} - -// erase clears the line -func erase(w io.Writer) error { - _, err := fmt.Fprint(w, "\r\033[K\r") - return err -} - -// eraseDumbTerm clears the line on dumb terminals -func (s *Spinner) eraseDumbTerm(w io.Writer) error { - if termModeForceNoTTY(s.termMode) { - // non-TTY outputs use \n instead of line erasure, - // so return early - return nil - } - - clear := "\r" + strings.Repeat(" ", s.lastPrintLen) + "\r" - - _, err := fmt.Fprint(w, clear) - return err -} - -func hideCursor(w io.Writer) error { - _, err := fmt.Fprint(w, "\r\033[?25l\r") - return err -} - -func unhideCursor(w io.Writer) error { - _, err := fmt.Fprint(w, "\r\033[?25h\r") - return err -} - -// padChar pads the spinner character so suffix / message offset from left is -// consistent -func padChar(char character, maxWidth int) string { - padSize := maxWidth - char.Size - return char.Value + strings.Repeat(" ", padSize) -} - -// paint writes a single line to the w, using the provided character, message, -// and color function -func paint(w io.Writer, maxWidth int, char character, prefix, message, suffix string, suffixAutoColon, colorAll, spinnerAtEnd, finalPaint, notTTY bool, colorFn func(format string, a ...interface{}) string) (int, error) { - var output string - - switch char.Size { - case 0: - if colorAll { - output = colorFn(message) - break - } - - output = message - - default: - c := padChar(char, maxWidth) - - if spinnerAtEnd { - if colorAll { - output = colorFn("%s%s%s%s", message, prefix, c, suffix) - break - } - - output = fmt.Sprintf("%s%s%s%s", message, prefix, colorFn(c), suffix) - break - } - - if suffixAutoColon { // also implicitly !spinnerAtEnd - if len(strings.TrimSpace(suffix)) > 0 && len(message) > 0 && message != "\n" { - suffix += ": " - } - } - - if colorAll { - output = colorFn("%s%s%s%s", prefix, c, suffix, message) - break - } - - output = fmt.Sprintf("%s%s%s%s", prefix, colorFn(c), suffix, message) - } - - if finalPaint || notTTY { - output += "\n" - } - - return fmt.Fprint(w, output) -} - -// Frequency updates the frequency of the spinner being animated. -func (s *Spinner) Frequency(d time.Duration) error { - if d < 1 { - return errors.New("duration must be greater than 0") - } - - if termModeForceNoTTY(s.termMode) { - // when output target is not a TTY, we don't animate spinner - // so there is no need to update the frequency - return nil - } - - s.mu.Lock() - defer s.mu.Unlock() - - s.frequency = d - - // non-blocking notification - select { - case s.frequencyUpdateCh <- d: - default: - } - - return nil -} - -// Prefix updates the Prefix before the spinner character. -func (s *Spinner) Prefix(prefix string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.prefix = prefix - - s.notifyDataChange() -} - -// Suffix updates the Suffix printed after the spinner character and before the -// message. It's recommended that this start with an empty space. -func (s *Spinner) Suffix(suffix string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.suffix = suffix - - s.notifyDataChange() -} - -// Message updates the Message displayed after the suffix. -func (s *Spinner) Message(message string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.message = message - - s.notifyDataChange() -} - -// Colors updates the github.com/fatih/colors for printing the spinner line. -// ColorAll config parameter controls whether only the spinner character is -// printed with these colors, or the whole line. -// -// StopColors() is the method to control the colors in the stop message. -func (s *Spinner) Colors(colors ...string) error { - colorFn, err := colorFunc(colors...) - if err != nil { - return fmt.Errorf("failed to build color function: %w", err) - } - - s.mu.Lock() - defer s.mu.Unlock() - - s.colorFn = colorFn - - s.notifyDataChange() - - return nil -} - -// StopMessage updates the Message used when Stop() is called. -func (s *Spinner) StopMessage(message string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.stopMsg = message - - s.notifyDataChange() -} - -// StopColors updates the colors used for the stop message. See Colors() method -// documentation for more context. -// -// StopFailColors() is the method to control the colors in the failed stop -// message. -func (s *Spinner) StopColors(colors ...string) error { - colorFn, err := colorFunc(colors...) - if err != nil { - return fmt.Errorf("failed to build stop color function: %w", err) - } - - s.mu.Lock() - defer s.mu.Unlock() - - s.stopColorFn = colorFn - - s.notifyDataChange() - - return nil -} - -// StopCharacter sets the single "character" to use for the spinner when -// stopping. Recommended character is ✓. -func (s *Spinner) StopCharacter(char string) { - n := runewidth.StringWidth(char) - - s.mu.Lock() - defer s.mu.Unlock() - - s.stopChar = character{Value: char, Size: n} - - if n > s.maxWidth { - s.maxWidth = n - } - - s.notifyDataChange() -} - -// StopFailMessage updates the Message used when StopFail() is called. -func (s *Spinner) StopFailMessage(message string) { - s.mu.Lock() - defer s.mu.Unlock() - - s.stopFailMsg = message - - s.notifyDataChange() -} - -// StopFailColors updates the colors used for the StopFail message. See Colors() method -// documentation for more context. -func (s *Spinner) StopFailColors(colors ...string) error { - colorFn, err := colorFunc(colors...) - if err != nil { - return fmt.Errorf("failed to build stop fail color function: %w", err) - } - - s.mu.Lock() - defer s.mu.Unlock() - - s.stopFailColorFn = colorFn - - s.notifyDataChange() - - return nil -} - -// StopFailCharacter sets the single "character" to use for the spinner when -// stopping for a failure. Recommended character is ✗. -func (s *Spinner) StopFailCharacter(char string) { - n := runewidth.StringWidth(char) - - s.mu.Lock() - defer s.mu.Unlock() - - s.stopFailChar = character{Value: char, Size: n} - - if n > s.maxWidth { - s.maxWidth = n - } - - s.notifyDataChange() -} - -// CharSet updates the set of characters (strings) to use for the spinner. You -// can provide your own, or use one from the yacspin.CharSets variable. -// -// The character sets available in the CharSets variable are from the -// https://github.com/briandowns/spinner project. -func (s *Spinner) CharSet(cs []string) error { - if len(cs) == 0 { - return errors.New("failed to set character set: must provide at least one string") - } - - chars, mw := setToCharSlice(cs) - s.mu.Lock() - defer s.mu.Unlock() - - if n := s.stopChar.Size; n > mw { - mw = s.stopChar.Size - } - - if n := s.stopFailChar.Size; n > mw { - mw = n - } - - s.chars = chars - s.maxWidth = mw - s.index = 0 - - return nil -} - -// Reverse flips the character set order of the spinner characters. -func (s *Spinner) Reverse() { - s.mu.Lock() - defer s.mu.Unlock() - - for i, j := 0, len(s.chars)-1; i < j; { - s.chars[i], s.chars[j] = s.chars[j], s.chars[i] - i++ - j-- - } - - s.index = 0 -} - -func uint32Ptr(u uint32) *uint32 { return &u } diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index c4bca5203e..b27c7e84a3 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -24,12 +24,10 @@ import ( "fmt" "math" "net/url" - "os" "slices" "strings" "sync" "sync/atomic" - "syscall" "time" "google.golang.org/grpc/balancer" @@ -1573,26 +1571,13 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, // to the provided transport.GoAwayInfo, as specified by gRFC A94: // https://github.com/grpc/proposal/blob/master/A94-grpc-subchannel-disconnections-metrics.md func disconnectErrorString(info transport.GoAwayInfo) string { - err := info.Err - var sysErr syscall.Errno - switch { - case info.Reason != transport.GoAwayInvalid: + if info.Reason != transport.GoAwayInvalid { return fmt.Sprintf("GOAWAY %s", info.GoAwayCode.String()) - case err == nil: - return "unknown" - case errors.Is(err, context.Canceled): - return "subchannel shutdown" - case errors.Is(err, syscall.ECONNRESET): - return "connection reset" - case errors.Is(err, syscall.ETIMEDOUT), errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded): - return "connection timed out" - case errors.Is(err, syscall.ECONNABORTED): - return "connection aborted" - case errors.As(err, &sysErr): - return "socket error" - default: + } + if info.Err == nil { return "unknown" } + return disconnectErrorLabel(info.Err) } // startHealthCheck starts the health checking stream (RPC) to watch the health diff --git a/vendor/google.golang.org/grpc/clientconn_disconnect_reason_noplan9.go b/vendor/google.golang.org/grpc/clientconn_disconnect_reason_noplan9.go new file mode 100644 index 0000000000..f0fcd88423 --- /dev/null +++ b/vendor/google.golang.org/grpc/clientconn_disconnect_reason_noplan9.go @@ -0,0 +1,48 @@ +//go:build !plan9 + +/* + * + * Copyright 2026 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "context" + "errors" + "os" + "syscall" +) + +// disconnectErrorLabel returns the grpc.disconnect_error metric label for a +// transport error, as specified by gRFC A94. +func disconnectErrorLabel(err error) string { + var sysErr syscall.Errno + switch { + case errors.Is(err, context.Canceled): + return "subchannel shutdown" + case errors.Is(err, syscall.ECONNRESET): + return "connection reset" + case errors.Is(err, syscall.ETIMEDOUT), errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded): + return "connection timed out" + case errors.Is(err, syscall.ECONNABORTED): + return "connection aborted" + case errors.As(err, &sysErr): + return "socket error" + default: + return "unknown" + } +} diff --git a/vendor/google.golang.org/grpc/clientconn_disconnect_reason_plan9.go b/vendor/google.golang.org/grpc/clientconn_disconnect_reason_plan9.go new file mode 100644 index 0000000000..930b12664c --- /dev/null +++ b/vendor/google.golang.org/grpc/clientconn_disconnect_reason_plan9.go @@ -0,0 +1,39 @@ +/* + * + * Copyright 2026 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package grpc + +import ( + "context" + "errors" + "os" +) + +// disconnectErrorLabel returns the grpc.disconnect_error metric label for a +// transport error, as specified by gRFC A94. syscall.Errno does not exist on +// plan9, so only the portable classifications are available. +func disconnectErrorLabel(err error) string { + switch { + case errors.Is(err, context.Canceled): + return "subchannel shutdown" + case errors.Is(err, context.DeadlineExceeded), errors.Is(err, os.ErrDeadlineExceeded): + return "connection timed out" + default: + return "unknown" + } +} diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index ba05b65d5b..29d332e7b6 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -141,6 +141,17 @@ var ( // feature if unforeseen issues arise, and it will be removed in a future // release. EnableHTTPFramerReadBufferPooling = boolFromEnv("GRPC_GO_EXPERIMENTAL_HTTP_FRAMER_READ_BUFFER_POOLING", true) + + // ControlBufferThrottleLimit is the maximum number of control frames that can + // be queued in the control buffer before throttling is applied. The value + // must be between 1 and 10,000, and is set to 100 by default. + // + // This environment variable serves as an escape hatch to increase the + // throttling limit if unforeseen issues arise, and it will be removed in a + // future release. + // + // TODO: Remove this env var once v1.83.0 is release. + ControlBufferThrottleLimit = uint64FromEnv("GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT", 100, 1, 10000) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index a2312f8eac..e4b6919138 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -69,9 +69,8 @@ var ( // https://github.com/grpc/proposal/blob/master/A87-mtls-spiffe-support.md XDSSPIFFEEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE", false) - // XDSHTTPConnectEnabled is true if gRPC should parse custom Metadata - // configuring use of an HTTP CONNECT proxy via xDS from cluster resources. - // For more details, see: + // XDSHTTPConnectEnabled controls support for dynamic HTTP CONNECT proxying + // configured via the xDS control plane. For more details, see: // https://github.com/grpc/proposal/blob/master/A86-xds-http-connect.md XDSHTTPConnectEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT", false) @@ -88,7 +87,7 @@ var ( // XDSORCAToLRSPropEnabled controls whether ORCA metrics are explicitly // filtered and prefix-propagated to the LRS server. For more details, see: // https://github.com/grpc/proposal/blob/master/A85-lrs-custom-metrics-changes.md - XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", false) + XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", true) // XDSClientExtProcEnabled indicates whether ExtProc filter is enabled on // the client side. For more details, see: diff --git a/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go index 9b6d8a1fa3..d4999fcca8 100644 --- a/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go +++ b/vendor/google.golang.org/grpc/internal/grpcsync/callback_serializer.go @@ -20,10 +20,15 @@ package grpcsync import ( "context" + "errors" "google.golang.org/grpc/internal/buffer" ) +// ErrSerializerClosed is returned by ScheduleAndWait if the CallbackSerializer +// was closed before the callback could be scheduled. +var ErrSerializerClosed = errors.New("callback serializer is closed") + // CallbackSerializer provides a mechanism to schedule callbacks in a // synchronized manner. It provides a FIFO guarantee on the order of execution // of scheduled callbacks. New callbacks can be scheduled by invoking the @@ -77,6 +82,27 @@ func (cs *CallbackSerializer) ScheduleOr(f func(ctx context.Context), onFailure } } +// ScheduleAndWait schedules the provided callback function f to be executed in +// the order it was added and blocks until f has run. If the context passed to +// NewCallbackSerializer was canceled before this method is called, f is not run +// and ScheduleAndWait returns ErrSerializerClosed. +// +// Callbacks are expected to honor the context when performing any blocking +// operations, and should return early when the context is canceled. +func (cs *CallbackSerializer) ScheduleAndWait(f func(ctx context.Context)) error { + done := make(chan struct{}) + var err error + cs.ScheduleOr(func(ctx context.Context) { + f(ctx) + close(done) + }, func() { + err = ErrSerializerClosed + close(done) + }) + <-done + return err +} + func (cs *CallbackSerializer) run(ctx context.Context) { defer close(cs.done) diff --git a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go index 6320e9b576..238950bbbf 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/config_selector.go +++ b/vendor/google.golang.org/grpc/internal/resolver/config_selector.go @@ -24,7 +24,6 @@ import ( "sync" "google.golang.org/grpc/internal/serviceconfig" - "google.golang.org/grpc/metadata" "google.golang.org/grpc/resolver" ) @@ -52,82 +51,7 @@ type RPCConfig struct { Context context.Context MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC OnCommitted func() // Called when the RPC has been committed (retries no longer possible) - Interceptor ClientInterceptor -} - -// ClientStream is the same as grpc.ClientStream, but defined here for circular -// dependency reasons. -type ClientStream interface { - // Header returns the header metadata received from the server if there - // is any. It blocks if the metadata is not ready to read. - Header() (metadata.MD, error) - // Trailer returns the trailer metadata from the server, if there is any. - // It must only be called after stream.CloseAndRecv has returned, or - // stream.Recv has returned a non-nil error (including io.EOF). - Trailer() metadata.MD - // CloseSend closes the send direction of the stream. It closes the stream - // when non-nil error is met. It is also not safe to call CloseSend - // concurrently with SendMsg. - CloseSend() error - // Context returns the context for this stream. - // - // It should not be called until after Header or RecvMsg has returned. Once - // called, subsequent client-side retries are disabled. - Context() context.Context - // SendMsg is generally called by generated code. On error, SendMsg aborts - // the stream. If the error was generated by the client, the status is - // returned directly; otherwise, io.EOF is returned and the status of - // the stream may be discovered using RecvMsg. - // - // SendMsg blocks until: - // - There is sufficient flow control to schedule m with the transport, or - // - The stream is done, or - // - The stream breaks. - // - // SendMsg does not wait until the message is received by the server. An - // untimely stream closure may result in lost messages. To ensure delivery, - // users should ensure the RPC completed successfully using RecvMsg. - // - // It is safe to have a goroutine calling SendMsg and another goroutine - // calling RecvMsg on the same stream at the same time, but it is not safe - // to call SendMsg on the same stream in different goroutines. It is also - // not safe to call CloseSend concurrently with SendMsg. - SendMsg(m any) error - // RecvMsg blocks until it receives a message into m or the stream is - // done. It returns io.EOF when the stream completes successfully. On - // any other error, the stream is aborted and the error contains the RPC - // status. - // - // It is safe to have a goroutine calling SendMsg and another goroutine - // calling RecvMsg on the same stream at the same time, but it is not - // safe to call RecvMsg on the same stream in different goroutines. - RecvMsg(m any) error -} - -// ClientInterceptor is an interceptor for gRPC client streams. -type ClientInterceptor interface { - // NewStream creates a ClientStream for an RPC. - // - // Implementations must delegate stream creation to the provided newStream - // function. To intercept or override stream behavior, implementations - // may wrap the ClientStream returned by the delegate. - // - // Note: RPCInfo.Context is currently unused and will be nil. - // - // The done function is invoked when the RPC has finished using its - // underlying connection or if a connection could not be assigned. Because - // interceptors operate at the application layer, RPC operations may - // continue on the ClientStream even after done has been called. The - // caller must ensure done is non-nil. - // - // To ensure RPC completion notifications propagate through the entire - // interceptor chain, implementations must ensure that the done function - // passed to the delegate newStream invokes the done function passed to - // NewStream. - NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error) - // Close closes the interceptor. Once called, no new calls to NewStream are - // accepted. Ongoing calls to NewStream are allowed to complete. - Close() + Interceptor any } // ServerInterceptor is an interceptor for incoming RPC's on gRPC server side. diff --git a/vendor/google.golang.org/grpc/internal/transport/client_stream.go b/vendor/google.golang.org/grpc/internal/transport/client_stream.go index ad382b0fda..046f0a5557 100644 --- a/vendor/google.golang.org/grpc/internal/transport/client_stream.go +++ b/vendor/google.golang.org/grpc/internal/transport/client_stream.go @@ -39,9 +39,8 @@ const nonGRPCDataMaxLen = 1024 type ClientStream struct { Stream // Embed for common stream functionality. - ct *http2Client - done chan struct{} // closed at the end of stream to unblock writers. - doneFunc func() // invoked at the end of stream. + ct *http2Client + done chan struct{} // closed at the end of stream to unblock writers. headerChan chan struct{} // closed to indicate the end of header metadata. header metadata.MD // the received header metadata diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index c5a76b70ad..b9bae02498 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -29,6 +29,7 @@ import ( "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/mem" ) @@ -96,61 +97,70 @@ func (il *itemList) isEmpty() bool { return il.head == nil } -// The following defines various control items which could flow through -// the control buffer of transport. They represent different aspects of -// control tasks, e.g., flow control, settings, streaming resetting, etc. - -// maxQueuedTransportResponseFrames is the most queued "transport response" -// frames we will buffer before preventing new reads from occurring on the -// transport. These are control frames sent in response to client requests, -// such as RST_STREAM due to bad headers or settings acks. -const maxQueuedTransportResponseFrames = 50 +// maxQueuedControlBufferItems is the maximum number of frames (other than +// HEADERS and DATA) that we will buffer before preventing new reads from +// occurring on the transport. These are control frames sent in response to +// client requests, or frames that result in work being scheduled, such as +// RST_STREAM due to bad headers or settings acks. +var maxQueuedControlBufferItems = int(envconfig.ControlBufferThrottleLimit) type cbItem interface { - isTransportResponseFrame() bool + isThrottled() bool } +// throttledItem represents every item in the controlBuffer to which the overall +// throttling limit applies, other than outgoing HEADERS and DATA frames. +type throttledItem struct{} + +func (throttledItem) isThrottled() bool { return true } + +// The following defines various control items which could flow through +// the control buffer of transport. They represent different aspects of +// control tasks, e.g., flow control, settings, streaming resetting, etc. + // registerStream is used to register an incoming stream with loopy writer. type registerStream struct { + throttledItem streamID uint32 wq *writeQuota } -func (*registerStream) isTransportResponseFrame() bool { return false } - -// headerFrame is also used to register stream on the client-side. -type headerFrame struct { +type clientHeaders struct { streamID uint32 hf []hpack.HeaderField - endStream bool // Valid on server side. - initStream func(uint32) error // Used only on the client side. + initStream func(uint32) error onWrite func() - wq *writeQuota // write quota for the stream created. - cleanup *cleanupStream // Valid on the server side. - onOrphaned func(error) // Valid on client-side + wq *writeQuota + onOrphaned func(error) } -func (h *headerFrame) isTransportResponseFrame() bool { - return h.cleanup != nil && h.cleanup.rst // Results in a RST_STREAM +func (*clientHeaders) isThrottled() bool { return false } + +type serverHeaders struct { + streamID uint32 + hf []hpack.HeaderField + endStream bool + onWrite func() + cleanup *cleanupStream } +func (h *serverHeaders) isThrottled() bool { return false } + type cleanupStream struct { + throttledItem streamID uint32 rst bool rstCode http2.ErrCode onWrite func() } -func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM - type earlyAbortStream struct { + throttledItem streamID uint32 rst bool hf []hpack.HeaderField // Pre-built header fields } -func (*earlyAbortStream) isTransportResponseFrame() bool { return false } - type dataFrame struct { streamID uint32 endStream bool @@ -162,70 +172,60 @@ type dataFrame struct { onEachWrite func() } -func (*dataFrame) isTransportResponseFrame() bool { return false } +func (*dataFrame) isThrottled() bool { return false } type incomingWindowUpdate struct { + throttledItem streamID uint32 increment uint32 } -func (*incomingWindowUpdate) isTransportResponseFrame() bool { return false } - type outgoingWindowUpdate struct { + throttledItem streamID uint32 increment uint32 } -func (*outgoingWindowUpdate) isTransportResponseFrame() bool { - return false // window updates are throttled by thresholds -} - type incomingSettings struct { + throttledItem ss []http2.Setting } -func (*incomingSettings) isTransportResponseFrame() bool { return true } // Results in a settings ACK - type outgoingSettings struct { + throttledItem ss []http2.Setting } -func (*outgoingSettings) isTransportResponseFrame() bool { return false } - type incomingGoAway struct { + throttledItem } -func (*incomingGoAway) isTransportResponseFrame() bool { return false } - type goAway struct { + throttledItem code http2.ErrCode debugData []byte headsUp bool closeConn error // if set, loopyWriter will exit with this error } -func (*goAway) isTransportResponseFrame() bool { return false } - type ping struct { + throttledItem ack bool data [8]byte } -func (*ping) isTransportResponseFrame() bool { return true } - type outFlowControlSizeRequest struct { + throttledItem resp chan uint32 } -func (*outFlowControlSizeRequest) isTransportResponseFrame() bool { return false } - // closeConnection is an instruction to tell the loopy writer to flush the // framer and exit, which will cause the transport's connection to be closed // (by the client or server). The transport itself will close after the reader // encounters the EOF caused by the connection closure. -type closeConnection struct{} - -func (closeConnection) isTransportResponseFrame() bool { return false } +type closeConnection struct { + throttledItem +} type outStreamState int @@ -379,9 +379,9 @@ func (c *controlBuffer) executeAndPut(f func() bool, it cbItem) (bool, error) { c.consumerWaiting = false } c.list.enqueue(it) - if it.isTransportResponseFrame() { + if it.isThrottled() { c.transportResponseFrames++ - if c.transportResponseFrames == maxQueuedTransportResponseFrames { + if c.transportResponseFrames == maxQueuedControlBufferItems { // We are adding the frame that puts us over the threshold; create // a throttling channel. ch := make(chan struct{}) @@ -436,8 +436,8 @@ func (c *controlBuffer) getOnceLocked() (any, error) { return nil, nil } h := c.list.dequeue().(cbItem) - if h.isTransportResponseFrame() { - if c.transportResponseFrames == maxQueuedTransportResponseFrames { + if h.isThrottled() { + if c.transportResponseFrames == maxQueuedControlBufferItems { // We are removing the frame that put us over the // threshold; close and clear the throttling channel. ch := c.trfChan.Swap(nil) @@ -464,10 +464,8 @@ func (c *controlBuffer) finish() { // is still not aware of these yet. for head := c.list.dequeueAll(); head != nil; head = head.next { switch v := head.it.(type) { - case *headerFrame: - if v.onOrphaned != nil { // It will be nil on the server-side. - v.onOrphaned(ErrConnClosing) - } + case *clientHeaders: + v.onOrphaned(ErrConnClosing) case *dataFrame: if !v.processing { v.data.Free() @@ -680,42 +678,38 @@ func (l *loopyWriter) registerStreamHandler(h *registerStream) { l.estdStreams[h.streamID] = str } -func (l *loopyWriter) headerHandler(h *headerFrame) error { - if l.side == serverSide { - str, ok := l.estdStreams[h.streamID] - if !ok { - if l.logger.V(logLevel) { - l.logger.Infof("Unrecognized streamID %d in loopyWriter", h.streamID) - } - return nil - } - // Case 1.A: Server is responding back with headers. - if !h.endStream { - return l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite) +func (l *loopyWriter) serverHeaderHandler(hdr *serverHeaders) error { + str, ok := l.estdStreams[hdr.streamID] + if !ok { + if l.logger.V(logLevel) { + l.logger.Infof("Unrecognized streamID %d in loopyWriter", hdr.streamID) } - // else: Case 1.B: Server wants to close stream. + return nil + } - if str.state != empty { // either active or waiting on stream quota. - // add it str's list of items. - str.itl.enqueue(h) - return nil - } - if err := l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite); err != nil { - return err - } - return l.cleanupStreamHandler(h.cleanup) + // Case 1: Server is responding back with headers. + if !hdr.endStream { + return l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite) + } + + // Case 2: Server is closing the stream. + if str.state != empty { // either active or waiting on stream quota. + str.itl.enqueue(hdr) + return nil + } + if err := l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite); err != nil { + return err } - // Case 2: Client wants to originate stream. + return l.cleanupStreamHandler(hdr.cleanup) +} + +func (l *loopyWriter) clientHeaderHandler(hdr *clientHeaders) error { str := &outStream{ - id: h.streamID, + id: hdr.streamID, state: empty, itl: &itemList{}, - wq: h.wq, + wq: hdr.wq, } - return l.originateStream(str, h) -} - -func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error { // l.draining is set when handling GoAway. In which case, we want to avoid // creating new streams. if l.draining { @@ -726,7 +720,7 @@ func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error { if err := hdr.initStream(str.id); err != nil { return err } - if err := l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil { + if err := l.writeHeader(str.id, false, hdr.hf, hdr.onWrite); err != nil { return err } l.estdStreams[str.id] = str @@ -882,8 +876,10 @@ func (l *loopyWriter) handle(i any) error { return l.incomingSettingsHandler(i) case *outgoingSettings: return l.outgoingSettingsHandler(i) - case *headerFrame: - return l.headerHandler(i) + case *clientHeaders: + return l.clientHeaderHandler(i) + case *serverHeaders: + return l.serverHeaderHandler(i) case *registerStream: l.registerStreamHandler(i) case *cleanupStream: @@ -1022,7 +1018,7 @@ func (l *loopyWriter) processData() (bool, error) { func (l *loopyWriter) updateStreamAfterWrite(str *outStream) error { if str.itl.isEmpty() { str.state = empty - } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers. + } else if trailer, ok := str.itl.peek().(*serverHeaders); ok { // the next item is trailers. if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil { return err } diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index 133f5d7065..c19b45080e 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -498,7 +498,6 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr, handler s ct: t, done: make(chan struct{}), headerChan: make(chan struct{}), - doneFunc: callHdr.DoneFunc, statsHandler: handler, } s.Stream.buf.init() @@ -806,9 +805,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s close(s.headerChan) } } - hdr := &headerFrame{ - hf: headerFields, - endStream: false, + hdr := &clientHeaders{ + hf: headerFields, initStream: func(uint32) error { t.mu.Lock() // TODO: handle transport closure in loopy instead and remove this @@ -999,9 +997,6 @@ func (t *http2Client) closeStream(s *ClientStream, err error, rst bool, rstCode t.controlBuf.executeAndPut(addBackStreamQuota, cleanup) // This will unblock write. close(s.done) - if s.doneFunc != nil { - s.doneFunc() - } } // Close kicks off the shutdown process of the transport. This should be called @@ -1251,7 +1246,10 @@ func (t *http2Client) handleData(f *parsedDataFrame) { dataLen := f.data.Len() if f.Header().Flags.Has(http2.FlagDataPadded) { if w := s.fc.onRead(size - uint32(dataLen)); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{s.id, w}) + t.controlBuf.put(&outgoingWindowUpdate{ + streamID: s.id, + increment: w, + }) } } if dataLen > 0 { @@ -1879,7 +1877,7 @@ func (t *http2Client) getOutFlowWindow() int64 { resp := make(chan uint32, 1) timer := time.NewTimer(time.Second) defer timer.Stop() - t.controlBuf.put(&outFlowControlSizeRequest{resp}) + t.controlBuf.put(&outFlowControlSizeRequest{resp: resp}) select { case sz := <-resp: return int64(sz) diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 1acd44be4b..be8ae9f9c5 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -810,7 +810,10 @@ func (t *http2Server) handleData(f *parsedDataFrame) { dataLen := f.data.Len() if f.Header().Flags.Has(http2.FlagDataPadded) { if w := s.fc.onRead(size - uint32(dataLen)); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{s.id, w}) + t.controlBuf.put(&outgoingWindowUpdate{ + streamID: s.id, + increment: w, + }) } } if dataLen > 0 { @@ -1047,7 +1050,7 @@ func (t *http2Server) writeHeaderLocked(s *ServerStream) error { headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress}) } headerFields = appendHeaderFieldsFromMD(headerFields, s.header) - hf := &headerFrame{ + hf := &serverHeaders{ streamID: s.id, hf: headerFields, endStream: false, @@ -1115,7 +1118,7 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error { // Attach the trailer metadata. headerFields = appendHeaderFieldsFromMD(headerFields, s.trailer) - trailingHeader := &headerFrame{ + trailingHeader := &serverHeaders{ streamID: s.id, hf: headerFields, endStream: true, @@ -1325,7 +1328,7 @@ func (t *http2Server) deleteStream(s *ServerStream, eosReceived bool) { } // finishStream closes the stream and puts the trailing headerFrame into controlbuf. -func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) { +func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *serverHeaders, eosReceived bool) { // In case stream sending and receiving are invoked in separate // goroutines (e.g., bi-directional streaming), cancel needs to be // called to interrupt the potential blocking on other goroutines. @@ -1464,7 +1467,7 @@ func (t *http2Server) getOutFlowWindow() int64 { resp := make(chan uint32, 1) timer := time.NewTimer(time.Second) defer timer.Stop() - t.controlBuf.put(&outFlowControlSizeRequest{resp}) + t.controlBuf.put(&outFlowControlSizeRequest{resp: resp}) select { case sz := <-resp: return int64(sz) diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 6dfae39849..d2e49538f0 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -594,8 +594,6 @@ type CallHdr struct { PreviousAttempts int // value of grpc-previous-rpc-attempts header to set - DoneFunc func() // called when the stream is finished - // Authority is used to explicitly override the `:authority` header. // // This value comes from one of two sources: diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index 4aac644a83..51aff85dfb 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -201,6 +201,15 @@ func endOfClientStream(cc *ClientConn, err error, opts ...CallOption) { } } +// clientInterceptor is structurally identical to the ClientInterceptor defined +// in internal/xds/httpfilter/httpfilter.go. It is defined locally here so that +// we can type-assert the generic Interceptor field in iresolver.RPCConfig +// without introducing a dependency on xDS packages. +type clientInterceptor interface { + NewStream(ctx context.Context, ri iresolver.RPCInfo, newStream func(ctx context.Context, opts ...CallOption) (ClientStream, error), opts ...CallOption) (ClientStream, error) + Close() +} + func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { if channelz.IsOn() { cc.incrCallsStarted() @@ -244,8 +253,11 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth mc := &emptyMethodConfig var onCommit func() - newStream := func(ctx context.Context, done func()) (iresolver.ClientStream, error) { - return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, nameResolutionDelayed, opts...) + newStream := func(ctx context.Context, filterOpts ...CallOption) (ClientStream, error) { + if filterOpts != nil { + opts = combine(opts, filterOpts) + } + return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, nameResolutionDelayed, opts...) } rpcInfo := iresolver.RPCInfo{Context: ctx, Method: method} @@ -270,20 +282,24 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth if rpcConfig.Interceptor != nil { rpcInfo.Context = nil ns := newStream - newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) { - cs, err := rpcConfig.Interceptor.NewStream(ctx, rpcInfo, done, ns) - if err != nil { - return nil, toRPCErr(err) + if interceptor, ok := rpcConfig.Interceptor.(clientInterceptor); ok { + newStream = func(ctx context.Context, filterOpts ...CallOption) (ClientStream, error) { + cs, err := interceptor.NewStream(ctx, rpcInfo, ns, filterOpts...) + if err != nil { + return nil, toRPCErr(err) + } + return cs, nil } - return cs, nil + } else { + return nil, status.Errorf(codes.Internal, "invalid client interceptor type %T", rpcConfig.Interceptor) } } } - return newStream(ctx, func() {}) + return newStream(ctx) } -func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc *serviceconfig.MethodConfig, onCommit, doneFunc func(), nameResolutionDelayed bool, opts ...CallOption) (_ iresolver.ClientStream, err error) { +func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc *serviceconfig.MethodConfig, onCommit func(), nameResolutionDelayed bool, opts ...CallOption) (_ ClientStream, err error) { callInfo := defaultCallInfo() if mc.WaitForReady != nil { callInfo.failFast = !*mc.WaitForReady @@ -321,7 +337,6 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client Host: cc.authority, Method: method, ContentSubtype: callInfo.contentSubtype, - DoneFunc: doneFunc, Authority: callInfo.authority, } if allowed := callInfo.acceptedResponseCompressors; len(allowed) > 0 { diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index cf114ef4bc..4083c03908 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.82.0" +const Version = "1.83.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index c7fe23c00c..f03b6ecb59 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2071,9 +2071,6 @@ github.com/testcontainers/testcontainers-go/wait # github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0 ## explicit; go 1.25.0 github.com/testcontainers/testcontainers-go/modules/opensearch -# github.com/theckman/yacspin v0.13.12 -## explicit; go 1.17 -github.com/theckman/yacspin # github.com/thejerf/suture/v4 v4.0.6 ## explicit; go 1.9 github.com/thejerf/suture/v4 @@ -2574,7 +2571,7 @@ google.golang.org/genproto/googleapis/api/httpbody ## explicit; go 1.25.0 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.82.0 +# google.golang.org/grpc v1.83.0 ## explicit; go 1.25.0 google.golang.org/grpc google.golang.org/grpc/attributes From fb72b82eb65c95dc2a85bdb3222e483e92676346 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 14:18:20 +0000 Subject: [PATCH 24/27] build(deps): bump github.com/open-policy-agent/opa from 1.18.2 to 1.19.0 Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.18.2 to 1.19.0. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.18.2...v1.19.0) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.19.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +- go.sum | 20 +- .../github.com/go-logr/logr/context_noslog.go | 1 - .../github.com/go-logr/logr/context_slog.go | 1 - vendor/github.com/go-logr/logr/funcr/funcr.go | 47 +- .../github.com/go-logr/logr/funcr/slogsink.go | 22 +- vendor/github.com/go-logr/logr/sloghandler.go | 1 - vendor/github.com/go-logr/logr/slogr.go | 1 - vendor/github.com/go-logr/logr/slogsink.go | 1 - .../opa/capabilities/v1.19.0.json | 5026 +++++++++++++++++ .../opa/internal/compiler/wasm/opa/opa.wasm | Bin 431796 -> 431796 bytes .../opa/internal/gojsonschema/errors.go | 9 +- .../opa/internal/gojsonschema/schema.go | 5 +- .../opa/internal/jsonv2/jsonv2.go | 79 + .../opa/internal/methodlesstemplate/LICENSE | 27 + .../opa/internal/methodlesstemplate/README.md | 28 + .../opa/internal/methodlesstemplate/doc.go | 502 ++ .../opa/internal/methodlesstemplate/exec.go | 1132 ++++ .../opa/internal/methodlesstemplate/funcs.go | 783 +++ .../internal/fmtsort/sort.go | 154 + .../opa/internal/methodlesstemplate/option.go | 72 + .../internal/methodlesstemplate/template.go | 236 + .../opa/internal/planner/planner.go | 163 +- .../opa/internal/planner/rules.go | 46 +- .../opa/internal/semver/semver.go | 6 +- .../opa/v1/ast/annotations.go | 108 +- .../opa/v1/ast/annotations_json.go | 124 + .../opa/v1/ast/annotations_jsonv2.go | 195 + .../open-policy-agent/opa/v1/ast/builtins.go | 50 +- .../open-policy-agent/opa/v1/ast/check.go | 4 +- .../open-policy-agent/opa/v1/ast/compare.go | 16 +- .../open-policy-agent/opa/v1/ast/compile.go | 282 +- .../open-policy-agent/opa/v1/ast/errors.go | 29 +- .../opa/v1/ast/external_source.go | 69 + .../open-policy-agent/opa/v1/ast/index.go | 311 +- .../opa/v1/ast/location/location.go | 40 - .../opa/v1/ast/location/location_json.go | 47 + .../opa/v1/ast/location/location_jsonv2.go | 50 + .../open-policy-agent/opa/v1/ast/map.go | 2 +- .../open-policy-agent/opa/v1/ast/mermaid.go | 24 +- .../open-policy-agent/opa/v1/ast/parser.go | 297 +- .../open-policy-agent/opa/v1/ast/policy.go | 394 +- .../opa/v1/ast/policy_appenders.go | 18 +- .../opa/v1/ast/policy_json.go | 289 + .../opa/v1/ast/policy_jsonv2.go | 524 ++ .../opa/v1/ast/string_length.go | 17 +- .../open-policy-agent/opa/v1/ast/term.go | 237 +- .../opa/v1/ast/term_appenders.go | 8 + .../open-policy-agent/opa/v1/ast/term_json.go | 95 + .../opa/v1/ast/term_jsonv2.go | 251 + .../opa/v1/ast/version_index.json | 5 + .../open-policy-agent/opa/v1/bundle/bundle.go | 157 +- .../open-policy-agent/opa/v1/bundle/file.go | 5 +- .../opa/v1/bundle/manifest.proto | 6 +- .../open-policy-agent/opa/v1/bundle/proto.go | 434 ++ .../opa/v1/bundle/v1pb/manifest.pb.go | 751 +++ .../open-policy-agent/opa/v1/format/format.go | 7 +- .../open-policy-agent/opa/v1/ir/proto.go | 340 ++ .../opa/v1/ir/v1pb/plan.pb.go | 3449 +++++++++++ .../open-policy-agent/opa/v1/keys/keys.go | 3 + .../open-policy-agent/opa/v1/logging/slog.go | 87 - .../opa/v1/storage/internal/ptr/ptr.go | 14 +- .../opa/v1/topdown/aggregates.go | 83 +- .../opa/v1/topdown/arithmetic.go | 28 + .../opa/v1/topdown/builtins/builtins.go | 15 +- .../open-policy-agent/opa/v1/topdown/cache.go | 57 +- .../copypropagation/copypropagation.go | 68 +- .../opa/v1/topdown/errors_jsonv2.go | 24 + .../open-policy-agent/opa/v1/topdown/eval.go | 506 +- .../open-policy-agent/opa/v1/topdown/query.go | 2 +- .../open-policy-agent/opa/v1/topdown/save.go | 30 +- .../opa/v1/topdown/strings.go | 105 +- .../opa/v1/topdown/template.go | 9 +- .../open-policy-agent/opa/v1/topdown/trace.go | 58 + .../open-policy-agent/opa/v1/util/compare.go | 16 + .../open-policy-agent/opa/v1/util/hashmap.go | 30 +- .../opa/v1/util/performance.go | 89 + .../open-policy-agent/opa/v1/util/queue.go | 91 + .../opa/v1/version/version.go | 2 +- .../vektah/gqlparser/v2/ast/definition.go | 8 +- .../vektah/gqlparser/v2/ast/dumper.go | 6 +- .../vektah/gqlparser/v2/lexer/lexer.go | 6 +- .../vektah/gqlparser/v2/lexer/lexer_test.yml | 6 + .../vektah/gqlparser/v2/parser/schema.go | 14 +- .../vektah/gqlparser/v2/validator/schema.go | 24 + .../gqlparser/v2/validator/schema_test.yml | 31 + .../vektah/gqlparser/v2/validator/vars.go | 19 +- vendor/modules.txt | 11 +- 88 files changed, 17053 insertions(+), 1392 deletions(-) create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v1.19.0.json create mode 100644 vendor/github.com/open-policy-agent/opa/internal/jsonv2/jsonv2.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/LICENSE create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/README.md create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/doc.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/exec.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/option.go create mode 100644 vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/annotations_json.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/annotations_jsonv2.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/location/location_json.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/location/location_jsonv2.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/policy_json.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ir/proto.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go delete mode 100644 vendor/github.com/open-policy-agent/opa/v1/logging/slog.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go diff --git a/go.mod b/go.mod index 53f5f8a0ab..2c7fbaa4ba 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.32.0 github.com/onsi/gomega v1.42.1 - github.com/open-policy-agent/opa v1.18.2 + github.com/open-policy-agent/opa v1.19.0 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d github.com/opencloud-eu/reva/v2 v2.47.0 @@ -210,7 +210,7 @@ require ( github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-micro/plugins/v4/events/natsjs v1.2.2 // indirect github.com/go-micro/plugins/v4/store/nats-js v1.2.1 // indirect @@ -362,7 +362,7 @@ require ( github.com/trustelem/zxcvbn v1.0.1 // indirect github.com/urfave/cli/v2 v2.27.7 // indirect github.com/valyala/fastjson v1.6.10 // indirect - github.com/vektah/gqlparser/v2 v2.5.34 // indirect + github.com/vektah/gqlparser/v2 v2.5.36 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/wk8/go-ordered-map v1.0.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect diff --git a/go.sum b/go.sum index c026746fe3..efebe28964 100644 --- a/go.sum +++ b/go.sum @@ -195,8 +195,6 @@ github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/ github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3 h1:h8Z0hBv5tg/uZMKu8V47+DKWYVQg0lYP8lXDQq7uRpE= github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3/go.mod h1:eE/tD53n3KbVrzrWxKLxdkGw45Fg1qaNLWjpJMvIUF4= -github.com/bytecodealliance/wasmtime-go/v44 v44.0.0 h1:WRZXnLPIer/TWs5aYPaMlmVcOlzmR6Ur6wjLRIQOhTQ= -github.com/bytecodealliance/wasmtime-go/v44 v44.0.0/go.mod h1:GP93piU+39CoFVCQ5xfHrPOUtL0APlMnkbblJ2d3YY0= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -281,8 +279,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjY github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= -github.com/dgraph-io/badger/v4 v4.9.2 h1:Wb5qw8gElqwV1a8msHTeQKova9b1V10heFKMIiPd80E= -github.com/dgraph-io/badger/v4 v4.9.2/go.mod h1:nJjaJTUOSsQEBhsq209FmwCvMJzEA3e74RjZw6V2pQI= +github.com/dgraph-io/badger/v4 v4.9.4 h1:bcw+waCpzRZ2nmcSPbnPvDVhiEsn98TKmvnAhK7r7LM= +github.com/dgraph-io/badger/v4 v4.9.4/go.mod h1:nJjaJTUOSsQEBhsq209FmwCvMJzEA3e74RjZw6V2pQI= github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE= github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= @@ -412,8 +410,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-micro/plugins/v4/client/grpc v1.2.1 h1:7xAwZRCO6mdUtBHsYIQs1/eCTdhCrnjF70GB+AVd6L0= @@ -936,8 +934,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= -github.com/open-policy-agent/opa v1.18.2 h1:VBiLJpioTuk7XTW1JoQi4ILo+FVxD2/8uD8iP9/OcxY= -github.com/open-policy-agent/opa v1.18.2/go.mod h1:9GY+hER4ZEXtxPlMjftVbqJJY9xLtCD3Q0oufRCfAKo= +github.com/open-policy-agent/opa v1.19.0 h1:+j2OCsjMezZEML2T1lI9giJdGJS/PL1XFKgkHPGIhpo= +github.com/open-policy-agent/opa v1.19.0/go.mod h1:pb6Y6klyf7X7X8uXNDflruA9dQC2gMqWROXI5w/kvv0= github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a h1:Sakl76blJAaM6NxylVkgSzktjo2dS504iDotEFJsh3M= github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY= github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+lP5lUUIzjRGDg93WrQfZJZCaV1ZP3KeyXi8bzY= @@ -1189,6 +1187,8 @@ github.com/testcontainers/testcontainers-go v0.43.0 h1:oEQx5MW2DGd9z3AeEQfB2lPM0 github.com/testcontainers/testcontainers-go v0.43.0/go.mod h1:+VxkT2NQnKOZPKi6praMuMKYHYyOGXr0XSBSlSMCzFo= github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0 h1:a1ipjF7d/VxPX1dgVPIk4F+t6YkgMbE2OtBuRQCHJt8= github.com/testcontainers/testcontainers-go/modules/opensearch v0.43.0/go.mod h1:OWSeUDiGMUy30iMsAltIJIo9uh/CleLv6KyxjYOsgR8= +github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU= +github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0= github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8= @@ -1227,8 +1227,8 @@ github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADT github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/vektah/gqlparser/v2 v2.5.34 h1:MEea5P0qhdcqfBL45ghKE+qr9laidVHTMHjav5h7ckk= -github.com/vektah/gqlparser/v2 v2.5.34/go.mod h1:mFdHLGCio7OGX1fby9ZjTW6FN+qxgmbnBcRIeeScE5s= +github.com/vektah/gqlparser/v2 v2.5.36 h1:CN9mKVHgMkc+XftdOWIhb4HEL8wKSYkFAqhf8booa7s= +github.com/vektah/gqlparser/v2 v2.5.36/go.mod h1:cAJ9qwVgPaUkWv6Gn8vn0mqOE0Ui5Pn56wNy5396XWo= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= diff --git a/vendor/github.com/go-logr/logr/context_noslog.go b/vendor/github.com/go-logr/logr/context_noslog.go index f012f9a18e..0a3d1a125e 100644 --- a/vendor/github.com/go-logr/logr/context_noslog.go +++ b/vendor/github.com/go-logr/logr/context_noslog.go @@ -1,5 +1,4 @@ //go:build !go1.21 -// +build !go1.21 /* Copyright 2019 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/context_slog.go b/vendor/github.com/go-logr/logr/context_slog.go index 065ef0b828..c69eb01ba1 100644 --- a/vendor/github.com/go-logr/logr/context_slog.go +++ b/vendor/github.com/go-logr/logr/context_slog.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2019 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/funcr/funcr.go b/vendor/github.com/go-logr/logr/funcr/funcr.go index b22c57d713..7f4996e9e4 100644 --- a/vendor/github.com/go-logr/logr/funcr/funcr.go +++ b/vendor/github.com/go-logr/logr/funcr/funcr.go @@ -426,7 +426,7 @@ func (f Formatter) colon() byte { } func (f Formatter) pretty(value any) string { - return f.prettyWithFlags(value, 0, 0) + return f.prettyWithFlags(value, 0, 0, 0, nil) } const ( @@ -434,7 +434,13 @@ const ( ) // TODO: This is not fast. Most of the overhead goes here. -func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { +// value: The value to render +// flags: Bitmask of flags (see above) +// depth: The current depth of nested structs, slices, arrays, and maps +// ptrDepth: The current depth of including pointer dereferences +// ptrMap: A map of pointers already seen, to avoid infinite recursion (usually +// nil unless ptrDepth is large) +func (f Formatter) prettyWithFlags(value any, flags uint32, depth int, ptrDepth int, ptrMap map[uintptr]bool) string { if depth > f.opts.MaxLogDepth { return `""` } @@ -504,7 +510,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { // arbitrary keys might need escaping buf.WriteString(prettyString(k)) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1)) + buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1, ptrDepth+1, ptrMap)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') @@ -576,7 +582,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { } printComma = true // if we got here, we are rendering a field if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" { - buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1)) + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1, ptrDepth+1, ptrMap)) continue } if name == "" { @@ -585,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { // field names can't contain characters which need escaping buf.WriteString(f.quoted(name, false)) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1, ptrDepth+1, ptrMap)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') @@ -612,7 +618,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { buf.WriteByte(f.comma()) } e := v.Index(i) - buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1, ptrDepth+1, ptrMap)) } buf.WriteByte(']') return buf.String() @@ -637,7 +643,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { keystr = prettyString(keystr) } else { // prettyWithFlags will produce already-escaped values - keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1) + // key depth is unrelated to overall depth + keystr = f.prettyWithFlags(it.Key().Interface(), 0, 0, ptrDepth, ptrMap) if t.Key().Kind() != reflect.String { // JSON only does string keys. Unlike Go's standard JSON, we'll // convert just about anything to a string. @@ -646,16 +653,34 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { } buf.WriteString(keystr) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1, ptrDepth+1, ptrMap)) i++ } buf.WriteByte('}') return buf.String() - case reflect.Ptr, reflect.Interface: + case reflect.Pointer, reflect.Interface: if v.IsNil() { return "null" } - return f.prettyWithFlags(v.Elem().Interface(), 0, depth) + // Special case: recursive pointers. For normal use we do not want to + // count pointer dereferences as depth, but if we see the same pointer + // again we have a recursion and need to stop. After a large number of + // pointer dereferences we will start tracking pointers to avoid the + // perf hit of doing it in the normal path. + // + // This should not happen accidentally (e.g. json decoding should never + // do this) but we can handle it gracefully. + if ptrMap != nil && ptrMap[uintptr(v.Pointer())] { + depth = f.opts.MaxLogDepth + 1 // force a depth error + } + const maxDepthFactor = 4 // arbitrary, but we want it large enough to not false-alert + if ptrDepth > f.opts.MaxLogDepth*maxDepthFactor && ptrMap == nil { + ptrMap = map[uintptr]bool{} + } + if ptrMap != nil { + ptrMap[(uintptr)(v.Pointer())] = true + } + return f.prettyWithFlags(v.Elem().Interface(), 0, depth, ptrDepth+1, ptrMap) } return fmt.Sprintf(`""`, t.Kind().String()) } @@ -697,7 +722,7 @@ func isEmpty(v reflect.Value) bool { return v.Float() == 0 case reflect.Complex64, reflect.Complex128: return v.Complex() == 0 - case reflect.Interface, reflect.Ptr: + case reflect.Interface, reflect.Pointer: return v.IsNil() } return false diff --git a/vendor/github.com/go-logr/logr/funcr/slogsink.go b/vendor/github.com/go-logr/logr/funcr/slogsink.go index 7bd84761e2..8b519c91e1 100644 --- a/vendor/github.com/go-logr/logr/funcr/slogsink.go +++ b/vendor/github.com/go-logr/logr/funcr/slogsink.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. @@ -33,7 +32,7 @@ const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink func (l fnlogger) Handle(_ context.Context, record slog.Record) error { kvList := make([]any, 0, 2*record.NumAttrs()) record.Attrs(func(attr slog.Attr) bool { - kvList = attrToKVs(attr, kvList) + kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth) return true }) @@ -49,7 +48,7 @@ func (l fnlogger) Handle(_ context.Context, record slog.Record) error { func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink { kvList := make([]any, 0, 2*len(attrs)) for _, attr := range attrs { - kvList = attrToKVs(attr, kvList) + kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth) } l.AddValues(kvList) return &l @@ -61,14 +60,25 @@ func (l fnlogger) WithGroup(name string) logr.SlogSink { } // attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups -// and other details of slog. -func attrToKVs(attr slog.Attr, kvList []any) []any { +// and other details of slog. maxDepth bounds recursion into nested groups so a +// deeply-nested slog.Group cannot exhaust the stack; it is decremented per group +// level and starts at the Formatter's MaxLogDepth (past which the formatter would +// truncate the rendering anyway). +func attrToKVs(attr slog.Attr, kvList []any, maxDepth int) []any { attrVal := attr.Value.Resolve() if attrVal.Kind() == slog.KindGroup { + if maxDepth <= 0 { + // Nesting is too deep to build without risking a stack overflow. + // Stop here; the formatter truncates below MaxLogDepth regardless. + if attr.Key != "" { + kvList = append(kvList, attr.Key, "") + } + return kvList + } groupVal := attrVal.Group() grpKVs := make([]any, 0, 2*len(groupVal)) for _, attr := range groupVal { - grpKVs = attrToKVs(attr, grpKVs) + grpKVs = attrToKVs(attr, grpKVs, maxDepth-1) } if attr.Key == "" { // slog says we have to inline these diff --git a/vendor/github.com/go-logr/logr/sloghandler.go b/vendor/github.com/go-logr/logr/sloghandler.go index 82d1ba4948..befaf5510f 100644 --- a/vendor/github.com/go-logr/logr/sloghandler.go +++ b/vendor/github.com/go-logr/logr/sloghandler.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/slogr.go b/vendor/github.com/go-logr/logr/slogr.go index 28a83d0243..bfe80eb8d6 100644 --- a/vendor/github.com/go-logr/logr/slogr.go +++ b/vendor/github.com/go-logr/logr/slogr.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/slogsink.go b/vendor/github.com/go-logr/logr/slogsink.go index 4060fcbc2b..ab76ea99fe 100644 --- a/vendor/github.com/go-logr/logr/slogsink.go +++ b/vendor/github.com/go-logr/logr/slogsink.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.0.json new file mode 100644 index 0000000000..1b02dd4ace --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.0.json @@ -0,0 +1,5026 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "not" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm index a294543e8823f45b8ce4ecfd1387e7a9c8b41663..25d39a83e97bc76efef0300f0473a4d77cf9e22f 100644 GIT binary patch delta 40 qcmdn8R%**ysSVfo8JU`|^S58;X9Qv arg2 + ge + Returns the boolean truth of arg1 >= arg2 + +For simpler multi-way equality tests, eq (only) accepts two or more +arguments and compares the second and subsequent to the first, +returning in effect + + arg1==arg2 || arg1==arg3 || arg1==arg4 ... + +(Unlike with || in Go, however, eq is a function call and all the +arguments will be evaluated.) + +The comparison functions work on any values whose type Go defines as +comparable. For basic types such as integers, the rules are relaxed: +size and exact type are ignored, so any integer value, signed or unsigned, +may be compared with any other integer value. (The arithmetic value is compared, +not the bit pattern, so all negative integers are less than all unsigned integers.) +However, as usual, one may not compare an int with a float32 and so on. + +Associated templates + +Each template is named by a string specified when it is created. Also, each +template is associated with zero or more other templates that it may invoke by +name; such associations are transitive and form a name space of templates. + +A template may use a template invocation to instantiate another associated +template; see the explanation of the "template" action above. The name must be +that of a template associated with the template that contains the invocation. + +Nested template definitions + +When parsing a template, another template may be defined and associated with the +template being parsed. Template definitions must appear at the top level of the +template, much like global variables in a Go program. + +The syntax of such definitions is to surround each template declaration with a +"define" and "end" action. + +The define action names the template being created by providing a string +constant. Here is a simple example: + + {{define "T1"}}ONE{{end}} + {{define "T2"}}TWO{{end}} + {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}} + {{template "T3"}} + +This defines two templates, T1 and T2, and a third T3 that invokes the other two +when it is executed. Finally it invokes T3. If executed this template will +produce the text + + ONE TWO + +By construction, a template may reside in only one association. If it's +necessary to have a template addressable from multiple associations, the +template definition must be parsed multiple times to create distinct *Template +values, or must be copied with [Template.Clone] or [Template.AddParseTree]. + +Parse may be called multiple times to assemble the various associated templates; +see [ParseFiles], [ParseGlob], [Template.ParseFiles] and [Template.ParseGlob] +for simple ways to parse related templates stored in files. + +A template may be executed directly or through [Template.ExecuteTemplate], which executes +an associated template identified by name. To invoke our example above, we +might write, + + err := tmpl.Execute(os.Stdout, "no data needed") + if err != nil { + log.Fatalf("execution failed: %s", err) + } + +or to invoke a particular template explicitly by name, + + err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed") + if err != nil { + log.Fatalf("execution failed: %s", err) + } + +*/ +package template diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/exec.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/exec.go new file mode 100644 index 0000000000..0ed449b937 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/exec.go @@ -0,0 +1,1132 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "errors" + "fmt" + "io" + "reflect" + "runtime" + "strings" + "text/template/parse" + + "github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort" +) + +// maxExecDepth specifies the maximum stack depth of templates within +// templates. This limit is only practically reached by accidentally +// recursive template invocations. This limit allows us to return +// an error instead of triggering a stack overflow. +var maxExecDepth = initMaxExecDepth() + +func initMaxExecDepth() int { + if runtime.GOARCH == "wasm" { + return 1000 + } + return 100000 +} + +// state represents the state of an execution. It's not part of the +// template so that multiple executions of the same template +// can execute in parallel. +type state struct { + tmpl *Template + wr io.Writer + node parse.Node // current node, for errors + vars []variable // push-down stack of variable values. + depth int // the height of the stack of executing templates. +} + +// variable holds the dynamic value of a variable such as $, $x etc. +type variable struct { + name string + value reflect.Value +} + +// push pushes a new variable on the stack. +func (s *state) push(name string, value reflect.Value) { + s.vars = append(s.vars, variable{name, value}) +} + +// mark returns the length of the variable stack. +func (s *state) mark() int { + return len(s.vars) +} + +// pop pops the variable stack up to the mark. +func (s *state) pop(mark int) { + s.vars = s.vars[0:mark] +} + +// setVar overwrites the last declared variable with the given name. +// Used by variable assignments. +func (s *state) setVar(name string, value reflect.Value) { + for i := s.mark() - 1; i >= 0; i-- { + if s.vars[i].name == name { + s.vars[i].value = value + return + } + } + s.errorf("undefined variable: %s", name) +} + +// setTopVar overwrites the top-nth variable on the stack. Used by range iterations. +func (s *state) setTopVar(n int, value reflect.Value) { + s.vars[len(s.vars)-n].value = value +} + +// varValue returns the value of the named variable. +func (s *state) varValue(name string) reflect.Value { + for i := s.mark() - 1; i >= 0; i-- { + if s.vars[i].name == name { + return s.vars[i].value + } + } + s.errorf("undefined variable: %s", name) + return zero +} + +var zero reflect.Value + +type missingValType struct{} + +var missingVal = reflect.ValueOf(missingValType{}) + +var missingValReflectType = reflect.TypeFor[missingValType]() + +func isMissing(v reflect.Value) bool { + return v.IsValid() && v.Type() == missingValReflectType +} + +// at marks the state to be on node n, for error reporting. +func (s *state) at(node parse.Node) { + s.node = node +} + +// doublePercent returns the string with %'s replaced by %%, if necessary, +// so it can be used safely inside a Printf format string. +func doublePercent(str string) string { + return strings.ReplaceAll(str, "%", "%%") +} + +// TODO: It would be nice if ExecError was more broken down, but +// the way ErrorContext embeds the template name makes the +// processing too clumsy. + +// ExecError is the custom error type returned when Execute has an +// error evaluating its template. (If a write error occurs, the actual +// error is returned; it will not be of type ExecError.) +type ExecError struct { + Name string // Name of template. + Err error // Pre-formatted error. +} + +func (e ExecError) Error() string { + return e.Err.Error() +} + +func (e ExecError) Unwrap() error { + return e.Err +} + +// errorf records an ExecError and terminates processing. +func (s *state) errorf(format string, args ...any) { + name := doublePercent(s.tmpl.Name()) + if s.node == nil { + format = fmt.Sprintf("template: %s: %s", name, format) + } else { + location, context := s.tmpl.ErrorContext(s.node) + format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format) + } + panic(ExecError{ + Name: s.tmpl.Name(), + Err: fmt.Errorf(format, args...), + }) +} + +// writeError is the wrapper type used internally when Execute has an +// error writing to its output. We strip the wrapper in errRecover. +// Note that this is not an implementation of error, so it cannot escape +// from the package as an error value. +type writeError struct { + Err error // Original error. +} + +func (s *state) writeError(err error) { + panic(writeError{ + Err: err, + }) +} + +// errRecover is the handler that turns panics into returns from the top +// level of Parse. +func errRecover(errp *error) { + e := recover() + if e != nil { + switch err := e.(type) { + case runtime.Error: + panic(e) + case writeError: + *errp = err.Err // Strip the wrapper. + case ExecError: + *errp = err // Keep the wrapper. + default: + panic(e) + } + } +} + +// ExecuteTemplate applies the template associated with t that has the given name +// to the specified data object and writes the output to wr. +// If an error occurs executing the template or writing its output, +// execution stops, but partial results may already have been written to +// the output writer. +// A template may be executed safely in parallel, although if parallel +// executions share a Writer the output may be interleaved. +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error { + tmpl := t.Lookup(name) + if tmpl == nil { + return fmt.Errorf("template: no template %q associated with template %q", name, t.name) + } + return tmpl.Execute(wr, data) +} + +// Execute applies a parsed template to the specified data object, +// and writes the output to wr. +// If an error occurs executing the template or writing its output, +// execution stops, but partial results may already have been written to +// the output writer. +// A template may be executed safely in parallel, although if parallel +// executions share a Writer the output may be interleaved. +// +// If data is a [reflect.Value], the template applies to the concrete +// value that the reflect.Value holds, as in [fmt.Print]. +func (t *Template) Execute(wr io.Writer, data any) error { + return t.execute(wr, data) +} + +func (t *Template) execute(wr io.Writer, data any) (err error) { + defer errRecover(&err) + value, ok := data.(reflect.Value) + if !ok { + value = reflect.ValueOf(data) + } + state := &state{ + tmpl: t, + wr: wr, + vars: []variable{{"$", value}}, + } + if t.Tree == nil || t.Root == nil { + state.errorf("%q is an incomplete or empty template", t.Name()) + } + state.walk(value, t.Root) + return +} + +// DefinedTemplates returns a string listing the defined templates, +// prefixed by the string "; defined templates are: ". If there are none, +// it returns the empty string. For generating an error message here +// and in [html/template]. +func (t *Template) DefinedTemplates() string { + if t.common == nil { + return "" + } + var b strings.Builder + t.muTmpl.RLock() + defer t.muTmpl.RUnlock() + for name, tmpl := range t.tmpl { + if tmpl.Tree == nil || tmpl.Root == nil { + continue + } + if b.Len() == 0 { + b.WriteString("; defined templates are: ") + } else { + b.WriteString(", ") + } + fmt.Fprintf(&b, "%q", name) + } + return b.String() +} + +// Sentinel errors for use with panic to signal early exits from range loops. +var ( + walkBreak = errors.New("break") + walkContinue = errors.New("continue") +) + +// Walk functions step through the major pieces of the template structure, +// generating output as they go. +func (s *state) walk(dot reflect.Value, node parse.Node) { + s.at(node) + switch node := node.(type) { + case *parse.ActionNode: + // Do not pop variables so they persist until next end. + // Also, if the action declares variables, don't print the result. + val := s.evalPipeline(dot, node.Pipe) + if len(node.Pipe.Decl) == 0 { + s.printValue(node, val) + } + case *parse.BreakNode: + panic(walkBreak) + case *parse.CommentNode: + case *parse.ContinueNode: + panic(walkContinue) + case *parse.IfNode: + s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) + case *parse.ListNode: + for _, node := range node.Nodes { + s.walk(dot, node) + } + case *parse.RangeNode: + s.walkRange(dot, node) + case *parse.TemplateNode: + s.walkTemplate(dot, node) + case *parse.TextNode: + if _, err := s.wr.Write(node.Text); err != nil { + s.writeError(err) + } + case *parse.WithNode: + s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList) + default: + s.errorf("unknown node: %s", node) + } +} + +// walkIfOrWith walks an 'if' or 'with' node. The two control structures +// are identical in behavior except that 'with' sets dot. +func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) { + defer s.pop(s.mark()) + val := s.evalPipeline(dot, pipe) + truth, ok := isTrue(indirectInterface(val)) + if !ok { + s.errorf("if/with can't use %v", val) + } + if truth { + if typ == parse.NodeWith { + s.walk(val, list) + } else { + s.walk(dot, list) + } + } else if elseList != nil { + s.walk(dot, elseList) + } +} + +// IsTrue reports whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. This is the definition of +// truth used by if and other such actions. +func IsTrue(val any) (truth, ok bool) { + return isTrue(reflect.ValueOf(val)) +} + +func isTrue(val reflect.Value) (truth, ok bool) { + if !val.IsValid() { + // Something like var x interface{}, never set. It's a form of nil. + return false, true + } + switch val.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + truth = val.Len() > 0 + case reflect.Bool: + truth = val.Bool() + case reflect.Complex64, reflect.Complex128: + truth = val.Complex() != 0 + case reflect.Chan, reflect.Func, reflect.Pointer, reflect.UnsafePointer, reflect.Interface: + truth = !val.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + truth = val.Int() != 0 + case reflect.Float32, reflect.Float64: + truth = val.Float() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + truth = val.Uint() != 0 + case reflect.Struct: + truth = true // Struct values are always true. + default: + return + } + return truth, true +} + +func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) { + s.at(r) + defer func() { + if r := recover(); r != nil && r != walkBreak { + panic(r) + } + }() + defer s.pop(s.mark()) + val, _ := indirect(s.evalPipeline(dot, r.Pipe)) + // mark top of stack before any variables in the body are pushed. + mark := s.mark() + oneIteration := func(index, elem reflect.Value) { + if len(r.Pipe.Decl) > 0 { + if r.Pipe.IsAssign { + // With two variables, index comes first. + // With one, we use the element. + if len(r.Pipe.Decl) > 1 { + s.setVar(r.Pipe.Decl[0].Ident[0], index) + } else { + s.setVar(r.Pipe.Decl[0].Ident[0], elem) + } + } else { + // Set top var (lexically the second if there + // are two) to the element. + s.setTopVar(1, elem) + } + } + if len(r.Pipe.Decl) > 1 { + if r.Pipe.IsAssign { + s.setVar(r.Pipe.Decl[1].Ident[0], elem) + } else { + // Set next var (lexically the first if there + // are two) to the index. + s.setTopVar(2, index) + } + } + defer s.pop(mark) + defer func() { + // Consume panic(walkContinue) + if r := recover(); r != nil && r != walkContinue { + panic(r) + } + }() + s.walk(elem, r.List) + } + switch val.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if len(r.Pipe.Decl) > 1 { + s.errorf("can't use %v to iterate over more than one variable", val) + break + } + run := false + for v := range val.Seq() { + run = true + // Pass element as second value, as we do for channels. + oneIteration(reflect.Value{}, v) + } + if !run { + break + } + return + case reflect.Array, reflect.Slice: + if val.Len() == 0 { + break + } + for i := 0; i < val.Len(); i++ { + oneIteration(reflect.ValueOf(i), val.Index(i)) + } + return + case reflect.Map: + if val.Len() == 0 { + break + } + om := fmtsort.Sort(val) + for _, m := range om { + oneIteration(m.Key, m.Value) + } + return + case reflect.Chan: + if val.IsNil() { + break + } + if val.Type().ChanDir() == reflect.SendDir { + s.errorf("range over send-only channel %v", val) + break + } + i := 0 + for ; ; i++ { + elem, ok := val.Recv() + if !ok { + break + } + oneIteration(reflect.ValueOf(i), elem) + } + if i == 0 { + break + } + return + case reflect.Invalid: + break // An invalid value is likely a nil map, etc. and acts like an empty map. + case reflect.Func: + if val.Type().CanSeq() { + if len(r.Pipe.Decl) > 1 { + s.errorf("can't use %v iterate over more than one variable", val) + break + } + run := false + for v := range val.Seq() { + run = true + // Pass element as second value, + // as we do for channels. + oneIteration(reflect.Value{}, v) + } + if !run { + break + } + return + } + if val.Type().CanSeq2() { + run := false + for i, v := range val.Seq2() { + run = true + if len(r.Pipe.Decl) > 1 { + oneIteration(i, v) + } else { + // If there is only one range variable, + // oneIteration will use the + // second value. + oneIteration(reflect.Value{}, i) + } + } + if !run { + break + } + return + } + fallthrough + default: + s.errorf("range can't iterate over %v", val) + } + if r.ElseList != nil { + s.walk(dot, r.ElseList) + } +} + +func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { + s.at(t) + tmpl := s.tmpl.Lookup(t.Name) + if tmpl == nil { + s.errorf("template %q not defined", t.Name) + } + if s.depth == maxExecDepth { + s.errorf("exceeded maximum template depth (%v)", maxExecDepth) + } + // Variables declared by the pipeline persist. + dot = s.evalPipeline(dot, t.Pipe) + newState := *s + newState.depth++ + newState.tmpl = tmpl + // No dynamic scoping: template invocations inherit no variables. + newState.vars = []variable{{"$", dot}} + newState.walk(dot, tmpl.Root) +} + +// Eval functions evaluate pipelines, commands, and their elements and extract +// values from the data structure by examining fields, calling methods, and so on. +// The printing of those values happens only through walk functions. + +// evalPipeline returns the value acquired by evaluating a pipeline. If the +// pipeline has a variable declaration, the variable will be pushed on the +// stack. Callers should therefore pop the stack after they are finished +// executing commands depending on the pipeline value. +func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) { + if pipe == nil { + return + } + s.at(pipe) + value = missingVal + for _, cmd := range pipe.Cmds { + value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg. + // If the object has type interface{}, dig down one level to the thing inside. + if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { + value = value.Elem() + } + } + for _, variable := range pipe.Decl { + if pipe.IsAssign { + s.setVar(variable.Ident[0], value) + } else { + s.push(variable.Ident[0], value) + } + } + return value +} + +func (s *state) notAFunction(args []parse.Node, final reflect.Value) { + if len(args) > 1 || !isMissing(final) { + s.errorf("can't give argument to non-function %s", args[0]) + } +} + +func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value { + firstWord := cmd.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + return s.evalFieldNode(dot, n, cmd.Args, final) + case *parse.ChainNode: + return s.evalChainNode(dot, n, cmd.Args, final) + case *parse.IdentifierNode: + // Must be a function. + return s.evalFunction(dot, n, cmd, cmd.Args, final) + case *parse.PipeNode: + // Parenthesized pipeline. The arguments are all inside the pipeline; final must be absent. + s.notAFunction(cmd.Args, final) + return s.evalPipeline(dot, n) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, cmd.Args, final) + } + s.at(firstWord) + s.notAFunction(cmd.Args, final) + switch word := firstWord.(type) { + case *parse.BoolNode: + return reflect.ValueOf(word.True) + case *parse.DotNode: + return dot + case *parse.NilNode: + s.errorf("nil is not a command") + case *parse.NumberNode: + return s.idealConstant(word) + case *parse.StringNode: + return reflect.ValueOf(word.Text) + } + s.errorf("can't evaluate command %q", firstWord) + panic("not reached") +} + +// idealConstant is called to return the value of a number in a context where +// we don't know the type. In that case, the syntax of the number tells us +// its type, and we use Go rules to resolve. Note there is no such thing as +// a uint ideal constant in this situation - the value must be of int type. +func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { + // These are ideal constants but we don't know the type + // and we have no context. (If it was a method argument, + // we'd know what we need.) The syntax guides us to some extent. + s.at(constant) + switch { + case constant.IsComplex: + return reflect.ValueOf(constant.Complex128) // incontrovertible. + + case constant.IsFloat && + !isHexInt(constant.Text) && !isRuneInt(constant.Text) && + strings.ContainsAny(constant.Text, ".eEpP"): + return reflect.ValueOf(constant.Float64) + + case constant.IsInt: + n := int(constant.Int64) + if int64(n) != constant.Int64 { + s.errorf("%s overflows int", constant.Text) + } + return reflect.ValueOf(n) + + case constant.IsUint: + s.errorf("%s overflows int", constant.Text) + } + return zero +} + +func isRuneInt(s string) bool { + return len(s) > 0 && s[0] == '\'' +} + +func isHexInt(s string) bool { + return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && !strings.ContainsAny(s, "pP") +} + +func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value { + s.at(field) + return s.evalFieldChain(dot, dot, field, field.Ident, args, final) +} + +func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value { + s.at(chain) + if len(chain.Field) == 0 { + s.errorf("internal error: no fields in evalChainNode") + } + if chain.Node.Type() == parse.NodeNil { + s.errorf("indirection through explicit nil in %s", chain) + } + // (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields. + pipe := s.evalArg(dot, nil, chain.Node) + return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final) +} + +func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value { + // $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields. + s.at(variable) + value := s.varValue(variable.Ident[0]) + if len(variable.Ident) == 1 { + s.notAFunction(args, final) + return value + } + return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final) +} + +// evalFieldChain evaluates .X.Y.Z possibly followed by arguments. +// dot is the environment in which to evaluate arguments, while +// receiver is the value being walked along the chain. +func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value { + n := len(ident) + for i := 0; i < n-1; i++ { + receiver = s.evalField(dot, ident[i], node, nil, missingVal, receiver) + } + // Now if it's a method, it gets the arguments. + return s.evalField(dot, ident[n-1], node, args, final, receiver) +} + +func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { + s.at(node) + name := node.Ident + function, isBuiltin, ok := findFunction(name, s.tmpl) + if !ok { + s.errorf("%q is not a defined function", name) + } + return s.evalCall(dot, function, isBuiltin, cmd, name, args, final) +} + +// evalField evaluates an expression like (.Field) or (.Field arg1 arg2). +// The 'final' argument represents the return value from the preceding +// value of the pipeline, if any. +func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { + if !receiver.IsValid() { + if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key. + s.errorf("nil data; no entry for key %q", fieldName) + } + return zero + } + typ := receiver.Type() + receiver, isNil := indirect(receiver) + if receiver.Kind() == reflect.Interface && isNil { + // Indexing into a nil interface can't work. + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + return zero + } + + // OPA-DCE (#7903): the upstream text/template resolves methods on the data + // value here via reflect.Value.MethodByName. A reachable non-constant + // MethodByName disables the Go linker's method-level dead-code elimination + // binary-wide (golang/go#72895), so that branch is deliberately removed. + // Rego values (and gojsonschema ErrorDetails) decode to + // map[string]any/[]any/scalars, which have no methods, so field/element + // resolution below is the only path OPA's callers need. + hasArgs := len(args) > 1 || !isMissing(final) + // It's not a method; must be a field of a struct or an element of a map. + switch receiver.Kind() { + case reflect.Struct: + tField, ok := receiver.Type().FieldByName(fieldName) + if ok { + field, err := receiver.FieldByIndexErr(tField.Index) + if !tField.IsExported() { + s.errorf("%s is an unexported field of struct type %s", fieldName, typ) + } + if err != nil { + s.errorf("%v", err) + } + // If it's a function, we must call it. + if hasArgs { + s.errorf("%s has arguments but cannot be invoked as function", fieldName) + } + return field + } + case reflect.Map: + // If it's a map, attempt to use the field name as a key. + nameVal := reflect.ValueOf(fieldName) + if nameVal.Type().AssignableTo(receiver.Type().Key()) { + if hasArgs { + s.errorf("%s is not a method but has arguments", fieldName) + } + result := receiver.MapIndex(nameVal) + if !result.IsValid() { + switch s.tmpl.option.missingKey { + case mapInvalid: + // Just use the invalid value. + case mapZeroValue: + result = reflect.Zero(receiver.Type().Elem()) + case mapError: + s.errorf("map has no entry for key %q", fieldName) + } + } + return result + } + case reflect.Pointer: + etyp := receiver.Type().Elem() + if etyp.Kind() == reflect.Struct { + if _, ok := etyp.FieldByName(fieldName); !ok { + // If there's no such field, say "can't evaluate" + // instead of "nil pointer evaluating". + break + } + } + if isNil { + s.errorf("nil pointer evaluating %s.%s", typ, fieldName) + } + } + s.errorf("can't evaluate field %s in type %s", fieldName, typ) + panic("not reached") +} + +var ( + errorType = reflect.TypeFor[error]() + fmtStringerType = reflect.TypeFor[fmt.Stringer]() + reflectValueType = reflect.TypeFor[reflect.Value]() +) + +// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so +// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] +// as the function itself. +func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { + if args != nil { + args = args[1:] // Zeroth arg is function name/node; not passed to function. + } + typ := fun.Type() + numIn := len(args) + if !isMissing(final) { + numIn++ + } + numFixed := len(args) + if typ.IsVariadic() { + numFixed = typ.NumIn() - 1 // last arg is the variadic one. + if numIn < numFixed { + s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) + } + } else if numIn != typ.NumIn() { + s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn) + } + if err := goodFunc(name, typ); err != nil { + s.errorf("%v", err) + } + + unwrap := func(v reflect.Value) reflect.Value { + if v.Type() == reflectValueType { + v = v.Interface().(reflect.Value) + } + return v + } + + // Special case for builtin and/or, which short-circuit. + if isBuiltin && (name == "and" || name == "or") { + argType := typ.In(0) + var v reflect.Value + for _, arg := range args { + v = s.evalArg(dot, argType, arg).Interface().(reflect.Value) + if truth(v) == (name == "or") { + // This value was already unwrapped + // by the .Interface().(reflect.Value). + return v + } + } + if !final.Equal(missingVal) { + // The last argument to and/or is coming from + // the pipeline. We didn't short circuit on an earlier + // argument, so we are going to return this one. + // We don't have to evaluate final, but we do + // have to check its type. Then, since we are + // going to return it, we have to unwrap it. + v = unwrap(s.validateType(final, argType)) + } + return v + } + + // Build the arg list. + argv := make([]reflect.Value, numIn) + // Args must be evaluated. Fixed args first. + i := 0 + for ; i < numFixed && i < len(args); i++ { + argv[i] = s.evalArg(dot, typ.In(i), args[i]) + } + // Now the ... args. + if typ.IsVariadic() { + argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. + for ; i < len(args); i++ { + argv[i] = s.evalArg(dot, argType, args[i]) + } + } + // Add final value if necessary. + if !isMissing(final) { + t := typ.In(typ.NumIn() - 1) + if typ.IsVariadic() { + if numIn-1 < numFixed { + // The added final argument corresponds to a fixed parameter of the function. + // Validate against the type of the actual parameter. + t = typ.In(numIn - 1) + } else { + // The added final argument corresponds to the variadic part. + // Validate against the type of the elements of the variadic slice. + t = t.Elem() + } + } + argv[i] = s.validateType(final, t) + } + + // Special case for the "call" builtin. + // Insert the name of the callee function as the first argument. + if isBuiltin && name == "call" { + var calleeName string + if len(args) == 0 { + // final must be present or we would have errored out above. + calleeName = final.String() + } else { + calleeName = args[0].String() + } + argv = append([]reflect.Value{reflect.ValueOf(calleeName)}, argv...) + fun = reflect.ValueOf(call) + } + + v, err := safeCall(fun, argv) + // If we have an error that is not nil, stop execution and return that + // error to the caller. + if err != nil { + s.at(node) + s.errorf("error calling %s: %w", name, err) + } + return unwrap(v) +} + +// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero. +func canBeNil(typ reflect.Type) bool { + switch typ.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return true + case reflect.Struct: + return typ == reflectValueType + } + return false +} + +// validateType guarantees that the value is valid and assignable to the type. +func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value { + if !value.IsValid() { + if typ == nil { + // An untyped nil interface{}. Accept as a proper nil value. + return reflect.ValueOf(nil) + } + if canBeNil(typ) { + // Like above, but use the zero value of the non-nil type. + return reflect.Zero(typ) + } + s.errorf("invalid value; expected %s", typ) + } + if typ == reflectValueType && value.Type() != typ { + return reflect.ValueOf(value) + } + if typ != nil && !value.Type().AssignableTo(typ) { + if value.Kind() == reflect.Interface && !value.IsNil() { + value = value.Elem() + if value.Type().AssignableTo(typ) { + return value + } + // fallthrough + } + // Does one dereference or indirection work? We could do more, as we + // do with method receivers, but that gets messy and method receivers + // are much more constrained, so it makes more sense there than here. + // Besides, one is almost always all you need. + switch { + case value.Kind() == reflect.Pointer && value.Type().Elem().AssignableTo(typ): + value = value.Elem() + if !value.IsValid() { + s.errorf("dereference of nil pointer of type %s", typ) + } + case reflect.PointerTo(value.Type()).AssignableTo(typ) && value.CanAddr(): + value = value.Addr() + default: + s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) + } + } + return value +} + +func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + switch arg := n.(type) { + case *parse.DotNode: + return s.validateType(dot, typ) + case *parse.NilNode: + if canBeNil(typ) { + return reflect.Zero(typ) + } + s.errorf("cannot assign nil to %s", typ) + case *parse.FieldNode: + return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, missingVal), typ) + case *parse.VariableNode: + return s.validateType(s.evalVariableNode(dot, arg, nil, missingVal), typ) + case *parse.PipeNode: + return s.validateType(s.evalPipeline(dot, arg), typ) + case *parse.IdentifierNode: + return s.validateType(s.evalFunction(dot, arg, arg, nil, missingVal), typ) + case *parse.ChainNode: + return s.validateType(s.evalChainNode(dot, arg, nil, missingVal), typ) + } + switch typ.Kind() { + case reflect.Bool: + return s.evalBool(typ, n) + case reflect.Complex64, reflect.Complex128: + return s.evalComplex(typ, n) + case reflect.Float32, reflect.Float64: + return s.evalFloat(typ, n) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return s.evalInteger(typ, n) + case reflect.Interface: + if typ.NumMethod() == 0 { + return s.evalEmptyInterface(dot, n) + } + case reflect.Struct: + if typ == reflectValueType { + return reflect.ValueOf(s.evalEmptyInterface(dot, n)) + } + case reflect.String: + return s.evalString(typ, n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return s.evalUnsignedInteger(typ, n) + } + s.errorf("can't handle %s for arg of type %s", n, typ) + panic("not reached") +} + +func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.BoolNode); ok { + value := reflect.New(typ).Elem() + value.SetBool(n.True) + return value + } + s.errorf("expected bool; found %s", n) + panic("not reached") +} + +func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.StringNode); ok { + value := reflect.New(typ).Elem() + value.SetString(n.Text) + return value + } + s.errorf("expected string; found %s", n) + panic("not reached") +} + +func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsInt { + value := reflect.New(typ).Elem() + value.SetInt(n.Int64) + return value + } + s.errorf("expected integer; found %s", n) + panic("not reached") +} + +func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsUint { + value := reflect.New(typ).Elem() + value.SetUint(n.Uint64) + return value + } + s.errorf("expected unsigned integer; found %s", n) + panic("not reached") +} + +func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value { + s.at(n) + if n, ok := n.(*parse.NumberNode); ok && n.IsFloat { + value := reflect.New(typ).Elem() + value.SetFloat(n.Float64) + return value + } + s.errorf("expected float; found %s", n) + panic("not reached") +} + +func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value { + if n, ok := n.(*parse.NumberNode); ok && n.IsComplex { + value := reflect.New(typ).Elem() + value.SetComplex(n.Complex128) + return value + } + s.errorf("expected complex; found %s", n) + panic("not reached") +} + +func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value { + s.at(n) + switch n := n.(type) { + case *parse.BoolNode: + return reflect.ValueOf(n.True) + case *parse.DotNode: + return dot + case *parse.FieldNode: + return s.evalFieldNode(dot, n, nil, missingVal) + case *parse.IdentifierNode: + return s.evalFunction(dot, n, n, nil, missingVal) + case *parse.NilNode: + // NilNode is handled in evalArg, the only place that calls here. + s.errorf("evalEmptyInterface: nil (can't happen)") + case *parse.NumberNode: + return s.idealConstant(n) + case *parse.StringNode: + return reflect.ValueOf(n.Text) + case *parse.VariableNode: + return s.evalVariableNode(dot, n, nil, missingVal) + case *parse.PipeNode: + return s.evalPipeline(dot, n) + } + s.errorf("can't handle assignment of %s to empty interface argument", n) + panic("not reached") +} + +// indirect returns the item at the end of indirection, and a bool to indicate +// if it's nil. If the returned bool is true, the returned value's kind will be +// either a pointer or interface. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() { + if v.IsNil() { + return v, true + } + } + return v, false +} + +// indirectInterface returns the concrete value in an interface value, +// or else the zero reflect.Value. +// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x): +// the fact that x was an interface value is forgotten. +func indirectInterface(v reflect.Value) reflect.Value { + if v.Kind() != reflect.Interface { + return v + } + if v.IsNil() { + return reflect.Value{} + } + return v.Elem() +} + +// printValue writes the textual representation of the value to the output of +// the template. +func (s *state) printValue(n parse.Node, v reflect.Value) { + s.at(n) + iface, ok := printableValue(v) + if !ok { + s.errorf("can't print %s of type %s", n, v.Type()) + } + _, err := fmt.Fprint(s.wr, iface) + if err != nil { + s.writeError(err) + } +} + +// printableValue returns the, possibly indirected, interface value inside v that +// is best for a call to formatted printer. +func printableValue(v reflect.Value) (any, bool) { + if v.Kind() == reflect.Pointer { + v, _ = indirect(v) // fmt.Fprint handles nil. + } + if !v.IsValid() { + return "", true + } + + if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { + if v.CanAddr() && (reflect.PointerTo(v.Type()).Implements(errorType) || reflect.PointerTo(v.Type()).Implements(fmtStringerType)) { + v = v.Addr() + } else { + switch v.Kind() { + case reflect.Chan, reflect.Func: + return nil, false + } + } + } + return v.Interface(), true +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go new file mode 100644 index 0000000000..4d733135fe --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go @@ -0,0 +1,783 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "errors" + "fmt" + "io" + "net/url" + "reflect" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +// FuncMap is the type of the map defining the mapping from names to functions. +// Each function must have either a single return value, or two return values of +// which the second has type error. In that case, if the second (error) +// return value evaluates to non-nil during execution, execution terminates and +// Execute returns that error. +// +// Errors returned by Execute wrap the underlying error; call [errors.As] to +// unwrap them. +// +// When template execution invokes a function with an argument list, that list +// must be assignable to the function's parameter types. Functions meant to +// apply to arguments of arbitrary type can use parameters of type interface{} or +// of type [reflect.Value]. Similarly, functions meant to return a result of arbitrary +// type can return interface{} or [reflect.Value]. +type FuncMap map[string]any + +// builtins returns the FuncMap. +// It is not a global variable so the linker can dead code eliminate +// more when this isn't called. See golang.org/issue/36021. +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed. +func builtins() FuncMap { + return FuncMap{ + "and": and, + "call": emptyCall, + "html": HTMLEscaper, + "index": index, + "slice": slice, + "js": JSEscaper, + "len": length, + "not": not, + "or": or, + "print": fmt.Sprint, + "printf": fmt.Sprintf, + "println": fmt.Sprintln, + "urlquery": URLQueryEscaper, + + // Comparisons + "eq": eq, // == + "ge": ge, // >= + "gt": gt, // > + "le": le, // <= + "lt": lt, // < + "ne": ne, // != + } +} + +var builtinFuncsOnce struct { + sync.Once + v map[string]reflect.Value +} + +// builtinFuncsOnce lazily computes & caches the builtinFuncs map. +// TODO: revert this back to a global map once golang.org/issue/2559 is fixed. +func builtinFuncs() map[string]reflect.Value { + builtinFuncsOnce.Do(func() { + builtinFuncsOnce.v = createValueFuncs(builtins()) + }) + return builtinFuncsOnce.v +} + +// createValueFuncs turns a FuncMap into a map[string]reflect.Value +func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { + m := make(map[string]reflect.Value) + addValueFuncs(m, funcMap) + return m +} + +// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. +func addValueFuncs(out map[string]reflect.Value, in FuncMap) { + for name, fn := range in { + if !goodName(name) { + panic(fmt.Errorf("function name %q is not a valid identifier", name)) + } + v := reflect.ValueOf(fn) + if v.Kind() != reflect.Func { + panic("value for " + name + " not a function") + } + if err := goodFunc(name, v.Type()); err != nil { + panic(err) + } + out[name] = v + } +} + +// addFuncs adds to values the functions in funcs. It does no checking of the input - +// call addValueFuncs first. +func addFuncs(out, in FuncMap) { + for name, fn := range in { + out[name] = fn + } +} + +// goodFunc reports whether the function or method has the right result signature. +func goodFunc(name string, typ reflect.Type) error { + // We allow functions with 1 result or 2 results where the second is an error. + switch numOut := typ.NumOut(); { + case numOut == 1: + return nil + case numOut == 2 && typ.Out(1) == errorType: + return nil + case numOut == 2: + return fmt.Errorf("invalid function signature for %s: second return value should be error; is %s", name, typ.Out(1)) + default: + return fmt.Errorf("function %s has %d return values; should be 1 or 2", name, typ.NumOut()) + } +} + +// goodName reports whether the function name is a valid identifier. +func goodName(name string) bool { + if name == "" { + return false + } + for i, r := range name { + switch { + case r == '_': + case i == 0 && !unicode.IsLetter(r): + return false + case !unicode.IsLetter(r) && !unicode.IsDigit(r): + return false + } + } + return true +} + +// findFunction looks for a function in the template, and global map. +func findFunction(name string, tmpl *Template) (v reflect.Value, isBuiltin, ok bool) { + if tmpl != nil && tmpl.common != nil { + tmpl.muFuncs.RLock() + defer tmpl.muFuncs.RUnlock() + if fn := tmpl.execFuncs[name]; fn.IsValid() { + return fn, false, true + } + } + if fn := builtinFuncs()[name]; fn.IsValid() { + return fn, true, true + } + return reflect.Value{}, false, false +} + +// prepareArg checks if value can be used as an argument of type argType, and +// converts an invalid value to appropriate zero if possible. +func prepareArg(value reflect.Value, argType reflect.Type) (reflect.Value, error) { + if !value.IsValid() { + if !canBeNil(argType) { + return reflect.Value{}, fmt.Errorf("value is nil; should be of type %s", argType) + } + value = reflect.Zero(argType) + } + if value.Type().AssignableTo(argType) { + return value, nil + } + if intLike(value.Kind()) && intLike(argType.Kind()) && value.Type().ConvertibleTo(argType) { + value = value.Convert(argType) + return value, nil + } + return reflect.Value{}, fmt.Errorf("value has type %s; should be %s", value.Type(), argType) +} + +func intLike(typ reflect.Kind) bool { + switch typ { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return true + } + return false +} + +// indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible. +func indexArg(index reflect.Value, cap int) (int, error) { + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + case reflect.Invalid: + return 0, fmt.Errorf("cannot index slice/array with nil") + default: + return 0, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + } + if x < 0 || int(x) < 0 || int(x) > cap { + return 0, fmt.Errorf("index out of range: %d", x) + } + return int(x), nil +} + +// Indexing. + +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func index(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { + item = indirectInterface(item) + if !item.IsValid() { + return reflect.Value{}, fmt.Errorf("index of untyped nil") + } + for _, index := range indexes { + index = indirectInterface(index) + var isNil bool + if item, isNil = indirect(item); isNil { + return reflect.Value{}, fmt.Errorf("index of nil pointer") + } + switch item.Kind() { + case reflect.Array, reflect.Slice, reflect.String: + x, err := indexArg(index, item.Len()) + if err != nil { + return reflect.Value{}, err + } + item = item.Index(x) + case reflect.Map: + index, err := prepareArg(index, item.Type().Key()) + if err != nil { + return reflect.Value{}, err + } + if x := item.MapIndex(index); x.IsValid() { + item = x + } else { + item = reflect.Zero(item.Type().Elem()) + } + case reflect.Invalid: + // the loop holds invariant: item.IsValid() + panic("unreachable") + default: + return reflect.Value{}, fmt.Errorf("can't index item of type %s", item.Type()) + } + } + return item, nil +} + +// Slicing. + +// slice returns the result of slicing its first argument by the remaining +// arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2], while "slice x" +// is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first +// argument must be a string, slice, or array. +func slice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { + item = indirectInterface(item) + if !item.IsValid() { + return reflect.Value{}, fmt.Errorf("slice of untyped nil") + } + if len(indexes) > 3 { + return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len(indexes)) + } + var cap int + switch item.Kind() { + case reflect.String: + if len(indexes) == 3 { + return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string") + } + cap = item.Len() + case reflect.Array, reflect.Slice: + cap = item.Cap() + default: + return reflect.Value{}, fmt.Errorf("can't slice item of type %s", item.Type()) + } + // set default values for cases item[:], item[i:]. + idx := [3]int{0, item.Len()} + for i, index := range indexes { + x, err := indexArg(index, cap) + if err != nil { + return reflect.Value{}, err + } + idx[i] = x + } + // given item[i:j], make sure i <= j. + if idx[0] > idx[1] { + return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1]) + } + if len(indexes) < 3 { + return item.Slice(idx[0], idx[1]), nil + } + // given item[i:j:k], make sure i <= j <= k. + if idx[1] > idx[2] { + return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2]) + } + return item.Slice3(idx[0], idx[1], idx[2]), nil +} + +// Length + +// length returns the length of the item, with an error if it has no defined length. +func length(item reflect.Value) (int, error) { + item, isNil := indirect(item) + if isNil { + return 0, fmt.Errorf("len of nil pointer") + } + switch item.Kind() { + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: + return item.Len(), nil + } + return 0, fmt.Errorf("len of type %s", item.Type()) +} + +// Function invocation + +func emptyCall(fn reflect.Value, args ...reflect.Value) reflect.Value { + panic("unreachable") // implemented as a special case in evalCall +} + +// call returns the result of evaluating the first argument as a function. +// The function must return 1 result, or 2 results, the second of which is an error. +func call(name string, fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { + fn = indirectInterface(fn) + if !fn.IsValid() { + return reflect.Value{}, fmt.Errorf("call of nil") + } + typ := fn.Type() + if typ.Kind() != reflect.Func { + return reflect.Value{}, fmt.Errorf("non-function %s of type %s", name, typ) + } + + if err := goodFunc(name, typ); err != nil { + return reflect.Value{}, err + } + numIn := typ.NumIn() + var dddType reflect.Type + if typ.IsVariadic() { + if len(args) < numIn-1 { + return reflect.Value{}, fmt.Errorf("wrong number of args for %s: got %d want at least %d", name, len(args), numIn-1) + } + dddType = typ.In(numIn - 1).Elem() + } else { + if len(args) != numIn { + return reflect.Value{}, fmt.Errorf("wrong number of args for %s: got %d want %d", name, len(args), numIn) + } + } + argv := make([]reflect.Value, len(args)) + for i, arg := range args { + arg = indirectInterface(arg) + // Compute the expected type. Clumsy because of variadics. + argType := dddType + if !typ.IsVariadic() || i < numIn-1 { + argType = typ.In(i) + } + + var err error + if argv[i], err = prepareArg(arg, argType); err != nil { + return reflect.Value{}, fmt.Errorf("arg %d: %w", i, err) + } + } + return safeCall(fn, argv) +} + +// safeCall runs fun.Call(args), and returns the resulting value and error, if +// any. If the call panics, the panic value is returned as an error. +func safeCall(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + if e, ok := r.(error); ok { + err = e + } else { + err = fmt.Errorf("%v", r) + } + } + }() + ret := fun.Call(args) + if len(ret) == 2 && !ret[1].IsNil() { + return ret[0], ret[1].Interface().(error) + } + return ret[0], nil +} + +// Boolean logic. + +func truth(arg reflect.Value) bool { + t, _ := isTrue(indirectInterface(arg)) + return t +} + +// and computes the Boolean AND of its arguments, returning +// the first false argument it encounters, or the last argument. +func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value { + panic("unreachable") // implemented as a special case in evalCall +} + +// or computes the Boolean OR of its arguments, returning +// the first true argument it encounters, or the last argument. +func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value { + panic("unreachable") // implemented as a special case in evalCall +} + +// not returns the Boolean negation of its argument. +func not(arg reflect.Value) bool { + return !truth(arg) +} + +// Comparison. + +// TODO: Perhaps allow comparison between signed and unsigned integers. + +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errNoComparison = errors.New("missing argument for comparison") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +// isNil returns true if v is the zero reflect.Value, or nil of its type. +func isNil(v reflect.Value) bool { + if !v.IsValid() { + return true + } + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: + return v.IsNil() + } + return false +} + +// canCompare reports whether v1 and v2 are both the same kind, or one is nil. +// Called only when dealing with nillable types, or there's about to be an error. +func canCompare(v1, v2 reflect.Value) bool { + k1 := v1.Kind() + k2 := v2.Kind() + if k1 == k2 { + return true + } + // We know the type can be compared to nil. + return k1 == reflect.Invalid || k2 == reflect.Invalid +} + +// eq evaluates the comparison a == b || a == c || ... +func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { + arg1 = indirectInterface(arg1) + if len(arg2) == 0 { + return false, errNoComparison + } + k1, _ := basicKind(arg1) + for _, arg := range arg2 { + arg = indirectInterface(arg) + k2, _ := basicKind(arg) + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = arg1.Int() >= 0 && uint64(arg1.Int()) == arg.Uint() + case k1 == uintKind && k2 == intKind: + truth = arg.Int() >= 0 && arg1.Uint() == uint64(arg.Int()) + default: + if arg1.IsValid() && arg.IsValid() { + return false, fmt.Errorf("incompatible types for comparison: %v and %v", arg1.Type(), arg.Type()) + } + } + } else { + switch k1 { + case boolKind: + truth = arg1.Bool() == arg.Bool() + case complexKind: + truth = arg1.Complex() == arg.Complex() + case floatKind: + truth = arg1.Float() == arg.Float() + case intKind: + truth = arg1.Int() == arg.Int() + case stringKind: + truth = arg1.String() == arg.String() + case uintKind: + truth = arg1.Uint() == arg.Uint() + default: + if !canCompare(arg1, arg) { + return false, fmt.Errorf("non-comparable types %s: %v, %s: %v", arg1, arg1.Type(), arg.Type(), arg) + } + if isNil(arg1) || isNil(arg) { + truth = isNil(arg) == isNil(arg1) + } else { + if !arg.Type().Comparable() { + return false, fmt.Errorf("non-comparable type %s: %v", arg, arg.Type()) + } + truth = arg1.Interface() == arg.Interface() + } + } + } + if truth { + return true, nil + } + } + return false, nil +} + +// ne evaluates the comparison a != b. +func ne(arg1, arg2 reflect.Value) (bool, error) { + // != is the inverse of ==. + equal, err := eq(arg1, arg2) + return !equal, err +} + +// lt evaluates the comparison a < b. +func lt(arg1, arg2 reflect.Value) (bool, error) { + arg1 = indirectInterface(arg1) + k1, err := basicKind(arg1) + if err != nil { + return false, err + } + arg2 = indirectInterface(arg2) + k2, err := basicKind(arg2) + if err != nil { + return false, err + } + truth := false + if k1 != k2 { + // Special case: Can compare integer values regardless of type's sign. + switch { + case k1 == intKind && k2 == uintKind: + truth = arg1.Int() < 0 || uint64(arg1.Int()) < arg2.Uint() + case k1 == uintKind && k2 == intKind: + truth = arg2.Int() >= 0 && arg1.Uint() < uint64(arg2.Int()) + default: + return false, fmt.Errorf("incompatible types for comparison: %v and %v", arg1.Type(), arg2.Type()) + } + } else { + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = arg1.Float() < arg2.Float() + case intKind: + truth = arg1.Int() < arg2.Int() + case stringKind: + truth = arg1.String() < arg2.String() + case uintKind: + truth = arg1.Uint() < arg2.Uint() + default: + panic("invalid kind") + } + } + return truth, nil +} + +// le evaluates the comparison <= b. +func le(arg1, arg2 reflect.Value) (bool, error) { + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) +} + +// gt evaluates the comparison a > b. +func gt(arg1, arg2 reflect.Value) (bool, error) { + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil +} + +// ge evaluates the comparison a >= b. +func ge(arg1, arg2 reflect.Value) (bool, error) { + // >= is the inverse of <. + lessThan, err := lt(arg1, arg2) + if err != nil { + return false, err + } + return !lessThan, nil +} + +// HTML escaping. + +var ( + htmlQuot = []byte(""") // shorter than """ + htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 + htmlAmp = []byte("&") + htmlLt = []byte("<") + htmlGt = []byte(">") + htmlNull = []byte("\uFFFD") +) + +// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. +func HTMLEscape(w io.Writer, b []byte) { + last := 0 + for i, c := range b { + var html []byte + switch c { + case '\000': + html = htmlNull + case '"': + html = htmlQuot + case '\'': + html = htmlApos + case '&': + html = htmlAmp + case '<': + html = htmlLt + case '>': + html = htmlGt + default: + continue + } + w.Write(b[last:i]) + w.Write(html) + last = i + 1 + } + w.Write(b[last:]) +} + +// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. +func HTMLEscapeString(s string) string { + // Avoid allocation if we can. + if !strings.ContainsAny(s, "'\"&<>\000") { + return s + } + var b strings.Builder + HTMLEscape(&b, []byte(s)) + return b.String() +} + +// HTMLEscaper returns the escaped HTML equivalent of the textual +// representation of its arguments. +func HTMLEscaper(args ...any) string { + return HTMLEscapeString(evalArgs(args)) +} + +// JavaScript escaping. + +var ( + jsLowUni = []byte(`\u00`) + hex = []byte("0123456789ABCDEF") + + jsBackslash = []byte(`\\`) + jsApos = []byte(`\'`) + jsQuot = []byte(`\"`) + jsLt = []byte(`\u003C`) + jsGt = []byte(`\u003E`) + jsAmp = []byte(`\u0026`) + jsEq = []byte(`\u003D`) +) + +// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. +func JSEscape(w io.Writer, b []byte) { + last := 0 + for i := 0; i < len(b); i++ { + c := b[i] + + if !jsIsSpecial(rune(c)) { + // fast path: nothing to do + continue + } + w.Write(b[last:i]) + + if c < utf8.RuneSelf { + // Quotes, slashes and angle brackets get quoted. + // Control characters get written as \u00XX. + switch c { + case '\\': + w.Write(jsBackslash) + case '\'': + w.Write(jsApos) + case '"': + w.Write(jsQuot) + case '<': + w.Write(jsLt) + case '>': + w.Write(jsGt) + case '&': + w.Write(jsAmp) + case '=': + w.Write(jsEq) + default: + w.Write(jsLowUni) + t, b := c>>4, c&0x0f + w.Write(hex[t : t+1]) + w.Write(hex[b : b+1]) + } + } else { + // Unicode rune. + r, size := utf8.DecodeRune(b[i:]) + if unicode.IsPrint(r) { + w.Write(b[i : i+size]) + } else { + fmt.Fprintf(w, "\\u%04X", r) + } + i += size - 1 + } + last = i + 1 + } + w.Write(b[last:]) +} + +// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. +func JSEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexFunc(s, jsIsSpecial) < 0 { + return s + } + var b strings.Builder + JSEscape(&b, []byte(s)) + return b.String() +} + +func jsIsSpecial(r rune) bool { + switch r { + case '\\', '\'', '"', '<', '>', '&', '=': + return true + } + return r < ' ' || utf8.RuneSelf <= r +} + +// JSEscaper returns the escaped JavaScript equivalent of the textual +// representation of its arguments. +func JSEscaper(args ...any) string { + return JSEscapeString(evalArgs(args)) +} + +// URLQueryEscaper returns the escaped value of the textual representation of +// its arguments in a form suitable for embedding in a URL query. +func URLQueryEscaper(args ...any) string { + return url.QueryEscape(evalArgs(args)) +} + +// evalArgs formats the list of arguments into a string. It is therefore equivalent to +// +// fmt.Sprint(args...) +// +// except that each argument is indirected (if a pointer), as required, +// using the same rules as the default string evaluation during template +// execution. +func evalArgs(args []any) string { + ok := false + var s string + // Fast path for simple common case. + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + for i, arg := range args { + a, ok := printableValue(reflect.ValueOf(arg)) + if ok { + args[i] = a + } // else let fmt do its thing + } + s = fmt.Sprint(args...) + } + return s +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go new file mode 100644 index 0000000000..f51cdc7083 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go @@ -0,0 +1,154 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fmtsort provides a general stable ordering mechanism +// for maps, on behalf of the fmt and text/template packages. +// It is not guaranteed to be efficient and works only for types +// that are valid map keys. +package fmtsort + +import ( + "cmp" + "reflect" + "slices" +) + +// Note: Throughout this package we avoid calling reflect.Value.Interface as +// it is not always legal to do so and it's easier to avoid the issue than to face it. + +// SortedMap is a slice of KeyValue pairs that simplifies sorting +// and iterating over map entries. +// +// Each KeyValue pair contains a map key and its corresponding value. +type SortedMap []KeyValue + +// KeyValue holds a single key and value pair found in a map. +type KeyValue struct { + Key, Value reflect.Value +} + +// Sort accepts a map and returns a SortedMap that has the same keys and +// values but in a stable sorted order according to the keys, modulo issues +// raised by unorderable key values such as NaNs. +// +// The ordering rules are more general than with Go's < operator: +// +// - when applicable, nil compares low +// - ints, floats, and strings order by < +// - NaN compares less than non-NaN floats +// - bool compares false before true +// - complex compares real, then imag +// - pointers compare by machine address +// - channel values compare by machine address +// - structs compare each field in turn +// - arrays compare each element in turn. +// Otherwise identical arrays compare by length. +// - interface values compare first by reflect.Type describing the concrete type +// and then by concrete value as described in the previous rules. +func Sort(mapValue reflect.Value) SortedMap { + if mapValue.Type().Kind() != reflect.Map { + return nil + } + // Note: this code is arranged to not panic even in the presence + // of a concurrent map update. The runtime is responsible for + // yelling loudly if that happens. See issue 33275. + n := mapValue.Len() + sorted := make(SortedMap, 0, n) + iter := mapValue.MapRange() + for iter.Next() { + sorted = append(sorted, KeyValue{iter.Key(), iter.Value()}) + } + slices.SortStableFunc(sorted, func(a, b KeyValue) int { + return compare(a.Key, b.Key) + }) + return sorted +} + +// compare compares two values of the same type. It returns -1, 0, 1 +// according to whether a > b (1), a == b (0), or a < b (-1). +// If the types differ, it returns -1. +// See the comment on Sort for the comparison rules. +func compare(aVal, bVal reflect.Value) int { + aType, bType := aVal.Type(), bVal.Type() + if aType != bType { + return -1 // No good answer possible, but don't return 0: they're not equal. + } + switch aVal.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return cmp.Compare(aVal.Int(), bVal.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return cmp.Compare(aVal.Uint(), bVal.Uint()) + case reflect.String: + return cmp.Compare(aVal.String(), bVal.String()) + case reflect.Float32, reflect.Float64: + return cmp.Compare(aVal.Float(), bVal.Float()) + case reflect.Complex64, reflect.Complex128: + a, b := aVal.Complex(), bVal.Complex() + if c := cmp.Compare(real(a), real(b)); c != 0 { + return c + } + return cmp.Compare(imag(a), imag(b)) + case reflect.Bool: + a, b := aVal.Bool(), bVal.Bool() + switch { + case a == b: + return 0 + case a: + return 1 + default: + return -1 + } + case reflect.Pointer, reflect.UnsafePointer: + return cmp.Compare(aVal.Pointer(), bVal.Pointer()) + case reflect.Chan: + if c, ok := nilCompare(aVal, bVal); ok { + return c + } + return cmp.Compare(aVal.Pointer(), bVal.Pointer()) + case reflect.Struct: + for i := 0; i < aVal.NumField(); i++ { + if c := compare(aVal.Field(i), bVal.Field(i)); c != 0 { + return c + } + } + return 0 + case reflect.Array: + for i := 0; i < aVal.Len(); i++ { + if c := compare(aVal.Index(i), bVal.Index(i)); c != 0 { + return c + } + } + return 0 + case reflect.Interface: + if c, ok := nilCompare(aVal, bVal); ok { + return c + } + c := compare(reflect.ValueOf(aVal.Elem().Type()), reflect.ValueOf(bVal.Elem().Type())) + if c != 0 { + return c + } + return compare(aVal.Elem(), bVal.Elem()) + default: + // Certain types cannot appear as keys (maps, funcs, slices), but be explicit. + panic("bad type in compare: " + aType.String()) + } +} + +// nilCompare checks whether either value is nil. If not, the boolean is false. +// If either value is nil, the boolean is true and the integer is the comparison +// value. The comparison is defined to be 0 if both are nil, otherwise the one +// nil value compares low. Both arguments must represent a chan, func, +// interface, map, pointer, or slice. +func nilCompare(aVal, bVal reflect.Value) (int, bool) { + if aVal.IsNil() { + if bVal.IsNil() { + return 0, true + } + return -1, true + } + if bVal.IsNil() { + return 1, true + } + return 0, false +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/option.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/option.go new file mode 100644 index 0000000000..ea2fd80c06 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/option.go @@ -0,0 +1,72 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains the code to handle template options. + +package template + +import "strings" + +// missingKeyAction defines how to respond to indexing a map with a key that is not present. +type missingKeyAction int + +const ( + mapInvalid missingKeyAction = iota // Return an invalid reflect.Value. + mapZeroValue // Return the zero value for the map element. + mapError // Error out +) + +type option struct { + missingKey missingKeyAction +} + +// Option sets options for the template. Options are described by +// strings, either a simple string or "key=value". There can be at +// most one equals sign in an option string. If the option string +// is unrecognized or otherwise invalid, Option panics. +// +// Known options: +// +// missingkey: Control the behavior during execution if a map is +// indexed with a key that is not present in the map. +// +// "missingkey=default" or "missingkey=invalid" +// The default behavior: Do nothing and continue execution. +// If printed, the result of the index operation is the string +// "". +// "missingkey=zero" +// The operation returns the zero value for the map type's element. +// "missingkey=error" +// Execution stops immediately with an error. +func (t *Template) Option(opt ...string) *Template { + t.init() + for _, s := range opt { + t.setOption(s) + } + return t +} + +func (t *Template) setOption(opt string) { + if opt == "" { + panic("empty option string") + } + // key=value + if key, value, ok := strings.Cut(opt, "="); ok { + switch key { + case "missingkey": + switch value { + case "invalid", "default": + t.option.missingKey = mapInvalid + return + case "zero": + t.option.missingKey = mapZeroValue + return + case "error": + t.option.missingKey = mapError + return + } + } + } + panic("unrecognized option: " + opt) +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go new file mode 100644 index 0000000000..9ae5a6ca5b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go @@ -0,0 +1,236 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "maps" + "reflect" + "sync" + "text/template/parse" +) + +// common holds the information shared by related templates. +type common struct { + tmpl map[string]*Template // Map from name to defined templates. + muTmpl sync.RWMutex // protects tmpl + option option + // We use two maps, one for parsing and one for execution. + // This separation makes the API cleaner since it doesn't + // expose reflection to the client. + muFuncs sync.RWMutex // protects parseFuncs and execFuncs + parseFuncs FuncMap + execFuncs map[string]reflect.Value +} + +// Template is the representation of a parsed template. The *parse.Tree +// field is exported only for use by [html/template] and should be treated +// as unexported by all other clients. +type Template struct { + name string + *parse.Tree + *common + leftDelim string + rightDelim string +} + +// New allocates a new, undefined template with the given name. +func New(name string) *Template { + t := &Template{ + name: name, + } + t.init() + return t +} + +// Name returns the name of the template. +func (t *Template) Name() string { + return t.name +} + +// New allocates a new, undefined template associated with the given one and with the same +// delimiters. The association, which is transitive, allows one template to +// invoke another with a {{template}} action. +// +// Because associated templates share underlying data, template construction +// cannot be done safely in parallel. Once the templates are constructed, they +// can be executed in parallel. +func (t *Template) New(name string) *Template { + t.init() + nt := &Template{ + name: name, + common: t.common, + leftDelim: t.leftDelim, + rightDelim: t.rightDelim, + } + return nt +} + +// init guarantees that t has a valid common structure. +func (t *Template) init() { + if t.common == nil { + c := new(common) + c.tmpl = make(map[string]*Template) + c.parseFuncs = make(FuncMap) + c.execFuncs = make(map[string]reflect.Value) + t.common = c + } +} + +// Clone returns a duplicate of the template, including all associated +// templates. The actual representation is not copied, but the name space of +// associated templates is, so further calls to [Template.Parse] in the copy will add +// templates to the copy but not to the original. Clone can be used to prepare +// common templates and use them with variant definitions for other templates +// by adding the variants after the clone is made. +func (t *Template) Clone() (*Template, error) { + nt := t.copy(nil) + nt.init() + if t.common == nil { + return nt, nil + } + nt.option = t.option + t.muTmpl.RLock() + defer t.muTmpl.RUnlock() + for k, v := range t.tmpl { + if k == t.name { + nt.tmpl[t.name] = nt + continue + } + // The associated templates share nt's common structure. + tmpl := v.copy(nt.common) + nt.tmpl[k] = tmpl + } + t.muFuncs.RLock() + defer t.muFuncs.RUnlock() + maps.Copy(nt.parseFuncs, t.parseFuncs) + maps.Copy(nt.execFuncs, t.execFuncs) + return nt, nil +} + +// copy returns a shallow copy of t, with common set to the argument. +func (t *Template) copy(c *common) *Template { + return &Template{ + name: t.name, + Tree: t.Tree, + common: c, + leftDelim: t.leftDelim, + rightDelim: t.rightDelim, + } +} + +// AddParseTree associates the argument parse tree with the template t, giving +// it the specified name. If the template has not been defined, this tree becomes +// its definition. If it has been defined and already has that name, the existing +// definition is replaced; otherwise a new template is created, defined, and returned. +func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { + t.init() + t.muTmpl.Lock() + defer t.muTmpl.Unlock() + nt := t + if name != t.name { + nt = t.New(name) + } + // Even if nt == t, we need to install it in the common.tmpl map. + if t.associate(nt, tree) || nt.Tree == nil { + nt.Tree = tree + } + return nt, nil +} + +// Templates returns a slice of defined templates associated with t. +func (t *Template) Templates() []*Template { + if t.common == nil { + return nil + } + // Return a slice so we don't expose the map. + t.muTmpl.RLock() + defer t.muTmpl.RUnlock() + m := make([]*Template, 0, len(t.tmpl)) + for _, v := range t.tmpl { + m = append(m, v) + } + return m +} + +// Delims sets the action delimiters to the specified strings, to be used in +// subsequent calls to [Template.Parse], [Template.ParseFiles], or [Template.ParseGlob]. Nested template +// definitions will inherit the settings. An empty delimiter stands for the +// corresponding default: {{ or }}. +// The return value is the template, so calls can be chained. +func (t *Template) Delims(left, right string) *Template { + t.init() + t.leftDelim = left + t.rightDelim = right + return t +} + +// Funcs adds the elements of the argument map to the template's function map. +// It must be called before the template is parsed. +// It panics if a value in the map is not a function with appropriate return +// type or if the name cannot be used syntactically as a function in a template. +// It is legal to overwrite elements of the map. The return value is the template, +// so calls can be chained. +func (t *Template) Funcs(funcMap FuncMap) *Template { + t.init() + t.muFuncs.Lock() + defer t.muFuncs.Unlock() + addValueFuncs(t.execFuncs, funcMap) + addFuncs(t.parseFuncs, funcMap) + return t +} + +// Lookup returns the template with the given name that is associated with t. +// It returns nil if there is no such template or the template has no definition. +func (t *Template) Lookup(name string) *Template { + if t.common == nil { + return nil + } + t.muTmpl.RLock() + defer t.muTmpl.RUnlock() + return t.tmpl[name] +} + +// Parse parses text as a template body for t. +// Named template definitions ({{define ...}} or {{block ...}} statements) in text +// define additional templates associated with t and are removed from the +// definition of t itself. +// +// Templates can be redefined in successive calls to Parse. +// A template definition with a body containing only white space and comments +// is considered empty and will not replace an existing template's body. +// This allows using Parse to add new named template definitions without +// overwriting the main template body. +func (t *Template) Parse(text string) (*Template, error) { + t.init() + t.muFuncs.RLock() + trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins()) + t.muFuncs.RUnlock() + if err != nil { + return nil, err + } + // Add the newly parsed trees, including the one for t, into our common structure. + for name, tree := range trees { + if _, err := t.AddParseTree(name, tree); err != nil { + return nil, err + } + } + return t, nil +} + +// associate installs the new template into the group of templates associated +// with t. The two are already known to share the common structure. +// The boolean return value reports whether to store this tree as t.Tree. +func (t *Template) associate(new *Template, tree *parse.Tree) bool { + if new.common != t.common { + panic("internal error: associate not common") + } + if old := t.tmpl[new.name]; old != nil && parse.IsEmptyTree(tree.Root) && old.Tree != nil { + // If a template by that name exists, + // don't replace it with an empty template. + return false + } + t.tmpl[new.name] = new + return true +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go index 9e71e83e72..a7d62542c2 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go @@ -648,6 +648,12 @@ func (p *Planner) planExpr(e *ast.Expr, iter planiter) error { case e.IsNegated(): return p.planNot(e, iter) + case e.IsAnd(): + return p.planExprLogicalAnd(e, iter) + + case e.IsOr(): + return p.planExprLogicalOr(e, iter) + case e.IsCall(): return p.planExprCall(e, iter) @@ -659,34 +665,34 @@ func (p *Planner) planExpr(e *ast.Expr, iter planiter) error { } func (p *Planner) planNot(e *ast.Expr, iter planiter) error { - not := &ir.NotStmt{ - Block: &ir.Block{}, - } - - prev := p.curr - p.curr = not.Block - if n, ok := e.Terms.(*ast.Not); ok { - cond := p.newLocal() // success condition + // We're constructing the following plan: + // + // | not + // | | # assigns Local = true at each success point + // | | is_defined &{Source:Local} # aborts inner block if body produced no success + // | iter() # caller's continuation - err := p.planQuery(n.Body, 0, func() error { - p.appendStmt(&ir.AssignVarStmt{ - Source: op(ir.Bool(true)), - Target: cond, - }) - return nil - }) + cond := p.newLocal() + sub, err := p.planBodyAsScope(n.Body, cond) if err != nil { return err } - p.appendStmt(&ir.IsDefinedStmt{ - Source: cond, - }) - } else { - if err := p.planExpr(e.Complement(), func() error { return nil }); err != nil { - return err - } + sub.Stmts = append(sub.Stmts, &ir.IsDefinedStmt{Source: cond}) + p.appendStmt(&ir.NotStmt{Block: sub}) + + return iter() + } + + // Legacy negation + + not := &ir.NotStmt{Block: &ir.Block{}} + prev := p.curr + p.curr = not.Block + + if err := p.planExpr(e.Complement(), func() error { return nil }); err != nil { + return err } p.curr = prev @@ -695,6 +701,119 @@ func (p *Planner) planNot(e *ast.Expr, iter planiter) error { return iter() } +func (p *Planner) planExprLogicalAnd(e *ast.Expr, iter planiter) error { + // We're constructing the following plan: + // + // | reset &{Target:Local} + // | block lhs + // | | + // | | assign_var &{Target:Local} # Local = true on success + // | is_defined &{Source:Local} # aborts outer if LHS produced no success + // | reset &{Target:Local} # clear before RHS + // | block rhs + // | | + // | | assign_var &{Target:Local} # Local = true on success + // | is_defined &{Source:Local} # aborts outer if RHS produced no success + // | iter() # caller's continuation + + and := e.Terms.(*ast.LogicalAnd) + cond := p.newLocal() // success condition + + if err := planLogicalOperand(p, and.Lhs, cond); err != nil { + return err + } + + if err := planLogicalOperand(p, and.Rhs, cond); err != nil { + return err + } + + return iter() +} + +func planLogicalOperand(p *Planner, body ast.Body, cond ir.Local) error { + p.appendStmt(&ir.ResetLocalStmt{Target: cond}) + + sub, err := p.planBodyAsScope(body, cond) + if err != nil { + return err + } + + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{sub}}) + p.appendStmt(&ir.IsDefinedStmt{Source: cond}) + + return nil +} + +func (p *Planner) planExprLogicalOr(e *ast.Expr, iter planiter) error { + // We're constructing the following plan: + // + // | reset &{Target:Local} + // | block lhs + // | | + // | | assign_var &{Target:Local} # Local = true on success + // | block outer + // | | block skip + // | | | is_defined &{Source:Local} # if defined .. + // | | | break &{Index:1} # .. break past RHS + // | | block rhs + // | | | + // | | | assign_var &{Target:Local} # Local = true on success + // | is_defined &{Source:Local} # aborts outer if neither produced a success + // | iter() # caller's continuation + + or := e.Terms.(*ast.LogicalOr) + + cond := p.newLocal() // success condition + p.appendStmt(&ir.ResetLocalStmt{Target: cond}) + + lhsBlock, err := p.planBodyAsScope(or.Lhs, cond) + if err != nil { + return err + } + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{lhsBlock}}) + + rhsBlock, err := p.planBodyAsScope(or.Rhs, cond) + if err != nil { + return err + } + + // skip-rhs-if-lhs-succeeded: if cond is defined, break out past the + // RHS block; otherwise this inner block aborts and the outer block + // falls through into the RHS plan. + skip := &ir.Block{Stmts: []ir.Stmt{ + &ir.IsDefinedStmt{Source: cond}, + &ir.BreakStmt{Index: 1}, + }} + outer := &ir.Block{Stmts: []ir.Stmt{ + &ir.BlockStmt{Blocks: []*ir.Block{skip}}, + &ir.BlockStmt{Blocks: []*ir.Block{rhsBlock}}, + }} + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{outer}}) + + p.appendStmt(&ir.IsDefinedStmt{Source: cond}) + return iter() +} + +func (p *Planner) planBodyAsScope(body ast.Body, cond ir.Local) (*ir.Block, error) { + sub := &ir.Block{} + prev := p.curr + p.curr = sub + p.vars.Push(map[ast.Var]ir.Local{}) + + err := p.planQuery(body, 0, func() error { + p.appendStmt(&ir.AssignVarStmt{ + Source: op(ir.Bool(true)), + Target: cond, + }) + return nil + }) + + p.vars.Pop() + p.curr = prev + + return sub, err +} + func (p *Planner) planWith(e *ast.Expr, iter planiter) error { // Plan the values that will be applied by the `with` modifiers. All values diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go index 9f3d115293..ed4da8571b 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go @@ -5,6 +5,7 @@ import ( "sort" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" ) // funcstack implements a simple map structure used to keep track of virtual @@ -270,19 +271,24 @@ func (t *ruletrie) DepthFirst(f func(*ruletrie) bool) { } func (t *ruletrie) Depth() int { - if len(t.Children()) == 0 { - return 0 - } - c := make([]int, 0, len(t.Children())) - for _, nodes := range t.children { - c = append(c, nodes[len(nodes)-1].Depth()) - } + // Avoid Children()'s slice allocation and sort: we only need the max + // depth over the child nodes. A nil last element is a pushed but + // not-yet-inserted node (see Push), matching Children()'s filter. max := 0 - for i := range c { - if max < c[i] { - max = c[i] + found := false + for _, nodes := range t.children { + last := nodes[len(nodes)-1] + if last == nil { + continue + } + found = true + if d := last.Depth(); d > max { + max = d } } + if !found { + return 0 + } return max + 1 } @@ -291,11 +297,9 @@ func (t *ruletrie) String() string { } type functionMocksStack struct { - stack []*functionMocksElem + stack util.GroupStack[frame] } -type functionMocksElem []frame - type frame map[string]*ast.Term func newFunctionMocksStack() *functionMocksStack { @@ -304,30 +308,24 @@ func newFunctionMocksStack() *functionMocksStack { return stack } -func newFunctionMocksElem() *functionMocksElem { - return &functionMocksElem{} -} - func (s *functionMocksStack) Push() { - s.stack = append(s.stack, newFunctionMocksElem()) + s.stack.PushGroup(nil) } func (s *functionMocksStack) Pop() { - s.stack = s.stack[:len(s.stack)-1] + s.stack.PopGroup() } func (s *functionMocksStack) PushFrame(f frame) { - current := s.stack[len(s.stack)-1] - *current = append(*current, f) + s.stack.Push(f) } func (s *functionMocksStack) PopFrame() { - current := s.stack[len(s.stack)-1] - *current = (*current)[:len(*current)-1] + s.stack.Pop() } func (s *functionMocksStack) Lookup(f string) *ast.Term { - current := *s.stack[len(s.stack)-1] + current := s.stack.PeekGroup() for i := len(current) - 1; i >= 0; i-- { if t, ok := current[i][f]; ok { return t diff --git a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go index 725f86318a..d46f80aeb8 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go +++ b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go @@ -101,10 +101,10 @@ func Compare(a, b string) int { return aV.Compare(bV) } -// AppendText appends the textual representation of the version to b and returns the extended buffer. +// AppendString appends the textual representation of the version to b and returns the extended buffer. // This method conforms to the encoding.TextAppender interface, and is useful for serializing the Version // without allocating, provided the caller has pre-allocated sufficient space in b. -func (v Version) AppendText(b []byte) ([]byte, error) { +func (v Version) AppendString(b []byte) ([]byte, error) { if b == nil { b = make([]byte, 0, length(v)) } @@ -126,7 +126,7 @@ func (v Version) AppendText(b []byte) ([]byte, error) { // String returns the string representation of the version. func (v Version) String() string { bs := make([]byte, 0, length(v)) - bs, _ = v.AppendText(bs) + bs, _ = v.AppendString(bs) return string(bs) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go index 14620a2a48..2b6a83eeeb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go @@ -13,7 +13,6 @@ import ( "strings" "github.com/open-policy-agent/opa/internal/deepcopy" - astJSON "github.com/open-policy-agent/opa/v1/ast/json" "github.com/open-policy-agent/opa/v1/util" ) @@ -40,8 +39,8 @@ type ( Labels map[string]any `json:"labels,omitempty"` Location *Location `json:"location,omitempty"` - comments []*Comment - node Node + endLoc *Location + node Node } // SchemaAnnotation contains a schema declaration for the document identified by the path. @@ -108,11 +107,10 @@ func (a *Annotations) SetLoc(l *Location) { // EndLoc returns the location of this annotation's last comment line. func (a *Annotations) EndLoc() *Location { - count := len(a.comments) - if count == 0 { + if a.endLoc == nil { return a.Location } - return a.comments[count-1].Location + return a.endLoc } // Compare returns an integer indicating if a is less than, equal to, or greater @@ -193,64 +191,6 @@ func (a *Annotations) GetTargetPath() Ref { } } -func (a *Annotations) MarshalJSON() ([]byte, error) { - if a == nil { - return []byte(`{"scope":""}`), nil - } - - data := map[string]any{ - "scope": a.Scope, - } - - if a.Title != "" { - data["title"] = a.Title - } - - if a.Description != "" { - data["description"] = a.Description - } - - if a.Entrypoint { - data["entrypoint"] = a.Entrypoint - } - - if len(a.Organizations) > 0 { - data["organizations"] = a.Organizations - } - - if len(a.RelatedResources) > 0 { - data["related_resources"] = a.RelatedResources - } - - if len(a.Authors) > 0 { - data["authors"] = a.Authors - } - - if len(a.Schemas) > 0 { - data["schemas"] = a.Schemas - } - - if a.Compile != nil { - data["compile"] = a.Compile - } - - if len(a.Custom) > 0 { - data["custom"] = a.Custom - } - - if len(a.Labels) > 0 { - data["labels"] = a.Labels - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Annotations { - if a.Location != nil { - data["location"] = a.Location - } - } - - return json.Marshal(data) -} - func NewAnnotationsRef(a *Annotations) *AnnotationsRef { var loc *Location if a.node != nil { @@ -285,34 +225,6 @@ func (ar *AnnotationsRef) GetRule() *Rule { } } -func (ar *AnnotationsRef) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "path": ar.Path, - } - - if ar.Annotations != nil { - data["annotations"] = ar.Annotations - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.AnnotationsRef { - if ar.Location != nil { - data["location"] = ar.Location - } - - // The location set for the schema ref terms is wrong (always set to - // row 1) and not really useful anyway.. so strip it out before marshalling - for _, schema := range ar.Annotations.Schemas { - if schema.Path != nil { - for _, term := range schema.Path { - term.Location = nil - } - } - } - } - - return json.Marshal(data) -} - func scopeCompare(s1, s2 string) int { o1 := scopeOrder(s1) o2 := scopeOrder(s2) @@ -698,18 +610,6 @@ func (rr *RelatedResourceAnnotation) String() string { return string(bs) } -func (rr *RelatedResourceAnnotation) MarshalJSON() ([]byte, error) { - d := map[string]any{ - "ref": rr.Ref.String(), - } - - if len(rr.Description) > 0 { - d["description"] = rr.Description - } - - return json.Marshal(d) -} - // Copy returns a deep copy of s. func (s *SchemaAnnotation) Copy() *SchemaAnnotation { cpy := *s diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_json.go b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_json.go new file mode 100644 index 0000000000..420a9f8093 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_json.go @@ -0,0 +1,124 @@ +//go:build !go1.27 + +package ast + +import ( + "encoding/json" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" +) + +func (a *Annotations) MarshalJSON() ([]byte, error) { + if a == nil { + return []byte(`{"scope":""}`), nil + } + + data := map[string]any{ + "scope": a.Scope, + } + + if a.Title != "" { + data["title"] = a.Title + } + + if a.Description != "" { + data["description"] = a.Description + } + + if a.Entrypoint { + data["entrypoint"] = a.Entrypoint + } + + if len(a.Organizations) > 0 { + data["organizations"] = a.Organizations + } + + if len(a.RelatedResources) > 0 { + data["related_resources"] = a.RelatedResources + } + + if len(a.Authors) > 0 { + data["authors"] = a.Authors + } + + if len(a.Schemas) > 0 { + data["schemas"] = a.Schemas + } + + if a.Compile != nil { + data["compile"] = a.Compile + } + + if len(a.Custom) > 0 { + data["custom"] = a.Custom + } + + if len(a.Labels) > 0 { + data["labels"] = a.Labels + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Annotations { + if a.Location != nil { + data["location"] = a.Location + } + } + + return json.Marshal(data) +} + +func (rr *RelatedResourceAnnotation) MarshalJSON() ([]byte, error) { + d := map[string]any{ + "ref": rr.Ref.String(), + } + + if len(rr.Description) > 0 { + d["description"] = rr.Description + } + + return json.Marshal(d) +} + +func (ar *AnnotationsRef) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": ar.Path, + } + + if ar.Annotations != nil { + data["annotations"] = ar.Annotations + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.AnnotationsRef { + if ar.Location != nil { + data["location"] = ar.Location + } + } + + return json.Marshal(data) +} + +// schemaAnnotationJSON mirrors SchemaAnnotation's JSON tags, with location-free +// path terms. +type schemaAnnotationJSON struct { + Path []termJSON `json:"path"` + Schema Ref `json:"schema,omitempty"` + Definition *any `json:"definition,omitempty"` +} + +func (s *SchemaAnnotation) MarshalJSON() ([]byte, error) { + d := schemaAnnotationJSON{ + Schema: s.Schema, + Definition: s.Definition, + } + + if s.Path != nil { + d.Path = make([]termJSON, len(s.Path)) + for i, t := range s.Path { + // The location is omitted: path terms are parsed on their own from + // the annotation's YAML key, so their locations are offsets into that + // key (always row 1) rather than positions in the module. + d.Path[i] = termJSON{Type: ValueName(t.Value), Value: t.Value} + } + } + + return json.Marshal(d) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_jsonv2.go new file mode 100644 index 0000000000..72e28caec4 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations_jsonv2.go @@ -0,0 +1,195 @@ +//go:build go1.27 + +package ast + +import ( + "encoding/json/jsontext" + "encoding/json/v2" + "fmt" + + "github.com/open-policy-agent/opa/internal/jsonv2" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" +) + +// These are exported types, so losing MarshalJSON here would be a breaking +// API change even though callers should go through json.Marshal, not this +// method directly. +var ( + _ json.Marshaler = &Annotations{} + _ json.Marshaler = &AnnotationsRef{} + _ json.Marshaler = &SchemaAnnotation{} + _ json.Marshaler = &RelatedResourceAnnotation{} +) + +func (a *Annotations) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if a == nil { + e.WriteToken(jsontext.String("scope")) + e.WriteToken(jsontext.String("")) + return e.WriteToken(jsontext.EndObject) + } + + if a.Description != "" { + e.WriteToken(jsontext.String("description")) + e.WriteToken(jsontext.String(a.Description)) + } + + if a.Entrypoint { + e.WriteToken(jsontext.String("entrypoint")) + e.WriteToken(jsontext.True) + } + + if len(a.Organizations) > 0 { + if err := jsonv2.WriteFieldValue(e, "organizations", a.Organizations); err != nil { + return err + } + } + + if len(a.RelatedResources) > 0 { + if err := jsonv2.WriteFieldArray(e, "related_resources", a.RelatedResources); err != nil { + return err + } + } + + if len(a.Authors) > 0 { + if err := jsonv2.WriteFieldValue(e, "authors", a.Authors); err != nil { + return err + } + } + + if len(a.Schemas) > 0 { + if err := jsonv2.WriteFieldArray(e, "schemas", a.Schemas); err != nil { + return err + } + } + + if a.Compile != nil { + if err := jsonv2.WriteFieldValue(e, "compile", a.Compile); err != nil { + return err + } + } + + if len(a.Custom) > 0 { + if err := jsonv2.WriteFieldValue(e, "custom", a.Custom); err != nil { + return err + } + } + + if len(a.Labels) > 0 { + if err := jsonv2.WriteFieldValue(e, "labels", a.Labels); err != nil { + return err + } + } + + e.WriteToken(jsontext.String("scope")) + e.WriteToken(jsontext.String(a.Scope)) + + if a.Title != "" { + e.WriteToken(jsontext.String("title")) + e.WriteToken(jsontext.String(a.Title)) + } + + if a.Location != nil && astJSON.GetOptions().MarshalOptions.IncludeLocation.Annotations { + if err := jsonv2.WriteField(e, "location", a.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (a *Annotations) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(a) +} + +func (ar *AnnotationsRef) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if ar.Annotations != nil { + if err := jsonv2.WriteField(e, "annotations", ar.Annotations); err != nil { + return err + } + } + + if ar.Location != nil && astJSON.GetOptions().MarshalOptions.IncludeLocation.AnnotationsRef { + if err := jsonv2.WriteField(e, "location", ar.Location); err != nil { + return err + } + } + + if err := jsonv2.WriteField(e, "path", ar.Path); err != nil { + return err + } + + return e.WriteToken(jsontext.EndObject) +} + +func (ar *AnnotationsRef) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(ar) +} + +func (s *SchemaAnnotation) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(s) +} + +func (s *SchemaAnnotation) MarshalJSONTo(e *jsontext.Encoder) error { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginObject) + + // Path has no omitempty tag, so it's always written. A nil ref is written + // as null, matching encoding/json v1's treatment of a nil slice. + e.WriteToken(jsontext.String("path")) + if s.Path == nil { + e.WriteToken(jsontext.Null) + } else { + e.WriteToken(jsontext.BeginArray) + for _, t := range s.Path { + // The location is omitted: path terms are parsed on their own from + // the annotation's YAML key, so their locations are offsets into that + // key (always row 1) rather than positions in the module. + e.WriteToken(jsontext.BeginObject) + e.WriteToken(jsontext.String("type")) + e.WriteToken(jsontext.String(ValueName(t.Value))) + e.WriteToken(jsontext.String("value")) + if err := marshalValueTo(e, t.Value); err != nil { + return fmt.Errorf("failed to marshal schema path term of %s: %w", ValueName(t.Value), err) + } + e.WriteToken(jsontext.EndObject) + } + e.WriteToken(jsontext.EndArray) + } + + if len(s.Schema) > 0 { + if err := jsonv2.WriteField(e, "schema", s.Schema); err != nil { + return err + } + } + + if s.Definition != nil { + if err := jsonv2.WriteFieldValue(e, "definition", s.Definition); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (rr *RelatedResourceAnnotation) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(rr) +} + +func (rr *RelatedResourceAnnotation) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + e.WriteToken(jsontext.String("ref")) + e.WriteToken(jsontext.String(rr.Ref.String())) + + if len(rr.Description) > 0 { + e.WriteToken(jsontext.String("description")) + e.WriteToken(jsontext.String(rr.Description)) + } + + return e.WriteToken(jsontext.EndObject) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go index d557a9bd4b..17ed06035d 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go @@ -141,6 +141,7 @@ var DefaultBuiltins = [...]*Builtin{ StartsWith, EndsWith, Split, + SplitN, Replace, ReplaceN, Trim, @@ -1281,6 +1282,21 @@ var Split = &Builtin{ CanSkipBctx: true, } +var SplitN = &Builtin{ + Name: "strings.split_n", + Description: "Returns an array of at most `n` parts of `x` split on `delimiter`. If `n` is positive, returns the first `n` parts. If `n` is negative, returns the last `abs(n)` parts. If `n` is zero, returns an empty array. If `abs(n)` exceeds the number of parts, all parts are returned.", + Decl: types.NewFunction( + types.Args( + types.Named("x", types.S).Description("string that is split"), + types.Named("delimiter", types.S).Description("delimiter used for splitting"), + types.Named("n", types.N).Description("number of parts to return; positive selects from the left, negative from the right, zero returns an empty array"), + ), + types.Named("ys", types.NewArray(nil, types.S)).Description("split parts"), + ), + Categories: stringsCat, + CanSkipBctx: true, +} + var Replace = &Builtin{ Name: "replace", Description: "Replace replaces all instances of a sub-string.", @@ -1624,7 +1640,10 @@ var JSONFilter = &Builtin{ ), )).Description("JSON string paths"), ), - types.Named("filtered", types.A).Description("remaining data from `object` with only keys specified in `paths`"), + types.Named("filtered", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("remaining data from `object` with only keys specified in `paths`"), ), Categories: objectCat, CanSkipBctx: true, @@ -1663,7 +1682,10 @@ var JSONRemove = &Builtin{ ), )).Description("JSON string paths"), ), - types.Named("output", types.A).Description("result of removing all keys specified in `paths`"), + types.Named("output", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("result of removing all keys specified in `paths`"), ), Categories: objectCat, CanSkipBctx: true, @@ -1724,7 +1746,7 @@ var ObjectSubset = &Builtin{ types.NewArray(nil, types.A), )).Description("object to test if super is a superset of"), ), - types.Named("result", types.A).Description("`true` if `sub` is a subset of `super`"), + types.Named("result", types.B).Description("`true` if `sub` is a subset of `super`, otherwise undefined"), ), CanSkipBctx: true, } @@ -1744,8 +1766,11 @@ var ObjectUnion = &Builtin{ types.NewDynamicProperty(types.A, types.A), )).Description("right-hand object"), ), - types.Named("output", types.A).Description("a new object which is the result of an asymmetric recursive union of two objects where conflicts are resolved by choosing the key from the right-hand object `b`"), - ), // TODO(sr): types.A? ^^^^^^^ (also below) + types.Named("output", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("a new object which is the result of an asymmetric recursive union of two objects where conflicts are resolved by choosing the key from the right-hand object `b`"), + ), CanSkipBctx: true, } @@ -1760,7 +1785,10 @@ var ObjectUnionN = &Builtin{ types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), )).Description("list of objects to merge"), ), - types.Named("output", types.A).Description("asymmetric recursive union of all objects in `objects`, merged from left to right, where conflicts are resolved by choosing the key from the right-hand object"), + types.Named("output", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("asymmetric recursive union of all objects in `objects`, merged from left to right, where conflicts are resolved by choosing the key from the right-hand object"), ), CanSkipBctx: true, } @@ -1780,7 +1808,10 @@ var ObjectRemove = &Builtin{ types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), )).Description("keys to remove from x"), ), - types.Named("output", types.A).Description("result of removing the specified `keys` from `object`"), + types.Named("output", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("result of removing the specified `keys` from `object`"), ), CanSkipBctx: true, } @@ -1801,7 +1832,10 @@ var ObjectFilter = &Builtin{ types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), )).Description("keys to keep in `object`"), ), - types.Named("filtered", types.A).Description("remaining data from `object` with only keys specified in `keys`"), + types.Named("filtered", types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + )).Description("remaining data from `object` with only keys specified in `keys`"), ), CanSkipBctx: true, } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go index 810f38b1d3..ed3d14a15f 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go @@ -1238,8 +1238,8 @@ func removeDuplicate(list []Value) []Value { return newResult } -func getArgTypes(env *TypeEnv, args []*Term) []types.Type { - pre := make([]types.Type, len(args)) +func getArgTypes(env *TypeEnv, args []*Term) (pre []types.Type) { + pre = make([]types.Type, len(args)) for i := range args { pre[i] = env.GetByValue(args[i].Value) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go index 1bf990a6ed..ef1ba033fd 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go @@ -336,26 +336,18 @@ func TermValueCompare(a, b *Term) int { return a.Value.Compare(b.Value) } -func TermValueEqual(a, b *Term) bool { - return ValueEqual(a.Value, b.Value) -} - func ValueEqual(a, b Value) bool { switch v := a.(type) { - case Null: - return v.Equal(b) - case Boolean: - return v.Equal(b) + case Null, Boolean, String, Var: + return v == b case Number: return v.Equal(b) - case String: - return v.Equal(b) - case Var: - return v.Equal(b) case Ref: return v.Equal(b) case *Array: return v.Equal(b) + case *Not: + return v.Equal(b) case *TemplateString: return v.Equal(b) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go index 8aa60e0f44..c642c508a8 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go @@ -1344,9 +1344,12 @@ func (c *Compiler) checkRuleConflicts() { kinds := make(map[RuleKind]struct{}, len(rules)) completeRules := 0 partialRules := 0 + // `p contains x` (set) vs `p[k] contains v` (object of sets): a mix is a conflict. + var hasMultiValueSet bool + var hasMultiValueObject bool arities := make(map[int]struct{}, len(rules)) name := "" - var conflicts []Ref + var conflicts []ruleRef defaultRules := make([]*Rule, 0) for _, rule := range rules { @@ -1403,6 +1406,15 @@ func (c *Compiler) checkRuleConflicts() { } else { partialRules++ } + + if r.Head.RuleKind() == MultiValue { + // A ground ref ends at the node (set); a longer one extends past it (object). + if ref.IsGround() { + hasMultiValueSet = true + } else { + hasMultiValueObject = true + } + } } // Functions cannot exist within a rule's dynamic extent, as there is no valid @@ -1422,9 +1434,9 @@ func (c *Compiler) checkRuleConflicts() { switch { case conflicts != nil: - return !c.err(NewError(TypeErr, rules[0].Loc(), "rule %v conflicts with %v", name, conflicts)) + return !c.err(NewError(TypeErr, rules[0].Loc(), "rule %v conflicts with%v", name, formatConflict(conflicts, rw))) - case len(kinds) > 1 || len(arities) > 1 || (completeRules >= 1 && partialRules >= 1): + case len(kinds) > 1 || len(arities) > 1 || (completeRules >= 1 && partialRules >= 1) || (hasMultiValueSet && hasMultiValueObject): return !c.err(NewError(TypeErr, rules[0].Loc(), "conflicting rules %v found", name)) case len(defaultRules) > 1: @@ -1596,11 +1608,15 @@ func (c *Compiler) checkSafetyRuleHeads() { if vars.DiffCount(vis.vars) > 0 { unsafe := vars.Diff(vis.vars) for v := range unsafe { + // vars is keyed by the original name, so the location must be + // read before v is replaced with the rewritten one -- otherwise + // the lookup misses and the error is reported without a location. + loc := vars[v].Location if w, ok := c.RewrittenVars[v]; ok { v = w } if !v.IsGenerated() { - if !c.err(NewError(UnsafeVarErr, vars[v].Location, "var %v is unsafe", v)) { + if !c.err(NewError(UnsafeVarErr, loc, "var %v is unsafe", v)) { return true } } @@ -1701,16 +1717,17 @@ func (parser *schemaParser) parseSchemaWithPropertyKey(schema any, propertyKey s // Handle referenced schemas, returns directly when a $ref is found if subSchema.RefSchema != nil { - if existing, ok := parser.definitionCache[subSchema.Ref.String()]; ok { + subSchemaStr := subSchema.Ref.String() + if existing, ok := parser.definitionCache[subSchemaStr]; ok { if existing.processing { if existing.rec == nil { - existing.rec = types.NewRecursive(subSchema.Ref.String(), nil) + existing.rec = types.NewRecursive(subSchemaStr, nil) } return existing.rec, nil } return existing.typ, nil } - return parser.parseSchemaWithPropertyKey(subSchema.RefSchema, subSchema.Ref.String()) + return parser.parseSchemaWithPropertyKey(subSchema.RefSchema, subSchemaStr) } // Cache this $ref definition and finalize it via defer when parsing @@ -2218,7 +2235,7 @@ func (c *Compiler) resolveAllRefs() { } for v, u := range globals { - if v.Equal(imp.Name()) && !u.used { + if v == imp.Name() && !u.used { if !c.err(NewError(CompileErr, imp.Location, "%s unused", imp.String())) { return } @@ -3327,6 +3344,50 @@ func (c *Compiler) rewriteLocalVarsInRule(rule *Rule, unusedArgs VarSet, argsSta stack := argsStack.Copy() + // A variable shadowing a built-in name (e.g. `count`) is allowed in Rego, + // but if left un-rewritten later stages (type checking, arity, partial + // eval) can mistake it for the built-in, causing spurious, + // map-order-dependent errors (issue #3729). Rewrite such variables to + // fresh locals, like `:=`-declared ones. + // + // Only variables bound in the body are rewritten. Excluded: head-only + // references (stay unsafe-var errors), call operators (SkipRefCallHead), + // and `with` targets/values (possible function mocks). + if len(c.builtins) > 0 { + bodyVis := NewVarVisitor().WithParams(VarVisitorParams{ + SkipRefCallHead: true, + SkipClosures: true, + }) + bodyVis.Walk(rule.Body) + bodyVars := bodyVis.Vars() + + declaredInBody := declaredVars(rule.Body) + + withVars := NewVarSet() + NewGenericVisitor(func(x any) bool { + if w, ok := x.(*With); ok { + WalkVars(w, func(v Var) bool { + withVars.Add(v) + return false + }) + } + return false + }).Walk(rule) + + for _, v := range bodyVars.Sorted() { + if _, ok := c.builtins[v.String()]; !ok { + continue + } + if declaredInBody.Contains(v) || withVars.Contains(v) { + continue + } + if _, ok := stack.Declared(v); ok { + continue + } + stack.Insert(v, gen.Generate(), seenVar) + } + } + body, declared, errs := rewriteLocalVars(gen, stack, used, rule.Body, c.strict) // For rewritten vars use the collection of all variables that @@ -4285,26 +4346,61 @@ func (n *TreeNode) add(path Ref, val any) { } } +// ExternalIndex ties an ExternalRuleSource-provided index to the package Ref it +// serves. It is internal plumbing exported only so the topdown evaluator can +// reach it across the ast/topdown package boundary; it is not part of OPA's +// supported public API and may change without notice. The stable surface for +// implementing external rule sources is the ExternalRuleSource and +// ExternalRuleIndex interfaces. type ExternalIndex struct { Index ExternalRuleIndex Ref Ref } -func (ei *ExternalIndex) Tree(ctx context.Context, rt *TreeNode, prefix Ref, input *Term, m metrics.Metrics, reqMD map[string]any, respMD map[string]any) (*TreeNode, ExternalRuleIndex, error) { - resolver := &termResolver{input: input} +// Tree resolves external rules for prefix, using resolver to resolve references +// while building search queries. Passing a save-set-aware resolver (e.g. the +// topdown evaluator) lets sources that opt into +// ExternalSourceOptions.DistinguishAbsentFromUnknown distinguish absent input +// from values that are unknown under partial evaluation. +// +// params carries the ground key values that followed the registered prefix for +// a parametrized source (see ParametrizedExternalRuleIndex); it is nil for +// conventional sources. The returned subtree is always rooted at prefix (the +// registered ref), regardless of params — the evaluator layers the parameter +// levels back on top. +// +// Like ExternalIndex, Tree is internal plumbing exported only for the topdown +// evaluator. It is not part of OPA's supported public API and may change +// without notice. +func (ei *ExternalIndex) Tree(ctx context.Context, rt *TreeNode, prefix Ref, params []Value, resolver ValueResolver, m metrics.Metrics, reqMD map[string]any, respMD map[string]any) (*TreeNode, ExternalRuleIndex, error) { + o := ei.Index.Opts() + + // Select the resolver handed to the source. By default we wrap the caller's + // resolver so external sources see the legacy behavior (absent and unknown + // both collapse to UnknownValueErr, non-input refs are never resolved). + // Sources that set DistinguishAbsentFromUnknown receive the caller's + // save-set-aware resolver unchanged, letting them tell absent from unknown. + lookupResolver := resolver + switch { + case lookupResolver == nil: + lookupResolver = unknownResolver{} + case o == nil || !o.DistinguishAbsentFromUnknown: + lookupResolver = legacyExternalResolver{inner: lookupResolver} + } rules, updatedIndex, err := ei.Index.Lookup(ctx, - LookupResolver(resolver), + LookupResolver(lookupResolver), LookupMetrics(m), LookupRequestMetadata(reqMD), LookupResponseMetadata(respMD), + LookupParams(params), ) if err != nil { return nil, nil, err } c0 := NewCompiler() - if o := ei.Index.Opts(); o != nil { + if o != nil { if len(o.SkippedStages) > 0 { c0.WithSkipStages(o.SkippedStages...) } @@ -4343,24 +4439,37 @@ func (ei *ExternalIndex) Tree(ctx context.Context, rt *TreeNode, prefix Ref, inp return node, updatedIndex, nil } -type termResolver struct { - input *Term +// legacyExternalResolver reproduces the historical external-source resolver +// behavior on top of an arbitrary (typically save-set-aware) resolver: only +// input references are resolvable, and any input reference that does not +// resolve to a concrete value is reported as UnknownValueErr. This collapses +// "absent from the concrete input" and "symbolic under partial evaluation" +// into a single signal, matching what external sources saw before +// ExternalSourceOptions.DistinguishAbsentFromUnknown existed. +type legacyExternalResolver struct { + inner ValueResolver } -func (r *termResolver) Resolve(ref Ref) (Value, error) { - if ref.HasPrefix(InputRootRef) { - if r.input == nil { - return nil, UnknownValueErr{} - } - v, err := r.input.Value.Find(ref[1:]) - if err != nil { - return nil, UnknownValueErr{} - } - return v, nil +func (r legacyExternalResolver) Resolve(ref Ref) (Value, error) { + if !ref.HasPrefix(InputRootRef) { + return nil, UnknownValueErr{} + } + v, err := r.inner.Resolve(ref) + if err != nil { + return nil, err } - return nil, UnknownValueErr{} + if v == nil { + return nil, UnknownValueErr{} + } + return v, nil } +// unknownResolver treats every reference as unknown. It is used as a safe +// fallback when Tree is invoked without a resolver. +type unknownResolver struct{} + +func (unknownResolver) Resolve(Ref) (Value, error) { return nil, UnknownValueErr{} } + // Size returns the number of rules in the tree. func (n *TreeNode) Size() (s int) { for _, c := range n.Children { @@ -4481,30 +4590,50 @@ func attachValueToNode(node *TreeNode, ref Ref, val any) { } } +type ruleRef struct { + ref Ref + loc *Location +} + // flattenChildren flattens all children's rule refs into a sorted array. -func (n *TreeNode) flattenChildren() []Ref { +func (n *TreeNode) flattenChildren() []ruleRef { return n.flattenMatchingChildren(func(_ *Rule) bool { return true }) } // flattenChildFunctions is like flattenChildren but only collects functions (rules with args). -func (n *TreeNode) flattenChildFunctions() []Ref { +func (n *TreeNode) flattenChildFunctions() []ruleRef { return n.flattenMatchingChildren(func(r *Rule) bool { return r.isFunction() }) } -func (n *TreeNode) flattenMatchingChildren(f func(*Rule) bool) []Ref { - ret := newRefSet() +func (n *TreeNode) flattenMatchingChildren(f func(*Rule) bool) []ruleRef { + var ret ruleRefSet for _, sub := range n.Children { // we only want the children, so don't use n.DepthFirst() right away sub.DepthFirst(func(x *TreeNode) bool { for _, rule := range x.Values { if f(rule) { - ret.AddPrefix(rule.Ref()) + ret.AddPrefix(ruleRef{ref: rule.Ref(), loc: rule.Loc()}) } } return false }) } - return util.SortedFunc(ret.s, RefCompare) + return util.SortedFunc(ret.s, func(a, b ruleRef) int { + return RefCompare(a.ref, b.ref) + }) +} + +func formatConflict(conflicts []ruleRef, rw varRewriter) string { + s := strings.Builder{} + s.WriteString(":\n") + for _, conflict := range conflicts { + s.WriteString(" rule ") + s.WriteString(rw(conflict.ref.Copy()).String()) + s.WriteString(" at ") + s.WriteString(conflict.loc.String()) + s.WriteString("\n") + } + return strings.TrimSuffix(s.String(), "\n") } // Copy creates a shallow copy of the TreeNode suitable for augmentation. @@ -4639,7 +4768,12 @@ func (g *Graph) Sort() (sorted []util.T, ok bool) { temp: map[util.T]struct{}{}, } + nodesList := make([]util.T, 0, len(g.nodes)) for node := range g.nodes { + nodesList = append(nodesList, node) + } + sortGraphNodes(nodesList) + for _, node := range nodesList { if !sorter.Visit(node) { return nil, false } @@ -4687,6 +4821,24 @@ type graphSort struct { temp map[util.T]struct{} } +// sortGraphNodes orders rule nodes deterministically (by location, then ref) +// so the topological sort, and thus the rule type-checking order, doesn't +// depend on Go's randomized map iteration (issue #3729). Head.Ref is used for +// the tie-break rather than Rule.Ref so nodes with a nil Module don't panic. +func sortGraphNodes(nodes []util.T) { + slices.SortStableFunc(nodes, func(a, b util.T) int { + ra, aok := a.(*Rule) + rb, bok := b.(*Rule) + if !aok || !bok { + return 0 + } + if c := ra.Location.Compare(rb.Location); c != 0 { + return c + } + return ra.Head.Ref().Compare(rb.Head.Ref()) + }) +} + func (sort *graphSort) Marked(node util.T) bool { _, marked := sort.marked[node] return marked @@ -4700,7 +4852,13 @@ func (sort *graphSort) Visit(node util.T) (ok bool) { return true } sort.temp[node] = struct{}{} - for other := range sort.deps(node) { + deps := sort.deps(node) + depList := make([]util.T, 0, len(deps)) + for other := range deps { + depList = append(depList, other) + } + sortGraphNodes(depList) + for _, other := range depList { if !sort.Visit(other) { return false } @@ -5249,6 +5407,15 @@ func outputVarsForExprEq(expr *Expr, safe VarSet, output VarSet) VarSet { output = outputVarsForTerms(expr, safe, output) output.Update(safe) + if expr.fromAssignment { + // The LHS of `:=` is a pure output; excluding it from the safe basis + // stops the RHS being made safe by unifying backwards through the LHS. + // See issue #3546. + WalkVars(expr.Operand(0), func(v Var) bool { + delete(output, v) + return false + }) + } output.Update(Unify(output, expr.Operand(0), expr.Operand(1))) diff := output.Diff(safe) @@ -6368,7 +6535,7 @@ func (s localDeclaredVars) Insert(x, y Var, occurrence varOccurrence) { // If the variable has been rewritten (where x != y, with y being // the generated value), store it in the map of rewritten vars. // Assume that the generated values are unique for the compilation. - if !x.Equal(y) { + if x != y { s.rewritten[y] = x } } @@ -6817,6 +6984,7 @@ func rewriteDeclaredAssignment(g *localVarGenerator, stack *localDeclaredVars, e if len(errs) == numErrsBefore { loc := expr.Operator()[0].Location expr.SetOperator(RefTerm(VarTerm(Equality.Name).SetLocation(loc)).SetLocation(loc)) + expr.fromAssignment = true } return expr, errs @@ -6845,7 +7013,7 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t case Call: ref := v[0] WalkVars(ref, func(v Var) bool { - if gv, ok := stack.Declared(v); ok && !gv.Equal(v) { + if gv, ok := stack.Declared(v); ok && gv != v { // We will rewrite the ref of a function call, which is never ok since we don't have first-class functions. errs = append(errs, NewError(CompileErr, term.Location, "called function %s shadowed", ref)) return true @@ -6900,11 +7068,11 @@ func rewriteDeclaredVarsInWithRecursive(g *localVarGenerator, stack *localDeclar if sdwInput, ok := stack.Declared(InputRootDocument.Value.(Var)); ok { // Was "input" shadowed... switch value := w.Target.Value.(type) { case Var: - if sdwInput.Equal(value) { // ...and replaced? If so, fix it + if sdwInput == value { // ...and replaced? If so, fix it w.Target.Value = InputRootRef } case Ref: - if sdwInput.Equal(value[0].Value.(Var)) { + if sdwInput.Equal(value[0].Value) { w.Target.Value.(Ref)[0].Value = InputRootDocument.Value } } @@ -7233,48 +7401,24 @@ func rewriteVarsInRef(vars ...map[Var]Var) varRewriter { } } -// NOTE(sr): This is duplicated with compile/compile.go; but moving it into another location -// would cause a circular dependency -- the refSet definition needs ast.Ref. If we make it -// public in the ast package, the compile package could take it from there, but it would also -// increase our public interface. Let's reconsider if we need it in a third place. -type refSet struct { - s []Ref -} - -func newRefSet(x ...Ref) *refSet { - result := &refSet{} - for i := range x { - result.AddPrefix(x[i]) - } - return result -} - -// ContainsPrefix returns true if r is prefixed by any of the existing refs in the set. -func (rs *refSet) ContainsPrefix(r Ref) bool { - return slices.ContainsFunc(rs.s, r.HasPrefix) +type ruleRefSet struct { + s []ruleRef } // AddPrefix inserts r into the set if r is not prefixed by any existing // refs in the set. If any existing refs are prefixed by r, those existing // refs are removed. -func (rs *refSet) AddPrefix(r Ref) { - if rs.ContainsPrefix(r) { - return +func (rs *ruleRefSet) AddPrefix(r ruleRef) { + for i := range rs.s { + if r.ref.HasPrefix(rs.s[i].ref) { + return + } } - cpy := []Ref{r} + cpy := []ruleRef{r} for i := range rs.s { - if !rs.s[i].HasPrefix(r) { + if !rs.s[i].ref.HasPrefix(r.ref) { cpy = append(cpy, rs.s[i]) } } rs.s = cpy } - -// Sorted returns a sorted slice of terms for refs in the set. -func (rs *refSet) Sorted() []*Term { - terms := make([]*Term, len(rs.s)) - for i := range rs.s { - terms[i] = NewTerm(rs.s[i]) - } - return util.SortedFunc(terms, TermValueCompare) -} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go b/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go index bf8bca7472..1188cd6e08 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/errors.go @@ -87,7 +87,6 @@ type Error struct { } func (e *Error) Error() string { - var prefix string if e.Location != nil { @@ -119,6 +118,34 @@ func (e *Error) Error() string { return sb.String() } +func (e *Error) Equal(other *Error) bool { + if e == other { + return true + } + + if e == nil || other == nil { + return false + } + + if e.Code != other.Code || e.Message != other.Message { + return false + } + + if !e.Location.Equal(other.Location) { + return false + } + + if (e.Details == nil) != (other.Details == nil) { + return false + } + + if e.Details != nil && !slices.Equal(e.Details.Lines(), other.Details.Lines()) { + return false + } + + return true +} + // NewError returns a new Error object. func NewError(code string, loc *Location, f string, a ...any) *Error { return newErrorString(code, loc, fmt.Sprintf(f, a...)) diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/external_source.go b/vendor/github.com/open-policy-agent/opa/v1/ast/external_source.go index ce3433cce0..4669789e11 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/external_source.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/external_source.go @@ -46,6 +46,39 @@ type ExternalRuleIndexCloser interface { Close() error } +// ParametrizedExternalRuleIndex is an optional interface implemented by external +// rule indexes that serve a family of sub-references under their registered +// prefix rather than a single exact ref. The Ref such a source is registered +// under is treated as a PREFIX: the leading elements of a query reference that +// follow the prefix are consumed as ground lookup parameters (handed to Lookup +// via LookupOptions.Params) rather than as descents into a static rule tree. +// This lets one registered source serve an unbounded family of sub-references — +// one distinct set of rules per parameter tuple — without registering each +// concretely, so references whose key only comes into existence at runtime +// resolve without a recompile. +// +// An index that does not implement this interface behaves as a conventional +// exact-ref source (equivalent to an arity of 0). +type ParametrizedExternalRuleIndex interface { + ExternalRuleIndex + + // ParamArity reports how many elements following the registered prefix this + // index consumes as lookup parameters, given the reference tail (the query + // reference elements after the prefix, or an empty Ref when none follow). + // + // The count may vary with the tail's *shape* — e.g. keying off a leading + // discriminator segment — which lets a single prefix back an uneven-depth + // tree. It must NOT depend on parameter *values*: ParamArity is consulted + // before the parameters are plugged, so the tail may contain non-ground + // elements, and the count decides the caching boundary. Returning 0 makes + // the reference resolve as a conventional exact ref. + // + // The parameter elements the count selects must be ground at evaluation + // time. A non-ground parameter yields an undefined result, except under + // partial evaluation where the reference is saved for residualization. + ParamArity(tail Ref) int +} + // ExternalSourceOptions contains options for registering an external rule source. type ExternalSourceOptions struct { // VisibleRefs controls which parts of the surrounding rule tree the external @@ -68,6 +101,25 @@ type ExternalSourceOptions struct { // This is forward-compatible: new compiler stages added in future releases // will be skipped automatically rather than running unexpectedly. SkippedStages []StageID + + // DistinguishAbsentFromUnknown controls how the resolver passed to Lookup + // (via LookupOptions.Resolver) reports references that do not resolve to a + // concrete value. + // + // When false (default), the legacy behavior is preserved for backwards + // compatibility: only input references are resolvable, and any input + // reference that cannot be resolved — whether it is genuinely absent from a + // concrete input or symbolic under partial evaluation — surfaces as + // UnknownValueErr. The two cases are indistinguishable. + // + // When true, the source opts into the same save-set-aware resolver the + // built-in rule indexer uses: a reference that is unknown under partial + // evaluation returns UnknownValueErr, while a reference that is simply + // absent from an otherwise-concrete input resolves to (nil, nil). This lets + // a source tell "deliberately symbolic" apart from "concretely missing" + // on a per-reference basis (e.g. input.foo unknown while input.bar is + // known). See ValueResolver and IsUnknownValueErr. + DistinguishAbsentFromUnknown bool } // LookupOption is a functional option for ExternalRuleIndex.Lookup calls. @@ -79,6 +131,7 @@ type LookupOptions struct { resolver ValueResolver requestMetadata map[string]any responseMetadata map[string]any + params []Value } // Metrics returns the metrics instance from the options, or nil if not set. @@ -101,6 +154,14 @@ func (o *LookupOptions) ResponseMetadata() map[string]any { return o.responseMetadata } +// Params returns the parameter values for a parametrized external source (see +// ParametrizedExternalRuleIndex). The slice holds the ground key values that +// followed the registered prefix in the query reference, in order. It is empty +// for conventional (non-parametrized) sources. +func (o *LookupOptions) Params() []Value { + return o.params +} + // LookupMetrics returns a LookupOption that sets the metrics instance // for the Lookup call. func LookupMetrics(m metrics.Metrics) LookupOption { @@ -126,3 +187,11 @@ func LookupResponseMetadata(m map[string]any) LookupOption { opts.responseMetadata = m } } + +// LookupParams returns a LookupOption that sets the parameter values handed to a +// parametrized external source (see ParametrizedExternalRuleIndex). +func LookupParams(params []Value) LookupOption { + return func(opts *LookupOptions) { + opts.params = params + } +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go index 45c0fa7a81..3502550798 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go @@ -6,46 +6,66 @@ package ast import ( "slices" - "sort" "strings" "sync" "github.com/open-policy-agent/opa/v1/util" ) -// RuleIndex defines the interface for rule indices. -type RuleIndex interface { - - // Build tries to construct an index for the given rules. If the index was - // constructed, it returns true, otherwise false. - Build(rules []*Rule) bool +var ( + equalityRef = Equality.Ref() + equalRef = Equal.Ref() + globMatchRef = GlobMatch.Ref() + internalPrintRef = InternalPrint.Ref() + internalTestCaseRef = InternalTestCase.Ref() + internalMemberRef = Member.Ref() - // Lookup searches the index for rules that will match the provided - // resolver. If the resolver returns an error, it is returned via err. - Lookup(resolver ValueResolver) (*IndexResult, error) + globwildcard = VarTerm("$globwildcard") + skipIndexing = NewSet(NewTerm(internalPrintRef), NewTerm(internalTestCaseRef)) - // AllRules traverses the index and returns all rules that will match - // the provided resolver without any optimizations (effectively with - // indexing disabled). If the resolver returns an error, it is returned - // via err. - AllRules(resolver ValueResolver) (*IndexResult, error) -} + // anyValue is a fake variable we used to put "naked ref" expressions + // into the rule index + anyValue Value = Var("__any__") +) -// IndexResult contains the result of an index lookup. -type IndexResult struct { - Rules []*Rule - Else map[*Rule][]*Rule - Default *Rule - Kind RuleKind - EarlyExit bool - OnlyGroundRefs bool -} +type ( + // RuleIndex defines the interface for rule indices. + RuleIndex interface { + // Build tries to construct an index for the given rules. If the index was + // constructed, it returns true, otherwise false. + Build(rules []*Rule) bool + + // Lookup searches the index for rules that will match the provided + // resolver. If the resolver returns an error, it is returned via err. + Lookup(resolver ValueResolver) (*IndexResult, error) + + // AllRules traverses the index and returns all rules that will match + // the provided resolver without any optimizations (effectively with + // indexing disabled). If the resolver returns an error, it is returned + // via err. + AllRules(resolver ValueResolver) (*IndexResult, error) + } + // IndexResult contains the result of an index lookup. + IndexResult struct { + Rules []*Rule + Else map[*Rule][]*Rule + Default *Rule + Kind RuleKind + EarlyExit bool + OnlyGroundRefs bool + } + baseDocEqIndex struct { + isVirtual func(Ref) bool + root *trieNode + defaultRule *Rule + kind RuleKind + onlyGroundRefs bool + } +) // NewIndexResult returns a new IndexResult object. func NewIndexResult(kind RuleKind) *IndexResult { - return &IndexResult{ - Kind: kind, - } + return &IndexResult{Kind: kind} } // Empty returns true if there are no rules to evaluate. @@ -53,25 +73,6 @@ func (ir *IndexResult) Empty() bool { return len(ir.Rules) == 0 && ir.Default == nil } -type baseDocEqIndex struct { - isVirtual func(Ref) bool - root *trieNode - defaultRule *Rule - kind RuleKind - onlyGroundRefs bool -} - -var ( - equalityRef = Equality.Ref() - equalRef = Equal.Ref() - globMatchRef = GlobMatch.Ref() - internalPrintRef = InternalPrint.Ref() - internalTestCaseRef = InternalTestCase.Ref() - internalMemberRef = Member.Ref() - - skipIndexing = NewSet(NewTerm(internalPrintRef), NewTerm(internalTestCaseRef)) -) - func newBaseDocEqIndex(isVirtual func(Ref) bool) *baseDocEqIndex { return &baseDocEqIndex{ isVirtual: isVirtual, @@ -99,14 +100,7 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { if i.onlyGroundRefs { i.onlyGroundRefs = rule.Head.Reference.IsGround() } - var skip bool - for i := range rule.Body { - if op := rule.Body[i].OperatorTerm(); op != nil && skipIndexing.Contains(op) { - skip = true - break - } - } - if !skip { + if !slices.ContainsFunc(rule.Body, skipIndexingOperator) { clear(values) for i := range rule.Body { indices.Update(rule, rule.Body[i], values) @@ -137,15 +131,7 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { } else if len(values) == 1 { node = node.Insert(ref, values[0].Value, values[0].Mapper) } else { - var hasVar bool - for i := range values { - if _, isVar := values[i].Value.(Var); isVar { - hasVar = true - break - } - } - - if hasVar { + if slices.ContainsFunc(values, (*refindex).isVar) { child := node.Insert(ref, anyValue, values[0].Mapper) for i := range values { if values[i].Mapper != nil { @@ -183,7 +169,12 @@ func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { tr := ttrPool.Get().(*trieTraversalResult) defer func() { - clear(tr.unordered) + // Note(anderseknert): `clear`ing the map is not good enough here, as it'd mean + // resetting each of its slice values, costing us new allocations on each append + // in subsequent lookups + for i := range tr.unordered { + tr.unordered[i] = tr.unordered[i][:0] + } tr.ordering = tr.ordering[:0] tr.multiple = false tr.exist = nil @@ -211,9 +202,10 @@ func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { clear(result.Else) for _, pos := range tr.ordering { - slices.SortFunc(tr.unordered[pos], func(a, b *ruleNode) int { - return a.prio[1] - b.prio[1] - }) + if len(tr.unordered[pos]) == 0 { + continue + } + slices.SortFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) nodes := tr.unordered[pos] root := nodes[0].rule @@ -267,9 +259,10 @@ func (i *baseDocEqIndex) AllRules(ValueResolver) (*IndexResult, error) { result.Rules = make([]*Rule, 0, len(tr.ordering)) for _, pos := range tr.ordering { - slices.SortFunc(tr.unordered[pos], func(a, b *ruleNode) int { - return a.prio[1] - b.prio[1] - }) + if len(tr.unordered[pos]) == 0 { + continue + } + slices.SortFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) nodes := tr.unordered[pos] root := nodes[0].rule result.Rules = append(result.Rules, root) @@ -326,15 +319,15 @@ func newrefindices(isVirtual func(Ref) bool) *refindices { } } -// anyValue is a fake variable we used to put "naked ref" expressions -// into the rule index -var anyValue = Var("__any__") +func (i *refindex) isVar() bool { + _, isVar := i.Value.(Var) + return isVar +} // Update attempts to update the refindices for the given expression in the // given rule. If the expression cannot be indexed the update does not affect // the indices. func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { - if len(expr.With) > 0 { // NOTE(tsandall): In the future, we may need to consider expressions // that have with statements applied to them. @@ -354,8 +347,8 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { // check for type "Var" here. But since it's impossible to call a // function with a undefined argument, there's no point to recording // "needs to be anything" for function args - if ref, ok := ts.Value.(Ref); ok { // "naked ref" - i.updateEq(rule, ref, anyValue, nil) + if _, ok := ts.Value.(Ref); ok { // "naked ref" + i.updateEq(rule, ts.Value, anyValue, nil) } } } @@ -397,27 +390,18 @@ func (i *refindices) isValidIndexRef(ref Ref) bool { // References that appear more frequently in the indexed rules are ordered // before less frequently appearing references. func (i *refindices) Sorted() []Ref { - if i.sorted == nil { - counts := make([]int, 0, i.frequency.Len()) - i.sorted = make([]Ref, 0, i.frequency.Len()) - - i.frequency.Iter(func(k Ref, v int) bool { - counts = append(counts, v) - i.sorted = append(i.sorted, k) - return false - }) - - sort.Slice(i.sorted, func(a, b int) bool { - if counts[a] > counts[b] { - return true - } else if counts[b] > counts[a] { - return false + i.sorted = util.SortedFunc(i.frequency.Keys(), func(a, b Ref) int { + countsA, _ := i.frequency.Get(a) + countsB, _ := i.frequency.Get(b) + if countsA < countsB { // descending, we want highest-freq first + return 1 + } else if countsA > countsB { + return -1 } - return i.sorted[a][0].Loc().Compare(i.sorted[b][0].Loc()) < 0 + return a[0].Loc().Compare(b[0].Loc()) }) } - return i.sorted } @@ -515,12 +499,10 @@ func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) { } func (i *refindices) updateMember(rule *Rule, expr *Expr, constants map[Var]Value) { - args := rule.Head.Args lhs, rhs := expr.Operand(0), expr.Operand(1) - lvar, ok := lhs.Value.(Var) if ok { - lref := resolveVarToRef(i.rules[rule], args, lvar) + lref := resolveVarToRef(i.rules[rule], rule.Head.Args, lvar) if lref != nil { i.updateMemberRefInValue(rule, lref, rhs, constants) // `ref in value` return @@ -528,7 +510,7 @@ func (i *refindices) updateMember(rule *Rule, expr *Expr, constants map[Var]Valu } // `var0 in var1` case (var0 may be constant, var1 ref) - i.updateMemberValueInRef(rule, args, lhs.Value, rhs, constants) + i.updateMemberValueInRef(rule, rule.Head.Args, lhs.Value, rhs, constants) } func (i *refindices) updateMemberValueInRef(rule *Rule, args []*Term, lval Value, rhs *Term, constants map[Var]Value) { @@ -615,12 +597,12 @@ func (i *refindices) resolveAndValidateRef(rule *Rule, args []*Term, term *Term) // as we're not capturing `var = var` expressions in the index. func resolveVarToRef(ri []*refindex, args []*Term, v Var) Ref { for _, other := range ri { - if ov, ok := other.Value.(Var); ok && ov.Equal(v) { + if v.Equal(other.Value) { return other.Ref } } for j, arg := range args { - if arg.Value.Compare(v) == 0 { + if v.Equal(arg.Value) { return Ref{FunctionArgRootDocument, InternedTerm(j)} } } @@ -636,7 +618,6 @@ func (i *refindices) insert(rule *Rule, index *refindex) { for pos, other := range i.rules[rule] { if other.Ref.Equal(index.Ref) { - if ValueEqual(other.Value, index.Value) { return } @@ -671,7 +652,7 @@ type trieTraversalResult struct { multiple bool } -var ttrPool = sync.Pool{ +var ttrPool = &sync.Pool{ New: func() any { return newTrieTraversalResult() }, @@ -679,21 +660,17 @@ var ttrPool = sync.Pool{ func newTrieTraversalResult() *trieTraversalResult { return &trieTraversalResult{ - unordered: map[int][]*ruleNode{}, + unordered: make(map[int][]*ruleNode, 16), } } func (tr *trieTraversalResult) Add(t *trieNode) { for _, node := range t.rules { root := node.prio[0] - nodes, ok := tr.unordered[root] - if !ok { + if nodes, ok := tr.unordered[root]; !ok || len(nodes) == 0 { tr.ordering = append(tr.ordering, root) - } - // Deduplicate: check if a ruleNode with this priority already exists - if !slices.ContainsFunc(nodes, func(existing *ruleNode) bool { - return existing.prio == node.prio - }) { + tr.unordered[root] = append(nodes, node) + } else if !slices.ContainsFunc(nodes, node.prioEqual) { tr.unordered[root] = append(nodes, node) } } @@ -740,10 +717,16 @@ type ruleNode struct { rule *Rule } +func (a *ruleNode) prio1Cmp(b *ruleNode) int { + return a.prio[1] - b.prio[1] +} + +func (a *ruleNode) prioEqual(b *ruleNode) bool { + return a.prio == b.prio +} + func newTrieNodeImpl() *trieNode { - return &trieNode{ - scalars: util.NewHasherMap[Value, *trieNode](ValueEqual), - } + return &trieNode{} } func (node *trieNode) Do(walker trieWalker) { @@ -768,7 +751,6 @@ func (node *trieNode) Do(walker trieWalker) { } func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNode { - if node.next == nil { node.next = newTrieNodeImpl() node.next.ref = ref @@ -782,7 +764,6 @@ func (node *trieNode) Insert(ref Ref, value Value, mapper *valueMapper) *trieNod } func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) error { - if node == nil { return nil } @@ -802,54 +783,70 @@ func (node *trieNode) addMapper(mapper *valueMapper) { } func (node *trieNode) insertValue(value Value) *trieNode { - switch value := value.(type) { case nil: - if node.undefined == nil { - node.undefined = newTrieNodeImpl() - } + node.undefined = util.Or(node.undefined, newTrieNodeImpl) return node.undefined case Var: - if node.any == nil { - node.any = newTrieNodeImpl() - } + node.any = util.Or(node.any, newTrieNodeImpl) return node.any case Null, Boolean, Number, String: child, ok := node.scalars.Get(value) if !ok { child = newTrieNodeImpl() + if node.scalars == nil { + node.scalars = util.NewHasherMap[Value, *trieNode](ValueEqual) + } node.scalars.Put(value, child) } return child case *Array: - if node.array == nil { - node.array = newTrieNodeImpl() - } + node.array = util.Or(node.array, newTrieNodeImpl) return node.array.insertArray(value) + + // `x in ` (see updateMemberRefInValue) inserts each element of + // the literal collection as-is, without restricting it to scalars/arrays + // like the equality-based indexing does (see indexValue). A ground + // Object or Set element can't be indexed precisely, so - like Var - it + // falls back to the "any" node: the rule stays a candidate for every + // input value. (The other composite Value types - Ref, comprehensions, + // Call - can't actually reach here: the compiler rewrites them into + // separate statements, bound to a Var, before the index is built.) + case Object, Set: + node.any = util.Or(node.any, newTrieNodeImpl) + return node.any } panic("illegal value") } func (node *trieNode) insertArray(arr *Array) *trieNode { - if arr.Len() == 0 { return node } switch head := arr.Elem(0).Value.(type) { case Var: - if node.any == nil { - node.any = newTrieNodeImpl() - } + node.any = util.Or(node.any, newTrieNodeImpl) return node.any.insertArray(arr.Slice(1, -1)) case Null, Boolean, Number, String: child, ok := node.scalars.Get(head) if !ok { child = newTrieNodeImpl() + if node.scalars == nil { + node.scalars = util.NewHasherMap[Value, *trieNode](ValueEqual) + } node.scalars.Put(head, child) } return child.insertArray(arr.Slice(1, -1)) + + // Same reasoning as in insertValue above: an array element can itself be + // a nested array, object, or set, none of which can be indexed precisely + // at this position, so fall back to "any" and keep indexing the + // remaining elements. + case *Array, Object, Set: + node.any = util.Or(node.any, newTrieNodeImpl) + return node.any.insertArray(arr.Slice(1, -1)) } panic("illegal value") @@ -868,8 +865,7 @@ func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) return err } - err = node.undefined.Traverse(resolver, tr) - if err != nil { + if err = node.undefined.Traverse(resolver, tr); err != nil { return err } @@ -877,13 +873,11 @@ func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) return nil } - err = node.any.Traverse(resolver, tr) - if err != nil { + if err = node.any.Traverse(resolver, tr); err != nil { return err } - err = node.traverseValue(resolver, tr, v) - if err != nil { + if err = node.traverseValue(resolver, tr, v); err != nil { return err } @@ -900,7 +894,6 @@ func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) } func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error { - switch value := value.(type) { case *Array, Set, Object: if node.array != nil { @@ -910,19 +903,13 @@ func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalRes } } } - if node.scalars.Len() > 0 { return node.traverseCollectionMembership(resolver, tr, value) } - - return nil - case Null, Boolean, Number, String: - child, ok := node.scalars.Get(value) - if !ok { - return nil + if child, ok := node.scalars.Get(value); ok { + return child.Traverse(resolver, tr) } - return child.Traverse(resolver, tr) } return nil @@ -951,7 +938,7 @@ func (node *trieNode) traverseCollectionMembership(resolver ValueResolver, tr *t return nil } -func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr *Array) error { +func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr *Array) (err error) { if node == nil { return nil } @@ -960,24 +947,15 @@ func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalRes return node.Traverse(resolver, tr) } - err := node.any.traverseArray(resolver, tr, arr.Slice(1, -1)) - if err != nil { - return err - } - - head := arr.Elem(0).Value - - if !IsScalar(head) { - return nil - } - - switch head := head.(type) { - case Null, Boolean, Number, String: - child, _ := node.scalars.Get(head) - return child.traverseArray(resolver, tr, arr.Slice(1, -1)) + if err = node.any.traverseArray(resolver, tr, arr.Slice(1, -1)); err == nil { + switch head := arr.Elem(0).Value.(type) { + case Null, Boolean, Number, String: + child, _ := node.scalars.Get(head) + return child.traverseArray(resolver, tr, arr.Slice(1, -1)) + } } - panic("illegal value") + return err } func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { @@ -1105,10 +1083,7 @@ func globDelimiterToString(delim *Term) (string, bool) { return result, true } -var globwildcard = VarTerm("$globwildcard") - func globPatternToArray(pattern *Term, delim string) *Term { - s, ok := pattern.Value.(String) if !ok { return nil @@ -1147,7 +1122,6 @@ func globPatternToArray(pattern *Term, delim string) *Term { // splits s on characters in delim except if delim characters have been escaped // with reverse solidus. func splitStringEscaped(s string, delim string) []string { - var last, curr int var escaped bool var result []string @@ -1171,7 +1145,12 @@ func splitStringEscaped(s string, delim string) []string { func stringSliceToArray(s []string) *Array { arr := make([]*Term, len(s)) for i, v := range s { - arr[i] = StringTerm(v) + arr[i] = InternedTerm(v) } return NewArray(arr...) } + +func skipIndexingOperator(expr *Expr) bool { + op := expr.OperatorTerm() + return op != nil && skipIndexing.Contains(op) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go index 4e3a080bed..e08088cff1 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go @@ -3,12 +3,10 @@ package location import ( "bytes" - "encoding/json" "errors" "fmt" "unicode/utf8" - astJSON "github.com/open-policy-agent/opa/v1/ast/json" "github.com/open-policy-agent/opa/v1/util" ) @@ -150,41 +148,3 @@ func (loc *Location) Compare(other *Location) int { } return 0 } - -func (loc *Location) MarshalJSON() ([]byte, error) { - // structs are used here to preserve the field ordering of the original Location struct - jsonOptions := astJSON.GetOptions().MarshalOptions - if jsonOptions.ExcludeLocationFile { - data := struct { - Row int `json:"row"` - Col int `json:"col"` - Text []byte `json:"text,omitempty"` - }{ - Row: loc.Row, - Col: loc.Col, - } - - if jsonOptions.IncludeLocationText { - data.Text = loc.Text - } - - return json.Marshal(data) - } - - data := struct { - File string `json:"file"` - Row int `json:"row"` - Col int `json:"col"` - Text []byte `json:"text,omitempty"` - }{ - Row: loc.Row, - Col: loc.Col, - File: loc.File, - } - - if jsonOptions.IncludeLocationText { - data.Text = loc.Text - } - - return json.Marshal(data) -} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_json.go b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_json.go new file mode 100644 index 0000000000..441ea86e46 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_json.go @@ -0,0 +1,47 @@ +//go:build !go1.27 + +package location + +import ( + "encoding/json" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" +) + +func (loc *Location) MarshalJSON() ([]byte, error) { + // structs are used here to preserve the field ordering of the original Location struct + jsonOptions := astJSON.GetOptions().MarshalOptions + if jsonOptions.ExcludeLocationFile { + data := struct { + Row int `json:"row"` + Col int `json:"col"` + Text []byte `json:"text,omitempty"` + }{ + Row: loc.Row, + Col: loc.Col, + } + + if jsonOptions.IncludeLocationText { + data.Text = loc.Text + } + + return json.Marshal(data) + } + + data := struct { + File string `json:"file"` + Row int `json:"row"` + Col int `json:"col"` + Text []byte `json:"text,omitempty"` + }{ + Row: loc.Row, + Col: loc.Col, + File: loc.File, + } + + if jsonOptions.IncludeLocationText { + data.Text = loc.Text + } + + return json.Marshal(data) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_jsonv2.go new file mode 100644 index 0000000000..1897538a14 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location_jsonv2.go @@ -0,0 +1,50 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build go1.27 + +package location + +import ( + "encoding/base64" + "encoding/json/jsontext" + "encoding/json/v2" + + "github.com/open-policy-agent/opa/internal/jsonv2" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" +) + +// Location is an exported type, so losing MarshalJSON here would be a +// breaking API change even though callers should go through json.Marshal, +// not this method directly. +var _ json.Marshaler = &Location{} + +// MarshalJSON returns the JSON encoding of loc. +func (loc *Location) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(loc) +} + +func (loc *Location) MarshalJSONTo(e *jsontext.Encoder) (err error) { + e.WriteToken(jsontext.BeginObject) + + jsonOptions := astJSON.GetOptions().MarshalOptions + if !jsonOptions.ExcludeLocationFile { + e.WriteToken(jsontext.String("file")) + e.WriteToken(jsontext.String(loc.File)) + } + + e.WriteToken(jsontext.String("row")) + e.WriteToken(jsontext.Int(int64(loc.Row))) + e.WriteToken(jsontext.String("col")) + e.WriteToken(jsontext.Int(int64(loc.Col))) + + // NOTE: len check to match the `json:"text,omitempty"` behaviour of the + // pre-go1.27 marshaller. + if jsonOptions.IncludeLocationText && len(loc.Text) > 0 { + e.WriteToken(jsontext.String("text")) + e.WriteToken(jsontext.String(base64.StdEncoding.EncodeToString(loc.Text))) + } + + return e.WriteToken(jsontext.EndObject) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/map.go b/vendor/github.com/open-policy-agent/opa/v1/ast/map.go index 31cad4d611..aa0e655b9e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/map.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/map.go @@ -10,7 +10,7 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -// ValueMap represents a key/value map between AST term values. Any type of term +// ValueMap represents a key/value map between AST term values. Any type of value // can be used as a key in the map. type ValueMap struct { hashMap *util.TypedHashMap[Value, Value] diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go b/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go index 217947bc83..5f0911ec11 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go @@ -152,7 +152,7 @@ func (rule *Rule) mermaidFormat(b *mermaidBuilder) string { bodyID := b.node("rect", "Body") b.edge(id, bodyID) for i, expr := range rule.Body { - exprID := mermaidFormatExpr(expr, i, b) + exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(bodyID, exprID, strconv.Itoa(i)) } } @@ -188,7 +188,7 @@ func mermaidFormatHead(head *Head, b *mermaidBuilder) string { return id } -func mermaidFormatExpr(expr *Expr, index int, b *mermaidBuilder) string { +func mermaidFormatExpr(expr *Expr, b *mermaidBuilder) string { label := expr.String() id := b.node("hex", label) @@ -245,8 +245,8 @@ func mermaidFormatEvery(every *Every, b *mermaidBuilder) string { b.edgeLabeled(id, domainID, "domain") bodyID := b.node("rect", "Body") b.edge(id, bodyID) - for i, expr := range every.Body { - exprID := mermaidFormatExpr(expr, i, b) + for _, expr := range every.Body { + exprID := mermaidFormatExpr(expr, b) b.edge(bodyID, exprID) } return id @@ -256,14 +256,14 @@ func mermaidFormatLogical(op string, lhs, rhs Body, b *mermaidBuilder) string { id := b.node("rect", op) lhsID := b.node("rect", "Lhs") b.edge(id, lhsID) - for i, expr := range lhs { - exprID := mermaidFormatExpr(expr, i, b) + for _, expr := range lhs { + exprID := mermaidFormatExpr(expr, b) b.edge(lhsID, exprID) } rhsID := b.node("rect", "Rhs") b.edge(id, rhsID) - for i, expr := range rhs { - exprID := mermaidFormatExpr(expr, i, b) + for _, expr := range rhs { + exprID := mermaidFormatExpr(expr, b) b.edge(rhsID, exprID) } return id @@ -283,7 +283,7 @@ func mermaidFormatWith(w *With, b *mermaidBuilder) string { func (not *Not) mermaidFormat(b *mermaidBuilder) string { id := b.node("stadium", "not") for i, expr := range not.Body { - exprID := mermaidFormatExpr(expr, i, b) + exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(id, exprID, strconv.Itoa(i)) } return id @@ -381,7 +381,7 @@ func (ac *ArrayComprehension) mermaidFormat(b *mermaidBuilder) string { bodyID := b.node("rect", "Body") b.edge(id, bodyID) for i, expr := range ac.Body { - exprID := mermaidFormatExpr(expr, i, b) + exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(bodyID, exprID, strconv.Itoa(i)) } return id @@ -396,7 +396,7 @@ func (oc *ObjectComprehension) mermaidFormat(b *mermaidBuilder) string { bodyID := b.node("rect", "Body") b.edge(id, bodyID) for i, expr := range oc.Body { - exprID := mermaidFormatExpr(expr, i, b) + exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(bodyID, exprID, strconv.Itoa(i)) } return id @@ -409,7 +409,7 @@ func (sc *SetComprehension) mermaidFormat(b *mermaidBuilder) string { bodyID := b.node("rect", "Body") b.edge(id, bodyID) for i, expr := range sc.Body { - exprID := mermaidFormatExpr(expr, i, b) + exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(bodyID, exprID, strconv.Itoa(i)) } return id diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go index 8af16314b9..3ecf654a54 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go @@ -764,15 +764,15 @@ func (p *Parser) parseImport() *Import { t := r[0] name := string(t.Value.(Var)) if IsKeywordInRegoVersion(name, p.po.EffectiveRegoVersion()) { - p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) p.hint("import a different path or use an alias") + p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) } } else if !FutureRootDocument.Equal(r[0]) { t := r[len(r)-1] name := string(t.Value.(String)) if IsKeywordInRegoVersion(name, p.po.EffectiveRegoVersion()) { - p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) p.hint("import a different path or use an alias") + p.errorf(t.Location, "unexpected import path, must not end with a keyword, got: %s", name) } } @@ -1246,17 +1246,25 @@ func (p *Parser) parseLiteral() (expr *Expr) { if outer == nil { return nil } - if p.s.tok == tokens.With { - if outer.With = p.parseWith(); outer.With == nil { - return nil - } - } - return outer + return p.attachWith(outer) } } p.restore(s) } + // LHS/whole parenthesized group at statement start: `(a or b)`, + // `(a or b) and c`, or `({a}) and c`. parseLogicalGroup only commits when the + // parens hold or precede an and/or; otherwise (`({})`, `({a})`, `(a == b)`) it + // restores and we fall through so parseExpr handles the term. + if p.s.tok == tokens.LParen && p.logicalKeywordsActive() { + if body, explicit, loc, committed := p.parseLogicalGroup(false); committed { + if body == nil { + return nil + } + return p.foldLogicalTail(body, explicit, loc) + } + } + // Check that we're not parsing a ref if p.isAllowedRefKeyword(p.s.tok) { // Scan ahead @@ -1280,14 +1288,10 @@ func (p *Parser) parseLiteral() (expr *Expr) { if negated && p.notBodies && p.s.tok == tokens.LBrace { nb := p.parseNotBody(notLoc) - - if nb != nil && p.s.tok == tokens.With { - if nb.With = p.parseWith(); nb.With == nil { - return nil - } + if nb == nil { + return nil } - - return nb + return p.attachWith(nb) } switch p.s.tok { @@ -1324,6 +1328,22 @@ func (p *Parser) parseLiteralExpr(negated bool, notLoc *Location) *Expr { startOffset := p.s.loc.Offset startLoc := p.s.Loc() s := p.save() + + // Negated parenthesized group: `not (a or b)`. The parens are an operand of + // `not`, so any `{...}` inside is a body. + if negated && p.notBodies && p.s.tok == tokens.LParen && p.logicalKeywordsActive() { + if body, explicit, _, committed := p.parseLogicalGroup(true); committed { + if body == nil { + return nil + } + + spanned := p.extendLoc(notLoc) + not := NewExpr(&Not{Body: body, ExplicitBody: explicit, Location: spanned}).SetLocation(spanned) + + return p.foldLogicalTail(NewBody(not), false, spanned) + } + } + expr := p.parseExpr() if expr != nil { var withLoc *Location @@ -1370,10 +1390,7 @@ func (p *Parser) parseLiteralExpr(negated bool, notLoc *Location) *Expr { if p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { if withLoc != nil { - kw := p.s.tok.String() - p.errorf(withLoc, - "`with` modifier is not allowed on operand of `%s`; wrap the operand in `{...}` to scope, or move `with` after the %s expression to apply it to the whole expression", - kw, kw) + p.errWithOnOperand(withLoc, p.s.tok.String()) return nil } @@ -1386,12 +1403,7 @@ func (p *Parser) parseLiteralExpr(negated bool, notLoc *Location) *Expr { if outer == nil { return nil } - if p.s.tok == tokens.With { - if outer.With = p.parseWith(); outer.With == nil { - return nil - } - } - return outer + return p.attachWith(outer) } } return expr @@ -1445,6 +1457,35 @@ func (p *Parser) parseWith() []*With { return withs } +func (p *Parser) attachWith(e *Expr) *Expr { + if e != nil && p.s.tok == tokens.With { + if e.With = p.parseWith(); e.With == nil { + return nil + } + } + return e +} + +func (p *Parser) errWithOnOperand(loc *Location, kw string) { + p.hint(fmt.Sprintf( + "Wrap the operand in `(...)` or `{...}` to scope, or move `with` after the `%s` expression to apply it to the whole expression", + kw)) + p.errorf(loc, + "`with` modifier is not allowed on operand of `%s`", + kw) +} + +func (p *Parser) foldLogicalTail(body Body, explicit bool, loc *Location) *Expr { + if p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { + outer := p.parseLogicalOrChain(body, explicit, loc) + if outer == nil { + return nil + } + return p.attachWith(outer) + } + return p.attachWith(body[0]) +} + func (p *Parser) parseSome() *Expr { decl := &SomeDecl{} @@ -1692,6 +1733,26 @@ func (p *Parser) parseLogicalOperand() (Body, bool, *Location) { return NewBody(nb), false, nb.Location } + // Parenthesized logical group operand: `(a or b)` or, when negated, + // `not (a or b)`. This is an operand of and/or/not, so a `{...}` inside is a + // body. If the parens don't hold a logical group parseLogicalGroup restores + // state and we fall through so parseExpr can handle `(a == b)` as a term. + if p.s.tok == tokens.LParen && p.logicalKeywordsActive() && (!negated || p.notBodies) { + if body, explicit, loc, committed := p.parseLogicalGroup(true); committed { + if body == nil { + return nil, false, nil + } + + if negated { + spanned := p.extendLoc(notLoc) + not := NewExpr(&Not{Body: body, ExplicitBody: explicit, Location: spanned}).SetLocation(spanned) + return NewBody(not), false, spanned + } + + return body, explicit, loc + } + } + startOffset := p.s.loc.Offset startLoc := p.s.Loc() expr := p.parseExpr() @@ -1716,6 +1777,161 @@ func (p *Parser) parseLogicalOperand() (Body, bool, *Location) { return NewBody(expr), false, expr.Location } +// isLogicalBody reports whether b is a single-expression body wrapping a +// LogicalAnd/LogicalOr node, i.e. the result of a parenthesized or nested group. +func isLogicalBody(b Body) bool { + if len(b) != 1 { + return false + } + switch b[0].Terms.(type) { + case *LogicalAnd, *LogicalOr: + return true + } + return false +} + +// isNegatedOperand reports whether b is a single negated operand, e.g. `not a` +// (either a *Not node or an expression with Negated set). +func isNegatedOperand(b Body) bool { + if len(b) != 1 { + return false + } + + if b[0].Negated { + return true + } + + _, ok := b[0].Terms.(*Not) + return ok +} + +// expectRParen consumes the closing `)` of a group, reporting an error if the +// current token is not `)`. +func (p *Parser) expectRParen() bool { + if p.s.tok != tokens.RParen { + p.error(p.s.Loc(), "expected ) to close parenthesized group") + return false + } + p.scan() + return true +} + +// parseLogicalGroup attempts to parse a parenthesized grouping of `and`/`or`/`not` +// operands starting at the current `(`. +// +// operandContext reports whether the `(` is already an operand of `and`/`or`/`not`. +func (p *Parser) parseLogicalGroup(operandContext bool) (Body, bool, *Location, bool) { + if !p.enter() { + return nil, false, nil, true + } + defer p.leave() + + s := p.save() + openLoc := p.s.Loc() + p.scan() // consume `(` + + if p.s.tok == tokens.RParen { + if operandContext { + p.error(openLoc, "empty parenthesized group") + return nil, false, nil, true + } + p.restore(s) + return nil, false, nil, false + } + + // A leading `{` is a body only in an operand context; otherwise it's an + // object/set literal and we backtrack to the term parser. + braceLead := p.s.tok == tokens.LBrace + + lhsBody, lhsExplicit, lhsLoc := p.parseLogicalOperand() + if lhsBody == nil { + // An empty `{}` operand (e.g. `not ({})`) is a body error. + if operandContext && braceLead { + return nil, false, nil, true + } + + p.restore(s) + + return nil, false, nil, false + } + + switch { + case p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr: + expr := p.parseLogicalOrChain(lhsBody, lhsExplicit, lhsLoc) + if expr == nil { + return nil, false, nil, true + } + + // A trailing `with` binds to the whole group, e.g. `(a and b with x)`. + if expr = p.attachWith(expr); expr == nil { + return nil, false, nil, true + } + + if !p.expectRParen() { + return nil, false, nil, true + } + + return NewBody(expr), false, p.extendLoc(openLoc), true + + case p.s.tok == tokens.With && !lhsExplicit && len(lhsBody) == 1: + // Single-operand group carrying a `with`, e.g. `(a with x)`; the `with` + // binds to the sole operand. + withLoc := p.s.Loc() + if p.attachWith(lhsBody[0]) == nil { + return nil, false, nil, true + } + + // A `with` on the operand followed by `and`/`or` is ambiguous. + if p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { + p.errWithOnOperand(withLoc, p.s.tok.String()) + return nil, false, nil, true + } + + if !p.expectRParen() { + return nil, false, nil, true + } + + if operandContext || p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { + return lhsBody, false, p.extendLoc(openLoc), true + } + + p.restore(s) + return nil, false, nil, false + + case lhsExplicit: + // `({ body })` + if !p.expectRParen() { + return nil, false, nil, true + } + + if operandContext || p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { + return lhsBody, true, p.extendLoc(openLoc), true + } + + p.restore(s) + return nil, false, nil, false + + case isLogicalBody(lhsBody): + // `(( ... ))`; redundant parens around a nested group. + if !p.expectRParen() { + return nil, false, nil, true + } + return lhsBody, false, p.extendLoc(openLoc), true + + case isNegatedOperand(lhsBody): + // `(not ...)` + if !p.expectRParen() { + return nil, false, nil, true + } + return lhsBody, false, p.extendLoc(openLoc), true + + default: + // Single non-logical operand, e.g. `(a == b)`: not a group. + p.restore(s) + return nil, false, nil, false + } +} + func (p *Parser) parseEvery() *Expr { qb := &Every{} qb.SetLoc(p.s.Loc()) @@ -3081,6 +3297,16 @@ func (b *metadataParser) Append(c *Comment) { var yamlLineErrRegex = regexp.MustCompile(`^yaml:(?: unmarshal errors:[\n\s]*)? line ([[:digit:]]+):`) +// endLoc returns the location of the last comment in the METADATA block, or nil +// if there are none. Only this location is retained on Annotations (for +// EndLoc), so the comment slice itself is never aliased onto the result. +func endLoc(comments []*Comment) *location.Location { + if len(comments) == 0 { + return nil + } + return comments[len(comments)-1].Location +} + func (b *metadataParser) Parse() (result *Annotations, err error) { if len(bytes.TrimSpace(b.buf.Bytes())) == 0 { return nil, errors.New("expected METADATA block, found whitespace") @@ -3091,8 +3317,7 @@ func (b *metadataParser) Parse() (result *Annotations, err error) { var comment *Comment match := yamlLineErrRegex.FindStringSubmatch(err.Error()) if len(match) == 2 { - index, err2 := strconv.Atoi(match[1]) - if err2 == nil { + if index, ok := util.Atoi(match[1]); ok { if index >= len(b.comments) { comment = b.comments[len(b.comments)-1] } else { @@ -3110,7 +3335,11 @@ func (b *metadataParser) Parse() (result *Annotations, err error) { } result = &Annotations{ - comments: b.comments, + // NOTE: only the last comment's location is retained (as endLoc); the + // comment slice itself is backed by a reused buffer (the metadataParser + // is pooled and Reset truncates rather than reallocates), so it must not + // be aliased here. + endLoc: endLoc(b.comments), Scope: raw.Scope, Entrypoint: raw.Entrypoint, Title: raw.Title, @@ -3173,7 +3402,7 @@ func (b *metadataParser) Parse() (result *Annotations, err error) { switch v := v.(type) { case string: - a.Schema, err = parseSchemaRef(v) + a.Schema, err = ParseSchemaRef(v) if err != nil { return nil, err } @@ -3276,10 +3505,14 @@ func unwrapPair(pair map[string]any) (string, any) { var errInvalidSchemaRef = errors.New("invalid schema reference") +// ParseSchemaRef parses a schema reference string into a Ref. Unlike +// ParseRef, it accepts the bare `schema` Var and Refs prefixed with the +// schema root document. +// // NOTE(tsandall): 'schema' is not registered as a root because it's not // supported by the compiler or evaluator today. Once we fix that, we can remove // this function. -func parseSchemaRef(s string) (Ref, error) { +func ParseSchemaRef(s string) (Ref, error) { term, err := ParseTerm(s) if err == nil { @@ -3507,9 +3740,9 @@ func (p *Parser) futureImport(imp *Import, allowedFutureKeywords map[string]toke if keyword == "not" { p.notBodies = true - } else { - kwds = []string{keyword} // overwrite } + + kwds = []string{keyword} // overwrite } for _, kw := range kwds { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go index f3bb43640f..c5592d99b8 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go @@ -6,7 +6,6 @@ package ast import ( "bytes" - "encoding/json" "fmt" "slices" "strings" @@ -273,6 +272,11 @@ type ( generatedFrom *Expr generates []*Expr + + // fromAssignment marks an equality expression that was rewritten from + // `:=`, so the safety checker can keep the RHS from being made safe + // through the LHS. See reorderBodyForSafety. + fromAssignment bool } // SomeDecl represents a variable declaration statement. The symbols are variables. @@ -397,33 +401,13 @@ func (mod *Module) String() string { func (mod *Module) RuleSet(name Var) RuleSet { rs := NewRuleSet() for _, rule := range mod.Rules { - if rule.Head.Name.Equal(name) { + if rule.Head.Name == name { rs.Add(rule) } } return rs } -// UnmarshalJSON parses bs and stores the result in mod. The rules in the module -// will have their module pointer set to mod. -func (mod *Module) UnmarshalJSON(bs []byte) error { - - // Declare a new type and use a type conversion to avoid recursively calling - // Module#UnmarshalJSON. - type module Module - - if err := util.UnmarshalJSON(bs, (*module)(mod)); err != nil { - return err - } - - WalkRules(mod, func(rule *Rule) bool { - rule.Module = mod - return false - }) - - return nil -} - func (mod *Module) regoV1Compatible() bool { return mod.regoVersion == RegoV1 || mod.regoVersion == RegoV0CompatV1 } @@ -514,20 +498,6 @@ func (pkg *Package) String() string { return util.ByteSliceToString(buf) } -func (pkg *Package) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "path": pkg.Path, - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Package { - if pkg.Location != nil { - data["location"] = pkg.Location - } - } - - return json.Marshal(data) -} - // IsValidImportPath returns an error indicating if the import path is invalid. // If the import path is valid, err is nil. func IsValidImportPath(v Value) (err error) { @@ -618,24 +588,6 @@ func (imp *Import) String() string { return util.ByteSliceToString(buf) } -func (imp *Import) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "path": imp.Path, - } - - if len(imp.Alias) != 0 { - data["alias"] = imp.Alias - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import { - if imp.Location != nil { - data["location"] = imp.Location - } - } - - return json.Marshal(data) -} - // Compare returns an integer indicating whether rule is less than, equal to, // or greater than other. func (rule *Rule) Compare(other *Rule) int { @@ -749,42 +701,6 @@ func (rule *Rule) isFunction() bool { return len(rule.Head.Args) > 0 } -// ruleJSON is used for JSON serialization of Rule to avoid map allocation overhead. -// Field order is alphabetical to match previous map-based output. -type ruleJSON struct { - Annotations []*Annotations `json:"annotations,omitempty"` - Body Body `json:"body"` - Default bool `json:"default,omitempty"` - Else *Rule `json:"else,omitempty"` - Head *Head `json:"head"` - Location *Location `json:"location,omitempty"` -} - -func (rule *Rule) MarshalJSON() ([]byte, error) { - data := ruleJSON{ - Head: rule.Head, - Body: rule.Body, - } - - if rule.Default { - data.Default = true - } - - if rule.Else != nil { - data.Else = rule.Else - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule { - data.Location = rule.Location - } - - if len(rule.Annotations) != 0 { - data.Annotations = rule.Annotations - } - - return json.Marshal(data) -} - // NewHead returns a new Head object. If args are provided, the first will be // used for the key and the second will be used for the value. func NewHead(name Var, args ...*Term) *Head { @@ -947,27 +863,6 @@ func (head *Head) stringWithOpts(opts toStringOpts) string { return util.ByteSliceToString(buf) } -func (head *Head) MarshalJSON() ([]byte, error) { - var loc *Location - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && head.Location != nil { - loc = head.Location - } - - // NOTE(sr): we do this to override the rendering of `head.Reference`. - // It's still what'll be used via the default means of encoding/json - // for unmarshaling a json object into a Head struct! - type h Head - return json.Marshal(struct { - h - Ref Ref `json:"ref"` - Location *Location `json:"location,omitempty"` - }{ - h: h(*head), - Ref: head.Ref(), - Location: loc, - }) -} - // Vars returns a set of vars found in the head. func (head *Head) Vars() VarSet { vis := NewVarVisitor() @@ -1046,17 +941,6 @@ func NewBody(exprs ...*Expr) Body { return Body(exprs) } -// MarshalJSON returns JSON encoded bytes representing body. -func (body Body) MarshalJSON() ([]byte, error) { - // Serialize empty Body to empty array. This handles both the empty case and the - // nil case (whereas by default the result would be null if body was nil.) - if len(body) == 0 { - return []byte(`[]`), nil - } - ret, err := json.Marshal([]*Expr(body)) - return ret, err -} - // Append adds the expr to the body and updates the expr's index accordingly. func (body *Body) Append(expr *Expr) { n := len(*body) @@ -1076,19 +960,7 @@ func (body Body) Set(expr *Expr, pos int) { // // If body is a subset of other, it is considered less than (and vice versa). func (body Body) Compare(other Body) int { - minLen := min(len(other), len(body)) - for i := range minLen { - if cmp := body[i].Compare(other[i]); cmp != 0 { - return cmp - } - } - if len(body) < len(other) { - return -1 - } - if len(other) < len(body) { - return 1 - } - return 0 + return slices.CompareFunc(body, other, (*Expr).Compare) } // Copy returns a deep copy of body. @@ -1149,10 +1021,6 @@ func (body Body) String() string { return util.ByteSliceToString(buf) } -func (body Body) AppendText(buf []byte) ([]byte, error) { - return AppendDelimeted(buf, body, "; ") -} - // Vars returns a VarSet containing variables in body. The params can be set to // control which vars are included. func (body Body) Vars(params VarVisitorParams) VarSet { @@ -1528,51 +1396,6 @@ func (expr *Expr) String() string { return util.ByteSliceToString(buf) } -// exprJSON is used for JSON serialization of Expr to avoid map allocation overhead. -// Field order is alphabetical to match previous map-based output. -type exprJSON struct { - Generated bool `json:"generated,omitempty"` - Index int `json:"index"` - Location *Location `json:"location,omitempty"` - Negated bool `json:"negated,omitempty"` - Terms any `json:"terms"` - With []*With `json:"with,omitempty"` -} - -func (expr *Expr) MarshalJSON() ([]byte, error) { - data := exprJSON{ - Index: expr.Index, - Terms: expr.Terms, - } - - if len(expr.With) > 0 { - data.With = expr.With - } - - if expr.Generated { - data.Generated = true - } - - if expr.Negated { - data.Negated = true - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Expr { - data.Location = expr.Location - } - - return json.Marshal(data) -} - -// UnmarshalJSON parses the byte array and stores the result in expr. -func (expr *Expr) UnmarshalJSON(bs []byte) error { - v := map[string]any{} - if err := util.UnmarshalJSON(bs, &v); err != nil { - return err - } - return unmarshalExpr(expr, v) -} - // Vars returns a VarSet containing variables in expr. The params can be set to // control which vars are included. func (expr *Expr) Vars(params VarVisitorParams) VarSet { @@ -1589,6 +1412,7 @@ func NewBuiltinExpr(terms ...*Term) *Expr { func (expr *Expr) CogeneratedExprs() []*Expr { visited := map[*Expr]struct{}{} + var result []*Expr visitCogeneratedExprs(expr, func(e *Expr) bool { if expr.Equal(e) { return true @@ -1597,13 +1421,13 @@ func (expr *Expr) CogeneratedExprs() []*Expr { return true } visited[e] = struct{}{} + // Append during visitation so the result order is deterministic; iterating + // the 'visited' map here would randomize the order and, in turn, make + // dependent output (e.g. PrettyEvent's --var-values) nondeterministic. + result = append(result, e) return false }) - result := make([]*Expr, 0, len(visited)) - for e := range visited { - result = append(result, e) - } return result } @@ -1660,20 +1484,6 @@ func (d *SomeDecl) Hash() int { return termSliceHash(d.Symbols) } -func (d *SomeDecl) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "symbols": d.Symbols, - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.SomeDecl { - if d.Location != nil { - data["location"] = d.Location - } - } - - return json.Marshal(data) -} - func (q *Every) String() string { if q.Key != nil { return fmt.Sprintf("every %s, %s in %s { %s }", @@ -1730,23 +1540,6 @@ func (q *Every) KeyValueVars() VarSet { return vis.vars } -func (q *Every) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "key": q.Key, - "value": q.Value, - "domain": q.Domain, - "body": q.Body, - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Every { - if q.Location != nil { - data["location"] = q.Location - } - } - - return json.Marshal(data) -} - func (a *LogicalAnd) String() string { return formatBinaryLogical("and", a.Lhs, a.Rhs, a.ExplicitLhs, a.ExplicitRhs) } @@ -1780,36 +1573,6 @@ func (a *LogicalAnd) Hash() int { return a.Lhs.Hash() + a.Rhs.Hash() } -func (a *LogicalAnd) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "type": "and", - "lhs": a.Lhs, - "rhs": a.Rhs, - } - if a.ExplicitLhs { - data["explicit_lhs"] = true - } - if a.ExplicitRhs { - data["explicit_rhs"] = true - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.And { - if a.Location != nil { - data["location"] = a.Location - } - } - - return json.Marshal(data) -} - -func (a *LogicalAnd) UnmarshalJSON(bs []byte) error { - v := map[string]any{} - if err := util.UnmarshalJSON(bs, &v); err != nil { - return err - } - return unmarshalLogical("and", &a.Lhs, &a.Rhs, &a.ExplicitLhs, &a.ExplicitRhs, v) -} - func (o *LogicalOr) String() string { return formatBinaryLogical("or", o.Lhs, o.Rhs, o.ExplicitLhs, o.ExplicitRhs) } @@ -1843,84 +1606,62 @@ func (o *LogicalOr) Hash() int { return o.Lhs.Hash() + o.Rhs.Hash() } -func (o *LogicalOr) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "type": "or", - "lhs": o.Lhs, - "rhs": o.Rhs, - } - if o.ExplicitLhs { - data["explicit_lhs"] = true - } - if o.ExplicitRhs { - data["explicit_rhs"] = true +func formatBinaryLogical(op string, lhs, rhs Body, explicitLhs, explicitRhs bool) string { + return formatLogicalOperand(lhs, explicitLhs, op, false) + " " + op + " " + formatLogicalOperand(rhs, explicitRhs, op, true) +} + +func formatLogicalOperand(b Body, explicit bool, parentOp string, rhs bool) string { + if explicit || len(b) != 1 { + return "{ " + b.String() + " }" } - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Or { - if o.Location != nil { - data["location"] = o.Location - } + if logicalOperandNeedsParens(b, parentOp, rhs) { + return "(" + b.String() + ")" } - return json.Marshal(data) + return b.String() } -func (o *LogicalOr) UnmarshalJSON(bs []byte) error { - v := map[string]any{} - if err := util.UnmarshalJSON(bs, &v); err != nil { - return err +func logicalOperandNeedsParens(b Body, parentOp string, rhs bool) bool { + if len(b) != 1 { + return false } - return unmarshalLogical("or", &o.Lhs, &o.Rhs, &o.ExplicitLhs, &o.ExplicitRhs, v) -} -func unmarshalLogical(typeName string, lhs, rhs *Body, explicitLhs, explicitRhs *bool, v map[string]any) error { - lhsRaw, ok := v["lhs"].([]any) - if !ok { - return fmt.Errorf("ast: unable to unmarshal %s, invalid lhs field type: %T (expected list)", typeName, v["lhs"]) - } - l, err := unmarshalBody(lhsRaw) - if err != nil { - return fmt.Errorf("ast: unable to unmarshal %s lhs: %w", typeName, err) + e := b[0] + if len(e.With) > 0 { + return true } - *lhs = l - rhsRaw, ok := v["rhs"].([]any) - if !ok { - return fmt.Errorf("ast: unable to unmarshal %s, invalid rhs field type: %T (expected list)", typeName, v["rhs"]) - } - r, err := unmarshalBody(rhsRaw) - if err != nil { - return fmt.Errorf("ast: unable to unmarshal %s rhs: %w", typeName, err) + switch e.Terms.(type) { + case *LogicalOr: + // `or` binds looser than `and`: always parenthesize under `and`; under + // `or`, parenthesize only the rhs to preserve right-nesting. + return parentOp == "and" || rhs + case *LogicalAnd: + // `and` binds tighter: no parens under `or`; under `and`, parenthesize + // only the rhs to preserve right-nesting. + return parentOp == "and" && rhs } - *rhs = r + return false +} - if x, ok := v["explicit_lhs"]; ok { - b, ok := x.(bool) - if !ok { - return fmt.Errorf("ast: unable to unmarshal %s explicit_lhs field with type: %T (expected true or false)", typeName, x) - } - *explicitLhs = b - } - if x, ok := v["explicit_rhs"]; ok { - b, ok := x.(bool) - if !ok { - return fmt.Errorf("ast: unable to unmarshal %s explicit_rhs field with type: %T (expected true or false)", typeName, x) - } - *explicitRhs = b +func notBodyNeedsParens(b Body) bool { + if len(b) != 1 { + return false } - return nil -} - -func formatBinaryLogical(op string, lhs, rhs Body, explicitLhs, explicitRhs bool) string { - return formatLogicalOperand(lhs, explicitLhs) + " " + op + " " + formatLogicalOperand(rhs, explicitRhs) -} + e := b[0] + if len(e.With) > 0 { + return true + } -func formatLogicalOperand(b Body, explicit bool) string { - if explicit { - return "{ " + b.String() + " }" + switch e.Terms.(type) { + case *LogicalOr, *LogicalAnd: + // `not` binds tighter than `and`/`or` + return true } - return b.String() + + return false } func (w *With) String() string { @@ -1982,27 +1723,6 @@ func (w *With) SetLoc(loc *Location) { w.Location = loc } -// withJSON is used for JSON serialization of With to avoid map allocation overhead. -// Field order is alphabetical to match previous map-based output. -type withJSON struct { - Location *Location `json:"location,omitempty"` - Target *Term `json:"target"` - Value *Term `json:"value"` -} - -func (w *With) MarshalJSON() ([]byte, error) { - data := withJSON{ - Target: w.Target, - Value: w.Value, - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.With { - data.Location = w.Location - } - - return json.Marshal(data) -} - // Copy returns a deep copy of the AST node x. If x is not an AST node, x is returned unmodified. func Copy(x any) any { switch x := x.(type) { @@ -2136,14 +1856,6 @@ func isGlobalBuiltin(expr *Expr, name Var) bool { return false } - // NOTE(tsandall): do not use Term#Equal or Value#Compare to avoid - // allocation here. ref, ok := terms[0].Value.(Ref) - if !ok || len(ref) != 1 { - return false - } - if head, ok := ref[0].Value.(Var); ok { - return head.Equal(name) - } - return false + return ok && len(ref) == 1 && name.Equal(ref[0].Value) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go index cead974b16..63b260e0a5 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go @@ -223,6 +223,10 @@ func (a Args) AppendText(buf []byte) ([]byte, error) { return append(buf, ')'), nil } +func (body Body) AppendText(buf []byte) ([]byte, error) { + return AppendDelimeted(buf, body, "; ") +} + func (expr *Expr) AppendText(buf []byte) ([]byte, error) { if expr.Negated { buf = append(buf, "not "...) @@ -335,17 +339,25 @@ func (o *LogicalOr) AppendText(buf []byte) ([]byte, error) { func appendLogical(buf []byte, op string, lhs, rhs Body, explicitLhs, explicitRhs bool) ([]byte, error) { var err error - if buf, err = appendLogicalOperand(buf, lhs, explicitLhs); err != nil { + if buf, err = appendLogicalOperand(buf, lhs, explicitLhs, op, false); err != nil { return nil, err } buf = append(buf, ' ') buf = append(buf, op...) buf = append(buf, ' ') - return appendLogicalOperand(buf, rhs, explicitRhs) + return appendLogicalOperand(buf, rhs, explicitRhs, op, true) } -func appendLogicalOperand(buf []byte, b Body, explicit bool) ([]byte, error) { +func appendLogicalOperand(buf []byte, b Body, explicit bool, parentOp string, rhs bool) ([]byte, error) { if !explicit && len(b) == 1 { + if logicalOperandNeedsParens(b, parentOp, rhs) { + buf = append(buf, '(') + var err error + if buf, err = b.AppendText(buf); err != nil { + return nil, err + } + return append(buf, ')'), nil + } return b.AppendText(buf) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_json.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_json.go new file mode 100644 index 0000000000..b63c035949 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_json.go @@ -0,0 +1,289 @@ +//go:build !go1.27 + +package ast + +import ( + "encoding/json" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +// ruleJSON is used for JSON serialization of Rule to avoid map allocation overhead. +// Field order is alphabetical to match previous map-based output. +type ruleJSON struct { + Annotations []*Annotations `json:"annotations,omitempty"` + Body Body `json:"body"` + Default bool `json:"default,omitempty"` + Else *Rule `json:"else,omitempty"` + Head *Head `json:"head"` + Location *Location `json:"location,omitempty"` +} + +// exprJSON is used for JSON serialization of Expr to avoid map allocation overhead. +// Field order is alphabetical to match previous map-based output. +type exprJSON struct { + Generated bool `json:"generated,omitempty"` + Index int `json:"index"` + Location *Location `json:"location,omitempty"` + Negated bool `json:"negated,omitempty"` + Terms any `json:"terms"` + With []*With `json:"with,omitempty"` +} + +// withJSON is used for JSON serialization of With to avoid map allocation overhead. +// Field order is alphabetical to match previous map-based output. +type withJSON struct { + Location *Location `json:"location,omitempty"` + Target *Term `json:"target"` + Value *Term `json:"value"` +} + +// UnmarshalJSON parses bs and stores the result in mod. The rules in the module +// will have their module pointer set to mod. +func (mod *Module) UnmarshalJSON(bs []byte) error { + + // Declare a new type and use a type conversion to avoid recursively calling + // Module#UnmarshalJSON. + type module Module + + if err := util.UnmarshalJSON(bs, (*module)(mod)); err != nil { + return err + } + + // The decoded rules have no module pointer, as it isn't part of the JSON + // representation; without this, an unmarshalled module can't be compiled. + WalkRules(mod, func(rule *Rule) bool { + rule.Module = mod + return false + }) + + return nil +} + +func (d *SomeDecl) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "symbols": d.Symbols, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.SomeDecl { + if d.Location != nil { + data["location"] = d.Location + } + } + + return json.Marshal(data) +} + +func (q *Every) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "key": q.Key, + "value": q.Value, + "domain": q.Domain, + "body": q.Body, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Every { + if q.Location != nil { + data["location"] = q.Location + } + } + + return json.Marshal(data) +} + +func (a *LogicalAnd) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "type": "and", + "lhs": a.Lhs, + "rhs": a.Rhs, + } + if a.ExplicitLhs { + data["explicit_lhs"] = true + } + if a.ExplicitRhs { + data["explicit_rhs"] = true + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.And { + if a.Location != nil { + data["location"] = a.Location + } + } + + return json.Marshal(data) +} + +func (a *LogicalAnd) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalLogical("and", &a.Lhs, &a.Rhs, &a.ExplicitLhs, &a.ExplicitRhs, v) +} + +func (o *LogicalOr) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "type": "or", + "lhs": o.Lhs, + "rhs": o.Rhs, + } + if o.ExplicitLhs { + data["explicit_lhs"] = true + } + if o.ExplicitRhs { + data["explicit_rhs"] = true + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Or { + if o.Location != nil { + data["location"] = o.Location + } + } + + return json.Marshal(data) +} + +func (o *LogicalOr) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalLogical("or", &o.Lhs, &o.Rhs, &o.ExplicitLhs, &o.ExplicitRhs, v) +} + +// UnmarshalJSON parses the byte array and stores the result in expr. +func (expr *Expr) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalExpr(expr, v) +} + +func (expr *Expr) MarshalJSON() ([]byte, error) { + data := exprJSON{ + Index: expr.Index, + Terms: expr.Terms, + } + + if len(expr.With) > 0 { + data.With = expr.With + } + + if expr.Generated { + data.Generated = true + } + + if expr.Negated { + data.Negated = true + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Expr { + data.Location = expr.Location + } + + return json.Marshal(data) +} + +func (w *With) MarshalJSON() ([]byte, error) { + data := withJSON{ + Target: w.Target, + Value: w.Value, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.With { + data.Location = w.Location + } + + return json.Marshal(data) +} + +func (pkg *Package) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": pkg.Path, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Package { + if pkg.Location != nil { + data["location"] = pkg.Location + } + } + + return json.Marshal(data) +} + +func (imp *Import) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "path": imp.Path, + } + + if len(imp.Alias) != 0 { + data["alias"] = imp.Alias + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import { + if imp.Location != nil { + data["location"] = imp.Location + } + } + + return json.Marshal(data) +} + +func (rule *Rule) MarshalJSON() ([]byte, error) { + data := ruleJSON{ + Head: rule.Head, + Body: rule.Body, + } + + if rule.Default { + data.Default = true + } + + if rule.Else != nil { + data.Else = rule.Else + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule { + data.Location = rule.Location + } + + if len(rule.Annotations) != 0 { + data.Annotations = rule.Annotations + } + + return json.Marshal(data) +} + +func (head *Head) MarshalJSON() ([]byte, error) { + var loc *Location + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && head.Location != nil { + loc = head.Location + } + + // NOTE(sr): we do this to override the rendering of `head.Reference`. + // It's still what'll be used via the default means of encoding/json + // for unmarshaling a json object into a Head struct! + type h Head + return json.Marshal(struct { + h + Ref Ref `json:"ref"` + Location *Location `json:"location,omitempty"` + }{ + h: h(*head), + Ref: head.Ref(), + Location: loc, + }) +} + +// MarshalJSON returns JSON encoded bytes representing body. +func (body Body) MarshalJSON() ([]byte, error) { + // Serialize empty Body to empty array. This handles both the empty case and the + // nil case (whereas by default the result would be null if body was nil.) + if len(body) == 0 { + return []byte(`[]`), nil + } + ret, err := json.Marshal([]*Expr(body)) + return ret, err +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go new file mode 100644 index 0000000000..faf1b55df7 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go @@ -0,0 +1,524 @@ +//go:build go1.27 + +package ast + +import ( + "encoding/base64" + "encoding/json/jsontext" + "encoding/json/v2" + "fmt" + + "github.com/open-policy-agent/opa/internal/jsonv2" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +var ( + _ json.Unmarshaler = &Module{} + + // These are exported types, so losing MarshalJSON here would be a breaking + // API change even though callers should go through json.Marshal, not this + // method directly. + _ json.Marshaler = Body{} + _ json.Marshaler = &Expr{} + _ json.Marshaler = &Package{} + _ json.Marshaler = &Import{} + _ json.Marshaler = &Rule{} + _ json.Marshaler = &Head{} + _ json.Marshaler = &With{} + _ json.Marshaler = &SomeDecl{} + _ json.Marshaler = &Every{} + _ json.Marshaler = &LogicalAnd{} + _ json.Marshaler = &LogicalOr{} +) + +// UnmarshalJSON parses bs and stores the result in mod. The rules in the module +// will have their module pointer set to mod. +func (mod *Module) UnmarshalJSON(bs []byte) error { + + // Declare a new type and use a type conversion to avoid recursively calling + // Module#UnmarshalJSON. + type module Module + + if err := util.UnmarshalJSON(bs, (*module)(mod)); err != nil { + return err + } + + // The decoded rules have no module pointer, as it isn't part of the JSON + // representation; without this, an unmarshalled module can't be compiled. + WalkRules(mod, func(rule *Rule) bool { + rule.Module = mod + return false + }) + + return nil +} + +// MarshalJSONTo is here to ensure that we do not fall down to TextAppender, +// which Go 1.27's encoding/json would otherwise use, encoding args as the Rego +// representation of the argument list rather than as a JSON array. +func (a Args) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArrayOrNull(e, a) +} + +// MarshalJSONTo is here to ensure that we do not fall down to TextAppender, +// which Go 1.27's encoding/json would otherwise use, encoding the module as +// Rego source rather than as JSON. Module's own fields are fully described by +// their struct tags, so the encoding is left to them, as it is pre-1.27. The +// field types provide their own MarshalJSONTo where one is needed. +func (m *Module) MarshalJSONTo(e *jsontext.Encoder) error { + // Declare a new type and use a type conversion to avoid recursively calling + // Module#MarshalJSONTo. It's the highest precedence marshaller, so there is + // nothing below it to fall to, and the new type has no methods of its own. + type module Module + + return json.MarshalEncode(e, (*module)(m)) +} + +func (pkg *Package) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Package && pkg.Location != nil { + if err := jsonv2.WriteField(e, "location", pkg.Location); err != nil { + return err + } + } + + if err := jsonv2.WriteField(e, "path", pkg.Path); err != nil { + return err + } + + return e.WriteToken(jsontext.EndObject) +} + +func (i *Import) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if err := jsonv2.WriteField(e, "path", i.Path); err != nil { + return err + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import && i.Location != nil { + if err := jsonv2.WriteField(e, "location", i.Location); err != nil { + return err + } + } + + if len(i.Alias) > 0 { + e.WriteToken(jsontext.String("alias")) + e.WriteToken(jsontext.String(string(i.Alias))) + } + + return e.WriteToken(jsontext.EndObject) +} + +func (r *Rule) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if r.Default { + e.WriteToken(jsontext.String("default")) + e.WriteToken(jsontext.True) + } + + if r.Else != nil { + if err := jsonv2.WriteField(e, "else", r.Else); err != nil { + return err + } + } + + if err := jsonv2.WriteField(e, "head", r.Head); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "body", r.Body); err != nil { + return err + } + + if len(r.Annotations) > 0 { + if err := jsonv2.WriteFieldArray(e, "annotations", r.Annotations); err != nil { + return err + } + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule && r.Location != nil { + if err := jsonv2.WriteField(e, "location", r.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (h *Head) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if h.Name != "" { + e.WriteToken(jsontext.String("name")) + e.WriteToken(jsontext.String(string(h.Name))) + } + + if err := jsonv2.WriteField(e, "ref", h.Ref()); err != nil { + return err + } + + if len(h.Args) > 0 { + if err := jsonv2.WriteFieldArray(e, "args", h.Args); err != nil { + return err + } + } + + if h.Key != nil { + if err := jsonv2.WriteField(e, "key", h.Key); err != nil { + return err + } + } + + if h.Value != nil { + if err := jsonv2.WriteField(e, "value", h.Value); err != nil { + return err + } + } + + if h.Assign { + e.WriteToken(jsontext.String("assign")) + e.WriteToken(jsontext.True) + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && h.Location != nil { + if err := jsonv2.WriteField(e, "location", h.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (c Call) MarshalJSONTo(e *jsontext.Encoder) (err error) { + return jsonv2.WriteMarshalerToArrayOrNull(e, c) +} + +func (c *Comment) MarshalJSONTo(e *jsontext.Encoder) error { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginObject) + + // Comment has no JSON tags, hence the capitalised keys, the base64 encoded + // text, and the location being written even when it's nil. + e.WriteToken(jsontext.String("Text")) + + buf := make([]byte, base64.StdEncoding.EncodedLen(len(c.Text))) + base64.StdEncoding.Encode(buf, c.Text) + + e.WriteValue(append(append(append(e.AvailableBuffer(), '"'), buf...), '"')) + + e.WriteToken(jsontext.String("Location")) + if c.Location != nil { + if err := c.Location.MarshalJSONTo(e); err != nil { + return err + } + } else { + e.WriteToken(jsontext.Null) + } + + return e.WriteToken(jsontext.EndObject) +} + +func (q *Every) MarshalJSONTo(e *jsontext.Encoder) error { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginObject) + + e.WriteToken(jsontext.String("key")) + if q.Key == nil { + e.WriteToken(jsontext.Null) + } else { + if err := q.Key.MarshalJSONTo(e); err != nil { + return err + } + } + + if err := jsonv2.WriteField(e, "value", q.Value); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "domain", q.Domain); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "body", q.Body); err != nil { + return err + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Every && q.Location != nil { + if err := jsonv2.WriteField(e, "location", q.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (b Body) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArray(e, b) +} + +// MarshalJSON returns JSON encoded bytes representing body. +func (body Body) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(body) +} + +func (expr *Expr) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(expr) +} + +// UnmarshalJSON parses the byte array and stores the result in expr. +func (expr *Expr) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalExpr(expr, v) +} + +func (e *Expr) MarshalJSONTo(enc *jsontext.Encoder) error { + enc.WriteToken(jsontext.BeginObject) + + enc.WriteToken(jsontext.String("index")) + enc.WriteToken(jsontext.Int(int64(e.Index))) + + includeLocation := astJSON.GetOptions().MarshalOptions.IncludeLocation + if e.Location != nil && includeLocation.Expr { + if err := jsonv2.WriteField(enc, "location", e.Location); err != nil { + return err + } + } + + if e.Negated { + enc.WriteToken(jsontext.String("negated")) + enc.WriteToken(jsontext.True) + } + + if e.Generated { + enc.WriteToken(jsontext.String("generated")) + enc.WriteToken(jsontext.True) + } + + enc.WriteToken(jsontext.String("terms")) + var err error + switch t := e.Terms.(type) { + case []*Term: + err = jsonv2.WriteMarshalerToArrayOrNull(enc, t) + case json.MarshalerTo: + err = t.MarshalJSONTo(enc) + default: + return fmt.Errorf("unsupported expr terms type: %T", e.Terms) + } + + if err != nil { + return fmt.Errorf("failed to marshal expr terms: %w", err) + } + + if len(e.With) > 0 { + if err := jsonv2.WriteFieldArray(enc, "with", e.With); err != nil { + return err + } + } + + return enc.WriteToken(jsontext.EndObject) +} + +func (a *LogicalAnd) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + e.WriteToken(jsontext.String("type")) + e.WriteToken(jsontext.String("and")) + if err := jsonv2.WriteField(e, "lhs", a.Lhs); err != nil { + return err + } + if err := jsonv2.WriteField(e, "rhs", a.Rhs); err != nil { + return err + } + + if a.ExplicitLhs { + e.WriteToken(jsontext.String("explicit_lhs")) + e.WriteToken(jsontext.True) + } + if a.ExplicitRhs { + e.WriteToken(jsontext.String("explicit_rhs")) + e.WriteToken(jsontext.True) + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.And && a.Location != nil { + if err := jsonv2.WriteField(e, "location", a.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (a *LogicalAnd) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalLogical("and", &a.Lhs, &a.Rhs, &a.ExplicitLhs, &a.ExplicitRhs, v) +} + +func (o *LogicalOr) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + e.WriteToken(jsontext.String("type")) + e.WriteToken(jsontext.String("or")) + + if err := jsonv2.WriteField(e, "lhs", o.Lhs); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "rhs", o.Rhs); err != nil { + return err + } + + if o.ExplicitLhs { + e.WriteToken(jsontext.String("explicit_lhs")) + e.WriteToken(jsontext.True) + } + if o.ExplicitRhs { + e.WriteToken(jsontext.String("explicit_rhs")) + e.WriteToken(jsontext.True) + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Or && o.Location != nil { + if err := jsonv2.WriteField(e, "location", o.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (o *LogicalOr) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalLogical("or", &o.Lhs, &o.Rhs, &o.ExplicitLhs, &o.ExplicitRhs, v) +} + +func (w *With) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if err := jsonv2.WriteField(e, "target", w.Target); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "value", w.Value); err != nil { + return err + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.With && w.Location != nil { + if err := jsonv2.WriteField(e, "location", w.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (d *SomeDecl) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + e.WriteToken(jsontext.String("symbols")) + if err := jsonv2.WriteMarshalerToArrayOrNull(e, d.Symbols); err != nil { + return err + } + + if d.Location != nil && astJSON.GetOptions().MarshalOptions.IncludeLocation.SomeDecl { + if err := jsonv2.WriteField(e, "location", d.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (ac *ArrayComprehension) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if err := jsonv2.WriteField(e, "term", ac.Term); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "body", ac.Body); err != nil { + return err + } + + return e.WriteToken(jsontext.EndObject) +} + +func (sc *SetComprehension) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if err := jsonv2.WriteField(e, "term", sc.Term); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "body", sc.Body); err != nil { + return err + } + + return e.WriteToken(jsontext.EndObject) +} + +func (oc *ObjectComprehension) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + + if err := jsonv2.WriteField(e, "key", oc.Key); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "value", oc.Value); err != nil { + return err + } + + if err := jsonv2.WriteField(e, "body", oc.Body); err != nil { + return err + } + + return e.WriteToken(jsontext.EndObject) +} + +func (pkg *Package) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(pkg) +} + +func (imp *Import) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(imp) +} + +func (rule *Rule) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(rule) +} + +func (head *Head) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(head) +} + +func (w *With) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(w) +} + +func (d *SomeDecl) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(d) +} + +func (q *Every) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(q) +} + +func (a *LogicalAnd) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(a) +} + +func (o *LogicalOr) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(o) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go b/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go index ccdc7dc7a4..09247effa6 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go @@ -365,6 +365,10 @@ func (c *Comment) StringLength() int { func (not *Not) StringLength() int { if !not.ExplicitBody && len(not.Body) == 1 { + if notBodyNeedsParens(not.Body) { + // "not (...)" + return 6 + not.Body.StringLength() + } // "not ..." return 4 + not.Body.StringLength() } @@ -373,19 +377,22 @@ func (not *Not) StringLength() int { } func (a *LogicalAnd) StringLength() int { - return logicalOperandStringLength(a.Lhs, a.ExplicitLhs) + + return logicalOperandStringLength(a.Lhs, a.ExplicitLhs, "and", false) + 5 + // " and " - logicalOperandStringLength(a.Rhs, a.ExplicitRhs) + logicalOperandStringLength(a.Rhs, a.ExplicitRhs, "and", true) } func (o *LogicalOr) StringLength() int { - return logicalOperandStringLength(o.Lhs, o.ExplicitLhs) + + return logicalOperandStringLength(o.Lhs, o.ExplicitLhs, "or", false) + 4 + // " or " - logicalOperandStringLength(o.Rhs, o.ExplicitRhs) + logicalOperandStringLength(o.Rhs, o.ExplicitRhs, "or", true) } -func logicalOperandStringLength(b Body, explicit bool) int { +func logicalOperandStringLength(b Body, explicit bool, parentOp string, rhs bool) int { if !explicit && len(b) == 1 { + if logicalOperandNeedsParens(b, parentOp, rhs) { + return b.StringLength() + 2 // "(" + body + ")" + } return b.StringLength() } return b.StringLength() + 4 // "{ " + body + " }" diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go index 178bc09e38..23820f13bb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go @@ -19,7 +19,6 @@ import ( "unicode" "github.com/cespare/xxhash/v2" - astJSON "github.com/open-policy-agent/opa/v1/ast/json" "github.com/open-policy-agent/opa/v1/ast/location" "github.com/open-policy-agent/opa/v1/util" ) @@ -380,15 +379,12 @@ func (term *Term) Copy() *Term { // Equal returns true if this term equals the other term. Equality is // defined for each kind of term, and does not compare the Location. func (term *Term) Equal(other *Term) bool { - if term == nil && other != nil { - return false - } - if term != nil && other == nil { - return false - } if term == other { return true } + if term == nil || other == nil { + return false + } return ValueEqual(term.Value, other.Value) } @@ -423,55 +419,10 @@ func (term *Term) IsGround() bool { return term.Value.IsGround() } -// termJSON is used to serialize Term to JSON without map allocation. -type termJSON struct { - Location *Location `json:"location,omitempty"` - Type string `json:"type"` - Value Value `json:"value"` -} - -// MarshalJSON returns the JSON encoding of the term. -// -// Specialized marshalling logic is required to include a type hint for Value. -func (term *Term) MarshalJSON() ([]byte, error) { - d := termJSON{ - Type: ValueName(term.Value), - Value: term.Value, - } - jsonOptions := astJSON.GetOptions().MarshalOptions - if jsonOptions.IncludeLocation.Term { - d.Location = term.Location - } - return json.Marshal(d) -} - func (term *Term) String() string { return term.Value.String() } -// UnmarshalJSON parses the byte array and stores the result in term. -// Specialized unmarshalling is required to handle Value and Location. -func (term *Term) UnmarshalJSON(bs []byte) error { - v := map[string]any{} - if err := util.UnmarshalJSON(bs, &v); err != nil { - return err - } - val, err := unmarshalValue(v) - if err != nil { - return err - } - term.Value = val - - if loc, ok := v["location"].(map[string]any); ok { - term.Location = &Location{} - err := unmarshalLocation(term.Location, loc) - if err != nil { - return err - } - } - return nil -} - // Vars returns a VarSet with variables contained in this term. func (term *Term) Vars() VarSet { vis := NewVarVisitor() @@ -654,62 +605,15 @@ func (n *Not) IsGround() bool { func (n *Not) String() string { if !n.ExplicitBody && len(n.Body) == 1 { + if notBodyNeedsParens(n.Body) { + return "not (" + n.Body.String() + ")" + } return "not " + n.Body.String() } return "not {" + n.Body.String() + "}" } -func (n *Not) MarshalJSON() ([]byte, error) { - data := map[string]any{ - "type": "not", - "body": n.Body, - "explicit_body": n.ExplicitBody, - } - - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Not { - if n.Location != nil { - data["location"] = n.Location - } - } - - return json.Marshal(data) -} - -func (n *Not) UnmarshalJSON(bs []byte) error { - v := map[string]any{} - if err := util.UnmarshalJSON(bs, &v); err != nil { - return err - } - - return unmarshalNot(n, v) -} - -func unmarshalNot(n *Not, v map[string]any) error { - var eb bool - if x, ok := v["explicit_body"]; ok { - eb, ok = x.(bool) - if !ok { - return fmt.Errorf("ast: unable to unmarshal explicit_body field with type: %T (expected true or false)", v["explicit_body"]) - } - } - - b, ok := v["body"].([]any) - if !ok { - return fmt.Errorf("ast: unable to unmarshal not, invalid body field type: %T (expected list)", v["body"]) - } - - body, err := unmarshalBody(b) - if err != nil { - return fmt.Errorf("ast: unable to unmarshal not body: %w", err) - } - - n.ExplicitBody = eb - n.Body = body - - return nil -} - // Null represents the null value defined by JSON. type Null struct{} @@ -874,7 +778,7 @@ func (num Number) Find(path Ref) (Value, error) { // Hash returns the hash code for the Value. func (num Number) Hash() int { if len(num) < 4 { - if i, err := strconv.Atoi(string(num)); err == nil { + if i, ok := util.Atoi(string(num)); ok { return i } } @@ -892,11 +796,7 @@ func (num Number) Int() (int, bool) { // Int64 returns the int64 representation of num if possible. func (num Number) Int64() (int64, bool) { - i, err := json.Number(num).Int64() - if err != nil { - return 0, false - } - return i, true + return util.Atoi64(string(num)) } // Float64 returns the float64 representation of num if possible. @@ -913,11 +813,6 @@ func (Number) IsGround() bool { return true } -// MarshalJSON returns JSON encoded bytes representing num. -func (num Number) MarshalJSON() ([]byte, error) { - return json.Marshal(json.Number(num)) -} - func (num Number) String() string { return string(num) } @@ -1696,14 +1591,6 @@ func (arr *Array) IsGround() bool { return arr.ground } -// MarshalJSON returns JSON encoded bytes representing arr. -func (arr *Array) MarshalJSON() ([]byte, error) { - if len(arr.elems) == 0 { - return []byte(`[]`), nil - } - return json.Marshal(arr.elems) -} - func (arr *Array) String() string { buf, _ := arr.AppendText(make([]byte, 0, arr.StringLength())) return util.ByteSliceToString(buf) @@ -2052,14 +1939,6 @@ func (s *set) Len() int { return len(s.keys) } -// MarshalJSON returns JSON encoded bytes representing s. -func (s *set) MarshalJSON() ([]byte, error) { - if s.keys == nil { - return []byte(`[]`), nil - } - return json.Marshal(s.sortedKeys()) -} - // Sorted returns an Array that contains the sorted elements of s. func (s *set) Sorted() *Array { cpy := make([]*Term, len(s.keys)) @@ -2228,10 +2107,6 @@ func (l *lazyObj) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error return l.force().Map(f) } -func (l *lazyObj) MarshalJSON() ([]byte, error) { - return l.force().(*object).MarshalJSON() -} - func (l *lazyObj) Merge(other Object) (Object, bool) { return l.force().Merge(other) } @@ -2609,15 +2484,6 @@ func (obj *object) KeysIterator() ObjectKeysIterator { return newobjectKeysIterator(obj) } -// MarshalJSON returns JSON encoded bytes representing obj. -func (obj *object) MarshalJSON() ([]byte, error) { - sl := make([][2]*Term, obj.Len()) - for i, node := range obj.sortedKeys() { - sl[i] = Item(node.key, node.value) - } - return json.Marshal(sl) -} - // Merge returns a new Object containing the non-overlapping keys of obj and other. If there are // overlapping keys between obj and other, the values of associated with the keys are merged. Only // objects can be merged with other objects. If the values cannot be merged, the second turn value @@ -3191,6 +3057,29 @@ func isControlOrBackslash(r rune) bool { // on the happy path and treats all errors the same. If better error // reporting is needed, the error paths will need to be fleshed out. +// UnmarshalJSON parses the byte array and stores the result in term. +// Specialized unmarshalling is required to handle Value and Location. +func (term *Term) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + val, err := unmarshalValue(v) + if err != nil { + return err + } + term.Value = val + + if loc, ok := v["location"].(map[string]any); ok { + term.Location = &Location{} + err := unmarshalLocation(term.Location, loc) + if err != nil { + return err + } + } + return nil +} + func unmarshalBody(b []any) (Body, error) { buf := Body{} for _, e := range b { @@ -3395,6 +3284,45 @@ func unmarshalWith(i any) (*With, error) { return nil, errors.New(`ast: unable to unmarshal with modifier (expected {"target": {...}, "value": {...}})`) } +func unmarshalLogical(typeName string, lhs, rhs *Body, explicitLhs, explicitRhs *bool, v map[string]any) error { + lhsRaw, ok := v["lhs"].([]any) + if !ok { + return fmt.Errorf("ast: unable to unmarshal %s, invalid lhs field type: %T (expected list)", typeName, v["lhs"]) + } + l, err := unmarshalBody(lhsRaw) + if err != nil { + return fmt.Errorf("ast: unable to unmarshal %s lhs: %w", typeName, err) + } + *lhs = l + + rhsRaw, ok := v["rhs"].([]any) + if !ok { + return fmt.Errorf("ast: unable to unmarshal %s, invalid rhs field type: %T (expected list)", typeName, v["rhs"]) + } + r, err := unmarshalBody(rhsRaw) + if err != nil { + return fmt.Errorf("ast: unable to unmarshal %s rhs: %w", typeName, err) + } + *rhs = r + + if x, ok := v["explicit_lhs"]; ok { + b, ok := x.(bool) + if !ok { + return fmt.Errorf("ast: unable to unmarshal %s explicit_lhs field with type: %T (expected true or false)", typeName, x) + } + *explicitLhs = b + } + if x, ok := v["explicit_rhs"]; ok { + b, ok := x.(bool) + if !ok { + return fmt.Errorf("ast: unable to unmarshal %s explicit_rhs field with type: %T (expected true or false)", typeName, x) + } + *explicitRhs = b + } + + return nil +} + func unmarshalValue(d map[string]any) (Value, error) { v := d["value"] switch d["type"] { @@ -3512,3 +3440,28 @@ func unmarshalValue(d map[string]any) (Value, error) { unmarshal_error: return nil, errors.New("ast: unable to unmarshal term") } + +func unmarshalNot(n *Not, v map[string]any) error { + var eb bool + if x, ok := v["explicit_body"]; ok { + eb, ok = x.(bool) + if !ok { + return fmt.Errorf("ast: unable to unmarshal explicit_body field with type: %T (expected true or false)", v["explicit_body"]) + } + } + + b, ok := v["body"].([]any) + if !ok { + return fmt.Errorf("ast: unable to unmarshal not, invalid body field type: %T (expected list)", v["body"]) + } + + body, err := unmarshalBody(b) + if err != nil { + return fmt.Errorf("ast: unable to unmarshal not body: %w", err) + } + + n.ExplicitBody = eb + n.Body = body + + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go index 63c3973b3f..60a9a088d7 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go @@ -289,6 +289,14 @@ func appendComprehensionTerm(buf []byte, term *Term) ([]byte, error) { func (not *Not) AppendText(buf []byte) ([]byte, error) { if !not.ExplicitBody && len(not.Body) == 1 { + if notBodyNeedsParens(not.Body) { + buf = append(buf, "not ("...) + var err error + if buf, err = not.Body.AppendText(buf); err != nil { + return nil, err + } + return append(buf, ')'), nil + } buf = append(buf, "not "...) return not.Body.AppendText(buf) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go new file mode 100644 index 0000000000..685801b66d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go @@ -0,0 +1,95 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +//go:build !go1.27 + +package ast + +import ( + "encoding/json" + + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +// termJSON is used to serialize Term to JSON without map allocation. +type termJSON struct { + Location *Location `json:"location,omitempty"` + Type string `json:"type"` + Value Value `json:"value"` +} + +// MarshalJSON returns the JSON encoding of the term. +// +// Specialized marshalling logic is required to include a type hint for Value. +func (term *Term) MarshalJSON() ([]byte, error) { + d := termJSON{ + Type: ValueName(term.Value), + Value: term.Value, + } + jsonOptions := astJSON.GetOptions().MarshalOptions + if jsonOptions.IncludeLocation.Term { + d.Location = term.Location + } + return json.Marshal(d) +} + +// MarshalJSON returns JSON encoded bytes representing arr. +func (arr *Array) MarshalJSON() ([]byte, error) { + if len(arr.elems) == 0 { + return []byte(`[]`), nil + } + return json.Marshal(arr.elems) +} + +// MarshalJSON returns JSON encoded bytes representing num. +func (num Number) MarshalJSON() ([]byte, error) { + return json.Marshal(json.Number(num)) +} + +// MarshalJSON returns JSON encoded bytes representing obj. +func (obj *object) MarshalJSON() ([]byte, error) { + sl := make([][2]*Term, obj.Len()) + for i, node := range obj.sortedKeys() { + sl[i] = Item(node.key, node.value) + } + return json.Marshal(sl) +} + +// MarshalJSON returns JSON encoded bytes representing s. +func (s *set) MarshalJSON() ([]byte, error) { + if s.keys == nil { + return []byte(`[]`), nil + } + return json.Marshal(s.sortedKeys()) +} + +func (l *lazyObj) MarshalJSON() ([]byte, error) { + return l.force().(*object).MarshalJSON() +} + +func (n *Not) MarshalJSON() ([]byte, error) { + data := map[string]any{ + "type": "not", + "body": n.Body, + "explicit_body": n.ExplicitBody, + } + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Not { + if n.Location != nil { + data["location"] = n.Location + } + } + + return json.Marshal(data) +} + +func (n *Not) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + + return unmarshalNot(n, v) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go new file mode 100644 index 0000000000..62189c3729 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go @@ -0,0 +1,251 @@ +//go:build go1.27 + +package ast + +import ( + "encoding" + "encoding/json/jsontext" + "encoding/json/v2" + "fmt" + + "github.com/open-policy-agent/opa/internal/jsonv2" + astJSON "github.com/open-policy-agent/opa/v1/ast/json" + "github.com/open-policy-agent/opa/v1/util" +) + +var ( + _ json.MarshalerTo = &Term{} + _ json.Unmarshaler = &LogicalOr{} + _ json.MarshalerTo = &LogicalOr{} + _ json.MarshalerTo = &Not{} + _ json.MarshalerTo = &Array{} + _ json.MarshalerTo = &set{} + _ json.MarshalerTo = &object{} + _ json.MarshalerTo = &TemplateString{} + _ json.MarshalerTo = &Ref{} + _ json.MarshalerTo = &lazyObj{} + _ json.MarshalerTo = Args{} + _ json.MarshalerTo = Boolean(false) + _ json.MarshalerTo = Null{} + _ json.MarshalerTo = Number("") + _ json.MarshalerTo = String("") + _ json.MarshalerTo = Var("") + _ json.Unmarshaler = &Not{} + + // These are exported types, so losing MarshalJSON here would be a breaking + // API change even though callers should go through json.Marshal, not this + // method directly. + _ json.Marshaler = Number("") + _ json.Marshaler = &Term{} + _ json.Marshaler = &Not{} + _ json.Marshaler = &lazyObj{} + _ json.Marshaler = &object{} + _ json.Marshaler = &Array{} + _ json.Marshaler = &set{} +) + +// These are here to ensure that we do not fall down to TextAppender, which +// Go 1.27's encoding/json would otherwise use, encoding these as JSON strings. + +func (b Boolean) MarshalJSONTo(e *jsontext.Encoder) error { + return e.WriteToken(jsontext.Bool(bool(b))) +} + +func (Null) MarshalJSONTo(e *jsontext.Encoder) error { + // Encoded as an empty object rather than null, as that's the representation + // callers have come to expect. See also [marshalValueTo]. + return e.WriteValue([]byte("{}")) +} + +func (v Var) MarshalJSONTo(e *jsontext.Encoder) error { + // Must produce the var name as a JSON string, wildcard vars included: that's + // what encoding/json v1 does for a type whose underlying kind is string. + return e.WriteToken(jsontext.String(string(v))) +} + +func (num Number) MarshalJSONTo(e *jsontext.Encoder) error { + if num == "" { + // Matches encoding/json v1, which encodes an empty json.Number as 0. + return e.WriteToken(jsontext.Int(0)) + } + return e.WriteValue(jsontext.Value(num)) +} + +// MarshalJSON returns JSON encoded bytes representing num. +func (num Number) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(num) +} + +func (str String) MarshalJSONTo(e *jsontext.Encoder) error { + return e.WriteToken(jsontext.String(string(str))) +} + +func (t *Term) MarshalJSONTo(e *jsontext.Encoder) (err error) { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginObject) + + includeLocation := astJSON.GetOptions().MarshalOptions.IncludeLocation + if t.Location != nil && includeLocation.Term { + if err := jsonv2.WriteField(e, "location", t.Location); err != nil { + return err + } + } + + e.WriteToken(jsontext.String("type")) + e.WriteToken(jsontext.String(ValueName(t.Value))) + + e.WriteToken(jsontext.String("value")) + if err = marshalValueTo(e, t.Value); err != nil { + return fmt.Errorf("failed to marshal term of %s: %w", ValueName(t.Value), err) + } + + return e.WriteToken(jsontext.EndObject) +} + +// MarshalJSON returns the JSON encoding of the term. +func (term *Term) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(term) +} + +func (r Ref) MarshalJSONTo(e *jsontext.Encoder) (err error) { + return jsonv2.WriteMarshalerToArrayOrNull(e, r) +} + +func (t *TemplateString) MarshalJSONTo(e *jsontext.Encoder) (err error) { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginObject) + e.WriteToken(jsontext.String("parts")) + if t.Parts == nil { + // Parts has no omitempty tag, so it's always written. Matches + // encoding/json v1, which encodes a nil slice as null rather than as an + // empty array. + e.WriteToken(jsontext.Null) + } else { + e.WriteToken(jsontext.BeginArray) + for _, p := range t.Parts { + switch v := p.(type) { + case *Expr: + if err := v.MarshalJSONTo(e); err != nil { + return err + } + case *Term: + if err := v.MarshalJSONTo(e); err != nil { + return err + } + } + } + e.WriteToken(jsontext.EndArray) + } + + e.WriteToken(jsontext.String("multi_line")) + e.WriteToken(jsontext.Bool(t.MultiLine)) + + return e.WriteToken(jsontext.EndObject) +} + +func (n *Not) MarshalJSONTo(e *jsontext.Encoder) error { + e.WriteToken(jsontext.BeginObject) + e.WriteToken(jsontext.String("type")) + e.WriteToken(jsontext.String("not")) + + if err := jsonv2.WriteField(e, "body", n.Body); err != nil { + return err + } + + e.WriteToken(jsontext.String("explicit_body")) + e.WriteToken(jsontext.Bool(n.ExplicitBody)) + + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Not && n.Location != nil { + if err := jsonv2.WriteField(e, "location", n.Location); err != nil { + return err + } + } + + return e.WriteToken(jsontext.EndObject) +} + +func (n *Not) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(n) +} + +func (n *Not) UnmarshalJSON(bs []byte) error { + v := map[string]any{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + + return unmarshalNot(n, v) +} + +func (obj *object) MarshalJSONTo(e *jsontext.Encoder) error { + // Token write errors are unchecked: an unbalanced value fails at the closing + // token. A marshaller can fail having written a balanced value, so is checked. + e.WriteToken(jsontext.BeginArray) + + for _, node := range obj.sortedKeys() { + e.WriteToken(jsontext.BeginArray) + if err := node.key.MarshalJSONTo(e); err != nil { + return err + } + if err := node.value.MarshalJSONTo(e); err != nil { + return err + } + e.WriteToken(jsontext.EndArray) + } + return e.WriteToken(jsontext.EndArray) +} + +func (l *lazyObj) MarshalJSONTo(e *jsontext.Encoder) error { + return l.force().(*object).MarshalJSONTo(e) +} + +func (l *lazyObj) MarshalJSON() ([]byte, error) { + return l.force().(*object).MarshalJSON() +} + +// MarshalJSON returns JSON encoded bytes representing obj. +func (obj *object) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(obj) +} + +func (a *Array) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArray(e, a.elems) +} + +// MarshalJSON returns JSON encoded bytes representing arr. +func (arr *Array) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(arr) +} + +func (s *set) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArray(e, s.sortedKeys()) +} + +// MarshalJSON returns JSON encoded bytes representing s. +func (s *set) MarshalJSON() ([]byte, error) { + return jsonv2.MarshalMarshalerTo(s) +} + +func marshalValueTo(e *jsontext.Encoder, val Value) (err error) { + switch v := val.(type) { + case json.MarshalerTo: + err = v.MarshalJSONTo(e) + case encoding.TextAppender: + var text []byte + if text, err = v.AppendText(nil); err != nil { + return err + } + + if text, err = jsontext.AppendQuote(e.AvailableBuffer(), text); err != nil { + return err + } + + err = e.WriteValue(text) + default: + err = json.MarshalEncode(e, v) + } + + return err +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json b/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json index 4e5442604c..91e4d09ff3 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json @@ -845,6 +845,11 @@ "Minor": 36, "Patch": 0 }, + "strings.split_n": { + "Major": 1, + "Minor": 19, + "Patch": 0 + }, "substring": { "Major": 0, "Minor": 17, diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go index b717a5a718..f697e336d5 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go @@ -23,11 +23,13 @@ import ( "github.com/gobwas/glob" "golang.org/x/sync/errgroup" + "google.golang.org/protobuf/proto" "github.com/open-policy-agent/opa/internal/file/archive" "github.com/open-policy-agent/opa/internal/merge" "github.com/open-policy-agent/opa/v1/ast" astJSON "github.com/open-policy-agent/opa/v1/ast/json" + pb "github.com/open-policy-agent/opa/v1/bundle/v1pb" "github.com/open-policy-agent/opa/v1/format" "github.com/open-policy-agent/opa/v1/metrics" "github.com/open-policy-agent/opa/v1/storage" @@ -39,7 +41,9 @@ const ( RegoExt = ".rego" WasmFile = "policy.wasm" PlanFile = "plan.json" + PlanProtoFile = "plan.pb" ManifestExt = ".manifest" + ManifestProtoExt = ".manifest.pb" SignaturesFile = "signatures.json" patchFile = "patch.json" dataFile = "data.json" @@ -70,6 +74,13 @@ type Bundle struct { lazyLoadingMode bool sizeLimitBytes int64 + manifestProto bool +} + +// SetManifestProto configures the bundle to serialize its manifest as +// protobuf at /.manifest.pb instead of JSON at /.manifest. +func (b *Bundle) SetManifestProto(yes bool) { + b.manifestProto = yes } // Raw contains raw bytes representing the bundle's content @@ -213,7 +224,7 @@ func (m Manifest) Equal(other Manifest) bool { // If both are nil, or both are empty, we consider them equal. if !(len(m.FileRegoVersions) == 0 && len(other.FileRegoVersions) == 0) && - !reflect.DeepEqual(m.FileRegoVersions, other.FileRegoVersions) { + !maps.Equal(m.FileRegoVersions, other.FileRegoVersions) { return false } @@ -641,6 +652,7 @@ func (r *Reader) Read() (Bundle, error) { } var modules []ModuleFile + var manifestPath string for _, f := range descriptors { buf, err := readFile(f, r.sizeLimitBytes) if err != nil { @@ -695,7 +707,7 @@ func (r *Reader) Read() (Bundle, error) { Path: r.fullPath(path), Raw: buf.Bytes(), }) - } else if filepath.Base(path) == PlanFile { + } else if filepath.Base(path) == PlanFile || filepath.Base(path) == PlanProtoFile { bundle.PlanModules = append(bundle.PlanModules, PlanModuleFile{ URL: f.URL(), Path: r.fullPath(path), @@ -741,7 +753,26 @@ func (r *Reader) Read() (Bundle, error) { return empty, err } + } else if strings.HasSuffix(path, ManifestProtoExt) { + if manifestPath != "" { + return empty, fmt.Errorf("bundle contains multiple manifest files: %q and %q", manifestPath, path) + } + manifestPath = path + pbManifest := &pb.Manifest{} + if err := proto.Unmarshal(buf.Bytes(), pbManifest); err != nil { + return empty, fmt.Errorf("bundle load failed on manifest decode: %w", err) + } + m, err := ManifestFromProto(pbManifest) + if err != nil { + return empty, fmt.Errorf("bundle load failed on manifest decode: %w", err) + } + bundle.Manifest = *m + bundle.manifestProto = true } else if strings.HasSuffix(path, ManifestExt) { + if manifestPath != "" { + return empty, fmt.Errorf("bundle contains multiple manifest files: %q and %q", manifestPath, path) + } + manifestPath = path if err := util.NewJSONDecoder(&buf).Decode(&bundle.Manifest); err != nil { return empty, fmt.Errorf("bundle load failed on manifest decode: %w", err) } @@ -935,6 +966,9 @@ func (w *Writer) DisableFormat(yes bool) *Writer { // Write writes the bundle to the writer's output stream. func (w *Writer) Write(bundle Bundle) error { + if err := validateBundleFormat(&bundle); err != nil { + return err + } tw := archive.NewTarGzWriter(w.w) if bundle.Type() == SnapshotBundleType { @@ -971,8 +1005,18 @@ func (w *Writer) Write(bundle Bundle) error { } if !bundle.Manifest.Empty() { - if err := tw.WriteJSONFile("/.manifest", bundle.Manifest); err != nil { - return err + if bundle.manifestProto { + bs, err := marshalManifestProto(&bundle.Manifest) + if err != nil { + return err + } + if err := tw.WriteFile(util.WithPrefix(ManifestProtoExt, "/"), bs); err != nil { + return err + } + } else { + if err := tw.WriteJSONFile("/.manifest", bundle.Manifest); err != nil { + return err + } } } @@ -1060,32 +1104,74 @@ func hashBundleFiles(hash SignatureHasher, b *Bundle) ([]FileInfo, error) { files = append(files, NewFile(strings.TrimPrefix(planmodule.Path, "/"), hex.EncodeToString(bs), defaultHashingAlg)) } - // If the manifest is essentially empty, don't add it to the signatures since it - // won't be written to the bundle. Otherwise: - // parse the manifest into a JSON structure; - // then recursively order the fields of all objects alphabetically and then apply - // the hash function to result to compute the hash. + // Skip empty manifest — Writer.Write skips it too, so no entry to hash. + // Proto manifest is hashed as raw deterministic-marshal bytes (matches + // what VerifyBundleFile sees, since IsStructuredDoc is false for /.manifest.pb). if !b.Manifest.Empty() { - mbs, err := json.Marshal(b.Manifest) - if err != nil { - return files, err - } + if b.manifestProto { + pbBytes, err := marshalManifestProto(&b.Manifest) + if err != nil { + return files, err + } + if bs, err = hash.HashFile(pbBytes); err != nil { + return files, err + } + files = append(files, NewFile(strings.TrimPrefix(ManifestProtoExt, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } else { + mbs, err := json.Marshal(b.Manifest) + if err != nil { + return files, err + } - var result map[string]any - if err := util.Unmarshal(mbs, &result); err != nil { - return files, err - } + var result map[string]any + if err := util.Unmarshal(mbs, &result); err != nil { + return files, err + } - if bs, err = hash.HashFile(result); err != nil { - return files, err - } + if bs, err = hash.HashFile(result); err != nil { + return files, err + } - files = append(files, NewFile(strings.TrimPrefix(ManifestExt, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + files = append(files, NewFile(strings.TrimPrefix(ManifestExt, "/"), hex.EncodeToString(bs), defaultHashingAlg)) + } } return files, err } +// marshalManifestProto returns the deterministic protobuf wire form so +// signer and writer produce byte-identical output (sign/verify on +// /.manifest.pb depends on it). +func marshalManifestProto(m *Manifest) ([]byte, error) { + pbManifest, err := ManifestToProto(m) + if err != nil { + return nil, err + } + return proto.MarshalOptions{Deterministic: true}.Marshal(pbManifest) +} + +// validateBundleFormat rejects bundles whose plan format disagrees with +// the manifest format (e.g. /plan.pb + /.manifest). +func validateBundleFormat(b *Bundle) error { + if b.Manifest.Empty() { + return nil + } + for _, pm := range b.PlanModules { + base := filepath.Base(pm.Path) + switch base { + case PlanFile: + if b.manifestProto { + return fmt.Errorf("bundle has proto manifest but JSON plan %q; SetManifestProto must agree with plan format", pm.Path) + } + case PlanProtoFile: + if !b.manifestProto { + return fmt.Errorf("bundle has JSON manifest but proto plan %q; SetManifestProto(true) required", pm.Path) + } + } + } + return nil +} + // FormatModules formats Rego modules // Modules will be formatted to comply with [ast.DefaultRegoVersion], but Rego compatibility of individual parsed modules will be respected (e.g. if 'rego.v1' is imported). func (b *Bundle) FormatModules(useModulePath bool) error { @@ -1473,11 +1559,34 @@ func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePat var roots []string var result Bundle + var planFile string + var manifestProto bool + var manifestProtoSet bool + for _, b := range bundles { if b.Manifest.Roots == nil { return nil, errors.New("bundle manifest not initialized") } + for _, pm := range b.PlanModules { + base := filepath.Base(pm.Path) + if base != PlanFile && base != PlanProtoFile { + continue + } + if planFile == "" { + planFile = base + } else if planFile != base { + return nil, fmt.Errorf("cannot merge bundles with mixed plan formats (%s and %s)", planFile, base) + } + } + + if !manifestProtoSet { + manifestProto = b.manifestProto + manifestProtoSet = true + } else if manifestProto != b.manifestProto { + return nil, errors.New("cannot merge bundles with mixed manifest formats") + } + roots = append(roots, *b.Manifest.Roots...) result.Modules = append(result.Modules, b.Modules...) @@ -1508,6 +1617,8 @@ func MergeWithRegoVersion(bundles []*Bundle, regoVersion ast.RegoVersion, usePat } } + result.manifestProto = manifestProto + // We respect the bundle rego-version, defaulting to the provided rego version if not set. result.SetRegoVersion(result.RegoVersion(regoVersion)) @@ -1666,7 +1777,9 @@ func modulePathWithPrefix(bundleName string, modulePath string) string { return path.Join(bundleName, modulePath) } -// IsStructuredDoc checks if the file name equals a structured file extension ex. ".json" +// IsStructuredDoc checks if the file name equals a structured file extension ex. ".json". +// Note: ManifestProtoExt (".manifest.pb") is intentionally absent — proto manifests are +// hashed as raw wire bytes on both the sign and verify paths. func IsStructuredDoc(name string) bool { base := filepath.Base(name) return base == dataFile || base == yamlDataFile || base == SignaturesFile || base == ManifestExt diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go index 4897ee7b91..e4f74cc0e4 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go @@ -200,7 +200,10 @@ func formatPath(fileName string, root string, pathFormat PathFormat) string { case Chrooted: // Trim off the root directory and return path as if chrooted result := strings.TrimPrefix(fileName, filepath.FromSlash(root)) - if root == "." && filepath.Base(fileName) == ManifestExt { + // TrimPrefix at root="." strips the leading dot from dotfile manifests + // (".manifest" → "manifest"), which then misses the Reader's HasSuffix + // check. Restore the original name for both manifest forms. + if root == "." && (filepath.Base(fileName) == ManifestExt || filepath.Base(fileName) == ManifestProtoExt) { result = fileName } if !strings.HasPrefix(result, string(filepath.Separator)) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/manifest.proto b/vendor/github.com/open-policy-agent/opa/v1/bundle/manifest.proto index 96a79a707a..ed3c1498ce 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/manifest.proto +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/manifest.proto @@ -16,7 +16,7 @@ message Manifest { // Bundle revision string. string revision = 1; - // Root paths the bundle owns. + // Root paths the bundle owns. See `roots_set` for nil-vs-empty. repeated string roots = 2; // Wasm resolvers attached to the bundle. JSON key is `wasm`. @@ -31,6 +31,10 @@ message Manifest { // Free-form metadata object. Modeled as `Struct` because the Go field // is `map[string]any`. google.protobuf.Struct metadata = 6; + + // True if `bundle.Manifest.Roots` was non-nil. `repeated string` can't + // distinguish nil (default to [""]) from explicit-empty (owns no paths). + bool roots_set = 7; } // WasmResolver mirrors `bundle.WasmResolver` in v1/bundle/bundle.go. diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go new file mode 100644 index 0000000000..500be418d5 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go @@ -0,0 +1,434 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "encoding/json" + "fmt" + "net/url" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" + pb "github.com/open-policy-agent/opa/v1/bundle/v1pb" +) + +// ManifestToProto converts a bundle Manifest to its protobuf wire-form, +// defined in v1/bundle/manifest.proto. The compiled-only fileRegoVersions +// cache is intentionally not modeled. Roots presence (nil vs explicit-empty) +// is preserved via the `roots_set` wire field. +func ManifestToProto(m *Manifest) (*pb.Manifest, error) { + if m == nil { + return nil, nil + } + out := &pb.Manifest{ + Revision: proto.String(m.Revision), + } + if m.Roots != nil { + out.Roots = append([]string(nil), (*m.Roots)...) + out.RootsSet = proto.Bool(true) + } + if len(m.WasmResolvers) > 0 { + out.Wasm = make([]*pb.WasmResolver, len(m.WasmResolvers)) + for i := range m.WasmResolvers { + wr, err := wasmResolverToProto(&m.WasmResolvers[i]) + if err != nil { + return nil, fmt.Errorf("manifest wasm[%d]: %w", i, err) + } + out.Wasm[i] = wr + } + } + if m.RegoVersion != nil { + out.RegoVersion = proto.Int32(int32(*m.RegoVersion)) + } + if len(m.FileRegoVersions) > 0 { + out.FileRegoVersions = make(map[string]int32, len(m.FileRegoVersions)) + for k, v := range m.FileRegoVersions { + out.FileRegoVersions[k] = int32(v) + } + } + if len(m.Metadata) > 0 { + s, err := jsonNormalizeStruct(m.Metadata) + if err != nil { + return nil, fmt.Errorf("manifest metadata: %w", err) + } + out.Metadata = s + } + return out, nil +} + +func wasmResolverToProto(w *WasmResolver) (*pb.WasmResolver, error) { + if w == nil { + return nil, nil + } + out := &pb.WasmResolver{ + Entrypoint: proto.String(w.Entrypoint), + Module: proto.String(w.Module), + } + if len(w.Annotations) > 0 { + out.Annotations = make([]*pb.Annotations, len(w.Annotations)) + for i, a := range w.Annotations { + ap, err := annotationsToProto(a) + if err != nil { + return nil, fmt.Errorf("annotations[%d]: %w", i, err) + } + out.Annotations[i] = ap + } + } + return out, nil +} + +func annotationsToProto(a *ast.Annotations) (*pb.Annotations, error) { + if a == nil { + return nil, nil + } + out := &pb.Annotations{ + Scope: proto.String(a.Scope), + Title: proto.String(a.Title), + Entrypoint: proto.Bool(a.Entrypoint), + Description: proto.String(a.Description), + Organizations: append([]string(nil), a.Organizations...), + } + if len(a.RelatedResources) > 0 { + out.RelatedResources = make([]*pb.RelatedResourceAnnotation, len(a.RelatedResources)) + for i, r := range a.RelatedResources { + out.RelatedResources[i] = relatedResourceToProto(r) + } + } + if len(a.Authors) > 0 { + out.Authors = make([]*pb.AuthorAnnotation, len(a.Authors)) + for i, au := range a.Authors { + out.Authors[i] = authorToProto(au) + } + } + if len(a.Schemas) > 0 { + out.Schemas = make([]*pb.SchemaAnnotation, len(a.Schemas)) + for i, s := range a.Schemas { + sa, err := schemaToProto(s) + if err != nil { + return nil, fmt.Errorf("schemas[%d]: %w", i, err) + } + out.Schemas[i] = sa + } + } + if a.Compile != nil { + out.Compile = compileToProto(a.Compile) + } + if len(a.Custom) > 0 { + s, err := jsonNormalizeStruct(a.Custom) + if err != nil { + return nil, fmt.Errorf("custom: %w", err) + } + out.Custom = s + } + if len(a.Labels) > 0 { + s, err := jsonNormalizeStruct(a.Labels) + if err != nil { + return nil, fmt.Errorf("labels: %w", err) + } + out.Labels = s + } + if a.Location != nil { + out.Location = locationToProto(a.Location) + } + return out, nil +} + +func relatedResourceToProto(r *ast.RelatedResourceAnnotation) *pb.RelatedResourceAnnotation { + if r == nil { + return nil + } + return &pb.RelatedResourceAnnotation{ + Ref: proto.String(r.Ref.String()), + Description: proto.String(r.Description), + } +} + +func authorToProto(a *ast.AuthorAnnotation) *pb.AuthorAnnotation { + if a == nil { + return nil + } + return &pb.AuthorAnnotation{ + Name: proto.String(a.Name), + Email: proto.String(a.Email), + } +} + +func schemaToProto(s *ast.SchemaAnnotation) (*pb.SchemaAnnotation, error) { + if s == nil { + return nil, nil + } + out := &pb.SchemaAnnotation{ + Path: proto.String(s.Path.String()), + Schema: proto.String(s.Schema.String()), + } + if s.Definition != nil { + v, err := jsonNormalizeValue(*s.Definition) + if err != nil { + return nil, fmt.Errorf("definition: %w", err) + } + out.Definition = v + } + return out, nil +} + +func compileToProto(c *ast.CompileAnnotation) *pb.CompileAnnotation { + if c == nil { + return nil + } + out := &pb.CompileAnnotation{ + MaskRule: proto.String(c.MaskRule.String()), + } + if len(c.Unknowns) > 0 { + out.Unknowns = make([]string, len(c.Unknowns)) + for i, u := range c.Unknowns { + out.Unknowns[i] = u.String() + } + } + return out +} + +func locationToProto(l *location.Location) *pb.Location { + if l == nil { + return nil + } + return &pb.Location{ + File: proto.String(l.File), + Row: proto.Int32(int32(l.Row)), + Col: proto.Int32(int32(l.Col)), + } +} + +// ManifestFromProto is the inverse of ManifestToProto. +func ManifestFromProto(m *pb.Manifest) (*Manifest, error) { + if m == nil { + return nil, nil + } + out := &Manifest{ + Revision: m.GetRevision(), + } + if m.GetRootsSet() { + roots := make([]string, len(m.Roots)) + copy(roots, m.Roots) + out.Roots = &roots + } + if len(m.Wasm) > 0 { + out.WasmResolvers = make([]WasmResolver, len(m.Wasm)) + for i, wr := range m.Wasm { + converted, err := wasmResolverFromProto(wr) + if err != nil { + return nil, fmt.Errorf("manifest wasm[%d]: %w", i, err) + } + out.WasmResolvers[i] = converted + } + } + if m.RegoVersion != nil { + v := int(*m.RegoVersion) + out.RegoVersion = &v + } + if len(m.FileRegoVersions) > 0 { + out.FileRegoVersions = make(map[string]int, len(m.FileRegoVersions)) + for k, v := range m.FileRegoVersions { + out.FileRegoVersions[k] = int(v) + } + } + if m.Metadata != nil { + out.Metadata = m.Metadata.AsMap() + } + return out, nil +} + +func wasmResolverFromProto(w *pb.WasmResolver) (WasmResolver, error) { + out := WasmResolver{ + Entrypoint: w.GetEntrypoint(), + Module: w.GetModule(), + } + if len(w.Annotations) > 0 { + out.Annotations = make([]*ast.Annotations, len(w.Annotations)) + for i, a := range w.Annotations { + converted, err := annotationsFromProto(a) + if err != nil { + return WasmResolver{}, fmt.Errorf("annotations[%d]: %w", i, err) + } + out.Annotations[i] = converted + } + } + return out, nil +} + +func annotationsFromProto(a *pb.Annotations) (*ast.Annotations, error) { + if a == nil { + return nil, nil + } + out := &ast.Annotations{ + Scope: a.GetScope(), + Title: a.GetTitle(), + Entrypoint: a.GetEntrypoint(), + Description: a.GetDescription(), + } + if len(a.Organizations) > 0 { + out.Organizations = append([]string(nil), a.Organizations...) + } + if len(a.RelatedResources) > 0 { + out.RelatedResources = make([]*ast.RelatedResourceAnnotation, len(a.RelatedResources)) + for i, r := range a.RelatedResources { + converted, err := relatedResourceFromProto(r) + if err != nil { + return nil, fmt.Errorf("related_resources[%d]: %w", i, err) + } + out.RelatedResources[i] = converted + } + } + if len(a.Authors) > 0 { + out.Authors = make([]*ast.AuthorAnnotation, len(a.Authors)) + for i, au := range a.Authors { + out.Authors[i] = authorFromProto(au) + } + } + if len(a.Schemas) > 0 { + out.Schemas = make([]*ast.SchemaAnnotation, len(a.Schemas)) + for i, s := range a.Schemas { + converted, err := schemaFromProto(s) + if err != nil { + return nil, fmt.Errorf("schemas[%d]: %w", i, err) + } + out.Schemas[i] = converted + } + } + if a.Compile != nil { + converted, err := compileFromProto(a.Compile) + if err != nil { + return nil, fmt.Errorf("compile: %w", err) + } + out.Compile = converted + } + if a.Custom != nil { + out.Custom = a.Custom.AsMap() + } + if a.Labels != nil { + out.Labels = a.Labels.AsMap() + } + if a.Location != nil { + out.Location = locationFromProto(a.Location) + } + return out, nil +} + +func relatedResourceFromProto(r *pb.RelatedResourceAnnotation) (*ast.RelatedResourceAnnotation, error) { + if r == nil { + return nil, nil + } + out := &ast.RelatedResourceAnnotation{ + Description: r.GetDescription(), + } + if ref := r.GetRef(); ref != "" { + u, err := url.Parse(ref) + if err != nil { + return nil, fmt.Errorf("ref %q: %w", ref, err) + } + out.Ref = *u + } + return out, nil +} + +func authorFromProto(a *pb.AuthorAnnotation) *ast.AuthorAnnotation { + if a == nil { + return nil + } + return &ast.AuthorAnnotation{ + Name: a.GetName(), + Email: a.GetEmail(), + } +} + +func schemaFromProto(s *pb.SchemaAnnotation) (*ast.SchemaAnnotation, error) { + if s == nil { + return nil, nil + } + out := &ast.SchemaAnnotation{} + if p := s.GetPath(); p != "" { + ref, err := ast.ParseRef(p) + if err != nil { + return nil, fmt.Errorf("path %q: %w", p, err) + } + out.Path = ref + } + if sc := s.GetSchema(); sc != "" { + ref, err := ast.ParseSchemaRef(sc) + if err != nil { + return nil, fmt.Errorf("schema %q: %w", sc, err) + } + out.Schema = ref + } + if s.Definition != nil { + def := s.Definition.AsInterface() + out.Definition = &def + } + return out, nil +} + +func compileFromProto(c *pb.CompileAnnotation) (*ast.CompileAnnotation, error) { + if c == nil { + return nil, nil + } + out := &ast.CompileAnnotation{} + if mr := c.GetMaskRule(); mr != "" { + ref, err := ast.ParseRef(mr) + if err != nil { + return nil, fmt.Errorf("mask_rule %q: %w", mr, err) + } + out.MaskRule = ref + } + if len(c.Unknowns) > 0 { + out.Unknowns = make([]ast.Ref, len(c.Unknowns)) + for i, u := range c.Unknowns { + ref, err := ast.ParseRef(u) + if err != nil { + return nil, fmt.Errorf("unknowns[%d] %q: %w", i, u, err) + } + out.Unknowns[i] = ref + } + } + return out, nil +} + +func locationFromProto(l *pb.Location) *location.Location { + if l == nil { + return nil + } + return &location.Location{ + File: l.GetFile(), + Row: int(l.GetRow()), + Col: int(l.GetCol()), + } +} + +// jsonNormalizeStruct routes a map through JSON before structpb.NewStruct +// so the proto path accepts the same value types the JSON path does. +func jsonNormalizeStruct(m map[string]any) (*structpb.Struct, error) { + bs, err := json.Marshal(m) + if err != nil { + return nil, err + } + var normalized map[string]any + if err := json.Unmarshal(bs, &normalized); err != nil { + return nil, err + } + return structpb.NewStruct(normalized) +} + +func jsonNormalizeValue(v any) (*structpb.Value, error) { + bs, err := json.Marshal(v) + if err != nil { + return nil, err + } + var normalized any + if err := json.Unmarshal(bs, &normalized); err != nil { + return nil, err + } + return structpb.NewValue(normalized) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go new file mode 100644 index 0000000000..018001f2d9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go @@ -0,0 +1,751 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.35.1 +// source: v1/bundle/manifest.proto + +package v1pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Manifest mirrors `bundle.Manifest` in v1/bundle/bundle.go. +type Manifest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Bundle revision string. + Revision *string `protobuf:"bytes,1,opt,name=revision" json:"revision,omitempty"` + // Root paths the bundle owns. See `roots_set` for nil-vs-empty. + Roots []string `protobuf:"bytes,2,rep,name=roots" json:"roots,omitempty"` + // Wasm resolvers attached to the bundle. JSON key is `wasm`. + Wasm []*WasmResolver `protobuf:"bytes,3,rep,name=wasm" json:"wasm,omitempty"` + // Global Rego version for the bundle. Currently 0 (RegoV0) or 1 (RegoV1). + RegoVersion *int32 `protobuf:"varint,4,opt,name=rego_version,json=regoVersion" json:"rego_version,omitempty"` + // Per-file Rego version overrides keyed by file path. + FileRegoVersions map[string]int32 `protobuf:"bytes,5,rep,name=file_rego_versions,json=fileRegoVersions" json:"file_rego_versions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` + // Free-form metadata object. Modeled as `Struct` because the Go field + // is `map[string]any`. + Metadata *structpb.Struct `protobuf:"bytes,6,opt,name=metadata" json:"metadata,omitempty"` + // True iff `bundle.Manifest.Roots` was non-nil. `repeated string` can't + // distinguish nil (default to [""]) from explicit-empty (owns no paths). + RootsSet *bool `protobuf:"varint,7,opt,name=roots_set,json=rootsSet" json:"roots_set,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Manifest) Reset() { + *x = Manifest{} + mi := &file_v1_bundle_manifest_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Manifest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Manifest) ProtoMessage() {} + +func (x *Manifest) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Manifest.ProtoReflect.Descriptor instead. +func (*Manifest) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{0} +} + +func (x *Manifest) GetRevision() string { + if x != nil && x.Revision != nil { + return *x.Revision + } + return "" +} + +func (x *Manifest) GetRoots() []string { + if x != nil { + return x.Roots + } + return nil +} + +func (x *Manifest) GetWasm() []*WasmResolver { + if x != nil { + return x.Wasm + } + return nil +} + +func (x *Manifest) GetRegoVersion() int32 { + if x != nil && x.RegoVersion != nil { + return *x.RegoVersion + } + return 0 +} + +func (x *Manifest) GetFileRegoVersions() map[string]int32 { + if x != nil { + return x.FileRegoVersions + } + return nil +} + +func (x *Manifest) GetMetadata() *structpb.Struct { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *Manifest) GetRootsSet() bool { + if x != nil && x.RootsSet != nil { + return *x.RootsSet + } + return false +} + +// WasmResolver mirrors `bundle.WasmResolver` in v1/bundle/bundle.go. +type WasmResolver struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Entrypoint policy ref this resolver targets. + Entrypoint *string `protobuf:"bytes,1,opt,name=entrypoint" json:"entrypoint,omitempty"` + // Path to the wasm module within the bundle. + Module *string `protobuf:"bytes,2,opt,name=module" json:"module,omitempty"` + // Rego annotations attached to the entrypoint. + Annotations []*Annotations `protobuf:"bytes,3,rep,name=annotations" json:"annotations,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WasmResolver) Reset() { + *x = WasmResolver{} + mi := &file_v1_bundle_manifest_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WasmResolver) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WasmResolver) ProtoMessage() {} + +func (x *WasmResolver) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WasmResolver.ProtoReflect.Descriptor instead. +func (*WasmResolver) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{1} +} + +func (x *WasmResolver) GetEntrypoint() string { + if x != nil && x.Entrypoint != nil { + return *x.Entrypoint + } + return "" +} + +func (x *WasmResolver) GetModule() string { + if x != nil && x.Module != nil { + return *x.Module + } + return "" +} + +func (x *WasmResolver) GetAnnotations() []*Annotations { + if x != nil { + return x.Annotations + } + return nil +} + +// Annotations mirrors `ast.Annotations` in v1/ast/annotations.go. +type Annotations struct { + state protoimpl.MessageState `protogen:"open.v1"` + Scope *string `protobuf:"bytes,1,opt,name=scope" json:"scope,omitempty"` + Title *string `protobuf:"bytes,2,opt,name=title" json:"title,omitempty"` + Entrypoint *bool `protobuf:"varint,3,opt,name=entrypoint" json:"entrypoint,omitempty"` + Description *string `protobuf:"bytes,4,opt,name=description" json:"description,omitempty"` + Organizations []string `protobuf:"bytes,5,rep,name=organizations" json:"organizations,omitempty"` + RelatedResources []*RelatedResourceAnnotation `protobuf:"bytes,6,rep,name=related_resources,json=relatedResources" json:"related_resources,omitempty"` + Authors []*AuthorAnnotation `protobuf:"bytes,7,rep,name=authors" json:"authors,omitempty"` + Schemas []*SchemaAnnotation `protobuf:"bytes,8,rep,name=schemas" json:"schemas,omitempty"` + Compile *CompileAnnotation `protobuf:"bytes,9,opt,name=compile" json:"compile,omitempty"` + // `custom` and `labels` are `map[string]any` in Go — genuinely + // free-form, so `Struct` is the right model. + Custom *structpb.Struct `protobuf:"bytes,10,opt,name=custom" json:"custom,omitempty"` + Labels *structpb.Struct `protobuf:"bytes,11,opt,name=labels" json:"labels,omitempty"` + Location *Location `protobuf:"bytes,12,opt,name=location" json:"location,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Annotations) Reset() { + *x = Annotations{} + mi := &file_v1_bundle_manifest_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Annotations) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Annotations) ProtoMessage() {} + +func (x *Annotations) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Annotations.ProtoReflect.Descriptor instead. +func (*Annotations) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{2} +} + +func (x *Annotations) GetScope() string { + if x != nil && x.Scope != nil { + return *x.Scope + } + return "" +} + +func (x *Annotations) GetTitle() string { + if x != nil && x.Title != nil { + return *x.Title + } + return "" +} + +func (x *Annotations) GetEntrypoint() bool { + if x != nil && x.Entrypoint != nil { + return *x.Entrypoint + } + return false +} + +func (x *Annotations) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *Annotations) GetOrganizations() []string { + if x != nil { + return x.Organizations + } + return nil +} + +func (x *Annotations) GetRelatedResources() []*RelatedResourceAnnotation { + if x != nil { + return x.RelatedResources + } + return nil +} + +func (x *Annotations) GetAuthors() []*AuthorAnnotation { + if x != nil { + return x.Authors + } + return nil +} + +func (x *Annotations) GetSchemas() []*SchemaAnnotation { + if x != nil { + return x.Schemas + } + return nil +} + +func (x *Annotations) GetCompile() *CompileAnnotation { + if x != nil { + return x.Compile + } + return nil +} + +func (x *Annotations) GetCustom() *structpb.Struct { + if x != nil { + return x.Custom + } + return nil +} + +func (x *Annotations) GetLabels() *structpb.Struct { + if x != nil { + return x.Labels + } + return nil +} + +func (x *Annotations) GetLocation() *Location { + if x != nil { + return x.Location + } + return nil +} + +// SchemaAnnotation mirrors `ast.SchemaAnnotation`. Path/Schema are +// `ast.Ref` in Go (a list of terms); the wire form is the canonical +// dotted ref string (e.g. `data.foo.bar`). Modeling the term tree +// faithfully would pull most of the AST into this schema and isn't +// worth the cost for annotations. +type SchemaAnnotation struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path *string `protobuf:"bytes,1,opt,name=path" json:"path,omitempty"` + Schema *string `protobuf:"bytes,2,opt,name=schema" json:"schema,omitempty"` + // `*any` on the Go side — a parsed JSON Schema document or any + // JSON value. `Value` (not `Struct`) because the top level may be + // a scalar, list, or null, not just an object. + Definition *structpb.Value `protobuf:"bytes,3,opt,name=definition" json:"definition,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SchemaAnnotation) Reset() { + *x = SchemaAnnotation{} + mi := &file_v1_bundle_manifest_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SchemaAnnotation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SchemaAnnotation) ProtoMessage() {} + +func (x *SchemaAnnotation) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SchemaAnnotation.ProtoReflect.Descriptor instead. +func (*SchemaAnnotation) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{3} +} + +func (x *SchemaAnnotation) GetPath() string { + if x != nil && x.Path != nil { + return *x.Path + } + return "" +} + +func (x *SchemaAnnotation) GetSchema() string { + if x != nil && x.Schema != nil { + return *x.Schema + } + return "" +} + +func (x *SchemaAnnotation) GetDefinition() *structpb.Value { + if x != nil { + return x.Definition + } + return nil +} + +// CompileAnnotation mirrors `ast.CompileAnnotation`. Refs are the +// canonical dotted form; see SchemaAnnotation for the trade-off. +type CompileAnnotation struct { + state protoimpl.MessageState `protogen:"open.v1"` + Unknowns []string `protobuf:"bytes,1,rep,name=unknowns" json:"unknowns,omitempty"` + MaskRule *string `protobuf:"bytes,2,opt,name=mask_rule,json=maskRule" json:"mask_rule,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CompileAnnotation) Reset() { + *x = CompileAnnotation{} + mi := &file_v1_bundle_manifest_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CompileAnnotation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompileAnnotation) ProtoMessage() {} + +func (x *CompileAnnotation) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompileAnnotation.ProtoReflect.Descriptor instead. +func (*CompileAnnotation) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{4} +} + +func (x *CompileAnnotation) GetUnknowns() []string { + if x != nil { + return x.Unknowns + } + return nil +} + +func (x *CompileAnnotation) GetMaskRule() string { + if x != nil && x.MaskRule != nil { + return *x.MaskRule + } + return "" +} + +// AuthorAnnotation mirrors `ast.AuthorAnnotation`. +type AuthorAnnotation struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Email *string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AuthorAnnotation) Reset() { + *x = AuthorAnnotation{} + mi := &file_v1_bundle_manifest_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AuthorAnnotation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthorAnnotation) ProtoMessage() {} + +func (x *AuthorAnnotation) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AuthorAnnotation.ProtoReflect.Descriptor instead. +func (*AuthorAnnotation) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{5} +} + +func (x *AuthorAnnotation) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *AuthorAnnotation) GetEmail() string { + if x != nil && x.Email != nil { + return *x.Email + } + return "" +} + +// RelatedResourceAnnotation mirrors `ast.RelatedResourceAnnotation`. +// `Ref` is a `url.URL` in Go, serialized to its `String()` form. +type RelatedResourceAnnotation struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ref *string `protobuf:"bytes,1,opt,name=ref" json:"ref,omitempty"` + Description *string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RelatedResourceAnnotation) Reset() { + *x = RelatedResourceAnnotation{} + mi := &file_v1_bundle_manifest_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RelatedResourceAnnotation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RelatedResourceAnnotation) ProtoMessage() {} + +func (x *RelatedResourceAnnotation) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RelatedResourceAnnotation.ProtoReflect.Descriptor instead. +func (*RelatedResourceAnnotation) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{6} +} + +func (x *RelatedResourceAnnotation) GetRef() string { + if x != nil && x.Ref != nil { + return *x.Ref + } + return "" +} + +func (x *RelatedResourceAnnotation) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +// Location mirrors `ast.Location` (= `location.Location`). Only the +// File/Row/Col triple is wire-relevant; `Text`, `Offset`, and `Tabs` +// are tagged `json:"-"` and intentionally absent. +// +// Distinct from `ir.Location`, which is promoted onto the `Stmt` +// envelope in plan.proto. The two are independent Go types. +type Location struct { + state protoimpl.MessageState `protogen:"open.v1"` + File *string `protobuf:"bytes,1,opt,name=file" json:"file,omitempty"` + Row *int32 `protobuf:"varint,2,opt,name=row" json:"row,omitempty"` + Col *int32 `protobuf:"varint,3,opt,name=col" json:"col,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Location) Reset() { + *x = Location{} + mi := &file_v1_bundle_manifest_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Location) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Location) ProtoMessage() {} + +func (x *Location) ProtoReflect() protoreflect.Message { + mi := &file_v1_bundle_manifest_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Location.ProtoReflect.Descriptor instead. +func (*Location) Descriptor() ([]byte, []int) { + return file_v1_bundle_manifest_proto_rawDescGZIP(), []int{7} +} + +func (x *Location) GetFile() string { + if x != nil && x.File != nil { + return *x.File + } + return "" +} + +func (x *Location) GetRow() int32 { + if x != nil && x.Row != nil { + return *x.Row + } + return 0 +} + +func (x *Location) GetCol() int32 { + if x != nil && x.Col != nil { + return *x.Col + } + return 0 +} + +var File_v1_bundle_manifest_proto protoreflect.FileDescriptor + +const file_v1_bundle_manifest_proto_rawDesc = "" + + "\n" + + "\x18v1/bundle/manifest.proto\x12\ropa.bundle.v1\x1a\x1cgoogle/protobuf/struct.proto\"\x84\x03\n" + + "\bManifest\x12\x1a\n" + + "\brevision\x18\x01 \x01(\tR\brevision\x12\x14\n" + + "\x05roots\x18\x02 \x03(\tR\x05roots\x12/\n" + + "\x04wasm\x18\x03 \x03(\v2\x1b.opa.bundle.v1.WasmResolverR\x04wasm\x12!\n" + + "\frego_version\x18\x04 \x01(\x05R\vregoVersion\x12[\n" + + "\x12file_rego_versions\x18\x05 \x03(\v2-.opa.bundle.v1.Manifest.FileRegoVersionsEntryR\x10fileRegoVersions\x123\n" + + "\bmetadata\x18\x06 \x01(\v2\x17.google.protobuf.StructR\bmetadata\x12\x1b\n" + + "\troots_set\x18\a \x01(\bR\brootsSet\x1aC\n" + + "\x15FileRegoVersionsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01\"\x84\x01\n" + + "\fWasmResolver\x12\x1e\n" + + "\n" + + "entrypoint\x18\x01 \x01(\tR\n" + + "entrypoint\x12\x16\n" + + "\x06module\x18\x02 \x01(\tR\x06module\x12<\n" + + "\vannotations\x18\x03 \x03(\v2\x1a.opa.bundle.v1.AnnotationsR\vannotations\"\xc1\x04\n" + + "\vAnnotations\x12\x14\n" + + "\x05scope\x18\x01 \x01(\tR\x05scope\x12\x14\n" + + "\x05title\x18\x02 \x01(\tR\x05title\x12\x1e\n" + + "\n" + + "entrypoint\x18\x03 \x01(\bR\n" + + "entrypoint\x12 \n" + + "\vdescription\x18\x04 \x01(\tR\vdescription\x12$\n" + + "\rorganizations\x18\x05 \x03(\tR\rorganizations\x12U\n" + + "\x11related_resources\x18\x06 \x03(\v2(.opa.bundle.v1.RelatedResourceAnnotationR\x10relatedResources\x129\n" + + "\aauthors\x18\a \x03(\v2\x1f.opa.bundle.v1.AuthorAnnotationR\aauthors\x129\n" + + "\aschemas\x18\b \x03(\v2\x1f.opa.bundle.v1.SchemaAnnotationR\aschemas\x12:\n" + + "\acompile\x18\t \x01(\v2 .opa.bundle.v1.CompileAnnotationR\acompile\x12/\n" + + "\x06custom\x18\n" + + " \x01(\v2\x17.google.protobuf.StructR\x06custom\x12/\n" + + "\x06labels\x18\v \x01(\v2\x17.google.protobuf.StructR\x06labels\x123\n" + + "\blocation\x18\f \x01(\v2\x17.opa.bundle.v1.LocationR\blocation\"v\n" + + "\x10SchemaAnnotation\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\x12\x16\n" + + "\x06schema\x18\x02 \x01(\tR\x06schema\x126\n" + + "\n" + + "definition\x18\x03 \x01(\v2\x16.google.protobuf.ValueR\n" + + "definition\"L\n" + + "\x11CompileAnnotation\x12\x1a\n" + + "\bunknowns\x18\x01 \x03(\tR\bunknowns\x12\x1b\n" + + "\tmask_rule\x18\x02 \x01(\tR\bmaskRule\"<\n" + + "\x10AuthorAnnotation\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + + "\x05email\x18\x02 \x01(\tR\x05email\"O\n" + + "\x19RelatedResourceAnnotation\x12\x10\n" + + "\x03ref\x18\x01 \x01(\tR\x03ref\x12 \n" + + "\vdescription\x18\x02 \x01(\tR\vdescription\"B\n" + + "\bLocation\x12\x12\n" + + "\x04file\x18\x01 \x01(\tR\x04file\x12\x10\n" + + "\x03row\x18\x02 \x01(\x05R\x03row\x12\x10\n" + + "\x03col\x18\x03 \x01(\x05R\x03colB3P\x01Z/github.com/open-policy-agent/opa/v1/bundle/v1pbb\beditionsp\xe8\a" + +var ( + file_v1_bundle_manifest_proto_rawDescOnce sync.Once + file_v1_bundle_manifest_proto_rawDescData []byte +) + +func file_v1_bundle_manifest_proto_rawDescGZIP() []byte { + file_v1_bundle_manifest_proto_rawDescOnce.Do(func() { + file_v1_bundle_manifest_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_v1_bundle_manifest_proto_rawDesc), len(file_v1_bundle_manifest_proto_rawDesc))) + }) + return file_v1_bundle_manifest_proto_rawDescData +} + +var file_v1_bundle_manifest_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_v1_bundle_manifest_proto_goTypes = []any{ + (*Manifest)(nil), // 0: opa.bundle.v1.Manifest + (*WasmResolver)(nil), // 1: opa.bundle.v1.WasmResolver + (*Annotations)(nil), // 2: opa.bundle.v1.Annotations + (*SchemaAnnotation)(nil), // 3: opa.bundle.v1.SchemaAnnotation + (*CompileAnnotation)(nil), // 4: opa.bundle.v1.CompileAnnotation + (*AuthorAnnotation)(nil), // 5: opa.bundle.v1.AuthorAnnotation + (*RelatedResourceAnnotation)(nil), // 6: opa.bundle.v1.RelatedResourceAnnotation + (*Location)(nil), // 7: opa.bundle.v1.Location + nil, // 8: opa.bundle.v1.Manifest.FileRegoVersionsEntry + (*structpb.Struct)(nil), // 9: google.protobuf.Struct + (*structpb.Value)(nil), // 10: google.protobuf.Value +} +var file_v1_bundle_manifest_proto_depIdxs = []int32{ + 1, // 0: opa.bundle.v1.Manifest.wasm:type_name -> opa.bundle.v1.WasmResolver + 8, // 1: opa.bundle.v1.Manifest.file_rego_versions:type_name -> opa.bundle.v1.Manifest.FileRegoVersionsEntry + 9, // 2: opa.bundle.v1.Manifest.metadata:type_name -> google.protobuf.Struct + 2, // 3: opa.bundle.v1.WasmResolver.annotations:type_name -> opa.bundle.v1.Annotations + 6, // 4: opa.bundle.v1.Annotations.related_resources:type_name -> opa.bundle.v1.RelatedResourceAnnotation + 5, // 5: opa.bundle.v1.Annotations.authors:type_name -> opa.bundle.v1.AuthorAnnotation + 3, // 6: opa.bundle.v1.Annotations.schemas:type_name -> opa.bundle.v1.SchemaAnnotation + 4, // 7: opa.bundle.v1.Annotations.compile:type_name -> opa.bundle.v1.CompileAnnotation + 9, // 8: opa.bundle.v1.Annotations.custom:type_name -> google.protobuf.Struct + 9, // 9: opa.bundle.v1.Annotations.labels:type_name -> google.protobuf.Struct + 7, // 10: opa.bundle.v1.Annotations.location:type_name -> opa.bundle.v1.Location + 10, // 11: opa.bundle.v1.SchemaAnnotation.definition:type_name -> google.protobuf.Value + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_v1_bundle_manifest_proto_init() } +func file_v1_bundle_manifest_proto_init() { + if File_v1_bundle_manifest_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_bundle_manifest_proto_rawDesc), len(file_v1_bundle_manifest_proto_rawDesc)), + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v1_bundle_manifest_proto_goTypes, + DependencyIndexes: file_v1_bundle_manifest_proto_depIdxs, + MessageInfos: file_v1_bundle_manifest_proto_msgTypes, + }.Build() + File_v1_bundle_manifest_proto = out.File + file_v1_bundle_manifest_proto_goTypes = nil + file_v1_bundle_manifest_proto_depIdxs = nil +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/format/format.go b/vendor/github.com/open-policy-agent/opa/v1/format/format.go index dfb4746334..1f25938bae 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/format/format.go +++ b/vendor/github.com/open-policy-agent/opa/v1/format/format.go @@ -640,7 +640,12 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) if (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException { w.write(" if") if len(rule.Body) == 1 { - if rule.Body[0].Location.Row == rule.Head.Location.Row { + // Keep `if ` on one line when the single body term sits on the + // same line as the end of the head. Comparing against the head's + // start row would wrongly expand the condition into a block whenever + // the head value spans multiple lines (e.g. a multi-line call). + headEndRow := rule.Head.Location.Row + strings.Count(string(rule.Head.Location.Text), "\n") + if rule.Body[0].Location.Row == headEndRow { w.write(" ") var err error comments, err = w.writeExpr(rule.Body[0], comments) diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go b/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go new file mode 100644 index 0000000000..0c89dcb105 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go @@ -0,0 +1,340 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import ( + "fmt" + "math" + + "google.golang.org/protobuf/proto" + + pb "github.com/open-policy-agent/opa/v1/ir/v1pb" +) + +// PolicyToProto converts an IR Policy to its protobuf wire-form, +// defined in v1/ir/plan.proto. Returns an error if the policy contains +// a Stmt or Val kind not yet covered by the encoder switch. +func PolicyToProto(p *Policy) (out *pb.Policy, err error) { + if p == nil { + return nil, nil + } + defer func() { + if r := recover(); r != nil { + out = nil + err = fmt.Errorf("ir: PolicyToProto: %v", r) + } + }() + return &pb.Policy{ + Static: staticToProto(p.Static), + Plans: plansToProto(p.Plans), + Funcs: funcsToProto(p.Funcs), + }, nil +} + +func staticToProto(s *Static) *pb.Static { + if s == nil { + return nil + } + out := &pb.Static{ + Strings: make([]*pb.StringConst, len(s.Strings)), + BuiltinFuncs: make([]*pb.BuiltinFunc, len(s.BuiltinFuncs)), + Files: make([]*pb.StringConst, len(s.Files)), + } + for i, sc := range s.Strings { + out.Strings[i] = stringConstToProto(sc) + } + for i, bf := range s.BuiltinFuncs { + out.BuiltinFuncs[i] = builtinFuncToProto(bf) + } + for i, f := range s.Files { + out.Files[i] = stringConstToProto(f) + } + return out +} + +func stringConstToProto(s *StringConst) *pb.StringConst { + if s == nil { + return nil + } + return &pb.StringConst{Value: proto.String(s.Value)} +} + +func builtinFuncToProto(b *BuiltinFunc) *pb.BuiltinFunc { + if b == nil { + return nil + } + return &pb.BuiltinFunc{Name: proto.String(b.Name)} +} + +func plansToProto(p *Plans) *pb.Plans { + if p == nil { + return nil + } + out := &pb.Plans{Plans: make([]*pb.Plan, len(p.Plans))} + for i, pl := range p.Plans { + out.Plans[i] = planToProto(pl) + } + return out +} + +func planToProto(p *Plan) *pb.Plan { + if p == nil { + return nil + } + out := &pb.Plan{Name: proto.String(p.Name), Blocks: make([]*pb.Block, len(p.Blocks))} + for i, b := range p.Blocks { + out.Blocks[i] = blockToProto(b) + } + return out +} + +func funcsToProto(f *Funcs) *pb.Funcs { + if f == nil { + return nil + } + out := &pb.Funcs{Funcs: make([]*pb.Func, len(f.Funcs))} + for i, fn := range f.Funcs { + out.Funcs[i] = funcToProto(fn) + } + return out +} + +func funcToProto(f *Func) *pb.Func { + if f == nil { + return nil + } + out := &pb.Func{ + Name: proto.String(f.Name), + Params: localsToInt32s(f.Params), + Result: proto.Int32(toInt32(f.Return)), + Blocks: make([]*pb.Block, len(f.Blocks)), + Path: f.Path, + } + for i, b := range f.Blocks { + out.Blocks[i] = blockToProto(b) + } + return out +} + +func blockToProto(b *Block) *pb.Block { + if b == nil { + return nil + } + out := &pb.Block{Stmts: make([]*pb.Stmt, len(b.Stmts))} + for i, s := range b.Stmts { + out.Stmts[i] = stmtToProto(s) + } + return out +} + +func operandToProto(o Operand) *pb.Operand { + return &pb.Operand{Value: valToProto(o.Value)} +} + +func operandsToProto(os []Operand) []*pb.Operand { + out := make([]*pb.Operand, len(os)) + for i, o := range os { + out[i] = operandToProto(o) + } + return out +} + +func valToProto(v Val) *pb.Val { + if v == nil { + return nil + } + switch x := v.(type) { + case Local: + return &pb.Val{Kind: &pb.Val_Local{Local: toInt32(x)}} + case StringIndex: + return &pb.Val{Kind: &pb.Val_StringIndex{StringIndex: toInt32(x)}} + case Bool: + return &pb.Val{Kind: &pb.Val_Bool{Bool: bool(x)}} + default: + panic(fmt.Sprintf("unsupported Val type %T", v)) + } +} + +// toInt32 narrows an int-based value to int32, panicking if it would +// overflow. PolicyToProto recovers from the panic and returns it as an +// error, so callers don't need to check the bound themselves. +func toInt32[T ~int](v T) int32 { + if int64(v) > math.MaxInt32 || int64(v) < math.MinInt32 { + panic(fmt.Sprintf("value %d overflows int32", int64(v))) + } + return int32(v) +} + +func localsToInt32s(ls []Local) []int32 { + out := make([]int32, len(ls)) + for i, l := range ls { + out[i] = toInt32(l) + } + return out +} + +func intsToInt32s(is []int) []int32 { + out := make([]int32, len(is)) + for i, v := range is { + out[i] = toInt32(v) + } + return out +} + +func stmtToProto(s Stmt) *pb.Stmt { + if s == nil { + return nil + } + loc := s.GetLocation() + out := &pb.Stmt{ + File: proto.Int32(toInt32(loc.File)), + Col: proto.Int32(toInt32(loc.Col)), + Row: proto.Int32(toInt32(loc.Row)), + } + switch x := s.(type) { + case *ArrayAppendStmt: + out.Kind = &pb.Stmt_ArrayAppendStmt{ArrayAppendStmt: &pb.ArrayAppendStmt{ + Value: operandToProto(x.Value), + Array: proto.Int32(toInt32(x.Array)), + }} + case *AssignIntStmt: + out.Kind = &pb.Stmt_AssignIntStmt{AssignIntStmt: &pb.AssignIntStmt{ + Value: proto.Int64(x.Value), + Target: proto.Int32(toInt32(x.Target)), + }} + case *AssignVarOnceStmt: + out.Kind = &pb.Stmt_AssignVarOnceStmt{AssignVarOnceStmt: &pb.AssignVarOnceStmt{ + Source: operandToProto(x.Source), + Target: proto.Int32(toInt32(x.Target)), + }} + case *AssignVarStmt: + out.Kind = &pb.Stmt_AssignVarStmt{AssignVarStmt: &pb.AssignVarStmt{ + Source: operandToProto(x.Source), + Target: proto.Int32(toInt32(x.Target)), + }} + case *BlockStmt: + body := &pb.BlockStmt{Blocks: make([]*pb.Block, len(x.Blocks))} + for i, b := range x.Blocks { + body.Blocks[i] = blockToProto(b) + } + out.Kind = &pb.Stmt_BlockStmt{BlockStmt: body} + case *BreakStmt: + out.Kind = &pb.Stmt_BreakStmt{BreakStmt: &pb.BreakStmt{Index: proto.Uint32(x.Index)}} + case *CallDynamicStmt: + out.Kind = &pb.Stmt_CallDynamicStmt{CallDynamicStmt: &pb.CallDynamicStmt{ + Args: localsToInt32s(x.Args), + Result: proto.Int32(toInt32(x.Result)), + Path: operandsToProto(x.Path), + }} + case *CallStmt: + out.Kind = &pb.Stmt_CallStmt{CallStmt: &pb.CallStmt{ + Function: proto.String(x.Func), + Args: operandsToProto(x.Args), + Result: proto.Int32(toInt32(x.Result)), + }} + case *DotStmt: + out.Kind = &pb.Stmt_DotStmt{DotStmt: &pb.DotStmt{ + Source: operandToProto(x.Source), + Key: operandToProto(x.Key), + Target: proto.Int32(toInt32(x.Target)), + }} + case *EqualStmt: + out.Kind = &pb.Stmt_EqualStmt{EqualStmt: &pb.EqualStmt{ + A: operandToProto(x.A), + B: operandToProto(x.B), + }} + case *IsArrayStmt: + out.Kind = &pb.Stmt_IsArrayStmt{IsArrayStmt: &pb.IsArrayStmt{Source: operandToProto(x.Source)}} + case *IsDefinedStmt: + out.Kind = &pb.Stmt_IsDefinedStmt{IsDefinedStmt: &pb.IsDefinedStmt{Source: proto.Int32(toInt32(x.Source))}} + case *IsObjectStmt: + out.Kind = &pb.Stmt_IsObjectStmt{IsObjectStmt: &pb.IsObjectStmt{Source: operandToProto(x.Source)}} + case *IsSetStmt: + out.Kind = &pb.Stmt_IsSetStmt{IsSetStmt: &pb.IsSetStmt{Source: operandToProto(x.Source)}} + case *IsUndefinedStmt: + out.Kind = &pb.Stmt_IsUndefinedStmt{IsUndefinedStmt: &pb.IsUndefinedStmt{Source: proto.Int32(toInt32(x.Source))}} + case *LenStmt: + out.Kind = &pb.Stmt_LenStmt{LenStmt: &pb.LenStmt{ + Source: operandToProto(x.Source), + Target: proto.Int32(toInt32(x.Target)), + }} + case *MakeArrayStmt: + out.Kind = &pb.Stmt_MakeArrayStmt{MakeArrayStmt: &pb.MakeArrayStmt{ + Capacity: proto.Int32(x.Capacity), + Target: proto.Int32(toInt32(x.Target)), + }} + case *MakeNullStmt: + out.Kind = &pb.Stmt_MakeNullStmt{MakeNullStmt: &pb.MakeNullStmt{Target: proto.Int32(toInt32(x.Target))}} + case *MakeNumberIntStmt: + out.Kind = &pb.Stmt_MakeNumberIntStmt{MakeNumberIntStmt: &pb.MakeNumberIntStmt{ + Value: proto.Int64(x.Value), + Target: proto.Int32(toInt32(x.Target)), + }} + case *MakeNumberRefStmt: + out.Kind = &pb.Stmt_MakeNumberRefStmt{MakeNumberRefStmt: &pb.MakeNumberRefStmt{ + Index: proto.Int32(toInt32(x.Index)), + Target: proto.Int32(toInt32(x.Target)), + }} + case *MakeObjectStmt: + out.Kind = &pb.Stmt_MakeObjectStmt{MakeObjectStmt: &pb.MakeObjectStmt{Target: proto.Int32(toInt32(x.Target))}} + case *MakeSetStmt: + out.Kind = &pb.Stmt_MakeSetStmt{MakeSetStmt: &pb.MakeSetStmt{Target: proto.Int32(toInt32(x.Target))}} + case *NopStmt: + out.Kind = &pb.Stmt_NopStmt{NopStmt: &pb.NopStmt{}} + case *NotEqualStmt: + out.Kind = &pb.Stmt_NotEqualStmt{NotEqualStmt: &pb.NotEqualStmt{ + A: operandToProto(x.A), + B: operandToProto(x.B), + }} + case *NotStmt: + out.Kind = &pb.Stmt_NotStmt{NotStmt: &pb.NotStmt{Block: blockToProto(x.Block)}} + case *ObjectInsertOnceStmt: + out.Kind = &pb.Stmt_ObjectInsertOnceStmt{ObjectInsertOnceStmt: &pb.ObjectInsertOnceStmt{ + Key: operandToProto(x.Key), + Value: operandToProto(x.Value), + Object: proto.Int32(toInt32(x.Object)), + }} + case *ObjectInsertStmt: + out.Kind = &pb.Stmt_ObjectInsertStmt{ObjectInsertStmt: &pb.ObjectInsertStmt{ + Key: operandToProto(x.Key), + Value: operandToProto(x.Value), + Object: proto.Int32(toInt32(x.Object)), + }} + case *ObjectMergeStmt: + out.Kind = &pb.Stmt_ObjectMergeStmt{ObjectMergeStmt: &pb.ObjectMergeStmt{ + A: proto.Int32(toInt32(x.A)), + B: proto.Int32(toInt32(x.B)), + Target: proto.Int32(toInt32(x.Target)), + }} + case *ResetLocalStmt: + out.Kind = &pb.Stmt_ResetLocalStmt{ResetLocalStmt: &pb.ResetLocalStmt{Target: proto.Int32(toInt32(x.Target))}} + case *ResultSetAddStmt: + out.Kind = &pb.Stmt_ResultSetAddStmt{ResultSetAddStmt: &pb.ResultSetAddStmt{Value: proto.Int32(toInt32(x.Value))}} + case *ReturnLocalStmt: + out.Kind = &pb.Stmt_ReturnLocalStmt{ReturnLocalStmt: &pb.ReturnLocalStmt{Source: proto.Int32(toInt32(x.Source))}} + case *ScanStmt: + out.Kind = &pb.Stmt_ScanStmt{ScanStmt: &pb.ScanStmt{ + Source: proto.Int32(toInt32(x.Source)), + Key: proto.Int32(toInt32(x.Key)), + Value: proto.Int32(toInt32(x.Value)), + Block: blockToProto(x.Block), + }} + case *SetAddStmt: + out.Kind = &pb.Stmt_SetAddStmt{SetAddStmt: &pb.SetAddStmt{ + Value: operandToProto(x.Value), + Set: proto.Int32(toInt32(x.Set)), + }} + case *WithStmt: + out.Kind = &pb.Stmt_WithStmt{WithStmt: &pb.WithStmt{ + Local: proto.Int32(toInt32(x.Local)), + Path: intsToInt32s(x.Path), + Value: operandToProto(x.Value), + Block: blockToProto(x.Block), + }} + default: + panic(fmt.Sprintf("unsupported Stmt type %T", s)) + } + return out +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go b/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go new file mode 100644 index 0000000000..a47afbcc0f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go @@ -0,0 +1,3449 @@ +// Copyright 2026 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.35.1 +// source: v1/ir/plan.proto + +package v1pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Policy mirrors `ir.Policy` in v1/ir/ir.go. +type Policy struct { + state protoimpl.MessageState `protogen:"open.v1"` + Static *Static `protobuf:"bytes,1,opt,name=static" json:"static,omitempty"` + Plans *Plans `protobuf:"bytes,2,opt,name=plans" json:"plans,omitempty"` + Funcs *Funcs `protobuf:"bytes,3,opt,name=funcs" json:"funcs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Policy) Reset() { + *x = Policy{} + mi := &file_v1_ir_plan_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Policy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Policy) ProtoMessage() {} + +func (x *Policy) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Policy.ProtoReflect.Descriptor instead. +func (*Policy) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{0} +} + +func (x *Policy) GetStatic() *Static { + if x != nil { + return x.Static + } + return nil +} + +func (x *Policy) GetPlans() *Plans { + if x != nil { + return x.Plans + } + return nil +} + +func (x *Policy) GetFuncs() *Funcs { + if x != nil { + return x.Funcs + } + return nil +} + +// Static mirrors `ir.Static` in v1/ir/ir.go. +type Static struct { + state protoimpl.MessageState `protogen:"open.v1"` + Strings []*StringConst `protobuf:"bytes,1,rep,name=strings" json:"strings,omitempty"` + BuiltinFuncs []*BuiltinFunc `protobuf:"bytes,2,rep,name=builtin_funcs,json=builtinFuncs" json:"builtin_funcs,omitempty"` + Files []*StringConst `protobuf:"bytes,3,rep,name=files" json:"files,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Static) Reset() { + *x = Static{} + mi := &file_v1_ir_plan_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Static) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Static) ProtoMessage() {} + +func (x *Static) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Static.ProtoReflect.Descriptor instead. +func (*Static) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{1} +} + +func (x *Static) GetStrings() []*StringConst { + if x != nil { + return x.Strings + } + return nil +} + +func (x *Static) GetBuiltinFuncs() []*BuiltinFunc { + if x != nil { + return x.BuiltinFuncs + } + return nil +} + +func (x *Static) GetFiles() []*StringConst { + if x != nil { + return x.Files + } + return nil +} + +// BuiltinFunc mirrors `ir.BuiltinFunc` in v1/ir/ir.go. +// +// `ir.BuiltinFunc.Decl` (the function's `types.Function` signature) is +// intentionally not modeled. Consumers that execute plans need their +// own builtin registry to dispatch host-language implementations, and +// that registry is the source of truth for signatures — bundling +// `Decl` here would be redundant and prone to drift. +type BuiltinFunc struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuiltinFunc) Reset() { + *x = BuiltinFunc{} + mi := &file_v1_ir_plan_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuiltinFunc) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuiltinFunc) ProtoMessage() {} + +func (x *BuiltinFunc) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuiltinFunc.ProtoReflect.Descriptor instead. +func (*BuiltinFunc) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{2} +} + +func (x *BuiltinFunc) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +// Plans mirrors `ir.Plans` in v1/ir/ir.go. +type Plans struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plans []*Plan `protobuf:"bytes,1,rep,name=plans" json:"plans,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Plans) Reset() { + *x = Plans{} + mi := &file_v1_ir_plan_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Plans) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plans) ProtoMessage() {} + +func (x *Plans) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Plans.ProtoReflect.Descriptor instead. +func (*Plans) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{3} +} + +func (x *Plans) GetPlans() []*Plan { + if x != nil { + return x.Plans + } + return nil +} + +// Funcs mirrors `ir.Funcs` in v1/ir/ir.go. +type Funcs struct { + state protoimpl.MessageState `protogen:"open.v1"` + Funcs []*Func `protobuf:"bytes,1,rep,name=funcs" json:"funcs,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Funcs) Reset() { + *x = Funcs{} + mi := &file_v1_ir_plan_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Funcs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Funcs) ProtoMessage() {} + +func (x *Funcs) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Funcs.ProtoReflect.Descriptor instead. +func (*Funcs) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{4} +} + +func (x *Funcs) GetFuncs() []*Func { + if x != nil { + return x.Funcs + } + return nil +} + +// Func mirrors `ir.Func` in v1/ir/ir.go. +// +// Each parameter and the return slot are local-variable indices; see +// `ir.Local` in v1/ir/ir.go. +type Func struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Params []int32 `protobuf:"varint,2,rep,packed,name=params" json:"params,omitempty"` + // The local that holds the function's return value. Renamed from + // `return` (the Go field is `Func.Return Local`, JSON-tagged `return`) + // to avoid colliding with a reserved keyword in many target languages + // when this proto is fed to protoc plugins. The JSON wire form + // continues to use `return`; only the proto-side identifier differs. + Result *int32 `protobuf:"varint,3,opt,name=result" json:"result,omitempty"` + Blocks []*Block `protobuf:"bytes,4,rep,name=blocks" json:"blocks,omitempty"` + Path []string `protobuf:"bytes,5,rep,name=path" json:"path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Func) Reset() { + *x = Func{} + mi := &file_v1_ir_plan_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Func) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Func) ProtoMessage() {} + +func (x *Func) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Func.ProtoReflect.Descriptor instead. +func (*Func) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{5} +} + +func (x *Func) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *Func) GetParams() []int32 { + if x != nil { + return x.Params + } + return nil +} + +func (x *Func) GetResult() int32 { + if x != nil && x.Result != nil { + return *x.Result + } + return 0 +} + +func (x *Func) GetBlocks() []*Block { + if x != nil { + return x.Blocks + } + return nil +} + +func (x *Func) GetPath() []string { + if x != nil { + return x.Path + } + return nil +} + +// Plan mirrors `ir.Plan` in v1/ir/ir.go. +type Plan struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Blocks []*Block `protobuf:"bytes,2,rep,name=blocks" json:"blocks,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Plan) Reset() { + *x = Plan{} + mi := &file_v1_ir_plan_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Plan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Plan) ProtoMessage() {} + +func (x *Plan) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Plan.ProtoReflect.Descriptor instead. +func (*Plan) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{6} +} + +func (x *Plan) GetName() string { + if x != nil && x.Name != nil { + return *x.Name + } + return "" +} + +func (x *Plan) GetBlocks() []*Block { + if x != nil { + return x.Blocks + } + return nil +} + +// Block mirrors `ir.Block` in v1/ir/ir.go. +type Block struct { + state protoimpl.MessageState `protogen:"open.v1"` + Stmts []*Stmt `protobuf:"bytes,1,rep,name=stmts" json:"stmts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Block) Reset() { + *x = Block{} + mi := &file_v1_ir_plan_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{7} +} + +func (x *Block) GetStmts() []*Stmt { + if x != nil { + return x.Stmts + } + return nil +} + +// StringConst mirrors `ir.StringConst` in v1/ir/ir.go. +type StringConst struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StringConst) Reset() { + *x = StringConst{} + mi := &file_v1_ir_plan_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StringConst) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringConst) ProtoMessage() {} + +func (x *StringConst) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringConst.ProtoReflect.Descriptor instead. +func (*StringConst) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{8} +} + +func (x *StringConst) GetValue() string { + if x != nil && x.Value != nil { + return *x.Value + } + return "" +} + +// Operand mirrors `ir.Operand` in v1/ir/ir.go. The `value` field is a +// polymorphic `Val` union; see the `Val` message below. +type Operand struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *Val `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Operand) Reset() { + *x = Operand{} + mi := &file_v1_ir_plan_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Operand) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Operand) ProtoMessage() {} + +func (x *Operand) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Operand.ProtoReflect.Descriptor instead. +func (*Operand) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{9} +} + +func (x *Operand) GetValue() *Val { + if x != nil { + return x.Value + } + return nil +} + +// Val mirrors the `ir.Val` interface in v1/ir/ir.go. Each oneof case +// corresponds to a concrete `Val` implementation; the case names match +// the JSON discriminator strings emitted by `*Operand.MarshalJSON`. +// +// Case-number assignments are a stability commitment. New cases must +// be added with the next unused number; existing numbers must never +// be repurposed. +type Val struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Kind: + // + // *Val_Bool + // *Val_Local + // *Val_StringIndex + Kind isVal_Kind `protobuf_oneof:"kind"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Val) Reset() { + *x = Val{} + mi := &file_v1_ir_plan_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Val) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Val) ProtoMessage() {} + +func (x *Val) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Val.ProtoReflect.Descriptor instead. +func (*Val) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{10} +} + +func (x *Val) GetKind() isVal_Kind { + if x != nil { + return x.Kind + } + return nil +} + +func (x *Val) GetBool() bool { + if x != nil { + if x, ok := x.Kind.(*Val_Bool); ok { + return x.Bool + } + } + return false +} + +func (x *Val) GetLocal() int32 { + if x != nil { + if x, ok := x.Kind.(*Val_Local); ok { + return x.Local + } + } + return 0 +} + +func (x *Val) GetStringIndex() int32 { + if x != nil { + if x, ok := x.Kind.(*Val_StringIndex); ok { + return x.StringIndex + } + } + return 0 +} + +type isVal_Kind interface { + isVal_Kind() +} + +type Val_Bool struct { + Bool bool `protobuf:"varint,1,opt,name=bool,oneof"` +} + +type Val_Local struct { + Local int32 `protobuf:"varint,2,opt,name=local,oneof"` +} + +type Val_StringIndex struct { + StringIndex int32 `protobuf:"varint,3,opt,name=string_index,json=stringIndex,oneof"` +} + +func (*Val_Bool) isVal_Kind() {} + +func (*Val_Local) isVal_Kind() {} + +func (*Val_StringIndex) isVal_Kind() {} + +// Stmt mirrors the `ir.Stmt` interface in v1/ir/ir.go. Every Stmt carries +// the source-location triple (file, col, row) on this envelope; the body +// messages below describe only the kind-specific payload. +// +// On the Go side, `ir.Location` is embedded into every concrete Stmt +// implementation, so `encoding/json` flattens File/Col/Row into the +// emitted JSON body. The proto promotes those fields to the envelope +// because that's both more idiomatic protobuf and lets every body +// message start its own field numbering at 1. +// +// Case-number assignments (4–37) are a stability commitment. Field +// numbers 1–3 are reserved for the location triple. New cases must be +// added with the next unused number; existing numbers must never be +// repurposed. +type Stmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + File *int32 `protobuf:"varint,1,opt,name=file" json:"file,omitempty"` + Col *int32 `protobuf:"varint,2,opt,name=col" json:"col,omitempty"` + Row *int32 `protobuf:"varint,3,opt,name=row" json:"row,omitempty"` + // Types that are valid to be assigned to Kind: + // + // *Stmt_ArrayAppendStmt + // *Stmt_AssignIntStmt + // *Stmt_AssignVarOnceStmt + // *Stmt_AssignVarStmt + // *Stmt_BlockStmt + // *Stmt_BreakStmt + // *Stmt_CallDynamicStmt + // *Stmt_CallStmt + // *Stmt_DotStmt + // *Stmt_EqualStmt + // *Stmt_IsArrayStmt + // *Stmt_IsDefinedStmt + // *Stmt_IsObjectStmt + // *Stmt_IsSetStmt + // *Stmt_IsUndefinedStmt + // *Stmt_LenStmt + // *Stmt_MakeArrayStmt + // *Stmt_MakeNullStmt + // *Stmt_MakeNumberIntStmt + // *Stmt_MakeNumberRefStmt + // *Stmt_MakeObjectStmt + // *Stmt_MakeSetStmt + // *Stmt_NopStmt + // *Stmt_NotEqualStmt + // *Stmt_NotStmt + // *Stmt_ObjectInsertOnceStmt + // *Stmt_ObjectInsertStmt + // *Stmt_ObjectMergeStmt + // *Stmt_ResetLocalStmt + // *Stmt_ResultSetAddStmt + // *Stmt_ReturnLocalStmt + // *Stmt_ScanStmt + // *Stmt_SetAddStmt + // *Stmt_WithStmt + Kind isStmt_Kind `protobuf_oneof:"kind"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Stmt) Reset() { + *x = Stmt{} + mi := &file_v1_ir_plan_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Stmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Stmt) ProtoMessage() {} + +func (x *Stmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Stmt.ProtoReflect.Descriptor instead. +func (*Stmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{11} +} + +func (x *Stmt) GetFile() int32 { + if x != nil && x.File != nil { + return *x.File + } + return 0 +} + +func (x *Stmt) GetCol() int32 { + if x != nil && x.Col != nil { + return *x.Col + } + return 0 +} + +func (x *Stmt) GetRow() int32 { + if x != nil && x.Row != nil { + return *x.Row + } + return 0 +} + +func (x *Stmt) GetKind() isStmt_Kind { + if x != nil { + return x.Kind + } + return nil +} + +func (x *Stmt) GetArrayAppendStmt() *ArrayAppendStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ArrayAppendStmt); ok { + return x.ArrayAppendStmt + } + } + return nil +} + +func (x *Stmt) GetAssignIntStmt() *AssignIntStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_AssignIntStmt); ok { + return x.AssignIntStmt + } + } + return nil +} + +func (x *Stmt) GetAssignVarOnceStmt() *AssignVarOnceStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_AssignVarOnceStmt); ok { + return x.AssignVarOnceStmt + } + } + return nil +} + +func (x *Stmt) GetAssignVarStmt() *AssignVarStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_AssignVarStmt); ok { + return x.AssignVarStmt + } + } + return nil +} + +func (x *Stmt) GetBlockStmt() *BlockStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_BlockStmt); ok { + return x.BlockStmt + } + } + return nil +} + +func (x *Stmt) GetBreakStmt() *BreakStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_BreakStmt); ok { + return x.BreakStmt + } + } + return nil +} + +func (x *Stmt) GetCallDynamicStmt() *CallDynamicStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_CallDynamicStmt); ok { + return x.CallDynamicStmt + } + } + return nil +} + +func (x *Stmt) GetCallStmt() *CallStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_CallStmt); ok { + return x.CallStmt + } + } + return nil +} + +func (x *Stmt) GetDotStmt() *DotStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_DotStmt); ok { + return x.DotStmt + } + } + return nil +} + +func (x *Stmt) GetEqualStmt() *EqualStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_EqualStmt); ok { + return x.EqualStmt + } + } + return nil +} + +func (x *Stmt) GetIsArrayStmt() *IsArrayStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_IsArrayStmt); ok { + return x.IsArrayStmt + } + } + return nil +} + +func (x *Stmt) GetIsDefinedStmt() *IsDefinedStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_IsDefinedStmt); ok { + return x.IsDefinedStmt + } + } + return nil +} + +func (x *Stmt) GetIsObjectStmt() *IsObjectStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_IsObjectStmt); ok { + return x.IsObjectStmt + } + } + return nil +} + +func (x *Stmt) GetIsSetStmt() *IsSetStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_IsSetStmt); ok { + return x.IsSetStmt + } + } + return nil +} + +func (x *Stmt) GetIsUndefinedStmt() *IsUndefinedStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_IsUndefinedStmt); ok { + return x.IsUndefinedStmt + } + } + return nil +} + +func (x *Stmt) GetLenStmt() *LenStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_LenStmt); ok { + return x.LenStmt + } + } + return nil +} + +func (x *Stmt) GetMakeArrayStmt() *MakeArrayStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeArrayStmt); ok { + return x.MakeArrayStmt + } + } + return nil +} + +func (x *Stmt) GetMakeNullStmt() *MakeNullStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeNullStmt); ok { + return x.MakeNullStmt + } + } + return nil +} + +func (x *Stmt) GetMakeNumberIntStmt() *MakeNumberIntStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeNumberIntStmt); ok { + return x.MakeNumberIntStmt + } + } + return nil +} + +func (x *Stmt) GetMakeNumberRefStmt() *MakeNumberRefStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeNumberRefStmt); ok { + return x.MakeNumberRefStmt + } + } + return nil +} + +func (x *Stmt) GetMakeObjectStmt() *MakeObjectStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeObjectStmt); ok { + return x.MakeObjectStmt + } + } + return nil +} + +func (x *Stmt) GetMakeSetStmt() *MakeSetStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_MakeSetStmt); ok { + return x.MakeSetStmt + } + } + return nil +} + +func (x *Stmt) GetNopStmt() *NopStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_NopStmt); ok { + return x.NopStmt + } + } + return nil +} + +func (x *Stmt) GetNotEqualStmt() *NotEqualStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_NotEqualStmt); ok { + return x.NotEqualStmt + } + } + return nil +} + +func (x *Stmt) GetNotStmt() *NotStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_NotStmt); ok { + return x.NotStmt + } + } + return nil +} + +func (x *Stmt) GetObjectInsertOnceStmt() *ObjectInsertOnceStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ObjectInsertOnceStmt); ok { + return x.ObjectInsertOnceStmt + } + } + return nil +} + +func (x *Stmt) GetObjectInsertStmt() *ObjectInsertStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ObjectInsertStmt); ok { + return x.ObjectInsertStmt + } + } + return nil +} + +func (x *Stmt) GetObjectMergeStmt() *ObjectMergeStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ObjectMergeStmt); ok { + return x.ObjectMergeStmt + } + } + return nil +} + +func (x *Stmt) GetResetLocalStmt() *ResetLocalStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ResetLocalStmt); ok { + return x.ResetLocalStmt + } + } + return nil +} + +func (x *Stmt) GetResultSetAddStmt() *ResultSetAddStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ResultSetAddStmt); ok { + return x.ResultSetAddStmt + } + } + return nil +} + +func (x *Stmt) GetReturnLocalStmt() *ReturnLocalStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ReturnLocalStmt); ok { + return x.ReturnLocalStmt + } + } + return nil +} + +func (x *Stmt) GetScanStmt() *ScanStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_ScanStmt); ok { + return x.ScanStmt + } + } + return nil +} + +func (x *Stmt) GetSetAddStmt() *SetAddStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_SetAddStmt); ok { + return x.SetAddStmt + } + } + return nil +} + +func (x *Stmt) GetWithStmt() *WithStmt { + if x != nil { + if x, ok := x.Kind.(*Stmt_WithStmt); ok { + return x.WithStmt + } + } + return nil +} + +type isStmt_Kind interface { + isStmt_Kind() +} + +type Stmt_ArrayAppendStmt struct { + ArrayAppendStmt *ArrayAppendStmt `protobuf:"bytes,4,opt,name=array_append_stmt,json=arrayAppendStmt,oneof"` +} + +type Stmt_AssignIntStmt struct { + AssignIntStmt *AssignIntStmt `protobuf:"bytes,5,opt,name=assign_int_stmt,json=assignIntStmt,oneof"` +} + +type Stmt_AssignVarOnceStmt struct { + AssignVarOnceStmt *AssignVarOnceStmt `protobuf:"bytes,6,opt,name=assign_var_once_stmt,json=assignVarOnceStmt,oneof"` +} + +type Stmt_AssignVarStmt struct { + AssignVarStmt *AssignVarStmt `protobuf:"bytes,7,opt,name=assign_var_stmt,json=assignVarStmt,oneof"` +} + +type Stmt_BlockStmt struct { + BlockStmt *BlockStmt `protobuf:"bytes,8,opt,name=block_stmt,json=blockStmt,oneof"` +} + +type Stmt_BreakStmt struct { + BreakStmt *BreakStmt `protobuf:"bytes,9,opt,name=break_stmt,json=breakStmt,oneof"` +} + +type Stmt_CallDynamicStmt struct { + CallDynamicStmt *CallDynamicStmt `protobuf:"bytes,10,opt,name=call_dynamic_stmt,json=callDynamicStmt,oneof"` +} + +type Stmt_CallStmt struct { + CallStmt *CallStmt `protobuf:"bytes,11,opt,name=call_stmt,json=callStmt,oneof"` +} + +type Stmt_DotStmt struct { + DotStmt *DotStmt `protobuf:"bytes,12,opt,name=dot_stmt,json=dotStmt,oneof"` +} + +type Stmt_EqualStmt struct { + EqualStmt *EqualStmt `protobuf:"bytes,13,opt,name=equal_stmt,json=equalStmt,oneof"` +} + +type Stmt_IsArrayStmt struct { + IsArrayStmt *IsArrayStmt `protobuf:"bytes,14,opt,name=is_array_stmt,json=isArrayStmt,oneof"` +} + +type Stmt_IsDefinedStmt struct { + IsDefinedStmt *IsDefinedStmt `protobuf:"bytes,15,opt,name=is_defined_stmt,json=isDefinedStmt,oneof"` +} + +type Stmt_IsObjectStmt struct { + IsObjectStmt *IsObjectStmt `protobuf:"bytes,16,opt,name=is_object_stmt,json=isObjectStmt,oneof"` +} + +type Stmt_IsSetStmt struct { + IsSetStmt *IsSetStmt `protobuf:"bytes,17,opt,name=is_set_stmt,json=isSetStmt,oneof"` +} + +type Stmt_IsUndefinedStmt struct { + IsUndefinedStmt *IsUndefinedStmt `protobuf:"bytes,18,opt,name=is_undefined_stmt,json=isUndefinedStmt,oneof"` +} + +type Stmt_LenStmt struct { + LenStmt *LenStmt `protobuf:"bytes,19,opt,name=len_stmt,json=lenStmt,oneof"` +} + +type Stmt_MakeArrayStmt struct { + MakeArrayStmt *MakeArrayStmt `protobuf:"bytes,20,opt,name=make_array_stmt,json=makeArrayStmt,oneof"` +} + +type Stmt_MakeNullStmt struct { + MakeNullStmt *MakeNullStmt `protobuf:"bytes,21,opt,name=make_null_stmt,json=makeNullStmt,oneof"` +} + +type Stmt_MakeNumberIntStmt struct { + MakeNumberIntStmt *MakeNumberIntStmt `protobuf:"bytes,22,opt,name=make_number_int_stmt,json=makeNumberIntStmt,oneof"` +} + +type Stmt_MakeNumberRefStmt struct { + MakeNumberRefStmt *MakeNumberRefStmt `protobuf:"bytes,23,opt,name=make_number_ref_stmt,json=makeNumberRefStmt,oneof"` +} + +type Stmt_MakeObjectStmt struct { + MakeObjectStmt *MakeObjectStmt `protobuf:"bytes,24,opt,name=make_object_stmt,json=makeObjectStmt,oneof"` +} + +type Stmt_MakeSetStmt struct { + MakeSetStmt *MakeSetStmt `protobuf:"bytes,25,opt,name=make_set_stmt,json=makeSetStmt,oneof"` +} + +type Stmt_NopStmt struct { + NopStmt *NopStmt `protobuf:"bytes,26,opt,name=nop_stmt,json=nopStmt,oneof"` +} + +type Stmt_NotEqualStmt struct { + NotEqualStmt *NotEqualStmt `protobuf:"bytes,27,opt,name=not_equal_stmt,json=notEqualStmt,oneof"` +} + +type Stmt_NotStmt struct { + NotStmt *NotStmt `protobuf:"bytes,28,opt,name=not_stmt,json=notStmt,oneof"` +} + +type Stmt_ObjectInsertOnceStmt struct { + ObjectInsertOnceStmt *ObjectInsertOnceStmt `protobuf:"bytes,29,opt,name=object_insert_once_stmt,json=objectInsertOnceStmt,oneof"` +} + +type Stmt_ObjectInsertStmt struct { + ObjectInsertStmt *ObjectInsertStmt `protobuf:"bytes,30,opt,name=object_insert_stmt,json=objectInsertStmt,oneof"` +} + +type Stmt_ObjectMergeStmt struct { + ObjectMergeStmt *ObjectMergeStmt `protobuf:"bytes,31,opt,name=object_merge_stmt,json=objectMergeStmt,oneof"` +} + +type Stmt_ResetLocalStmt struct { + ResetLocalStmt *ResetLocalStmt `protobuf:"bytes,32,opt,name=reset_local_stmt,json=resetLocalStmt,oneof"` +} + +type Stmt_ResultSetAddStmt struct { + ResultSetAddStmt *ResultSetAddStmt `protobuf:"bytes,33,opt,name=result_set_add_stmt,json=resultSetAddStmt,oneof"` +} + +type Stmt_ReturnLocalStmt struct { + ReturnLocalStmt *ReturnLocalStmt `protobuf:"bytes,34,opt,name=return_local_stmt,json=returnLocalStmt,oneof"` +} + +type Stmt_ScanStmt struct { + ScanStmt *ScanStmt `protobuf:"bytes,35,opt,name=scan_stmt,json=scanStmt,oneof"` +} + +type Stmt_SetAddStmt struct { + SetAddStmt *SetAddStmt `protobuf:"bytes,36,opt,name=set_add_stmt,json=setAddStmt,oneof"` +} + +type Stmt_WithStmt struct { + WithStmt *WithStmt `protobuf:"bytes,37,opt,name=with_stmt,json=withStmt,oneof"` +} + +func (*Stmt_ArrayAppendStmt) isStmt_Kind() {} + +func (*Stmt_AssignIntStmt) isStmt_Kind() {} + +func (*Stmt_AssignVarOnceStmt) isStmt_Kind() {} + +func (*Stmt_AssignVarStmt) isStmt_Kind() {} + +func (*Stmt_BlockStmt) isStmt_Kind() {} + +func (*Stmt_BreakStmt) isStmt_Kind() {} + +func (*Stmt_CallDynamicStmt) isStmt_Kind() {} + +func (*Stmt_CallStmt) isStmt_Kind() {} + +func (*Stmt_DotStmt) isStmt_Kind() {} + +func (*Stmt_EqualStmt) isStmt_Kind() {} + +func (*Stmt_IsArrayStmt) isStmt_Kind() {} + +func (*Stmt_IsDefinedStmt) isStmt_Kind() {} + +func (*Stmt_IsObjectStmt) isStmt_Kind() {} + +func (*Stmt_IsSetStmt) isStmt_Kind() {} + +func (*Stmt_IsUndefinedStmt) isStmt_Kind() {} + +func (*Stmt_LenStmt) isStmt_Kind() {} + +func (*Stmt_MakeArrayStmt) isStmt_Kind() {} + +func (*Stmt_MakeNullStmt) isStmt_Kind() {} + +func (*Stmt_MakeNumberIntStmt) isStmt_Kind() {} + +func (*Stmt_MakeNumberRefStmt) isStmt_Kind() {} + +func (*Stmt_MakeObjectStmt) isStmt_Kind() {} + +func (*Stmt_MakeSetStmt) isStmt_Kind() {} + +func (*Stmt_NopStmt) isStmt_Kind() {} + +func (*Stmt_NotEqualStmt) isStmt_Kind() {} + +func (*Stmt_NotStmt) isStmt_Kind() {} + +func (*Stmt_ObjectInsertOnceStmt) isStmt_Kind() {} + +func (*Stmt_ObjectInsertStmt) isStmt_Kind() {} + +func (*Stmt_ObjectMergeStmt) isStmt_Kind() {} + +func (*Stmt_ResetLocalStmt) isStmt_Kind() {} + +func (*Stmt_ResultSetAddStmt) isStmt_Kind() {} + +func (*Stmt_ReturnLocalStmt) isStmt_Kind() {} + +func (*Stmt_ScanStmt) isStmt_Kind() {} + +func (*Stmt_SetAddStmt) isStmt_Kind() {} + +func (*Stmt_WithStmt) isStmt_Kind() {} + +// ArrayAppendStmt mirrors `ir.ArrayAppendStmt` in v1/ir/ir.go. +type ArrayAppendStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *Operand `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + Array *int32 `protobuf:"varint,2,opt,name=array" json:"array,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ArrayAppendStmt) Reset() { + *x = ArrayAppendStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ArrayAppendStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ArrayAppendStmt) ProtoMessage() {} + +func (x *ArrayAppendStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ArrayAppendStmt.ProtoReflect.Descriptor instead. +func (*ArrayAppendStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{12} +} + +func (x *ArrayAppendStmt) GetValue() *Operand { + if x != nil { + return x.Value + } + return nil +} + +func (x *ArrayAppendStmt) GetArray() int32 { + if x != nil && x.Array != nil { + return *x.Array + } + return 0 +} + +// AssignIntStmt mirrors `ir.AssignIntStmt` in v1/ir/ir.go. +type AssignIntStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *int64 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AssignIntStmt) Reset() { + *x = AssignIntStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AssignIntStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssignIntStmt) ProtoMessage() {} + +func (x *AssignIntStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssignIntStmt.ProtoReflect.Descriptor instead. +func (*AssignIntStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{13} +} + +func (x *AssignIntStmt) GetValue() int64 { + if x != nil && x.Value != nil { + return *x.Value + } + return 0 +} + +func (x *AssignIntStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// AssignVarOnceStmt mirrors `ir.AssignVarOnceStmt` in v1/ir/ir.go. +type AssignVarOnceStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AssignVarOnceStmt) Reset() { + *x = AssignVarOnceStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AssignVarOnceStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssignVarOnceStmt) ProtoMessage() {} + +func (x *AssignVarOnceStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssignVarOnceStmt.ProtoReflect.Descriptor instead. +func (*AssignVarOnceStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{14} +} + +func (x *AssignVarOnceStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +func (x *AssignVarOnceStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// AssignVarStmt mirrors `ir.AssignVarStmt` in v1/ir/ir.go. +type AssignVarStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AssignVarStmt) Reset() { + *x = AssignVarStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AssignVarStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssignVarStmt) ProtoMessage() {} + +func (x *AssignVarStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssignVarStmt.ProtoReflect.Descriptor instead. +func (*AssignVarStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{15} +} + +func (x *AssignVarStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +func (x *AssignVarStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// BlockStmt mirrors `ir.BlockStmt` in v1/ir/ir.go. +type BlockStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Blocks []*Block `protobuf:"bytes,1,rep,name=blocks" json:"blocks,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BlockStmt) Reset() { + *x = BlockStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BlockStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockStmt) ProtoMessage() {} + +func (x *BlockStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockStmt.ProtoReflect.Descriptor instead. +func (*BlockStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{16} +} + +func (x *BlockStmt) GetBlocks() []*Block { + if x != nil { + return x.Blocks + } + return nil +} + +// BreakStmt mirrors `ir.BreakStmt` in v1/ir/ir.go. +type BreakStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Index *uint32 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BreakStmt) Reset() { + *x = BreakStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BreakStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BreakStmt) ProtoMessage() {} + +func (x *BreakStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BreakStmt.ProtoReflect.Descriptor instead. +func (*BreakStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{17} +} + +func (x *BreakStmt) GetIndex() uint32 { + if x != nil && x.Index != nil { + return *x.Index + } + return 0 +} + +// CallDynamicStmt mirrors `ir.CallDynamicStmt` in v1/ir/ir.go. +type CallDynamicStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Args []int32 `protobuf:"varint,1,rep,packed,name=args" json:"args,omitempty"` + Result *int32 `protobuf:"varint,2,opt,name=result" json:"result,omitempty"` + Path []*Operand `protobuf:"bytes,3,rep,name=path" json:"path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CallDynamicStmt) Reset() { + *x = CallDynamicStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CallDynamicStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CallDynamicStmt) ProtoMessage() {} + +func (x *CallDynamicStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CallDynamicStmt.ProtoReflect.Descriptor instead. +func (*CallDynamicStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{18} +} + +func (x *CallDynamicStmt) GetArgs() []int32 { + if x != nil { + return x.Args + } + return nil +} + +func (x *CallDynamicStmt) GetResult() int32 { + if x != nil && x.Result != nil { + return *x.Result + } + return 0 +} + +func (x *CallDynamicStmt) GetPath() []*Operand { + if x != nil { + return x.Path + } + return nil +} + +// CallStmt mirrors `ir.CallStmt` in v1/ir/ir.go. +type CallStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Renamed from `func` (Go field `CallStmt.Func`, JSON-tagged `func`) + // to avoid colliding with reserved keywords in target languages, + // mirroring the Func.return → result rename above. + Function *string `protobuf:"bytes,1,opt,name=function" json:"function,omitempty"` + Args []*Operand `protobuf:"bytes,2,rep,name=args" json:"args,omitempty"` + Result *int32 `protobuf:"varint,3,opt,name=result" json:"result,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CallStmt) Reset() { + *x = CallStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CallStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CallStmt) ProtoMessage() {} + +func (x *CallStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CallStmt.ProtoReflect.Descriptor instead. +func (*CallStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{19} +} + +func (x *CallStmt) GetFunction() string { + if x != nil && x.Function != nil { + return *x.Function + } + return "" +} + +func (x *CallStmt) GetArgs() []*Operand { + if x != nil { + return x.Args + } + return nil +} + +func (x *CallStmt) GetResult() int32 { + if x != nil && x.Result != nil { + return *x.Result + } + return 0 +} + +// DotStmt mirrors `ir.DotStmt` in v1/ir/ir.go. +type DotStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + Key *Operand `protobuf:"bytes,2,opt,name=key" json:"key,omitempty"` + Target *int32 `protobuf:"varint,3,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DotStmt) Reset() { + *x = DotStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DotStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DotStmt) ProtoMessage() {} + +func (x *DotStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DotStmt.ProtoReflect.Descriptor instead. +func (*DotStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{20} +} + +func (x *DotStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +func (x *DotStmt) GetKey() *Operand { + if x != nil { + return x.Key + } + return nil +} + +func (x *DotStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// EqualStmt mirrors `ir.EqualStmt` in v1/ir/ir.go. +type EqualStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + A *Operand `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"` + B *Operand `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EqualStmt) Reset() { + *x = EqualStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EqualStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EqualStmt) ProtoMessage() {} + +func (x *EqualStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EqualStmt.ProtoReflect.Descriptor instead. +func (*EqualStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{21} +} + +func (x *EqualStmt) GetA() *Operand { + if x != nil { + return x.A + } + return nil +} + +func (x *EqualStmt) GetB() *Operand { + if x != nil { + return x.B + } + return nil +} + +// IsArrayStmt mirrors `ir.IsArrayStmt` in v1/ir/ir.go. +type IsArrayStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsArrayStmt) Reset() { + *x = IsArrayStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsArrayStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsArrayStmt) ProtoMessage() {} + +func (x *IsArrayStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsArrayStmt.ProtoReflect.Descriptor instead. +func (*IsArrayStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{22} +} + +func (x *IsArrayStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +// IsDefinedStmt mirrors `ir.IsDefinedStmt` in v1/ir/ir.go. +type IsDefinedStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *int32 `protobuf:"varint,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsDefinedStmt) Reset() { + *x = IsDefinedStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsDefinedStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsDefinedStmt) ProtoMessage() {} + +func (x *IsDefinedStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsDefinedStmt.ProtoReflect.Descriptor instead. +func (*IsDefinedStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{23} +} + +func (x *IsDefinedStmt) GetSource() int32 { + if x != nil && x.Source != nil { + return *x.Source + } + return 0 +} + +// IsObjectStmt mirrors `ir.IsObjectStmt` in v1/ir/ir.go. +type IsObjectStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsObjectStmt) Reset() { + *x = IsObjectStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsObjectStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsObjectStmt) ProtoMessage() {} + +func (x *IsObjectStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsObjectStmt.ProtoReflect.Descriptor instead. +func (*IsObjectStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{24} +} + +func (x *IsObjectStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +// IsSetStmt mirrors `ir.IsSetStmt` in v1/ir/ir.go. +type IsSetStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsSetStmt) Reset() { + *x = IsSetStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsSetStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsSetStmt) ProtoMessage() {} + +func (x *IsSetStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsSetStmt.ProtoReflect.Descriptor instead. +func (*IsSetStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{25} +} + +func (x *IsSetStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +// IsUndefinedStmt mirrors `ir.IsUndefinedStmt` in v1/ir/ir.go. +type IsUndefinedStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *int32 `protobuf:"varint,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *IsUndefinedStmt) Reset() { + *x = IsUndefinedStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *IsUndefinedStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IsUndefinedStmt) ProtoMessage() {} + +func (x *IsUndefinedStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IsUndefinedStmt.ProtoReflect.Descriptor instead. +func (*IsUndefinedStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{26} +} + +func (x *IsUndefinedStmt) GetSource() int32 { + if x != nil && x.Source != nil { + return *x.Source + } + return 0 +} + +// LenStmt mirrors `ir.LenStmt` in v1/ir/ir.go. +type LenStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *Operand `protobuf:"bytes,1,opt,name=source" json:"source,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LenStmt) Reset() { + *x = LenStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LenStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LenStmt) ProtoMessage() {} + +func (x *LenStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LenStmt.ProtoReflect.Descriptor instead. +func (*LenStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{27} +} + +func (x *LenStmt) GetSource() *Operand { + if x != nil { + return x.Source + } + return nil +} + +func (x *LenStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeArrayStmt mirrors `ir.MakeArrayStmt` in v1/ir/ir.go. +type MakeArrayStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Capacity *int32 `protobuf:"varint,1,opt,name=capacity" json:"capacity,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeArrayStmt) Reset() { + *x = MakeArrayStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeArrayStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeArrayStmt) ProtoMessage() {} + +func (x *MakeArrayStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeArrayStmt.ProtoReflect.Descriptor instead. +func (*MakeArrayStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{28} +} + +func (x *MakeArrayStmt) GetCapacity() int32 { + if x != nil && x.Capacity != nil { + return *x.Capacity + } + return 0 +} + +func (x *MakeArrayStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeNullStmt mirrors `ir.MakeNullStmt` in v1/ir/ir.go. +type MakeNullStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Target *int32 `protobuf:"varint,1,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeNullStmt) Reset() { + *x = MakeNullStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeNullStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeNullStmt) ProtoMessage() {} + +func (x *MakeNullStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeNullStmt.ProtoReflect.Descriptor instead. +func (*MakeNullStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{29} +} + +func (x *MakeNullStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeNumberIntStmt mirrors `ir.MakeNumberIntStmt` in v1/ir/ir.go. +type MakeNumberIntStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *int64 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeNumberIntStmt) Reset() { + *x = MakeNumberIntStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeNumberIntStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeNumberIntStmt) ProtoMessage() {} + +func (x *MakeNumberIntStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeNumberIntStmt.ProtoReflect.Descriptor instead. +func (*MakeNumberIntStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{30} +} + +func (x *MakeNumberIntStmt) GetValue() int64 { + if x != nil && x.Value != nil { + return *x.Value + } + return 0 +} + +func (x *MakeNumberIntStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeNumberRefStmt mirrors `ir.MakeNumberRefStmt` in v1/ir/ir.go. +// +// The Go field is named `Index` (no `json` tag). The historical JSON +// shape emitted both `index` and `Index`; the canonical key going +// forward is `index` and the deprecated `Index` alias will be removed +// in a future major release. This proto models only the canonical +// `index` field. +type MakeNumberRefStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Index *int32 `protobuf:"varint,1,opt,name=index" json:"index,omitempty"` + Target *int32 `protobuf:"varint,2,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeNumberRefStmt) Reset() { + *x = MakeNumberRefStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeNumberRefStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeNumberRefStmt) ProtoMessage() {} + +func (x *MakeNumberRefStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[31] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeNumberRefStmt.ProtoReflect.Descriptor instead. +func (*MakeNumberRefStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{31} +} + +func (x *MakeNumberRefStmt) GetIndex() int32 { + if x != nil && x.Index != nil { + return *x.Index + } + return 0 +} + +func (x *MakeNumberRefStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeObjectStmt mirrors `ir.MakeObjectStmt` in v1/ir/ir.go. +type MakeObjectStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Target *int32 `protobuf:"varint,1,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeObjectStmt) Reset() { + *x = MakeObjectStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeObjectStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeObjectStmt) ProtoMessage() {} + +func (x *MakeObjectStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeObjectStmt.ProtoReflect.Descriptor instead. +func (*MakeObjectStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{32} +} + +func (x *MakeObjectStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// MakeSetStmt mirrors `ir.MakeSetStmt` in v1/ir/ir.go. +type MakeSetStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Target *int32 `protobuf:"varint,1,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeSetStmt) Reset() { + *x = MakeSetStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeSetStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeSetStmt) ProtoMessage() {} + +func (x *MakeSetStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeSetStmt.ProtoReflect.Descriptor instead. +func (*MakeSetStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{33} +} + +func (x *MakeSetStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// NopStmt mirrors `ir.NopStmt` in v1/ir/ir.go. +type NopStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NopStmt) Reset() { + *x = NopStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NopStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NopStmt) ProtoMessage() {} + +func (x *NopStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[34] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NopStmt.ProtoReflect.Descriptor instead. +func (*NopStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{34} +} + +// NotEqualStmt mirrors `ir.NotEqualStmt` in v1/ir/ir.go. +type NotEqualStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + A *Operand `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"` + B *Operand `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NotEqualStmt) Reset() { + *x = NotEqualStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NotEqualStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotEqualStmt) ProtoMessage() {} + +func (x *NotEqualStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[35] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotEqualStmt.ProtoReflect.Descriptor instead. +func (*NotEqualStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{35} +} + +func (x *NotEqualStmt) GetA() *Operand { + if x != nil { + return x.A + } + return nil +} + +func (x *NotEqualStmt) GetB() *Operand { + if x != nil { + return x.B + } + return nil +} + +// NotStmt mirrors `ir.NotStmt` in v1/ir/ir.go. +type NotStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Block *Block `protobuf:"bytes,1,opt,name=block" json:"block,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NotStmt) Reset() { + *x = NotStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NotStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NotStmt) ProtoMessage() {} + +func (x *NotStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[36] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NotStmt.ProtoReflect.Descriptor instead. +func (*NotStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{36} +} + +func (x *NotStmt) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +// ObjectInsertOnceStmt mirrors `ir.ObjectInsertOnceStmt` in v1/ir/ir.go. +type ObjectInsertOnceStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Key *Operand `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + Value *Operand `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` + Object *int32 `protobuf:"varint,3,opt,name=object" json:"object,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ObjectInsertOnceStmt) Reset() { + *x = ObjectInsertOnceStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ObjectInsertOnceStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectInsertOnceStmt) ProtoMessage() {} + +func (x *ObjectInsertOnceStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[37] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectInsertOnceStmt.ProtoReflect.Descriptor instead. +func (*ObjectInsertOnceStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{37} +} + +func (x *ObjectInsertOnceStmt) GetKey() *Operand { + if x != nil { + return x.Key + } + return nil +} + +func (x *ObjectInsertOnceStmt) GetValue() *Operand { + if x != nil { + return x.Value + } + return nil +} + +func (x *ObjectInsertOnceStmt) GetObject() int32 { + if x != nil && x.Object != nil { + return *x.Object + } + return 0 +} + +// ObjectInsertStmt mirrors `ir.ObjectInsertStmt` in v1/ir/ir.go. +type ObjectInsertStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Key *Operand `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` + Value *Operand `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` + Object *int32 `protobuf:"varint,3,opt,name=object" json:"object,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ObjectInsertStmt) Reset() { + *x = ObjectInsertStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ObjectInsertStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectInsertStmt) ProtoMessage() {} + +func (x *ObjectInsertStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[38] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectInsertStmt.ProtoReflect.Descriptor instead. +func (*ObjectInsertStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{38} +} + +func (x *ObjectInsertStmt) GetKey() *Operand { + if x != nil { + return x.Key + } + return nil +} + +func (x *ObjectInsertStmt) GetValue() *Operand { + if x != nil { + return x.Value + } + return nil +} + +func (x *ObjectInsertStmt) GetObject() int32 { + if x != nil && x.Object != nil { + return *x.Object + } + return 0 +} + +// ObjectMergeStmt mirrors `ir.ObjectMergeStmt` in v1/ir/ir.go. +type ObjectMergeStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + A *int32 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"` + B *int32 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"` + Target *int32 `protobuf:"varint,3,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ObjectMergeStmt) Reset() { + *x = ObjectMergeStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ObjectMergeStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectMergeStmt) ProtoMessage() {} + +func (x *ObjectMergeStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[39] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ObjectMergeStmt.ProtoReflect.Descriptor instead. +func (*ObjectMergeStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{39} +} + +func (x *ObjectMergeStmt) GetA() int32 { + if x != nil && x.A != nil { + return *x.A + } + return 0 +} + +func (x *ObjectMergeStmt) GetB() int32 { + if x != nil && x.B != nil { + return *x.B + } + return 0 +} + +func (x *ObjectMergeStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// ResetLocalStmt mirrors `ir.ResetLocalStmt` in v1/ir/ir.go. +type ResetLocalStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Target *int32 `protobuf:"varint,1,opt,name=target" json:"target,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResetLocalStmt) Reset() { + *x = ResetLocalStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResetLocalStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetLocalStmt) ProtoMessage() {} + +func (x *ResetLocalStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[40] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetLocalStmt.ProtoReflect.Descriptor instead. +func (*ResetLocalStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{40} +} + +func (x *ResetLocalStmt) GetTarget() int32 { + if x != nil && x.Target != nil { + return *x.Target + } + return 0 +} + +// ResultSetAddStmt mirrors `ir.ResultSetAddStmt` in v1/ir/ir.go. +type ResultSetAddStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *int32 `protobuf:"varint,1,opt,name=value" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ResultSetAddStmt) Reset() { + *x = ResultSetAddStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ResultSetAddStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResultSetAddStmt) ProtoMessage() {} + +func (x *ResultSetAddStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[41] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResultSetAddStmt.ProtoReflect.Descriptor instead. +func (*ResultSetAddStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{41} +} + +func (x *ResultSetAddStmt) GetValue() int32 { + if x != nil && x.Value != nil { + return *x.Value + } + return 0 +} + +// ReturnLocalStmt mirrors `ir.ReturnLocalStmt` in v1/ir/ir.go. +type ReturnLocalStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *int32 `protobuf:"varint,1,opt,name=source" json:"source,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReturnLocalStmt) Reset() { + *x = ReturnLocalStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReturnLocalStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReturnLocalStmt) ProtoMessage() {} + +func (x *ReturnLocalStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[42] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReturnLocalStmt.ProtoReflect.Descriptor instead. +func (*ReturnLocalStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{42} +} + +func (x *ReturnLocalStmt) GetSource() int32 { + if x != nil && x.Source != nil { + return *x.Source + } + return 0 +} + +// ScanStmt mirrors `ir.ScanStmt` in v1/ir/ir.go. +type ScanStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source *int32 `protobuf:"varint,1,opt,name=source" json:"source,omitempty"` + Key *int32 `protobuf:"varint,2,opt,name=key" json:"key,omitempty"` + Value *int32 `protobuf:"varint,3,opt,name=value" json:"value,omitempty"` + Block *Block `protobuf:"bytes,4,opt,name=block" json:"block,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ScanStmt) Reset() { + *x = ScanStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ScanStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ScanStmt) ProtoMessage() {} + +func (x *ScanStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[43] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ScanStmt.ProtoReflect.Descriptor instead. +func (*ScanStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{43} +} + +func (x *ScanStmt) GetSource() int32 { + if x != nil && x.Source != nil { + return *x.Source + } + return 0 +} + +func (x *ScanStmt) GetKey() int32 { + if x != nil && x.Key != nil { + return *x.Key + } + return 0 +} + +func (x *ScanStmt) GetValue() int32 { + if x != nil && x.Value != nil { + return *x.Value + } + return 0 +} + +func (x *ScanStmt) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +// SetAddStmt mirrors `ir.SetAddStmt` in v1/ir/ir.go. +type SetAddStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value *Operand `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + Set *int32 `protobuf:"varint,2,opt,name=set" json:"set,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetAddStmt) Reset() { + *x = SetAddStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetAddStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetAddStmt) ProtoMessage() {} + +func (x *SetAddStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[44] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetAddStmt.ProtoReflect.Descriptor instead. +func (*SetAddStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{44} +} + +func (x *SetAddStmt) GetValue() *Operand { + if x != nil { + return x.Value + } + return nil +} + +func (x *SetAddStmt) GetSet() int32 { + if x != nil && x.Set != nil { + return *x.Set + } + return 0 +} + +// WithStmt mirrors `ir.WithStmt` in v1/ir/ir.go. +type WithStmt struct { + state protoimpl.MessageState `protogen:"open.v1"` + Local *int32 `protobuf:"varint,1,opt,name=local" json:"local,omitempty"` + Path []int32 `protobuf:"varint,2,rep,packed,name=path" json:"path,omitempty"` + Value *Operand `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"` + Block *Block `protobuf:"bytes,4,opt,name=block" json:"block,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WithStmt) Reset() { + *x = WithStmt{} + mi := &file_v1_ir_plan_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WithStmt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WithStmt) ProtoMessage() {} + +func (x *WithStmt) ProtoReflect() protoreflect.Message { + mi := &file_v1_ir_plan_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WithStmt.ProtoReflect.Descriptor instead. +func (*WithStmt) Descriptor() ([]byte, []int) { + return file_v1_ir_plan_proto_rawDescGZIP(), []int{45} +} + +func (x *WithStmt) GetLocal() int32 { + if x != nil && x.Local != nil { + return *x.Local + } + return 0 +} + +func (x *WithStmt) GetPath() []int32 { + if x != nil { + return x.Path + } + return nil +} + +func (x *WithStmt) GetValue() *Operand { + if x != nil { + return x.Value + } + return nil +} + +func (x *WithStmt) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +var File_v1_ir_plan_proto protoreflect.FileDescriptor + +const file_v1_ir_plan_proto_rawDesc = "" + + "\n" + + "\x10v1/ir/plan.proto\x12\topa.ir.v1\"\x83\x01\n" + + "\x06Policy\x12)\n" + + "\x06static\x18\x01 \x01(\v2\x11.opa.ir.v1.StaticR\x06static\x12&\n" + + "\x05plans\x18\x02 \x01(\v2\x10.opa.ir.v1.PlansR\x05plans\x12&\n" + + "\x05funcs\x18\x03 \x01(\v2\x10.opa.ir.v1.FuncsR\x05funcs\"\xa5\x01\n" + + "\x06Static\x120\n" + + "\astrings\x18\x01 \x03(\v2\x16.opa.ir.v1.StringConstR\astrings\x12;\n" + + "\rbuiltin_funcs\x18\x02 \x03(\v2\x16.opa.ir.v1.BuiltinFuncR\fbuiltinFuncs\x12,\n" + + "\x05files\x18\x03 \x03(\v2\x16.opa.ir.v1.StringConstR\x05files\"!\n" + + "\vBuiltinFunc\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\".\n" + + "\x05Plans\x12%\n" + + "\x05plans\x18\x01 \x03(\v2\x0f.opa.ir.v1.PlanR\x05plans\".\n" + + "\x05Funcs\x12%\n" + + "\x05funcs\x18\x01 \x03(\v2\x0f.opa.ir.v1.FuncR\x05funcs\"\x88\x01\n" + + "\x04Func\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + + "\x06params\x18\x02 \x03(\x05R\x06params\x12\x16\n" + + "\x06result\x18\x03 \x01(\x05R\x06result\x12(\n" + + "\x06blocks\x18\x04 \x03(\v2\x10.opa.ir.v1.BlockR\x06blocks\x12\x12\n" + + "\x04path\x18\x05 \x03(\tR\x04path\"D\n" + + "\x04Plan\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12(\n" + + "\x06blocks\x18\x02 \x03(\v2\x10.opa.ir.v1.BlockR\x06blocks\".\n" + + "\x05Block\x12%\n" + + "\x05stmts\x18\x01 \x03(\v2\x0f.opa.ir.v1.StmtR\x05stmts\"#\n" + + "\vStringConst\x12\x14\n" + + "\x05value\x18\x01 \x01(\tR\x05value\"/\n" + + "\aOperand\x12$\n" + + "\x05value\x18\x01 \x01(\v2\x0e.opa.ir.v1.ValR\x05value\"`\n" + + "\x03Val\x12\x14\n" + + "\x04bool\x18\x01 \x01(\bH\x00R\x04bool\x12\x16\n" + + "\x05local\x18\x02 \x01(\x05H\x00R\x05local\x12#\n" + + "\fstring_index\x18\x03 \x01(\x05H\x00R\vstringIndexB\x06\n" + + "\x04kind\"\xf5\x11\n" + + "\x04Stmt\x12\x12\n" + + "\x04file\x18\x01 \x01(\x05R\x04file\x12\x10\n" + + "\x03col\x18\x02 \x01(\x05R\x03col\x12\x10\n" + + "\x03row\x18\x03 \x01(\x05R\x03row\x12H\n" + + "\x11array_append_stmt\x18\x04 \x01(\v2\x1a.opa.ir.v1.ArrayAppendStmtH\x00R\x0farrayAppendStmt\x12B\n" + + "\x0fassign_int_stmt\x18\x05 \x01(\v2\x18.opa.ir.v1.AssignIntStmtH\x00R\rassignIntStmt\x12O\n" + + "\x14assign_var_once_stmt\x18\x06 \x01(\v2\x1c.opa.ir.v1.AssignVarOnceStmtH\x00R\x11assignVarOnceStmt\x12B\n" + + "\x0fassign_var_stmt\x18\a \x01(\v2\x18.opa.ir.v1.AssignVarStmtH\x00R\rassignVarStmt\x125\n" + + "\n" + + "block_stmt\x18\b \x01(\v2\x14.opa.ir.v1.BlockStmtH\x00R\tblockStmt\x125\n" + + "\n" + + "break_stmt\x18\t \x01(\v2\x14.opa.ir.v1.BreakStmtH\x00R\tbreakStmt\x12H\n" + + "\x11call_dynamic_stmt\x18\n" + + " \x01(\v2\x1a.opa.ir.v1.CallDynamicStmtH\x00R\x0fcallDynamicStmt\x122\n" + + "\tcall_stmt\x18\v \x01(\v2\x13.opa.ir.v1.CallStmtH\x00R\bcallStmt\x12/\n" + + "\bdot_stmt\x18\f \x01(\v2\x12.opa.ir.v1.DotStmtH\x00R\adotStmt\x125\n" + + "\n" + + "equal_stmt\x18\r \x01(\v2\x14.opa.ir.v1.EqualStmtH\x00R\tequalStmt\x12<\n" + + "\ris_array_stmt\x18\x0e \x01(\v2\x16.opa.ir.v1.IsArrayStmtH\x00R\visArrayStmt\x12B\n" + + "\x0fis_defined_stmt\x18\x0f \x01(\v2\x18.opa.ir.v1.IsDefinedStmtH\x00R\risDefinedStmt\x12?\n" + + "\x0eis_object_stmt\x18\x10 \x01(\v2\x17.opa.ir.v1.IsObjectStmtH\x00R\fisObjectStmt\x126\n" + + "\vis_set_stmt\x18\x11 \x01(\v2\x14.opa.ir.v1.IsSetStmtH\x00R\tisSetStmt\x12H\n" + + "\x11is_undefined_stmt\x18\x12 \x01(\v2\x1a.opa.ir.v1.IsUndefinedStmtH\x00R\x0fisUndefinedStmt\x12/\n" + + "\blen_stmt\x18\x13 \x01(\v2\x12.opa.ir.v1.LenStmtH\x00R\alenStmt\x12B\n" + + "\x0fmake_array_stmt\x18\x14 \x01(\v2\x18.opa.ir.v1.MakeArrayStmtH\x00R\rmakeArrayStmt\x12?\n" + + "\x0emake_null_stmt\x18\x15 \x01(\v2\x17.opa.ir.v1.MakeNullStmtH\x00R\fmakeNullStmt\x12O\n" + + "\x14make_number_int_stmt\x18\x16 \x01(\v2\x1c.opa.ir.v1.MakeNumberIntStmtH\x00R\x11makeNumberIntStmt\x12O\n" + + "\x14make_number_ref_stmt\x18\x17 \x01(\v2\x1c.opa.ir.v1.MakeNumberRefStmtH\x00R\x11makeNumberRefStmt\x12E\n" + + "\x10make_object_stmt\x18\x18 \x01(\v2\x19.opa.ir.v1.MakeObjectStmtH\x00R\x0emakeObjectStmt\x12<\n" + + "\rmake_set_stmt\x18\x19 \x01(\v2\x16.opa.ir.v1.MakeSetStmtH\x00R\vmakeSetStmt\x12/\n" + + "\bnop_stmt\x18\x1a \x01(\v2\x12.opa.ir.v1.NopStmtH\x00R\anopStmt\x12?\n" + + "\x0enot_equal_stmt\x18\x1b \x01(\v2\x17.opa.ir.v1.NotEqualStmtH\x00R\fnotEqualStmt\x12/\n" + + "\bnot_stmt\x18\x1c \x01(\v2\x12.opa.ir.v1.NotStmtH\x00R\anotStmt\x12X\n" + + "\x17object_insert_once_stmt\x18\x1d \x01(\v2\x1f.opa.ir.v1.ObjectInsertOnceStmtH\x00R\x14objectInsertOnceStmt\x12K\n" + + "\x12object_insert_stmt\x18\x1e \x01(\v2\x1b.opa.ir.v1.ObjectInsertStmtH\x00R\x10objectInsertStmt\x12H\n" + + "\x11object_merge_stmt\x18\x1f \x01(\v2\x1a.opa.ir.v1.ObjectMergeStmtH\x00R\x0fobjectMergeStmt\x12E\n" + + "\x10reset_local_stmt\x18 \x01(\v2\x19.opa.ir.v1.ResetLocalStmtH\x00R\x0eresetLocalStmt\x12L\n" + + "\x13result_set_add_stmt\x18! \x01(\v2\x1b.opa.ir.v1.ResultSetAddStmtH\x00R\x10resultSetAddStmt\x12H\n" + + "\x11return_local_stmt\x18\" \x01(\v2\x1a.opa.ir.v1.ReturnLocalStmtH\x00R\x0freturnLocalStmt\x122\n" + + "\tscan_stmt\x18# \x01(\v2\x13.opa.ir.v1.ScanStmtH\x00R\bscanStmt\x129\n" + + "\fset_add_stmt\x18$ \x01(\v2\x15.opa.ir.v1.SetAddStmtH\x00R\n" + + "setAddStmt\x122\n" + + "\twith_stmt\x18% \x01(\v2\x13.opa.ir.v1.WithStmtH\x00R\bwithStmtB\x06\n" + + "\x04kind\"Q\n" + + "\x0fArrayAppendStmt\x12(\n" + + "\x05value\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x05value\x12\x14\n" + + "\x05array\x18\x02 \x01(\x05R\x05array\"=\n" + + "\rAssignIntStmt\x12\x14\n" + + "\x05value\x18\x01 \x01(\x03R\x05value\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"W\n" + + "\x11AssignVarOnceStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"S\n" + + "\rAssignVarStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"5\n" + + "\tBlockStmt\x12(\n" + + "\x06blocks\x18\x01 \x03(\v2\x10.opa.ir.v1.BlockR\x06blocks\"!\n" + + "\tBreakStmt\x12\x14\n" + + "\x05index\x18\x01 \x01(\rR\x05index\"e\n" + + "\x0fCallDynamicStmt\x12\x12\n" + + "\x04args\x18\x01 \x03(\x05R\x04args\x12\x16\n" + + "\x06result\x18\x02 \x01(\x05R\x06result\x12&\n" + + "\x04path\x18\x03 \x03(\v2\x12.opa.ir.v1.OperandR\x04path\"f\n" + + "\bCallStmt\x12\x1a\n" + + "\bfunction\x18\x01 \x01(\tR\bfunction\x12&\n" + + "\x04args\x18\x02 \x03(\v2\x12.opa.ir.v1.OperandR\x04args\x12\x16\n" + + "\x06result\x18\x03 \x01(\x05R\x06result\"s\n" + + "\aDotStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\x12$\n" + + "\x03key\x18\x02 \x01(\v2\x12.opa.ir.v1.OperandR\x03key\x12\x16\n" + + "\x06target\x18\x03 \x01(\x05R\x06target\"O\n" + + "\tEqualStmt\x12 \n" + + "\x01a\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x01a\x12 \n" + + "\x01b\x18\x02 \x01(\v2\x12.opa.ir.v1.OperandR\x01b\"9\n" + + "\vIsArrayStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\"'\n" + + "\rIsDefinedStmt\x12\x16\n" + + "\x06source\x18\x01 \x01(\x05R\x06source\":\n" + + "\fIsObjectStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\"7\n" + + "\tIsSetStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\")\n" + + "\x0fIsUndefinedStmt\x12\x16\n" + + "\x06source\x18\x01 \x01(\x05R\x06source\"M\n" + + "\aLenStmt\x12*\n" + + "\x06source\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x06source\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"C\n" + + "\rMakeArrayStmt\x12\x1a\n" + + "\bcapacity\x18\x01 \x01(\x05R\bcapacity\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"&\n" + + "\fMakeNullStmt\x12\x16\n" + + "\x06target\x18\x01 \x01(\x05R\x06target\"A\n" + + "\x11MakeNumberIntStmt\x12\x14\n" + + "\x05value\x18\x01 \x01(\x03R\x05value\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"A\n" + + "\x11MakeNumberRefStmt\x12\x14\n" + + "\x05index\x18\x01 \x01(\x05R\x05index\x12\x16\n" + + "\x06target\x18\x02 \x01(\x05R\x06target\"(\n" + + "\x0eMakeObjectStmt\x12\x16\n" + + "\x06target\x18\x01 \x01(\x05R\x06target\"%\n" + + "\vMakeSetStmt\x12\x16\n" + + "\x06target\x18\x01 \x01(\x05R\x06target\"\t\n" + + "\aNopStmt\"R\n" + + "\fNotEqualStmt\x12 \n" + + "\x01a\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x01a\x12 \n" + + "\x01b\x18\x02 \x01(\v2\x12.opa.ir.v1.OperandR\x01b\"1\n" + + "\aNotStmt\x12&\n" + + "\x05block\x18\x01 \x01(\v2\x10.opa.ir.v1.BlockR\x05block\"~\n" + + "\x14ObjectInsertOnceStmt\x12$\n" + + "\x03key\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x03key\x12(\n" + + "\x05value\x18\x02 \x01(\v2\x12.opa.ir.v1.OperandR\x05value\x12\x16\n" + + "\x06object\x18\x03 \x01(\x05R\x06object\"z\n" + + "\x10ObjectInsertStmt\x12$\n" + + "\x03key\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x03key\x12(\n" + + "\x05value\x18\x02 \x01(\v2\x12.opa.ir.v1.OperandR\x05value\x12\x16\n" + + "\x06object\x18\x03 \x01(\x05R\x06object\"E\n" + + "\x0fObjectMergeStmt\x12\f\n" + + "\x01a\x18\x01 \x01(\x05R\x01a\x12\f\n" + + "\x01b\x18\x02 \x01(\x05R\x01b\x12\x16\n" + + "\x06target\x18\x03 \x01(\x05R\x06target\"(\n" + + "\x0eResetLocalStmt\x12\x16\n" + + "\x06target\x18\x01 \x01(\x05R\x06target\"(\n" + + "\x10ResultSetAddStmt\x12\x14\n" + + "\x05value\x18\x01 \x01(\x05R\x05value\")\n" + + "\x0fReturnLocalStmt\x12\x16\n" + + "\x06source\x18\x01 \x01(\x05R\x06source\"r\n" + + "\bScanStmt\x12\x16\n" + + "\x06source\x18\x01 \x01(\x05R\x06source\x12\x10\n" + + "\x03key\x18\x02 \x01(\x05R\x03key\x12\x14\n" + + "\x05value\x18\x03 \x01(\x05R\x05value\x12&\n" + + "\x05block\x18\x04 \x01(\v2\x10.opa.ir.v1.BlockR\x05block\"H\n" + + "\n" + + "SetAddStmt\x12(\n" + + "\x05value\x18\x01 \x01(\v2\x12.opa.ir.v1.OperandR\x05value\x12\x10\n" + + "\x03set\x18\x02 \x01(\x05R\x03set\"\x86\x01\n" + + "\bWithStmt\x12\x14\n" + + "\x05local\x18\x01 \x01(\x05R\x05local\x12\x12\n" + + "\x04path\x18\x02 \x03(\x05R\x04path\x12(\n" + + "\x05value\x18\x03 \x01(\v2\x12.opa.ir.v1.OperandR\x05value\x12&\n" + + "\x05block\x18\x04 \x01(\v2\x10.opa.ir.v1.BlockR\x05blockB/P\x01Z+github.com/open-policy-agent/opa/v1/ir/v1pbb\beditionsp\xe8\a" + +var ( + file_v1_ir_plan_proto_rawDescOnce sync.Once + file_v1_ir_plan_proto_rawDescData []byte +) + +func file_v1_ir_plan_proto_rawDescGZIP() []byte { + file_v1_ir_plan_proto_rawDescOnce.Do(func() { + file_v1_ir_plan_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_v1_ir_plan_proto_rawDesc), len(file_v1_ir_plan_proto_rawDesc))) + }) + return file_v1_ir_plan_proto_rawDescData +} + +var file_v1_ir_plan_proto_msgTypes = make([]protoimpl.MessageInfo, 46) +var file_v1_ir_plan_proto_goTypes = []any{ + (*Policy)(nil), // 0: opa.ir.v1.Policy + (*Static)(nil), // 1: opa.ir.v1.Static + (*BuiltinFunc)(nil), // 2: opa.ir.v1.BuiltinFunc + (*Plans)(nil), // 3: opa.ir.v1.Plans + (*Funcs)(nil), // 4: opa.ir.v1.Funcs + (*Func)(nil), // 5: opa.ir.v1.Func + (*Plan)(nil), // 6: opa.ir.v1.Plan + (*Block)(nil), // 7: opa.ir.v1.Block + (*StringConst)(nil), // 8: opa.ir.v1.StringConst + (*Operand)(nil), // 9: opa.ir.v1.Operand + (*Val)(nil), // 10: opa.ir.v1.Val + (*Stmt)(nil), // 11: opa.ir.v1.Stmt + (*ArrayAppendStmt)(nil), // 12: opa.ir.v1.ArrayAppendStmt + (*AssignIntStmt)(nil), // 13: opa.ir.v1.AssignIntStmt + (*AssignVarOnceStmt)(nil), // 14: opa.ir.v1.AssignVarOnceStmt + (*AssignVarStmt)(nil), // 15: opa.ir.v1.AssignVarStmt + (*BlockStmt)(nil), // 16: opa.ir.v1.BlockStmt + (*BreakStmt)(nil), // 17: opa.ir.v1.BreakStmt + (*CallDynamicStmt)(nil), // 18: opa.ir.v1.CallDynamicStmt + (*CallStmt)(nil), // 19: opa.ir.v1.CallStmt + (*DotStmt)(nil), // 20: opa.ir.v1.DotStmt + (*EqualStmt)(nil), // 21: opa.ir.v1.EqualStmt + (*IsArrayStmt)(nil), // 22: opa.ir.v1.IsArrayStmt + (*IsDefinedStmt)(nil), // 23: opa.ir.v1.IsDefinedStmt + (*IsObjectStmt)(nil), // 24: opa.ir.v1.IsObjectStmt + (*IsSetStmt)(nil), // 25: opa.ir.v1.IsSetStmt + (*IsUndefinedStmt)(nil), // 26: opa.ir.v1.IsUndefinedStmt + (*LenStmt)(nil), // 27: opa.ir.v1.LenStmt + (*MakeArrayStmt)(nil), // 28: opa.ir.v1.MakeArrayStmt + (*MakeNullStmt)(nil), // 29: opa.ir.v1.MakeNullStmt + (*MakeNumberIntStmt)(nil), // 30: opa.ir.v1.MakeNumberIntStmt + (*MakeNumberRefStmt)(nil), // 31: opa.ir.v1.MakeNumberRefStmt + (*MakeObjectStmt)(nil), // 32: opa.ir.v1.MakeObjectStmt + (*MakeSetStmt)(nil), // 33: opa.ir.v1.MakeSetStmt + (*NopStmt)(nil), // 34: opa.ir.v1.NopStmt + (*NotEqualStmt)(nil), // 35: opa.ir.v1.NotEqualStmt + (*NotStmt)(nil), // 36: opa.ir.v1.NotStmt + (*ObjectInsertOnceStmt)(nil), // 37: opa.ir.v1.ObjectInsertOnceStmt + (*ObjectInsertStmt)(nil), // 38: opa.ir.v1.ObjectInsertStmt + (*ObjectMergeStmt)(nil), // 39: opa.ir.v1.ObjectMergeStmt + (*ResetLocalStmt)(nil), // 40: opa.ir.v1.ResetLocalStmt + (*ResultSetAddStmt)(nil), // 41: opa.ir.v1.ResultSetAddStmt + (*ReturnLocalStmt)(nil), // 42: opa.ir.v1.ReturnLocalStmt + (*ScanStmt)(nil), // 43: opa.ir.v1.ScanStmt + (*SetAddStmt)(nil), // 44: opa.ir.v1.SetAddStmt + (*WithStmt)(nil), // 45: opa.ir.v1.WithStmt +} +var file_v1_ir_plan_proto_depIdxs = []int32{ + 1, // 0: opa.ir.v1.Policy.static:type_name -> opa.ir.v1.Static + 3, // 1: opa.ir.v1.Policy.plans:type_name -> opa.ir.v1.Plans + 4, // 2: opa.ir.v1.Policy.funcs:type_name -> opa.ir.v1.Funcs + 8, // 3: opa.ir.v1.Static.strings:type_name -> opa.ir.v1.StringConst + 2, // 4: opa.ir.v1.Static.builtin_funcs:type_name -> opa.ir.v1.BuiltinFunc + 8, // 5: opa.ir.v1.Static.files:type_name -> opa.ir.v1.StringConst + 6, // 6: opa.ir.v1.Plans.plans:type_name -> opa.ir.v1.Plan + 5, // 7: opa.ir.v1.Funcs.funcs:type_name -> opa.ir.v1.Func + 7, // 8: opa.ir.v1.Func.blocks:type_name -> opa.ir.v1.Block + 7, // 9: opa.ir.v1.Plan.blocks:type_name -> opa.ir.v1.Block + 11, // 10: opa.ir.v1.Block.stmts:type_name -> opa.ir.v1.Stmt + 10, // 11: opa.ir.v1.Operand.value:type_name -> opa.ir.v1.Val + 12, // 12: opa.ir.v1.Stmt.array_append_stmt:type_name -> opa.ir.v1.ArrayAppendStmt + 13, // 13: opa.ir.v1.Stmt.assign_int_stmt:type_name -> opa.ir.v1.AssignIntStmt + 14, // 14: opa.ir.v1.Stmt.assign_var_once_stmt:type_name -> opa.ir.v1.AssignVarOnceStmt + 15, // 15: opa.ir.v1.Stmt.assign_var_stmt:type_name -> opa.ir.v1.AssignVarStmt + 16, // 16: opa.ir.v1.Stmt.block_stmt:type_name -> opa.ir.v1.BlockStmt + 17, // 17: opa.ir.v1.Stmt.break_stmt:type_name -> opa.ir.v1.BreakStmt + 18, // 18: opa.ir.v1.Stmt.call_dynamic_stmt:type_name -> opa.ir.v1.CallDynamicStmt + 19, // 19: opa.ir.v1.Stmt.call_stmt:type_name -> opa.ir.v1.CallStmt + 20, // 20: opa.ir.v1.Stmt.dot_stmt:type_name -> opa.ir.v1.DotStmt + 21, // 21: opa.ir.v1.Stmt.equal_stmt:type_name -> opa.ir.v1.EqualStmt + 22, // 22: opa.ir.v1.Stmt.is_array_stmt:type_name -> opa.ir.v1.IsArrayStmt + 23, // 23: opa.ir.v1.Stmt.is_defined_stmt:type_name -> opa.ir.v1.IsDefinedStmt + 24, // 24: opa.ir.v1.Stmt.is_object_stmt:type_name -> opa.ir.v1.IsObjectStmt + 25, // 25: opa.ir.v1.Stmt.is_set_stmt:type_name -> opa.ir.v1.IsSetStmt + 26, // 26: opa.ir.v1.Stmt.is_undefined_stmt:type_name -> opa.ir.v1.IsUndefinedStmt + 27, // 27: opa.ir.v1.Stmt.len_stmt:type_name -> opa.ir.v1.LenStmt + 28, // 28: opa.ir.v1.Stmt.make_array_stmt:type_name -> opa.ir.v1.MakeArrayStmt + 29, // 29: opa.ir.v1.Stmt.make_null_stmt:type_name -> opa.ir.v1.MakeNullStmt + 30, // 30: opa.ir.v1.Stmt.make_number_int_stmt:type_name -> opa.ir.v1.MakeNumberIntStmt + 31, // 31: opa.ir.v1.Stmt.make_number_ref_stmt:type_name -> opa.ir.v1.MakeNumberRefStmt + 32, // 32: opa.ir.v1.Stmt.make_object_stmt:type_name -> opa.ir.v1.MakeObjectStmt + 33, // 33: opa.ir.v1.Stmt.make_set_stmt:type_name -> opa.ir.v1.MakeSetStmt + 34, // 34: opa.ir.v1.Stmt.nop_stmt:type_name -> opa.ir.v1.NopStmt + 35, // 35: opa.ir.v1.Stmt.not_equal_stmt:type_name -> opa.ir.v1.NotEqualStmt + 36, // 36: opa.ir.v1.Stmt.not_stmt:type_name -> opa.ir.v1.NotStmt + 37, // 37: opa.ir.v1.Stmt.object_insert_once_stmt:type_name -> opa.ir.v1.ObjectInsertOnceStmt + 38, // 38: opa.ir.v1.Stmt.object_insert_stmt:type_name -> opa.ir.v1.ObjectInsertStmt + 39, // 39: opa.ir.v1.Stmt.object_merge_stmt:type_name -> opa.ir.v1.ObjectMergeStmt + 40, // 40: opa.ir.v1.Stmt.reset_local_stmt:type_name -> opa.ir.v1.ResetLocalStmt + 41, // 41: opa.ir.v1.Stmt.result_set_add_stmt:type_name -> opa.ir.v1.ResultSetAddStmt + 42, // 42: opa.ir.v1.Stmt.return_local_stmt:type_name -> opa.ir.v1.ReturnLocalStmt + 43, // 43: opa.ir.v1.Stmt.scan_stmt:type_name -> opa.ir.v1.ScanStmt + 44, // 44: opa.ir.v1.Stmt.set_add_stmt:type_name -> opa.ir.v1.SetAddStmt + 45, // 45: opa.ir.v1.Stmt.with_stmt:type_name -> opa.ir.v1.WithStmt + 9, // 46: opa.ir.v1.ArrayAppendStmt.value:type_name -> opa.ir.v1.Operand + 9, // 47: opa.ir.v1.AssignVarOnceStmt.source:type_name -> opa.ir.v1.Operand + 9, // 48: opa.ir.v1.AssignVarStmt.source:type_name -> opa.ir.v1.Operand + 7, // 49: opa.ir.v1.BlockStmt.blocks:type_name -> opa.ir.v1.Block + 9, // 50: opa.ir.v1.CallDynamicStmt.path:type_name -> opa.ir.v1.Operand + 9, // 51: opa.ir.v1.CallStmt.args:type_name -> opa.ir.v1.Operand + 9, // 52: opa.ir.v1.DotStmt.source:type_name -> opa.ir.v1.Operand + 9, // 53: opa.ir.v1.DotStmt.key:type_name -> opa.ir.v1.Operand + 9, // 54: opa.ir.v1.EqualStmt.a:type_name -> opa.ir.v1.Operand + 9, // 55: opa.ir.v1.EqualStmt.b:type_name -> opa.ir.v1.Operand + 9, // 56: opa.ir.v1.IsArrayStmt.source:type_name -> opa.ir.v1.Operand + 9, // 57: opa.ir.v1.IsObjectStmt.source:type_name -> opa.ir.v1.Operand + 9, // 58: opa.ir.v1.IsSetStmt.source:type_name -> opa.ir.v1.Operand + 9, // 59: opa.ir.v1.LenStmt.source:type_name -> opa.ir.v1.Operand + 9, // 60: opa.ir.v1.NotEqualStmt.a:type_name -> opa.ir.v1.Operand + 9, // 61: opa.ir.v1.NotEqualStmt.b:type_name -> opa.ir.v1.Operand + 7, // 62: opa.ir.v1.NotStmt.block:type_name -> opa.ir.v1.Block + 9, // 63: opa.ir.v1.ObjectInsertOnceStmt.key:type_name -> opa.ir.v1.Operand + 9, // 64: opa.ir.v1.ObjectInsertOnceStmt.value:type_name -> opa.ir.v1.Operand + 9, // 65: opa.ir.v1.ObjectInsertStmt.key:type_name -> opa.ir.v1.Operand + 9, // 66: opa.ir.v1.ObjectInsertStmt.value:type_name -> opa.ir.v1.Operand + 7, // 67: opa.ir.v1.ScanStmt.block:type_name -> opa.ir.v1.Block + 9, // 68: opa.ir.v1.SetAddStmt.value:type_name -> opa.ir.v1.Operand + 9, // 69: opa.ir.v1.WithStmt.value:type_name -> opa.ir.v1.Operand + 7, // 70: opa.ir.v1.WithStmt.block:type_name -> opa.ir.v1.Block + 71, // [71:71] is the sub-list for method output_type + 71, // [71:71] is the sub-list for method input_type + 71, // [71:71] is the sub-list for extension type_name + 71, // [71:71] is the sub-list for extension extendee + 0, // [0:71] is the sub-list for field type_name +} + +func init() { file_v1_ir_plan_proto_init() } +func file_v1_ir_plan_proto_init() { + if File_v1_ir_plan_proto != nil { + return + } + file_v1_ir_plan_proto_msgTypes[10].OneofWrappers = []any{ + (*Val_Bool)(nil), + (*Val_Local)(nil), + (*Val_StringIndex)(nil), + } + file_v1_ir_plan_proto_msgTypes[11].OneofWrappers = []any{ + (*Stmt_ArrayAppendStmt)(nil), + (*Stmt_AssignIntStmt)(nil), + (*Stmt_AssignVarOnceStmt)(nil), + (*Stmt_AssignVarStmt)(nil), + (*Stmt_BlockStmt)(nil), + (*Stmt_BreakStmt)(nil), + (*Stmt_CallDynamicStmt)(nil), + (*Stmt_CallStmt)(nil), + (*Stmt_DotStmt)(nil), + (*Stmt_EqualStmt)(nil), + (*Stmt_IsArrayStmt)(nil), + (*Stmt_IsDefinedStmt)(nil), + (*Stmt_IsObjectStmt)(nil), + (*Stmt_IsSetStmt)(nil), + (*Stmt_IsUndefinedStmt)(nil), + (*Stmt_LenStmt)(nil), + (*Stmt_MakeArrayStmt)(nil), + (*Stmt_MakeNullStmt)(nil), + (*Stmt_MakeNumberIntStmt)(nil), + (*Stmt_MakeNumberRefStmt)(nil), + (*Stmt_MakeObjectStmt)(nil), + (*Stmt_MakeSetStmt)(nil), + (*Stmt_NopStmt)(nil), + (*Stmt_NotEqualStmt)(nil), + (*Stmt_NotStmt)(nil), + (*Stmt_ObjectInsertOnceStmt)(nil), + (*Stmt_ObjectInsertStmt)(nil), + (*Stmt_ObjectMergeStmt)(nil), + (*Stmt_ResetLocalStmt)(nil), + (*Stmt_ResultSetAddStmt)(nil), + (*Stmt_ReturnLocalStmt)(nil), + (*Stmt_ScanStmt)(nil), + (*Stmt_SetAddStmt)(nil), + (*Stmt_WithStmt)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_ir_plan_proto_rawDesc), len(file_v1_ir_plan_proto_rawDesc)), + NumEnums: 0, + NumMessages: 46, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v1_ir_plan_proto_goTypes, + DependencyIndexes: file_v1_ir_plan_proto_depIdxs, + MessageInfos: file_v1_ir_plan_proto_msgTypes, + }.Build() + File_v1_ir_plan_proto = out.File + file_v1_ir_plan_proto_goTypes = nil + file_v1_ir_plan_proto_depIdxs = nil +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/keys/keys.go b/vendor/github.com/open-policy-agent/opa/v1/keys/keys.go index fba7a9c939..0581317172 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/keys/keys.go +++ b/vendor/github.com/open-policy-agent/opa/v1/keys/keys.go @@ -33,6 +33,9 @@ type Config struct { // Equal returns true if this key config is equal to the other. func (k *Config) Equal(other *Config) bool { + if k == other { + return true + } return other != nil && *k == *other } diff --git a/vendor/github.com/open-policy-agent/opa/v1/logging/slog.go b/vendor/github.com/open-policy-agent/opa/v1/logging/slog.go deleted file mode 100644 index b6ee1c7c71..0000000000 --- a/vendor/github.com/open-policy-agent/opa/v1/logging/slog.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2026 The OPA Authors. All rights reserved. -// Use of this source code is governed by an Apache2 -// license that can be found in the LICENSE file. - -package logging - -import ( - "context" - "log/slog" -) - -// AsSlogLogger returns a *slog.Logger that forwards log records to the given Logger. -// Structured fields are forwarded via WithFields; log levels map to the equivalent -// Logger methods. Groups are not supported and are ignored. -func AsSlogLogger(logger Logger) *slog.Logger { - return slog.New(slogHandler{logger: logger}) -} - -// AsSlogLoggerWithPinnedLevel returns a *slog.Logger that forwards all log records -// to the given Logger at the specified fixed level, ignoring the slog record's own level. -// This is useful when bridging a library that uses slog into OPA's logger at a fixed verbosity. -func AsSlogLoggerWithPinnedLevel(logger Logger, level Level) *slog.Logger { - return slog.New(slogHandler{logger: logger, pinnedLevel: &level}) -} - -type slogHandler struct { - logger Logger - pinnedLevel *Level -} - -func (slogHandler) Enabled(_ context.Context, _ slog.Level) bool { return true } - -func (h slogHandler) Handle(_ context.Context, r slog.Record) error { - fields := make(map[string]any, r.NumAttrs()) - - r.Attrs(func(a slog.Attr) bool { - fields[a.Key] = a.Value.Any() - - return true - }) - - l := h.logger.WithFields(fields) - - if h.pinnedLevel != nil { - dispatchAtLevel(l, *h.pinnedLevel, r.Message) - return nil - } - - // slog.Level is int; cascade from most-severe so custom levels between named ones route correctly. - switch { - case r.Level >= slog.LevelError: - l.Error(r.Message) - case r.Level >= slog.LevelWarn: - l.Warn(r.Message) - case r.Level >= slog.LevelInfo: - l.Info(r.Message) - default: - l.Debug(r.Message) - } - - return nil -} - -func (h slogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - fields := make(map[string]any, len(attrs)) - - for _, a := range attrs { - fields[a.Key] = a.Value.Any() - } - - return slogHandler{logger: h.logger.WithFields(fields), pinnedLevel: h.pinnedLevel} -} - -func (h slogHandler) WithGroup(_ string) slog.Handler { return h } - -func dispatchAtLevel(l Logger, level Level, msg string) { - switch level { - case Error: - l.Error(msg) - case Warn: - l.Warn(msg) - case Info: - l.Info(msg) - default: - l.Debug(msg) - } -} diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/internal/ptr/ptr.go b/vendor/github.com/open-policy-agent/opa/v1/storage/internal/ptr/ptr.go index bef39ebf49..62c3a51e90 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/internal/ptr/ptr.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/internal/ptr/ptr.go @@ -6,11 +6,10 @@ package ptr import ( - "strconv" - "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/storage" "github.com/open-policy-agent/opa/v1/storage/internal/errors" + "github.com/open-policy-agent/opa/v1/util" ) func Ptr(data any, path storage.Path) (any, error) { @@ -92,7 +91,7 @@ func ValuePtr(data ast.Value, path storage.Path) (ast.Value, error) { } func ValidateArrayIndex(arr []any, s string, path storage.Path) (int, error) { - idx, ok := isInt(s) + idx, ok := util.Atoi(s) if !ok { return 0, errors.NewNotFoundErrorWithHint(path, errors.ArrayIndexTypeMsg) } @@ -100,7 +99,7 @@ func ValidateArrayIndex(arr []any, s string, path storage.Path) (int, error) { } func ValidateASTArrayIndex(arr *ast.Array, s string, path storage.Path) (int, error) { - idx, ok := isInt(s) + idx, ok := util.Atoi(s) if !ok { return 0, errors.NewNotFoundErrorWithHint(path, errors.ArrayIndexTypeMsg) } @@ -111,18 +110,13 @@ func ValidateASTArrayIndex(arr *ast.Array, s string, path storage.Path) (int, er // array element like `ValidateArrayIndex`, but returns a `resource_conflict` error // if it is not. func ValidateArrayIndexForWrite(arr []any, s string, i int, path storage.Path) (int, error) { - idx, ok := isInt(s) + idx, ok := util.Atoi(s) if !ok { return 0, errors.NewWriteConflictError(path[:i-1]) } return inRange(idx, arr, path) } -func isInt(s string) (int, bool) { - idx, err := strconv.Atoi(s) - return idx, err == nil -} - func inRange(i int, arr any, path storage.Path) (int, error) { var arrLen int diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go index 356e7b38b0..f018057213 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go @@ -25,6 +25,44 @@ func builtinCount(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) e return builtins.NewOperandTypeErr(1, operands[0].Value, "array", "object", "set", "string") } +// termIterable is satisfied by both *ast.Array and ast.Set. +type termIterable interface { + Iter(func(*ast.Term) error) error +} + +// exactIntAccumulate accumulates the numbers in a with op on exact big.Ints, reporting false if +// any element is not an integer, in which case the caller falls back to the float path. +// +// That float path accumulates in a big.Float carrying the default mantissa, so integers needing +// more significant bits are silently rounded. +func exactIntAccumulate(a termIterable, init int64, op func(z, x, y *big.Int) *big.Int) (ast.Number, bool) { + acc := big.NewInt(init) + exact := true + + _ = a.Iter(func(x *ast.Term) error { + if !exact { + return nil + } + n, ok := x.Value.(ast.Number) + if !ok { + exact = false + return nil + } + i, err := builtins.NumberToInt(n) + if err != nil { + exact = false + return nil + } + op(acc, acc, i) + return nil + }) + + if !exact { + return "", false + } + return builtins.IntToNumber(acc), true +} + func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { switch a := operands[0].Value.(type) { case *ast.Array: @@ -44,6 +82,10 @@ func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err } // Non-integer values found, so we need to sum as floats. + if n, ok := exactIntAccumulate(a, 0, (*big.Int).Add); ok { + return iter(ast.NewTerm(n)) + } + sum := big.NewFloat(0) tmp := new(big.Float) err := a.Iter(func(x *ast.Term) error { @@ -74,6 +116,10 @@ func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err return iter(ast.InternedTerm(is)) } + if n, ok := exactIntAccumulate(a, 0, (*big.Int).Add); ok { + return iter(ast.NewTerm(n)) + } + sum := big.NewFloat(0) tmp := new(big.Float) err := a.Iter(func(x *ast.Term) error { @@ -95,6 +141,10 @@ func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err func builtinProduct(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { switch a := operands[0].Value.(type) { case *ast.Array: + if n, ok := exactIntAccumulate(a, 1, (*big.Int).Mul); ok { + return iter(ast.NewTerm(n)) + } + product := big.NewFloat(1) tmp := new(big.Float) err := a.Iter(func(x *ast.Term) error { @@ -110,6 +160,10 @@ func builtinProduct(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) } return iter(ast.NewTerm(builtins.FloatToNumber(product))) case ast.Set: + if n, ok := exactIntAccumulate(a, 1, (*big.Int).Mul); ok { + return iter(ast.NewTerm(n)) + } + product := big.NewFloat(1) tmp := new(big.Float) err := a.Iter(func(x *ast.Term) error { @@ -260,34 +314,29 @@ func builtinAny(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err } func builtinMember(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - containee := operands[0] switch c := operands[1].Value.(type) { case ast.Set: - return iter(ast.InternedTerm(c.Contains(containee))) + return iter(ast.InternedTerm(c.Contains(operands[0]))) case *ast.Array: - for i := range c.Len() { - if c.Elem(i).Value.Compare(containee.Value) == 0 { - return iter(ast.InternedTerm(true)) - } - } - return iter(ast.InternedTerm(false)) + return iter(ast.InternedTerm(c.Until(operands[0].Equal))) case ast.Object: return iter(ast.InternedTerm(c.Until(func(_, v *ast.Term) bool { - return v.Value.Compare(containee.Value) == 0 + return operands[0].Equal(v) }))) } return iter(ast.InternedTerm(false)) } func builtinMemberWithKey(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - key, val := operands[0], operands[1] - switch c := operands[2].Value.(type) { - case interface{ Get(*ast.Term) *ast.Term }: - ret := false - if act := c.Get(key); act != nil { - ret = act.Value.Compare(val.Value) == 0 - } - return iter(ast.InternedTerm(ret)) + type getter interface { + Get(*ast.Term) *ast.Term + } + col, key, val := operands[2], operands[0], operands[1] + switch c := col.Value.(type) { + case ast.Set: + return iter(ast.InternedTerm(c.Contains(key) && key.Equal(val))) + case getter: + return iter(ast.InternedTerm(val.Equal(c.Get(key)))) } return iter(ast.InternedTerm(false)) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go index 91190330fa..ce870904f5 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go @@ -53,6 +53,22 @@ func arithFloor(a *big.Float) (*big.Float, error) { return new(big.Float).Sub(f, big.NewFloat(1.0)), nil } +// exactIntArith applies op to n1 and n2 as exact big.Ints when both are integers. +// +// The big.Float path used otherwise carries the default mantissa, so integers needing more +// significant bits than that are silently rounded before the operation is applied. +func exactIntArith(n1, n2 ast.Number, op func(z, x, y *big.Int) *big.Int) (ast.Number, bool) { + x, err := builtins.NumberToInt(n1) + if err != nil { + return "", false + } + y, err := builtins.NumberToInt(n2) + if err != nil { + return "", false + } + return builtins.IntToNumber(op(new(big.Int), x, y)), true +} + func builtinPlus(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { n1, err := builtins.NumberOperand(operands[0].Value, 1) if err != nil { @@ -70,6 +86,10 @@ func builtinPlus(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) er return iter(ast.InternedTerm(x + y)) } + if n, ok := exactIntArith(n1, n2, (*big.Int).Add); ok { + return iter(ast.NewTerm(n)) + } + f := new(big.Float).Add(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) return iter(ast.NewTerm(builtins.FloatToNumber(f))) @@ -92,6 +112,10 @@ func builtinMultiply(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term return iter(ast.InternedTerm(x * y)) } + if n, ok := exactIntArith(n1, n2, (*big.Int).Mul); ok { + return iter(ast.NewTerm(n)) + } + f := new(big.Float).Mul(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) return iter(ast.NewTerm(builtins.FloatToNumber(f))) @@ -158,6 +182,10 @@ func builtinMinus(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) e return iter(ast.InternedTerm(x - y)) } + if n, ok := exactIntArith(n1, n2, (*big.Int).Sub); ok { + return iter(ast.NewTerm(n)) + } + f := new(big.Float).Sub(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) return iter(ast.NewTerm(builtins.FloatToNumber(f))) diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins/builtins.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins/builtins.go index 0f02021f24..8726284279 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins/builtins.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins/builtins.go @@ -269,12 +269,19 @@ func FloatToNumber(f *big.Float) ast.Number { // NumberToInt converts n to a big int. // If n cannot be converted to an big int, an error is returned. func NumberToInt(n ast.Number) (*big.Int, error) { - f := NumberToFloat(n) - r, accuracy := f.Int(nil) - if accuracy != big.Exact { + // Integer literals are parsed exactly. Going through NumberToFloat first would round any + // value needing more than the big.Float default mantissa, and because the rounded value is + // itself an integer, the accuracy check below cannot detect that it happened. + if i, ok := new(big.Int).SetString(string(n), 10); ok { + return i, nil + } + // Fractional and exponent forms. big.Rat parses both exactly, so a value such as 1e30 stays + // exact, and a genuinely fractional value is rejected rather than silently truncated. + r, ok := new(big.Rat).SetString(string(n)) + if !ok || !r.IsInt() { return nil, errors.New("illegal value") } - return r, nil + return new(big.Int).Set(r.Num()), nil } // IntToNumber converts i to a number. diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go index a6c89b4537..9a162e2a57 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go @@ -41,7 +41,7 @@ type BaseCache interface { } type virtualCache struct { - stack []*virtualCacheElem + stack util.SliceStack[*virtualCacheElem] } type virtualCacheElem struct { @@ -57,11 +57,11 @@ func NewVirtualCache() VirtualCache { } func (c *virtualCache) Push() { - c.stack = append(c.stack, newVirtualCacheElem()) + c.stack.Push(newVirtualCacheElem()) } func (c *virtualCache) Pop() { - c.stack = c.stack[:len(c.stack)-1] + c.stack.Pop() } // Returns the resolved value of the AST term and a flag indicating if the value @@ -72,7 +72,7 @@ func (c *virtualCache) Pop() { // nil, false indicates the ref has not been cached // ast.Term, true is impossible func (c *virtualCache) Get(ref ast.Ref) (*ast.Term, bool) { - node := c.stack[len(c.stack)-1] + node := c.stack.Peek() for i := range ref { x, ok := node.children.Get(ref[i]) if !ok { @@ -90,7 +90,7 @@ func (c *virtualCache) Get(ref ast.Ref) (*ast.Term, bool) { // If value is a nil pointer, set the 'undefined' flag on the cache element to // indicate that the Ref has resolved to undefined. func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) { - node := c.stack[len(c.stack)-1] + node := c.stack.Peek() for i := range ref { x, ok := node.children.Get(ref[i]) if ok { @@ -109,7 +109,7 @@ func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) { } func (c *virtualCache) Keys() []ast.Ref { - node := c.stack[len(c.stack)-1] + node := c.stack.Peek() return keysRecursive(nil, node) } @@ -133,7 +133,7 @@ func newVirtualCacheElem() *virtualCacheElem { } func newVirtualCacheHashMap() *util.HasherMap[*ast.Term, *virtualCacheElem] { - return util.NewHasherMap[*ast.Term, *virtualCacheElem](ast.TermValueEqual) + return util.NewHasherMap[*ast.Term, *virtualCacheElem]((*ast.Term).Equal) } // baseCache implements a trie structure to cache base documents read out of @@ -204,7 +204,7 @@ func (e *baseCacheElem) set(value ast.Value) { } type refStack struct { - sl []refStackElem + sl util.SliceStack[refStackElem] } type refStackElem struct { @@ -216,20 +216,21 @@ func newRefStack() *refStack { } func (s *refStack) Push(refs []ast.Ref) { - s.sl = append(s.sl, refStackElem{refs: refs}) + s.sl.Push(refStackElem{refs: refs}) } func (s *refStack) Pop() { if s == nil { return } - s.sl = s.sl[:len(s.sl)-1] + s.sl.Pop() } func (s *refStack) Prefixed(ref ast.Ref) bool { if s != nil { - for i := len(s.sl) - 1; i >= 0; i-- { - if slices.ContainsFunc(s.sl[i].refs, ref.HasPrefix) { + sl := s.sl.Slice() + for i := len(sl) - 1; i >= 0; i-- { + if slices.ContainsFunc(sl[i].refs, ref.HasPrefix) { return true } } @@ -238,7 +239,7 @@ func (s *refStack) Prefixed(ref ast.Ref) bool { } type comprehensionCache struct { - stack []map[*ast.Term]*comprehensionCacheElem + stack util.SliceStack[map[*ast.Term]*comprehensionCacheElem] } type comprehensionCacheElem struct { @@ -253,20 +254,20 @@ func newComprehensionCache() *comprehensionCache { } func (c *comprehensionCache) Push() { - c.stack = append(c.stack, map[*ast.Term]*comprehensionCacheElem{}) + c.stack.Push(map[*ast.Term]*comprehensionCacheElem{}) } func (c *comprehensionCache) Pop() { - c.stack = c.stack[:len(c.stack)-1] + c.stack.Pop() } func (c *comprehensionCache) Elem(t *ast.Term) (*comprehensionCacheElem, bool) { - elem, ok := c.stack[len(c.stack)-1][t] + elem, ok := c.stack.Peek()[t] return elem, ok } func (c *comprehensionCache) Set(t *ast.Term, elem *comprehensionCacheElem) { - c.stack[len(c.stack)-1][t] = elem + c.stack.Peek()[t] = elem } func newComprehensionCacheElem() *comprehensionCacheElem { @@ -301,15 +302,13 @@ func (c *comprehensionCacheElem) Put(key []*ast.Term, value *ast.Term) { } func newComprehensionCacheHashMap() *util.HasherMap[*ast.Term, *comprehensionCacheElem] { - return util.NewHasherMap[*ast.Term, *comprehensionCacheElem](ast.TermValueEqual) + return util.NewHasherMap[*ast.Term, *comprehensionCacheElem]((*ast.Term).Equal) } type functionMocksStack struct { - stack []*functionMocksElem + stack util.GroupStack[frame] } -type functionMocksElem []frame - type frame map[string]*ast.Term func newFunctionMocksStack() *functionMocksStack { @@ -318,21 +317,16 @@ func newFunctionMocksStack() *functionMocksStack { return stack } -func newFunctionMocksElem() *functionMocksElem { - return &functionMocksElem{} -} - func (s *functionMocksStack) Push() { - s.stack = append(s.stack, newFunctionMocksElem()) + s.stack.PushGroup(nil) } func (s *functionMocksStack) Pop() { - s.stack = s.stack[:len(s.stack)-1] + s.stack.PopGroup() } func (s *functionMocksStack) PopPairs() { - current := s.stack[len(s.stack)-1] - *current = (*current)[:len(*current)-1] + s.stack.Pop() } func (s *functionMocksStack) PutPairs(mocks [][2]*ast.Term) { @@ -344,8 +338,7 @@ func (s *functionMocksStack) PutPairs(mocks [][2]*ast.Term) { } func (s *functionMocksStack) Put(el frame) { - current := s.stack[len(s.stack)-1] - *current = append(*current, el) + s.stack.Push(el) } func (s *functionMocksStack) Get(f ast.Ref) (*ast.Term, bool) { @@ -353,7 +346,7 @@ func (s *functionMocksStack) Get(f ast.Ref) (*ast.Term, bool) { return nil, false } - current := *s.stack[len(s.stack)-1] + current := s.stack.PeekGroup() for i := len(current) - 1; i >= 0; i-- { if r, ok := current[i][f.String()]; ok { return r, true diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go index ae30723dfb..799a716136 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go @@ -34,6 +34,9 @@ type CopyPropagator struct { ensureNonEmptyBody bool compiler *ast.Compiler localvargen *localVarGenerator + // placeholders holds vars synthesized to keep a ref alive for its definedness. + // They appear nowhere else, so their bindings can be emitted as the bare ref. + placeholders ast.VarSet } type localVarGenerator struct { @@ -47,10 +50,17 @@ func (l *localVarGenerator) Generate() ast.Var { } +// generatePlaceholder returns a fresh local variable, recorded as a placeholder. +func (p *CopyPropagator) generatePlaceholder() ast.Var { + v := p.localvargen.Generate() + p.placeholders.Add(v) + return v +} + // New returns a new CopyPropagator that optimizes queries while preserving vars // in the livevars set. func New(livevars ast.VarSet) *CopyPropagator { - return &CopyPropagator{livevars: livevars, sorted: util.KeysSorted(livevars), localvargen: &localVarGenerator{}} + return &CopyPropagator{livevars: livevars, sorted: util.KeysSorted(livevars), localvargen: &localVarGenerator{}, placeholders: ast.NewVarSet()} } // WithEnsureNonEmptyBody configures p to ensure that results are always non-empty. @@ -188,7 +198,14 @@ func (p *CopyPropagator) Apply(query ast.Body) ast.Body { } if providesSafety || (!safevarRef && !containedIn(b.v, result)) { - result.Append(removedEq) + // For a placeholder key, emit the bare ref rather than `__localcp0__ = + // input.project`: both only require the ref to be defined, but the + // equality leaks the internal var into results (#6378). + if expr := p.placeholderRef(b); expr != nil { + result.Append(expr) + } else { + result.Append(removedEq) + } safe.Update(outputVars) } } @@ -244,11 +261,10 @@ func (t bindingPlugTransform) Transform(x any) (any, error) { } func (bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) ast.Value { - var result ast.Value = v // Apply union-find to remove redundant variables from input. - root, ok := pctx.uf.Find(v) + root, ok := pctx.uf.Find(result) if ok { result = root.Value() } @@ -258,7 +274,7 @@ func (bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) ast.Va if !ok { return result } - b := pctx.removedEqs.Get(v) + b := pctx.removedEqs.Get(result) if b == nil { return result } @@ -266,7 +282,7 @@ func (bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) ast.Va return result } - if r, ok := b.(ast.Ref); ok && r.OutputVars().Contains(v) { + if ast.NewTerm(b).Vars().Contains(v) { return result } @@ -311,7 +327,7 @@ func (p *CopyPropagator) updateBindings(pctx *plugContext, expr *ast.Expr) bool a, b := expr.Operand(0), expr.Operand(1) if a.Equal(b) { if p.livevarRef(a) { - pctx.removedEqs.Put(p.localvargen.Generate(), a.Value) + pctx.removedEqs.Put(p.generatePlaceholder(), a.Value) } return false } @@ -351,6 +367,20 @@ func (p *CopyPropagator) livevarRef(a *ast.Term) bool { return false } +// placeholderRef returns the ref a placeholder binding maps to, wrapped as a +// bare expression, or nil if b is not a placeholder-to-ref binding. +func (p *CopyPropagator) placeholderRef(b *binding) *ast.Expr { + k, ok := b.k.(ast.Var) + if !ok || !p.placeholders.Contains(k) { + return nil + } + ref, ok := b.v.(ast.Ref) + if !ok { + return nil + } + return ast.NewExpr(ast.NewTerm(ref)) +} + func (p *CopyPropagator) updateBindingsEq(a, b *ast.Term) (ast.Var, ast.Value, bool) { k, v, keep := p.updateBindingsEqAsymmetric(a, b) if !keep { @@ -498,18 +528,20 @@ func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) { func isNoop(expr *ast.Expr) bool { - if !expr.IsCall() && !expr.IsEvery() { - term := expr.Terms.(*ast.Term) - if !ast.IsConstant(term.Value) { + switch t := expr.Terms.(type) { + case []*ast.Term: + // A==A can be ignored + if expr.Operator().Equal(ast.Equal.Ref()) { + return expr.Operand(0).Equal(expr.Operand(1)) + } + return false + case *ast.Term: + if !ast.IsConstant(t.Value) { return false } - return !ast.Boolean(false).Equal(term.Value) - } - - // A==A can be ignored - if expr.Operator().Equal(ast.Equal.Ref()) { - return expr.Operand(0).Equal(expr.Operand(1)) + return !ast.Boolean(false).Equal(t.Value) + default: + // *ast.Every, *ast.Not, *ast.LogicalAnd, *ast.LogicalOr — none are no-ops. + return false } - - return false } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go new file mode 100644 index 0000000000..29b6f3761e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors_jsonv2.go @@ -0,0 +1,24 @@ +//go:build go1.27 + +package topdown + +import ( + "encoding/json/jsontext" + "errors" + + "github.com/open-policy-agent/opa/internal/jsonv2" +) + +func (e *Error) MarshalJSONTo(enc *jsontext.Encoder) (err error) { + enc.WriteToken(jsontext.BeginObject) + enc.WriteToken(jsontext.String("code")) + enc.WriteToken(jsontext.String(e.Code)) + enc.WriteToken(jsontext.String("message")) + enc.WriteToken(jsontext.String(e.Message)) + + if e.Location != nil { + err = jsonv2.WriteField(enc, "location", e.Location) + } + + return errors.Join(err, enc.WriteToken(jsontext.EndObject)) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go index b6700549de..f22af29267 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go @@ -8,7 +8,6 @@ import ( "slices" "strconv" "strings" - "sync" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/metrics" @@ -130,52 +129,13 @@ type eval struct { responseMetadata map[string]any } -type ( - evfp struct{ pool sync.Pool } - evbp struct{ pool sync.Pool } -) - -func (ep *evfp) Put(e *evalFunc) { - if e != nil { - e.e, e.terms, e.ir = nil, nil, nil - ep.pool.Put(e) - } -} - -func (ep *evfp) Get() *evalFunc { - return ep.pool.Get().(*evalFunc) -} - -func (ep *evbp) Put(e *evalBuiltin) { - if e != nil { - e.e, e.bi, e.bctx, e.f, e.terms = nil, nil, nil, nil, nil - ep.pool.Put(e) - } -} - -func (ep *evbp) Get() *evalBuiltin { - return ep.pool.Get().(*evalBuiltin) -} - var ( - evalPool = util.NewSyncPool[eval]() - deecPool = util.NewSyncPool[deferredEarlyExitContainer]() - resolverPool = util.NewSyncPool[evalResolver]() - arraysRecPool = util.NewSyncPool[biunifyArraysRecParams]() - evalFuncPool = &evfp{ - pool: sync.Pool{ - New: func() any { - return &evalFunc{} - }, - }, - } - evalBuiltinPool = &evbp{ - pool: sync.Pool{ - New: func() any { - return &evalBuiltin{} - }, - }, - } + evalPool = util.NewSyncPool[eval]() + deecPool = util.NewSyncPool[deferredEarlyExitContainer]() + resolverPool = util.NewSyncPool[evalResolver]() + arraysRecPool = util.NewSyncPool[biunifyArraysRecParams]() + evalFuncPool = util.NewResettablePool[evalFunc, *evalFunc]() + evalBuiltinPool = util.NewResettablePool[evalBuiltin, *evalBuiltin]() ) func (e *eval) Run(iter evalIterator) error { @@ -545,6 +505,30 @@ func (e *eval) evalStep(iter evalIterator) error { return err }) + case *ast.LogicalAnd: + ea := evalLogicalAnd{ + e: e, + and: terms, + } + err = ea.eval(func(e *eval) error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + + case *ast.LogicalOr: + eo := evalLogicalOr{ + e: e, + or: terms, + } + err = eo.eval(func(e *eval) error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + default: // guard-rail for adding extra (Expr).Terms types return fmt.Errorf("got %T terms: %[1]v", terms) } @@ -604,6 +588,24 @@ func (e *eval) evalStep(iter evalIterator) error { return iter(e) }) + case *ast.LogicalAnd: + ea := evalLogicalAnd{ + e: e, + and: terms, + } + err = ea.eval(func(e *eval) error { + return iter(e) + }) + + case *ast.LogicalOr: + eo := evalLogicalOr{ + e: e, + or: terms, + } + err = eo.eval(func(e *eval) error { + return iter(e) + }) + default: // guard-rail for adding extra (Expr).Terms types return fmt.Errorf("got %T terms: %[1]v", terms) } @@ -2063,6 +2065,12 @@ type evalBuiltin struct { terms []*ast.Term } +// Reset clears the fields before this evalBuiltin is returned to its pool, +// so pooling it doesn't keep terms/bindings from the previous call alive. +func (e *evalBuiltin) Reset() { + e.e, e.bi, e.bctx, e.f, e.terms = nil, nil, nil, nil, nil +} + // Is this builtin non-deterministic, and did the caller provide an NDBCache? func (e *evalBuiltin) canUseNDBCache(bi *ast.Builtin) bool { return bi.Nondeterministic && e.bctx != nil && e.bctx.NDBuiltinCache != nil @@ -2178,6 +2186,12 @@ type evalFunc struct { terms []*ast.Term } +// Reset clears the fields before this evalFunc is returned to its pool, +// so pooling it doesn't keep terms/index results from the previous call alive. +func (e *evalFunc) Reset() { + e.e, e.terms, e.ir = nil, nil, nil +} + func (e *evalFunc) eval(iter unifyIterator) error { if e.ir.Empty() { return nil @@ -2587,30 +2601,104 @@ func (e evalTree) next(iter unifyIterator, plugged *ast.Term) error { externalRef := node.External.Ref externalIndex := node.External.Index - // Initialize externalTreeStack if needed - if e.e.externalTreeStack == nil { - e.e.externalTreeStack = newExternalTreeStack(e.e) + // For a parametrized (prefix) external source, the leading + // elements of the reference after the prefix are lookup + // parameters rather than tree descents. The source reports how + // many via ParamArity, keyed off the reference tail's shape (so + // one prefix can back an uneven-depth tree). They must be ground; + // the resolved sub-tree is cached under the full reference + // (prefix + params) so distinct parameters do not collide within + // a single evaluation. + arity := 0 + if p, ok := externalIndex.(ast.ParametrizedExternalRuleIndex); ok { + arity = p.ParamArity(e.ref[e.pos+1:]) } + var params []ast.Value + cacheRef := externalRef + expand := true + + if arity > 0 { + params = make([]ast.Value, 0, arity) + cacheRef = make(ast.Ref, len(externalRef), len(externalRef)+arity) + copy(cacheRef, externalRef) + for i := 1; i <= arity; i++ { + idx := e.pos + i + if idx >= len(e.ref) { + expand = false + break + } + k := e.bindings.Plug(e.ref[idx]) + if !k.IsGround() { + expand = false + break + } + params = append(params, k.Value) + cacheRef = append(cacheRef, k) + } - // Check cache first - cachedNode, _, found := e.e.externalTreeStack.findCached(externalRef) - if found { - node = cachedNode - } else { - // Call Tree() and cache the result - e.e.instr.startTimer(evalOpExternalRuleSource) - tree, updatedIndex, err := node.External.Tree(e.e.ctx, e.e.compiler.RuleTree, externalRef, e.e.input, e.e.metrics, e.e.requestMetadata, e.e.responseMetadata) - e.e.instr.stopTimer(evalOpExternalRuleSource) - if err != nil { - return err + if !expand { + // The parameter key(s) are not ground, so we cannot + // select a concrete sub-source. Under partial evaluation + // the reference is unknown and must be residualized; + // otherwise it is simply undefined and we fall through + // with the bare (rule-less) external node. + if e.e.partial() { + saved := make(ast.Ref, len(e.ref)) + for i := range e.ref { + saved[i] = e.bindings.Plug(e.ref[i]) + } + return e.e.saveUnify(ast.NewTerm(saved), e.rterm, e.bindings, e.rbindings, iter) + } + } + } + + if expand { + // Initialize externalTreeStack if needed + if e.e.externalTreeStack == nil { + e.e.externalTreeStack = newExternalTreeStack(e.e) } + + // Check cache first + cachedNode, _, found := e.e.externalTreeStack.findCached(cacheRef) + var tree *ast.TreeNode + if found { + tree = cachedNode + } else { + // Call Tree() and cache the result. + e.e.instr.startTimer(evalOpExternalRuleSource) + // Pass the eval itself as the resolver: it is save-set + // aware, so external sources that opt into + // ExternalSourceOptions.DistinguishAbsentFromUnknown can + // tell references that are unknown under partial evaluation + // apart from references that are simply absent from the + // concrete input. The parameter terms (params) select the + // concrete sub-source for a parametrized prefix. + t, updatedIndex, err := node.External.Tree(e.e.ctx, e.e.compiler.RuleTree, externalRef, params, e.e, e.e.metrics, e.e.requestMetadata, e.e.responseMetadata) + e.e.instr.stopTimer(evalOpExternalRuleSource) + if err != nil { + return err + } + if t != nil { + if updatedIndex != nil { + externalIndex = updatedIndex + } + e.e.externalTreeStack.Push(cacheRef, t, externalIndex, e.e.input) + pushedExternalTree = true + } + tree = t + } + if tree != nil { - if updatedIndex != nil { - externalIndex = updatedIndex + if arity > 0 { + // The resolved sub-tree is rooted at the prefix, but + // the walk still has to consume the parameter + // element(s). Re-insert them as ordinary tree levels + // so the descent below (and any further descent into + // rules) proceeds unchanged. + node = wrapExternalParams(cacheRef[len(externalRef):], tree) + } else { + node = tree } - e.e.externalTreeStack.Push(externalRef, tree, externalIndex, e.e.input) - node = tree - pushedExternalTree = true } } } @@ -3295,7 +3383,7 @@ func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, _ ast.Ref) (b head.Key = ruleRef[len(ruleRef)-1] } - if head.Name.Equal(ast.Var("")) && (len(ruleRef) == 1 || (len(ruleRef) == 2 && rule.Head.RuleKind() == ast.SingleValue)) { + if head.Name == "" && (len(ruleRef) == 1 || (len(ruleRef) == 2 && rule.Head.RuleKind() == ast.SingleValue)) { head.Name = ruleRef[0].Value.(ast.Var) } @@ -4214,7 +4302,7 @@ func (e *evalEvery) save(iter unifyIterator) error { func (e *evalEvery) plug(expr *ast.Expr) (*ast.Expr, error) { cpy := expr.Copy() every := cpy.Terms.(*ast.Every) - if err := e.plugBody(every.Body); err != nil { + if err := plugBody(e.e, every.Body); err != nil { return nil, err } @@ -4225,45 +4313,6 @@ func (e *evalEvery) plug(expr *ast.Expr) (*ast.Expr, error) { return cpy, nil } -func (e *evalEvery) plugBody(body ast.Body) error { - for i := range body { - switch t := body[i].Terms.(type) { - case *ast.Term: - plugged, err := e.plugTerm(t) - if err != nil { - return err - } - body[i].Terms = plugged - case []*ast.Term: - for j := 1; j < len(t); j++ { // don't plug operator, t[0] - plugged, err := e.plugTerm(t[j]) - if err != nil { - return err - } - t[j] = plugged - } - case *ast.Every: - plugged, err := e.plug(body[i]) - if err != nil { - return err - } - body[i] = plugged - case *ast.Not: - if err := e.plugBody(t.Body); err != nil { - return err - } - } - } - return nil -} - -func (e *evalEvery) plugTerm(t *ast.Term) (*ast.Term, error) { - if ast.IsComprehension(t.Value) { - return e.e.amendComprehension(t, e.e.bindings) - } - return e.e.bindings.PlugNamespaced(t, e.e.caller.bindings), nil -} - type evalNot struct { e *eval not *ast.Not @@ -4364,6 +4413,212 @@ func (e evalNot) evalPartial(iter evalIterator) error { return e.e.evalNotPartial(expr, unNegate, ast.Complement, supportTerms, iter) } +type evalLogicalAnd struct { + e *eval + and *ast.LogicalAnd +} + +func (e evalLogicalAnd) eval(iter evalIterator) error { + if e.e.partial() && (e.e.unknown(e.and.Lhs, e.e.bindings) || e.e.unknown(e.and.Rhs, e.e.bindings)) { + return e.evalPartial(iter) + } + + lhsDefined, err := evalLogicalOperand(e.e, e.and.Lhs) + if err != nil { + return err + } + if !lhsDefined { + // short-circuit: RHS is not evaluated if LHS is undefined + return nil + } + + rhsDefined, err := evalLogicalOperand(e.e, e.and.Rhs) + if err != nil { + return err + } + if !rhsDefined { + return nil + } + + return iter(e.e) +} + +func (e evalLogicalAnd) evalPartial(iter evalIterator) error { + // Plug and save the expression to produce a valid, but non-optimized PE result + expr := e.e.query[e.e.index] + + plugged, err := e.plug(expr) + if err != nil { + return err + } + + return e.e.saveExpr(plugged, e.e.bindings, func() error { + return iter(e.e) + }) +} + +func (e evalLogicalAnd) plug(expr *ast.Expr) (*ast.Expr, error) { + cpy := expr.Copy() + and := cpy.Terms.(*ast.LogicalAnd) + + if err := plugBody(e.e, and.Lhs); err != nil { + return nil, err + } + if err := plugBody(e.e, and.Rhs); err != nil { + return nil, err + } + + cpy.Terms = and + return cpy, nil +} + +type evalLogicalOr struct { + e *eval + or *ast.LogicalOr +} + +func (e evalLogicalOr) eval(iter evalIterator) error { + if e.e.partial() && (e.e.unknown(e.or.Lhs, e.e.bindings) || e.e.unknown(e.or.Rhs, e.e.bindings)) { + return e.evalPartial(iter) + } + + lhsDefined, err := evalLogicalOperand(e.e, e.or.Lhs) + if err != nil { + return err + } + if lhsDefined { + // short-circuit: RHS is not evaluated if LHS is defined + return iter(e.e) + } + + rhsDefined, err := evalLogicalOperand(e.e, e.or.Rhs) + if err != nil { + return err + } + if !rhsDefined { + return nil + } + + return iter(e.e) +} + +func (e evalLogicalOr) evalPartial(iter evalIterator) error { + // Plug and save the expression to produce a valid, but non-optimized PE result + expr := e.e.query[e.e.index] + + plugged, err := e.plug(expr) + if err != nil { + return err + } + + return e.e.saveExpr(plugged, e.e.bindings, func() error { + return iter(e.e) + }) +} + +func (e evalLogicalOr) plug(expr *ast.Expr) (*ast.Expr, error) { + cpy := expr.Copy() + or := cpy.Terms.(*ast.LogicalOr) + + if err := plugBody(e.e, or.Lhs); err != nil { + return nil, err + } + if err := plugBody(e.e, or.Rhs); err != nil { + return nil, err + } + + cpy.Terms = or + return cpy, nil +} + +// evalLogicalOperand runs body as a closed scope that contributes at most one +// success. Returns whether the body succeeded; bindings introduced inside body +// do not propagate to the caller. +func evalLogicalOperand(parent *eval, body ast.Body) (bool, error) { + child := evalPool.Get() + defer evalPool.Put(child) + + parent.closure(body, child) + child.findOne = true + + if parent.traceEnabled { + child.traceEnter(body) + } + + defined := false + err := child.eval(func(*eval) error { + if parent.traceEnabled { + child.traceExit(body) + child.traceRedo(body) + } + defined = true + return nil + }) + + // findOne raises an earlyExitError once the iter callback fires; that's + // our signal to stop, not an error to propagate to the caller. + if err := suppressEarlyExit(err); err != nil { + return false, err + } + + return defined, nil +} + +func plugBody(e *eval, body ast.Body) error { + for i := range body { + switch t := body[i].Terms.(type) { + case *ast.Term: + plugged, err := plugTerm(e, t) + if err != nil { + return err + } + body[i].Terms = plugged + case []*ast.Term: + for j := 1; j < len(t); j++ { // don't plug operator, t[0] + plugged, err := plugTerm(e, t[j]) + if err != nil { + return err + } + t[j] = plugged + } + case *ast.Every: + ev := evalEvery{e: e, every: t, expr: body[i]} + plugged, err := ev.plug(body[i]) + if err != nil { + return err + } + body[i] = plugged + case *ast.Not: + if err := plugBody(e, t.Body); err != nil { + return err + } + case *ast.LogicalAnd: + if err := plugBody(e, t.Lhs); err != nil { + return err + } + if err := plugBody(e, t.Rhs); err != nil { + return err + } + case *ast.LogicalOr: + if err := plugBody(e, t.Lhs); err != nil { + return err + } + if err := plugBody(e, t.Rhs); err != nil { + return err + } + } + } + + return nil +} + +func plugTerm(e *eval, t *ast.Term) (*ast.Term, error) { + if ast.IsComprehension(t.Value) { + return e.amendComprehension(t, e.bindings) + } + return e.bindings.PlugNamespaced(t, e.caller.bindings), nil +} + func (e *eval) comprehensionIndex(term *ast.Term) *ast.ComprehensionIndex { if e.queryCompiler != nil { return e.queryCompiler.ComprehensionIndex(term) @@ -4518,17 +4773,29 @@ func containsNestedRefOrCall(vis *nestedCheckVisitor, expr *ast.Expr) bool { } if n, ok := expr.Terms.(*ast.Not); ok { - for _, nExpr := range n.Body { - if containsNestedRefOrCall(vis, nExpr) { - return true - } - } - return false + return containsNestedRefOrCallInBody(vis, n.Body) + } + + if a, ok := expr.Terms.(*ast.LogicalAnd); ok { + return containsNestedRefOrCallInBody(vis, a.Lhs) || containsNestedRefOrCallInBody(vis, a.Rhs) + } + + if o, ok := expr.Terms.(*ast.LogicalOr); ok { + return containsNestedRefOrCallInBody(vis, o.Lhs) || containsNestedRefOrCallInBody(vis, o.Rhs) } return containsNestedRefOrCallInTerm(vis, expr.Terms.(*ast.Term)) } +func containsNestedRefOrCallInBody(vis *nestedCheckVisitor, body ast.Body) bool { + for _, expr := range body { + if containsNestedRefOrCall(vis, expr) { + return true + } + } + return false +} + func containsNestedRefOrCallInTerm(vis *nestedCheckVisitor, term *ast.Term) bool { switch v := term.Value.(type) { case ast.Ref: @@ -4690,6 +4957,21 @@ func (e *eval) updateSavedMocks(withs []*ast.With) []*ast.With { return ret } +// wrapExternalParams re-inserts the parameter levels consumed by a +// parametrized external source (see ast.ParametrizedExternalRuleIndex) on +// top of the resolved sub-tree, which is rooted at the registered prefix. The +// evaluator's descent can then consume the parameter element(s) as ordinary +// tree levels. keys are the ground parameter terms in reference order. +func wrapExternalParams(keys []*ast.Term, tree *ast.TreeNode) *ast.TreeNode { + node := tree + for i := len(keys) - 1; i >= 0; i-- { + node = &ast.TreeNode{ + Children: map[ast.Value]*ast.TreeNode{keys[i].Value: node}, + } + } + return node +} + // simpleTreeNode provides minimal tree structure for navigation type simpleTreeNode struct { tree *ast.TreeNode diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go index 85b3ed9e93..971d62b33b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go @@ -479,7 +479,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] // Build output from saved expressions. body := ast.NewBody() - for _, elem := range e.saveStack.Stack[len(e.saveStack.Stack)-1] { + for _, elem := range e.saveStack.Peek() { body.Append(elem.Plug(e.bindings)) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go index 7a922b5f7b..a1c0fdd65a 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" ) // saveSet contains a stack of terms that are considered 'unknown' during @@ -183,40 +184,33 @@ func (sse *saveSetElem) containsVar(t *ast.Term, b *bindings) bool { // partially evaluated. In this case, the partially evaluated rule will be // output in the support module. type saveStack struct { - Stack []saveStackQuery + Stack util.GroupStack[saveStackElem] } func newSaveStack() *saveStack { - return &saveStack{ - Stack: []saveStackQuery{ - {}, - }, - } + s := &saveStack{} + s.Stack.PushGroup(nil) + return s } func (s *saveStack) PushQuery(query saveStackQuery) { - s.Stack = append(s.Stack, query) + s.Stack.PushGroup(query) } func (s *saveStack) PopQuery() saveStackQuery { - last := s.Stack[len(s.Stack)-1] - s.Stack = s.Stack[:len(s.Stack)-1] - return last + return s.Stack.PopGroup() } func (s *saveStack) Peek() saveStackQuery { - return s.Stack[len(s.Stack)-1] + return s.Stack.PeekGroup() } func (s *saveStack) Push(expr *ast.Expr, b1 *bindings, b2 *bindings) { - idx := len(s.Stack) - 1 - s.Stack[idx] = append(s.Stack[idx], saveStackElem{expr, b1, b2}) + s.Stack.Push(saveStackElem{expr, b1, b2}) } func (s *saveStack) Pop() { - idx := len(s.Stack) - 1 - query := s.Stack[idx] - s.Stack[idx] = query[:len(query)-1] + s.Stack.Pop() } type saveStackQuery []saveStackElem @@ -298,7 +292,7 @@ func (s *saveSupport) Exists(path ast.Ref) bool { if len(ruleRef) == 1 { name := ruleRef[0].Value.(ast.Var) for _, rule := range module.Rules { - if rule.Head.Name.Equal(name) { + if rule.Head.Name == name { return true } } @@ -586,7 +580,7 @@ func (i *inliningControl) DisabledVar(v ast.Var, ignoreInternal bool) bool { } for _, frame := range i.disable { - if (!frame.internal || !ignoreInternal) && frame.v.Equal(v) { + if (!frame.internal || !ignoreInternal) && frame.v == v { return true } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go index b0b5684a8c..4ffd307c78 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go @@ -129,11 +129,14 @@ func builtinFormatInt(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter } var format string + var radix int switch base { case ast.Number("2"): format = "%b" + radix = 2 case ast.Number("8"): format = "%o" + radix = 8 case ast.Number("10"): // Fast path: for numbers whose decimal string is already interned (e.g. // "0"–"100"), we can skip strconv.ParseInt entirely. @@ -144,12 +147,23 @@ func builtinFormatInt(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter return iter(ast.InternedIntegerString(i)) } format = "%d" + radix = 10 case ast.Number("16"): format = "%x" + radix = 16 default: return builtins.NewOperandEnumErr(2, "2", "8", "10", "16") } + // For integer inputs, format the exact big.Int. Routing integers through a + // float (as the fractional path below does) loses precision for values that + // need more than a float64's 53-bit mantissa, e.g. 18446744073709551617. + if i, ok := new(big.Int).SetString(string(input), 10); ok { + return iter(ast.InternedTerm(i.Text(radix))) + } + + // Fractional inputs (e.g. 15.9) are truncated toward zero, matching the + // historical behaviour: format_int(15.9, 16) == "f", format_int(-15.9, 16) == "-f". f := builtins.NumberToFloat(input) i, _ := f.Int(nil) @@ -543,6 +557,55 @@ func builtinSplit(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) e return iter(ast.ArrayTerm(util.SplitMap(text, delim, ast.InternedTerm)...)) } +func builtinSplitN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + d, err := builtins.StringOperand(operands[1].Value, 2) + if err != nil { + return err + } + + n, err := builtins.IntOperand(operands[2].Value, 3) + if err != nil { + return err + } + + text, delim := string(s), string(d) + + var result []*ast.Term + if n >= 0 { + // n+1 may overflow for very large n; a negative limit means no limit. + limit := n + 1 + if limit < 0 { + limit = -1 + } + parts := strings.SplitN(text, delim, limit) + end := n + if end > len(parts) { + end = len(parts) + } + result = make([]*ast.Term, end) + for i := range result { + result[i] = ast.InternedTerm(parts[i]) + } + } else { + parts := strings.Split(text, delim) + start := len(parts) + n + if start < 0 { + start = 0 + } + result = make([]*ast.Term, len(parts)-start) + for i, p := range parts[start:] { + result[i] = ast.InternedTerm(p) + } + } + + return iter(ast.ArrayTerm(result...)) +} + func builtinReplace(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { s, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { @@ -722,15 +785,15 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) return err } - astArr, ok := operands[1].Value.(*ast.Array) - if !ok { - return builtins.NewOperandTypeErr(2, operands[1].Value, "array") + a, err := builtins.ArrayOperand(operands[1].Value, 2) + if err != nil { + return err } // Optimized path for where sprintf is used as a "to_string" function for // a single integer, i.e. sprintf("%d", [x]) where x is an integer. - if s == "%d" && astArr.Len() == 1 { - if n, ok := astArr.Elem(0).Value.(ast.Number); ok { + if s == "%d" && a.Len() == 1 { + if n, ok := a.Elem(0).Value.(ast.Number); ok { if i, ok := n.Int(); ok { if interned := ast.InternedIntegerString(i); interned != nil { return iter(interned) @@ -740,24 +803,35 @@ func builtinSprintf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) } } - args := make([]any, astArr.Len()) + args := make([]any, a.Len()) for i := range args { - switch v := astArr.Elem(i).Value.(type) { + t := a.Elem(i) + switch v := t.Value.(type) { case ast.Number: - if n, ok := v.Int(); ok { - args[i] = n - } else if b, ok := new(big.Int).SetString(v.String(), 10); ok { - args[i] = b - } else if f, ok := v.Float64(); ok { - args[i] = f + ns := string(v) + if x, ok := util.Atoi64(ns); ok { + args[i] = x } else { - args[i] = v.String() + if strings.ContainsRune(ns, '.') { + if f, ok := v.Float64(); ok { + args[i] = f + continue + } else { + args[i] = ns + } + } else { + if b, ok := new(big.Int).SetString(ns, 10); ok { + args[i] = b + } else { + args[i] = ns + } + } } case ast.String: args[i] = string(v) default: - args[i] = astArr.Elem(i).String() + args[i] = t.Value.String() } } @@ -806,6 +880,7 @@ func init() { RegisterBuiltinFunc(ast.Upper.Name, builtinUpper) RegisterBuiltinFunc(ast.Lower.Name, builtinLower) RegisterBuiltinFunc(ast.Split.Name, builtinSplit) + RegisterBuiltinFunc(ast.SplitN.Name, builtinSplitN) RegisterBuiltinFunc(ast.Replace.Name, builtinReplace) RegisterBuiltinFunc(ast.ReplaceN.Name, builtinReplaceN) RegisterBuiltinFunc(ast.Trim.Name, builtinTrim) diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/template.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/template.go index 524c5bde0d..940fa85c34 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/template.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/template.go @@ -3,7 +3,14 @@ package topdown import ( "bytes" "strings" - "text/template" + + // A method-less copy of text/template (see internal/methodlesstemplate). Rego values + // decode to map[string]any/[]any/scalars, which have no methods, so eliding + // method calls is a no-op here; it keeps text/template's evalField + // MethodByName off the reachable graph, which otherwise disables the Go + // linker's method-level dead-code elimination binary-wide (golang/go#72895, + // #7903). + template "github.com/open-policy-agent/opa/internal/methodlesstemplate" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go index 52451dc6bd..8b672da54e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go @@ -598,6 +598,49 @@ type varInfo struct { col int // 0-indexed column } +// resolveLocalRef resolves a ground ref whose base is a local variable (e.g. 'tc.data') +// against the given local bindings, returning the selected value. It returns nil if the +// ref can't be resolved: the base isn't a bound local, a path element isn't ground (after +// resolving any variable keys), or the path doesn't exist in the value. +func resolveLocalRef(ref ast.Ref, locals *ast.ValueMap) ast.Value { + if len(ref) < 2 || locals == nil { + return nil + } + + base, ok := ref[0].Value.(ast.Var) + if !ok { + return nil + } + + baseVal := locals.Get(base) + if baseVal == nil { + return nil + } + + path := make(ast.Ref, 0, len(ref)-1) + for _, t := range ref[1:] { + if key, ok := t.Value.(ast.Var); ok { + // A variable key (e.g. 'y[i]') must itself be resolved from the local bindings. + keyVal := locals.Get(key) + if keyVal == nil { + return nil + } + path = append(path, ast.NewTerm(keyVal)) + continue + } + if !t.IsGround() { + return nil + } + path = append(path, t) + } + + val, err := baseVal.Find(path) + if err != nil { + return nil + } + return val +} + func (v varInfo) Value() string { if v.val != nil { return v.val.String() @@ -691,6 +734,21 @@ func PrettyEvent(w io.Writer, e *Event, opts PrettyEventOpts) error { case *ast.ArrayComprehension, *ast.SetComprehension, *ast.ObjectComprehension: // we don't report on the internals of a comprehension, as it's already evaluated, and we won't have the local vars. return true + case ast.Ref: + // For a ref that selects into a local variable (e.g. 'tc.data'), report the + // selected value rather than only the base variable's (potentially large) value. + // We keep descending (return false) so the base variable is still reported too. + if val := resolveLocalRef(v, e.Locals); val != nil { + info := varInfo{ + VarMetadata: VarMetadata{Name: ast.Var(term.Location.Text)}, + val: val, + exprLoc: term.Location, + col: term.Location.Col, + } + if existing, exists := exprVars[info.Title()]; !exists || existing.val == nil { + exprVars[info.Title()] = info + } + } case ast.Var: var info *varInfo if meta, ok := e.LocalMetadata[v]; ok { diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go index a930db21c5..ec12210c6e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go @@ -19,6 +19,22 @@ const ( objectSort ) +// Or works like [cmp.Or] but allows supplier functions to be tried rather than +// alternative values. This allows deferring computation of the alternatives to +// only when needed. +func Or[T comparable](val T, suppliers ...func() T) T { + var zero T + if val == zero { + for _, f := range suppliers { + if alt := f(); alt != zero { + return alt + } + } + } + + return val +} + // SliceLenCompare is a convenience function for comparing / sorting // slices by their length using the various slices.SortX functions. func SliceLenCompare[T any, S ~[]T](a, b S) int { diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/hashmap.go b/vendor/github.com/open-policy-agent/opa/v1/util/hashmap.go index 69a90cbb53..b90ea85169 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/hashmap.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/hashmap.go @@ -208,9 +208,11 @@ func NewHasherMap[K Hasher, V any](keq func(K, K) bool) *HasherMap[K, V] { // Get returns the value for k. func (h *HasherMap[K, V]) Get(k K) (V, bool) { - for entry := h.table[k.Hash()]; entry != nil; entry = entry.next { - if h.keq(entry.k, k) { - return entry.v, true + if h != nil { + for entry := h.table[k.Hash()]; entry != nil; entry = entry.next { + if h.keq(entry.k, k) { + return entry.v, true + } } } var zero V @@ -250,11 +252,28 @@ func (h *HasherMap[K, V]) Delete(k K) { } } +// Keys returns a slice containing all keys in the HasherMap. +func (h *HasherMap[K, V]) Keys() []K { + if h == nil { + return nil + } + keys := make([]K, 0, h.size) + for _, entry := range h.table { + for ; entry != nil; entry = entry.next { + keys = append(keys, entry.k) + } + } + return keys +} + // Iter invokes the iter function for each element in the HasherMap. // If the iter function returns true, iteration stops and the return value is true. // If the iter function never returns true, iteration proceeds through all elements // and the return value is false. func (h *HasherMap[K, V]) Iter(iter func(K, V) bool) bool { + if h == nil { + return false + } for _, entry := range h.table { for ; entry != nil; entry = entry.next { if iter(entry.k, entry.v) { @@ -265,7 +284,10 @@ func (h *HasherMap[K, V]) Iter(iter func(K, V) bool) bool { return false } -// Len returns the current size of this HashMap. +// Len returns the current size of this HashMap, or 0 if the HasherMap is nil. func (h *HasherMap[K, V]) Len() int { + if h == nil { + return 0 + } return h.size } diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go index 3c852638e2..f269a2e1d7 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go @@ -36,6 +36,43 @@ func (p *SyncPool[T]) Put(x *T) { } } +// resettable is implemented by *T when used with [ResettablePool], allowing +// pooled values to clear their internal state (e.g. drop pointers so they +// don't outlive their useful life) before being returned to the pool. +type resettable[T any] interface { + *T + Reset() +} + +// ResettablePool is like [SyncPool], but for types whose pointer clears its +// own fields via a Reset method before being pooled. Unlike a runtime +// interface check on every Put, the PT type parameter is resolved at compile +// time, so there's no extra dispatch cost over a hand-written pool. +type ResettablePool[T any, PT resettable[T]] struct { + pool sync.Pool +} + +func NewResettablePool[T any, PT resettable[T]]() *ResettablePool[T, PT] { + return &ResettablePool[T, PT]{ + pool: sync.Pool{ + New: func() any { + return new(T) + }, + }, + } +} + +func (p *ResettablePool[T, PT]) Get() *T { + return p.pool.Get().(*T) +} + +func (p *ResettablePool[T, PT]) Put(x *T) { + if x != nil { + PT(x).Reset() + p.pool.Put(x) + } +} + // NewPtrSlice returns a slice of pointers to T with length n, // with only 2 allocations performed no matter the size of n. // See: @@ -114,6 +151,58 @@ func AppendInt(buf []byte, n int) []byte { return strconv.AppendInt(buf, int64(n), 10) } +// Atoi is a convenience function for [Atoi64] where an int is preferable to an int64. +// See the documentation of [Atoi64] for details on the performance benefits of this +// function over strconv.Atoi. +func Atoi(s string) (int, bool) { + if i, ok := Atoi64(s); ok { + return int(i), true + } + return 0, false +} + +// Atoi64 is an alternative implementation of strconv.Atoi which is slightly faster for the +// (for our use case) common case of a successful conversion, and crucially — *much* faster +// for the failure case, as this function allocates nothing for any given input string, while +// strconv.Atoi performs 1-2 allocations on failure in its error handling. The callers in this +// codebase — most notably ast.Number's Int() and Int64() methods — have no interest in the +// details of the failure, and keeping this allocation free means both methods can be used +// not only for conversion, but as a most efficient "IsInt64" check. +func Atoi64(s string) (int64, bool) { + sLen := len(s) + if sLen > 0 { + negative := s[0] == '-' + if negative || s[0] == '+' { + s = s[1:] + sLen-- + } + if sLen == 0 || sLen > 19 { + return 0, false + } + + var n int64 + for _, ch := range []byte(s) { + ch -= '0' + if ch > 9 { + return 0, false + } + n = n*10 + int64(ch) + } + if !negative && n < 0 { + return 0, false // overflow + } + if negative { + n = -n + if n > 0 { + return 0, false // underflow + } + } + return n, true + } + + return 0, false +} + // SplitMap calls fn for each delim-separated part of text and returns a slice of the results. // Cheaper than calling fn on strings.Split(text, delim), as it avoids allocating an intermediate slice of strings. func SplitMap[T any](text string, delim string, fn func(string) T) []T { diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/queue.go b/vendor/github.com/open-policy-agent/opa/v1/util/queue.go index 63a2ffc16a..9208ecdbec 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/queue.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/queue.go @@ -111,3 +111,94 @@ func (s *FIFO) Pop() (T, bool) { func (s *FIFO) Size() int { return s.size } + +// SliceStack is a generic LIFO stack backed by a slice. +type SliceStack[T any] struct { + s []T +} + +// Push adds v to the top of the stack. +func (s *SliceStack[T]) Push(v T) { + s.s = append(s.s, v) +} + +// Pop removes and returns the top element of the stack. +// It panics if the stack is empty. +func (s *SliceStack[T]) Pop() T { + idx := len(s.s) - 1 + v := s.s[idx] + var zero T + s.s[idx] = zero // avoid retaining a reference to v in the backing array + s.s = s.s[:idx] + return v +} + +// Peek returns the top element of the stack without removing it. +// It panics if the stack is empty. +func (s *SliceStack[T]) Peek() T { + return s.s[len(s.s)-1] +} + +// PeekPtr returns a pointer to the top element, so callers can mutate it in place. +// It panics if the stack is empty. +func (s *SliceStack[T]) PeekPtr() *T { + return &s.s[len(s.s)-1] +} + +// Slice returns the stack's underlying slice, bottom-to-top. +func (s *SliceStack[T]) Slice() []T { + return s.s +} + +// Len returns the number of elements on the stack. +func (s *SliceStack[T]) Len() int { + return len(s.s) +} + +// GroupStack is a two-level stack: a stack of groups, where each group is a +// slice of T. Whole groups are pushed and popped with PushGroup/PopGroup, +// while individual elements are pushed and popped onto the top group with +// Push/Pop. Element lookups (Peek) always target the top group. +// +// Both levels zero their vacated slots when popping, so a popped group or +// element isn't kept alive by the backing arrays. +type GroupStack[T any] struct { + groups SliceStack[[]T] +} + +// PushGroup pushes a new group onto the stack. Pass nil for an empty group. +func (g *GroupStack[T]) PushGroup(group []T) { + g.groups.Push(group) +} + +// PopGroup removes and returns the top group. It panics if there are no groups. +func (g *GroupStack[T]) PopGroup() []T { + return g.groups.Pop() +} + +// PeekGroup returns the top group without removing it. It panics if there are +// no groups. +func (g *GroupStack[T]) PeekGroup() []T { + return g.groups.Peek() +} + +// Push appends v to the top group. It panics if there are no groups. +func (g *GroupStack[T]) Push(v T) { + top := g.groups.PeekPtr() + *top = append(*top, v) +} + +// Pop removes the top element of the top group. It panics if there are no +// groups or the top group is empty. +func (g *GroupStack[T]) Pop() { + top := g.groups.PeekPtr() + idx := len(*top) - 1 + var zero T + (*top)[idx] = zero // avoid retaining a reference in the backing array + *top = (*top)[:idx] +} + +// Len returns the number of groups on the stack. +func (g *GroupStack[T]) Len() int { + return g.groups.Len() +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/version/version.go b/vendor/github.com/open-policy-agent/opa/v1/version/version.go index 92bf2fdaf2..cb30f722fc 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/version/version.go +++ b/vendor/github.com/open-policy-agent/opa/v1/version/version.go @@ -10,7 +10,7 @@ import ( "runtime/debug" ) -var Version = "1.18.2" +var Version = "1.19.0" // GoVersion is the version of Go this was built with var GoVersion = runtime.Version() diff --git a/vendor/github.com/vektah/gqlparser/v2/ast/definition.go b/vendor/github.com/vektah/gqlparser/v2/ast/definition.go index 426db7588c..a8760c15f1 100644 --- a/vendor/github.com/vektah/gqlparser/v2/ast/definition.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/definition.go @@ -32,7 +32,13 @@ type Definition struct { EnumValues EnumValueList // enum Position *Position `dump:"-" json:"-"` - BuiltIn bool `dump:"-"` + // TypePositions holds the source position of each Types entry (a union's + // member types), parallel to Types. The parser populates it so that + // validation can point at the offending member; when populated its length + // equals len(Types). It is empty for definitions built programmatically, in + // which case validators fall back to the definition's own Position. + TypePositions []*Position `dump:"-" json:"-"` + BuiltIn bool `dump:"-"` BeforeDescriptionComment *CommentGroup AfterDescriptionComment *CommentGroup diff --git a/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go b/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go index 26cf693d9c..201688183a 100644 --- a/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go @@ -58,7 +58,7 @@ func (d *dumper) dump(v reflect.Value) { case reflect.Array, reflect.Slice: d.dumpArray(v) - case reflect.Interface, reflect.Ptr: + case reflect.Interface, reflect.Pointer: d.dumpPtr(v) case reflect.Struct: @@ -79,7 +79,7 @@ func (d *dumper) nl() { } func typeName(t reflect.Type) string { - if t.Kind() == reflect.Ptr { + if t.Kind() == reflect.Pointer { return typeName(t.Elem()) } return t.Name() @@ -122,7 +122,7 @@ func (d *dumper) dumpStruct(v reflect.Value) { func isZero(v reflect.Value) bool { switch v.Kind() { - case reflect.Ptr, reflect.Interface: + case reflect.Pointer, reflect.Interface: return v.IsNil() case reflect.Func, reflect.Map: return v.IsNil() diff --git a/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go index 7a82a42b33..1af14c6a48 100644 --- a/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go +++ b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go @@ -193,7 +193,7 @@ func (s *Lexer) ReadToken() (Token, error) { s.endRunes-- if r < 0x0020 && r != 0x0009 && r != 0x000a && r != 0x000d { - return s.makeError(`Cannot contain the invalid character "\u%04d"`, r) + return s.makeError(`Cannot contain the invalid character "\u%04x"`, r) } if r == '\'' { @@ -365,7 +365,7 @@ func (s *Lexer) readString() (Token, error) { break } if r < 0x0020 && r != '\t' { - return s.makeError(`Invalid character within String: "\u%04d".`, r) + return s.makeError(`Invalid character within String: "\u%04x".`, r) } switch r { default: @@ -505,7 +505,7 @@ func (s *Lexer) readBlockString() (Token, error) { // SourceCharacter if r < 0x0020 && r != '\t' && r != '\n' && r != '\r' { - return s.makeError(`Invalid character within String: "\u%04d".`, r) + return s.makeError(`Invalid character within String: "\u%04x".`, r) } switch { diff --git a/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml index 0899f4ca9b..22956b84cd 100644 --- a/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml @@ -231,6 +231,12 @@ lex reports useful string errors: message: 'Invalid character within String: "\u0000".' locations: [{ line: 1, column: 19 }] + - name: control character codepoint reported in hex + input: "\"contains \u000e sub char\"" + error: + message: 'Invalid character within String: "\u000e".' + locations: [{ line: 1, column: 11 }] + - name: unterminated newline input: "\"multi\nline\"" error: diff --git a/vendor/github.com/vektah/gqlparser/v2/parser/schema.go b/vendor/github.com/vektah/gqlparser/v2/parser/schema.go index a004a0f6c0..7152963332 100644 --- a/vendor/github.com/vektah/gqlparser/v2/parser/schema.go +++ b/vendor/github.com/vektah/gqlparser/v2/parser/schema.go @@ -329,22 +329,26 @@ func (p *parser) parseUnionTypeDefinition(description descriptionWithComment) *D def.AfterDescriptionComment = comment def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.Types = p.parseUnionMemberTypes() + def.Types, def.TypePositions = p.parseUnionMemberTypes() return &def } -func (p *parser) parseUnionMemberTypes() []string { - var types []string +// parseUnionMemberTypes parses a union's member type list. It returns the member +// type names alongside their source positions; the two slices have equal length +// (one position per name), so callers can report errors at a specific member. +func (p *parser) parseUnionMemberTypes() (types []string, positions []*Position) { if p.skip(lexer.Equals) { // optional leading pipe p.skip(lexer.Pipe) + positions = append(positions, p.peekPos()) types = append(types, p.parseName()) for p.skip(lexer.Pipe) && p.err == nil { + positions = append(positions, p.peekPos()) types = append(types, p.parseName()) } } - return types + return types, positions } func (p *parser) parseEnumTypeDefinition(description descriptionWithComment) *Definition { @@ -506,7 +510,7 @@ func (p *parser) parseUnionTypeExtension(comment *CommentGroup) *Definition { def.Kind = Union def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.Types = p.parseUnionMemberTypes() + def.Types, def.TypePositions = p.parseUnionMemberTypes() if len(def.Directives) == 0 && len(def.Types) == 0 { p.unexpectedError() diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/schema.go b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go index b1049aaf28..f8d9472754 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/schema.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go @@ -62,6 +62,7 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) { def.Interfaces = append(def.Interfaces, ext.Interfaces...) def.Fields = append(def.Fields, ext.Fields...) def.Types = append(def.Types, ext.Types...) + def.TypePositions = append(def.TypePositions, ext.TypePositions...) def.EnumValues = append(def.EnumValues, ext.EnumValues...) } @@ -421,6 +422,29 @@ func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error { } } + // Reject duplicate union member types, pointing at the duplicate member's + // position when it is known. TypePositions is parallel to Types (populated by + // the parser); when it is absent or not aligned, fall back to the + // definition's own position. + memberPosAligned := len(def.TypePositions) == len(def.Types) + for i, typ1 := range def.Types { + for j := i + 1; j < len(def.Types); j++ { + if typ1 != def.Types[j] { + continue + } + pos := def.Position + if memberPosAligned && def.TypePositions[j] != nil { + pos = def.TypePositions[j] + } + return gqlerror.ErrorPosf( + pos, + "Union type %s can only include type %s once.", + def.Name, + def.Types[j], + ) + } + } + if !def.BuiltIn { // GraphQL spec has reserved type names a lot! err := validateName(def.Position, def.Name) diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml index 5b59711ffc..b66e1e3d5a 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml @@ -494,6 +494,37 @@ unions: error: message: "UNION type \"Baz\" must be OBJECT." locations: [{line: 1, column: 7}] + - name: cannot include same union member twice at same definition + input: | + union Foo = Bar | Bar + type Bar { + id: ID + } + error: + message: "Union type Foo can only include type Bar once." + locations: [{line: 1, column: 19}] + - name: cannot include same union member twice across extension + input: | + union Foo = Bar + extend union Foo = Bar + type Bar { + id: ID + } + error: + message: "Union type Foo can only include type Bar once." + locations: [{line: 2, column: 20}] + - name: reports the second occurrence among multiple members + input: | + union Foo = Bar | Baz | Bar + type Bar { + id: ID + } + type Baz { + id: ID + } + error: + message: "Union type Foo can only include type Bar once." + locations: [{line: 1, column: 25}] - name: unions of pure type extensions are valid input: | diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/vars.go b/vendor/github.com/vektah/gqlparser/v2/validator/vars.go index 50e2cdb295..8d3534d940 100644 --- a/vendor/github.com/vektah/gqlparser/v2/validator/vars.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/vars.go @@ -86,7 +86,7 @@ func VariableValues( rv = reflect.ValueOf(f) } } - if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + if rv.Kind() == reflect.Pointer || rv.Kind() == reflect.Interface { rv = rv.Elem() } @@ -117,6 +117,14 @@ func (v *varValidator) validateVarType( v.path = currentPath } defer resetPath() + + if !val.IsValid() { + if typ.NonNull { + return val, gqlerror.ErrorPathf(v.path, "cannot be null") + } + return val, nil + } + if typ.Elem != nil { if val.Kind() != reflect.Slice { // GraphQL spec says that non-null values should be coerced to an array when possible. @@ -129,7 +137,7 @@ func (v *varValidator) validateVarType( resetPath() v.path = append(v.path, ast.PathIndex(i)) field := val.Index(i) - if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface { + if field.Kind() == reflect.Pointer || field.Kind() == reflect.Interface { if typ.Elem.NonNull && field.IsNil() { return val, gqlerror.ErrorPathf(v.path, "cannot be null") } @@ -147,11 +155,6 @@ func (v *varValidator) validateVarType( panic(fmt.Errorf("missing def for %s", typ.NamedType)) } - if !typ.NonNull && !val.IsValid() { - // If the type is not null and we got a invalid value namely null/nil, then it's valid - return val, nil - } - switch def.Kind { case ast.Enum: kind := val.Type().Kind() @@ -245,7 +248,7 @@ func (v *varValidator) validateVarType( continue } - if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface { + if field.Kind() == reflect.Pointer || field.Kind() == reflect.Interface { if fieldDef.Type.NonNull && field.IsNil() { return val, gqlerror.ErrorPathf(v.path, "cannot be null") } diff --git a/vendor/modules.txt b/vendor/modules.txt index f03b6ecb59..64f125c0d9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -548,7 +548,7 @@ github.com/go-ldap/ldif # github.com/go-logfmt/logfmt v0.5.1 ## explicit; go 1.17 github.com/go-logfmt/logfmt -# github.com/go-logr/logr v1.4.3 +# github.com/go-logr/logr v1.4.4 ## explicit; go 1.18 github.com/go-logr/logr github.com/go-logr/logr/funcr @@ -1274,7 +1274,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types -# github.com/open-policy-agent/opa v1.18.2 +# github.com/open-policy-agent/opa v1.19.0 ## explicit; go 1.25.0 github.com/open-policy-agent/opa/ast github.com/open-policy-agent/opa/ast/json @@ -1294,9 +1294,12 @@ github.com/open-policy-agent/opa/internal/file/url github.com/open-policy-agent/opa/internal/future github.com/open-policy-agent/opa/internal/gojsonschema github.com/open-policy-agent/opa/internal/json/patch +github.com/open-policy-agent/opa/internal/jsonv2 github.com/open-policy-agent/opa/internal/lcss github.com/open-policy-agent/opa/internal/leb128 github.com/open-policy-agent/opa/internal/merge +github.com/open-policy-agent/opa/internal/methodlesstemplate +github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort github.com/open-policy-agent/opa/internal/planner github.com/open-policy-agent/opa/internal/providers/aws github.com/open-policy-agent/opa/internal/providers/aws/crypto @@ -1325,9 +1328,11 @@ github.com/open-policy-agent/opa/v1/ast/internal/tokens github.com/open-policy-agent/opa/v1/ast/json github.com/open-policy-agent/opa/v1/ast/location github.com/open-policy-agent/opa/v1/bundle +github.com/open-policy-agent/opa/v1/bundle/v1pb github.com/open-policy-agent/opa/v1/capabilities github.com/open-policy-agent/opa/v1/format github.com/open-policy-agent/opa/v1/ir +github.com/open-policy-agent/opa/v1/ir/v1pb github.com/open-policy-agent/opa/v1/keys github.com/open-policy-agent/opa/v1/loader github.com/open-policy-agent/opa/v1/loader/extension @@ -2122,7 +2127,7 @@ github.com/urfave/cli/v2 ## explicit; go 1.24 github.com/valyala/fastjson github.com/valyala/fastjson/fastfloat -# github.com/vektah/gqlparser/v2 v2.5.34 +# github.com/vektah/gqlparser/v2 v2.5.36 ## explicit; go 1.22 github.com/vektah/gqlparser/v2/ast github.com/vektah/gqlparser/v2/gqlerror From 67877e8bd0ea7ece67db08223f9838844437d64b Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Mon, 3 Aug 2026 17:43:51 +0200 Subject: [PATCH 25/27] chore: reva bump -2.48.0 (#3230) --- go.mod | 18 +- go.sum | 36 +- pkg/version/version.go | 2 +- .../antithesis-sdk-go/assert/assert.go | 2 +- .../antithesis-sdk-go/assert/assert_noop.go | 2 +- .../assert/boolean_guidance.go | 2 +- .../antithesis-sdk-go/assert/location.go | 2 +- .../assert/numeric_guidance.go | 2 +- .../antithesis-sdk-go/assert/rich_assert.go | 2 +- .../assert/rich_assert_nop.go | 2 +- .../antithesis-sdk-go/assert/tracker.go | 2 +- .../antithesis-sdk-go/internal/emit.go | 2 +- .../internal/voidstar_handler.go | 2 +- .../internal/voidstar_handler_noop.go | 2 +- .../mattn/go-colorable/colorable_windows.go | 26 +- .../mattn/go-isatty/isatty_others.go | 4 +- .../mattn/go-isatty/isatty_windows.go | 15 +- .../github.com/mattn/go-runewidth/SECURITY.md | 25 + .../mattn/go-runewidth/runewidth.go | 151 +- .../mattn/go-sqlite3/.coderabbit.yaml | 15 + vendor/github.com/mattn/go-sqlite3/README.md | 15 +- .../github.com/mattn/go-sqlite3/SECURITY.md | 33 + .../github.com/mattn/go-sqlite3/callback.go | 136 +- .../mattn/go-sqlite3/sqlite3-binding.c | 12820 ++++++++++------ .../mattn/go-sqlite3/sqlite3-binding.h | 591 +- vendor/github.com/mattn/go-sqlite3/sqlite3.go | 365 +- .../mattn/go-sqlite3/sqlite3_context.go | 13 +- .../go-sqlite3/sqlite3_load_extension.go | 6 +- .../mattn/go-sqlite3/sqlite3_opt_dbstat.go | 15 + .../go-sqlite3/sqlite3_opt_preupdate_hook.go | 8 +- .../mattn/go-sqlite3/sqlite3_opt_serialize.go | 3 + .../go-sqlite3/sqlite3_opt_unlock_notify.go | 3 +- .../mattn/go-sqlite3/sqlite3_opt_vtable.go | 89 +- .../github.com/mattn/go-sqlite3/sqlite3ext.h | 11 +- .../reva/v2/pkg/storage/cache/kv.go | 6 +- .../decomposedfs/metadata/hybrid_backend.go | 18 +- .../pkg/decomposedfs/tree/propagator/async.go | 4 +- .../pkg/decomposedfs/tree/propagator/sync.go | 3 +- .../opencloud-eu/reva/v2/pkg/utils/grpc.go | 6 + .../go-diceware/diceware/generate.go | 12 +- .../go-password/password/generate.go | 19 +- vendor/modules.txt | 24 +- 42 files changed, 9512 insertions(+), 5002 deletions(-) create mode 100644 vendor/github.com/mattn/go-runewidth/SECURITY.md create mode 100644 vendor/github.com/mattn/go-sqlite3/.coderabbit.yaml create mode 100644 vendor/github.com/mattn/go-sqlite3/SECURITY.md create mode 100644 vendor/github.com/mattn/go-sqlite3/sqlite3_opt_dbstat.go diff --git a/go.mod b/go.mod index 2c7fbaa4ba..d2fadc4d45 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/open-policy-agent/opa v1.19.0 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d - github.com/opencloud-eu/reva/v2 v2.47.0 + github.com/opencloud-eu/reva/v2 v2.48.0 github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 @@ -135,7 +135,7 @@ require ( github.com/ajg/form v1.5.1 // indirect github.com/alexedwards/argon2id v1.0.0 // indirect github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 // indirect - github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op // indirect + github.com/antithesishq/antithesis-sdk-go v0.7.2 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -274,10 +274,10 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.10 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.23 // indirect - github.com/mattn/go-sqlite3 v1.14.42 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect + github.com/mattn/go-sqlite3 v1.14.49 // indirect github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b // indirect github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 // indirect github.com/miekg/dns v1.1.68 // indirect @@ -342,8 +342,8 @@ require ( github.com/segmentio/ksuid v1.0.4 // indirect github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/sethvargo/go-diceware v0.5.0 // indirect - github.com/sethvargo/go-password v0.3.1 // indirect + github.com/sethvargo/go-diceware v0.6.0 // indirect + github.com/sethvargo/go-password v0.4.0 // indirect github.com/shirou/gopsutil/v4 v4.26.5 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect @@ -389,7 +389,7 @@ require ( golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect gopkg.in/ini.v1 v1.67.3 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index efebe28964..e3518668b1 100644 --- a/go.sum +++ b/go.sum @@ -115,8 +115,8 @@ github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/ github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op h1:p2zFsAzvhIpFya8AIOHIbWf7NGvO34QpLGclyf7nXj8= -github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= +github.com/antithesishq/antithesis-sdk-go v0.7.2 h1:oEEedg1Xgi8drRjqB0f9tfjhLoInE0IYZfZ6zAhQUbY= +github.com/antithesishq/antithesis-sdk-go v0.7.2/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= @@ -799,23 +799,23 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= -github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo= -github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w= +github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -942,8 +942,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.47.0 h1:bYul45qS8GmmN9PKplSZ78ZTZ6A+9xp/FfogqFVud18= -github.com/opencloud-eu/reva/v2 v2.47.0/go.mod h1:zdpEKIMDT14w+MGUOWAi+rh+PAZBPUlb7AtIGFWx7Ds= +github.com/opencloud-eu/reva/v2 v2.48.0 h1:G/4Jbv0DWWOfA5u5DtV0CB75pi9Wwtj7JkJQOEBvErs= +github.com/opencloud-eu/reva/v2 v2.48.0/go.mod h1:ZCo/xQM6if+upZa7rJCmdifZ/Y5XHLCrscHytC39yI4= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -1100,10 +1100,10 @@ github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aep github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIehsD+yR0= -github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw= -github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= -github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= +github.com/sethvargo/go-diceware v0.6.0 h1:B3nhMhbBP7KwtTQ7hHRIOmv5FqeD8bJs77RFrV24iWk= +github.com/sethvargo/go-diceware v0.6.0/go.mod h1:lHmdB0xuWaJ06KCraW6bztRT+71Dp+lsXQvborhhsBc= +github.com/sethvargo/go-password v0.4.0 h1:eSidVKQw5C7CmTDAtH3RipBTSjdU1ZRxQaynD2GWLVU= +github.com/sethvargo/go-password v0.4.0/go.mod h1:PO3nYHwUpcHPR0F9woy7a4abZPvzRuqJr0GaeIYTm3k= github.com/shamaton/msgpack/v2 v2.4.1 h1:JtJ141QoQ3NqgPDsjq2v9VXlaON8SiQOwEaoNLEK/MQ= github.com/shamaton/msgpack/v2 v2.4.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -1703,8 +1703,8 @@ google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgn google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 h1:mJiOtnGp0k/BcSgdu03G2NwnscCfCH+h2QKUBZr18KI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/pkg/version/version.go b/pkg/version/version.go index e1c8d65a42..1a2423a51f 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -34,7 +34,7 @@ var ( // LatestTag is the latest released version plus the dev meta version. // Will be overwritten by the release pipeline // Needs a manual change for every tagged release - LatestTag = "7.3.0+dev" + LatestTag = "7.4.0+dev" // Date indicates the build date. // This has been removed, it looks like you can only replace static strings with recent go versions diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go index 2d50c62460..a0bc717446 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk // Package assert enables defining [test properties] about your program or [workload]. It is part of the [Antithesis Go SDK], which enables Go applications to integrate with the [Antithesis platform]. // diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert_noop.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert_noop.go index 640f18ef10..04e5ccd80b 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert_noop.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/assert_noop.go @@ -1,4 +1,4 @@ -//go:build !enable_antithesis_sdk +//go:build no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/boolean_guidance.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/boolean_guidance.go index 7a2af845e4..8735fb42cd 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/boolean_guidance.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/boolean_guidance.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/location.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/location.go index 463340a86a..a0cf41d3ee 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/location.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/location.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/numeric_guidance.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/numeric_guidance.go index 1bba49e90c..f084defa7f 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/numeric_guidance.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/numeric_guidance.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert.go index 0edc73622a..8e5f785c73 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert_nop.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert_nop.go index 79ca12c92f..b0e2dcc5ab 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert_nop.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/rich_assert_nop.go @@ -1,4 +1,4 @@ -//go:build !enable_antithesis_sdk +//go:build no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/tracker.go b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/tracker.go index c88497d363..50b5d1adff 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/assert/tracker.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/assert/tracker.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package assert diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go index 9a0eda76b8..1adeb44675 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/emit.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk +//go:build !no_antithesis_sdk package internal diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go index dab2363554..6d28016c48 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk && linux && amd64 && cgo +//go:build !no_antithesis_sdk && linux && amd64 && cgo package internal diff --git a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler_noop.go b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler_noop.go index 75c67a1659..48e7c883a6 100644 --- a/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler_noop.go +++ b/vendor/github.com/antithesishq/antithesis-sdk-go/internal/voidstar_handler_noop.go @@ -1,4 +1,4 @@ -//go:build enable_antithesis_sdk && (!linux || !amd64 || !cgo) +//go:build !no_antithesis_sdk && (!linux || !amd64 || !cgo) package internal diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go index 2df7b8598a..426a409ca4 100644 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ b/vendor/github.com/mattn/go-colorable/colorable_windows.go @@ -5,13 +5,13 @@ package colorable import ( "bytes" + syscall "golang.org/x/sys/windows" "io" "math" "os" "strconv" "strings" "sync" - syscall "golang.org/x/sys/windows" "unsafe" "github.com/mattn/go-isatty" @@ -93,6 +93,7 @@ type writer struct { handle syscall.Handle althandle syscall.Handle oldattr word + curattr word oldpos coord rest bytes.Buffer mutex sync.Mutex @@ -112,7 +113,7 @@ func NewColorable(file *os.File) io.Writer { var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} + return &writer{out: file, handle: handle, oldattr: csbi.attributes, curattr: csbi.attributes, oldpos: coord{0, 0}} } return file } @@ -438,7 +439,11 @@ func (w *writer) Write(data []byte) (n int, err error) { w.mutex.Lock() defer w.mutex.Unlock() var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) + + if w.rest.Len() == 0 && bytes.IndexByte(data, 0x1b) == -1 { + w.out.Write(data) + return len(data), nil + } handle := w.handle @@ -517,7 +522,7 @@ loop: w.rest.Reset() break } - buf.Write([]byte(string(c))) + buf.WriteByte(c) } if m == 0 { break loop @@ -678,11 +683,11 @@ loop: procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes + attr := w.curattr cs := buf.String() if cs == "" { procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) + w.curattr = w.oldattr continue } token := strings.Split(cs, ";") @@ -814,9 +819,12 @@ loop: attr |= backgroundBlue } } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) } } + if attr != w.curattr { + procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) + w.curattr = attr + } case 'h': var ci consoleCursorInfo cs := buf.String() @@ -834,6 +842,8 @@ loop: w.althandle = syscall.Handle(h) if w.althandle != 0 { handle = w.althandle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } } @@ -853,6 +863,8 @@ loop: syscall.CloseHandle(w.althandle) w.althandle = 0 handle = w.handle + procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) + w.curattr = csbi.attributes } } case 's': diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go index 7402e0618a..b24a2fadc9 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,5 +1,5 @@ -//go:build (appengine || js || nacl || tinygo || wasm) && !windows -// +build appengine js nacl tinygo wasm +//go:build (appengine || js || nacl || tinygo || wasm || wasip1 || wasip2) && !windows +// +build appengine js nacl tinygo wasm wasip1 wasip2 // +build !windows package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go index 8e3c99171b..5f29c11dd2 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_windows.go +++ b/vendor/github.com/mattn/go-isatty/isatty_windows.go @@ -31,6 +31,10 @@ func init() { if procGetFileInformationByHandleEx.Find() != nil { procGetFileInformationByHandleEx = nil } + // Check if NtQueryObject is available. + if procNtQueryObject.Find() != nil { + procNtQueryObject = nil + } } // IsTerminal return true if the file descriptor is terminal. @@ -43,6 +47,7 @@ func IsTerminal(fd uintptr) bool { // Check pipe name is used for cygwin/msys2 pty. // Cygwin/MSYS2 PTY has a name like: // \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +// On Windows 7 a trailing suffix (e.g. "-nat") may be appended. func isCygwinPipeName(name string) bool { token := strings.Split(name, "-") if len(token) < 5 { @@ -72,13 +77,19 @@ func isCygwinPipeName(name string) bool { return false } + for _, t := range token[5:] { + if t == "" { + return false + } + } + return true } -// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler +// getFileNameByHandle use the undocumented ntdll NtQueryObject to get file full name from file handler // since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion // guys are using Windows XP, this is a workaround for those guys, it will also work on system from -// Windows vista to 10 +// Windows Vista to 10 // see https://stackoverflow.com/a/18792477 for details func getFileNameByHandle(fd uintptr) (string, error) { if procNtQueryObject == nil { diff --git a/vendor/github.com/mattn/go-runewidth/SECURITY.md b/vendor/github.com/mattn/go-runewidth/SECURITY.md new file mode 100644 index 0000000000..a6898ee701 --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/SECURITY.md @@ -0,0 +1,25 @@ +# Security Policy + +## Supported Versions + +The following versions of go-runewidth are currently supported with +security updates. + +| Version | Supported | +| -------- | ------------------ | +| 0.0.23 | :white_check_mark: | +| < 0.0.23 | :x: | + +## Reporting a Vulnerability + +If you discover a security vulnerability in go-runewidth, please report it +privately via GitHub's "Report a vulnerability" feature on the Security tab +of the repository (https://github.com/mattn/go-runewidth/security), or by +emailing the maintainer at mattn.jp@gmail.com. + +Please include a description of the issue, reproduction steps, and the +affected version. You can expect an initial response within one week. If +the vulnerability is accepted, a fix will be prepared and a new release +will be published; you will be credited in the release notes unless you +request otherwise. If the report is declined, you will receive an +explanation of the reasoning. diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go index f6c0058222..6b958fdd49 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -2,6 +2,7 @@ package runewidth import ( "os" + "sort" "strings" "unicode/utf8" @@ -25,13 +26,19 @@ var ( ) var ( - zerowidth table // combining + nonprint merged for faster zero-width lookup - widewidth table // ambiguous + doublewidth merged for EA path + zerowidth table // combining + nonprint merged for faster zero-width lookup + widewidth table // ambiguous + doublewidth merged for EA path + eastAsianWidth widthTable + eastAsianWidth0 [0x300]byte ) func init() { zerowidth = mergeIntervals(combining, nonprint) widewidth = mergeIntervals(ambiguous, doublewidth) + eastAsianWidth = makeWidthTable(zerowidth, widewidth) + for r := range eastAsianWidth0 { + eastAsianWidth0[r] = byte(runeWidthEastAsian(rune(r))) + } handleEnv() } @@ -90,6 +97,14 @@ type interval struct { type table []interval +type widthInterval struct { + first rune + last rune + width byte +} + +type widthTable []widthInterval + func inTable(r rune, t table) bool { if r < t[0].first { return false @@ -116,6 +131,71 @@ func inTable(r rune, t table) bool { return false } +func makeWidthTable(zero, two table) widthTable { + wt := make(widthTable, 0, len(zero)+len(two)) + zi := 0 + for _, iv := range two { + start := iv.first + for zi < len(zero) && zero[zi].last < start { + zi++ + } + for i := zi; i < len(zero) && zero[i].first <= iv.last; i++ { + if start < zero[i].first { + wt = append(wt, widthInterval{start, zero[i].first - 1, 2}) + } + if start <= zero[i].last { + start = zero[i].last + 1 + } + if start > iv.last { + break + } + } + if start <= iv.last { + wt = append(wt, widthInterval{start, iv.last, 2}) + } + } + for _, iv := range zero { + wt = append(wt, widthInterval{iv.first, iv.last, 0}) + } + sort.Slice(wt, func(i, j int) bool { + return wt[i].first < wt[j].first + }) + return wt +} + +func inWidthTable(r rune, t widthTable) (int, bool) { + if r < t[0].first { + return 0, false + } + if r > t[len(t)-1].last { + return 0, false + } + + bot := 0 + top := len(t) - 1 + for top >= bot { + mid := (bot + top) >> 1 + + switch { + case t[mid].last < r: + bot = mid + 1 + case t[mid].first > r: + top = mid - 1 + default: + return int(t[mid].width), true + } + } + + return 0, false +} + +func runeWidthEastAsian(r rune) int { + if w, ok := inWidthTable(r, eastAsianWidth); ok { + return w + } + return 1 +} + var private = table{ {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, } @@ -153,34 +233,35 @@ func (c *Condition) RuneWidth(r rune) int { } // optimized version, verified by TestRuneWidthChecksums() if !c.EastAsianWidth { - switch { - case r < 0x20: + if r < 0x20 { return 0 - case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint - return 0 - case r < 0x300: - return 1 - case inTable(r, zerowidth): + } + if (r >= 0x7F && r <= 0x9F) || r == 0xAD { // nonprint return 0 - case inTable(r, doublewidth): - return 2 - default: + } + if r < 0x300 { return 1 } - } else { switch { case inTable(r, zerowidth): return 0 - case inTable(r, narrow): - return 1 - case inTable(r, widewidth): - return 2 - case !c.StrictEmojiNeutral && inTable(r, emoji): + case inTable(r, doublewidth): return 2 default: return 1 } } + + if r < 0x300 { + return int(eastAsianWidth0[r]) + } + if w, ok := inWidthTable(r, eastAsianWidth); ok { + return w + } + if !c.StrictEmojiNeutral && inTable(r, emoji) { + return 2 + } + return 1 } // CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation. @@ -206,6 +287,13 @@ func (c *Condition) CreateLUT() { // StringWidth return width as you can see func (c *Condition) StringWidth(s string) (width int) { + if len(s) == 1 { + b := s[0] + if b < 0x20 || b == 0x7F { + return 0 + } + return 1 + } if len(s) > 0 && len(s) <= utf8.UTFMax { r, size := utf8.DecodeRuneInString(s) if size == len(s) { @@ -213,15 +301,19 @@ func (c *Condition) StringWidth(s string) (width int) { } } // ASCII fast path: no grapheme clustering needed for pure ASCII - if isAllASCII(s) { - for i := 0; i < len(s); i++ { - b := s[i] - if b >= 0x20 && b != 0x7F { - width++ - } + for i := 0; i < len(s); i++ { + b := s[i] + if b >= 0x80 { + goto graphemes + } + if b >= 0x20 && b != 0x7F { + width++ } - return } + return + +graphemes: + width = 0 g := graphemes.FromString(s) for g.Next() { var chWidth int @@ -236,15 +328,6 @@ func (c *Condition) StringWidth(s string) (width int) { return } -func isAllASCII(s string) bool { - for i := 0; i < len(s); i++ { - if s[i] >= 0x80 { - return false - } - } - return true -} - // Truncate return string truncated with w cells func (c *Condition) Truncate(s string, w int, tail string) string { if c.StringWidth(s) <= w { diff --git a/vendor/github.com/mattn/go-sqlite3/.coderabbit.yaml b/vendor/github.com/mattn/go-sqlite3/.coderabbit.yaml new file mode 100644 index 0000000000..2c81325622 --- /dev/null +++ b/vendor/github.com/mattn/go-sqlite3/.coderabbit.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +language: en-US +reviews: + # Skip the vendored SQLite amalgamation. These files are copied verbatim from + # upstream SQLite (see the License section in README.md) and are not code that + # this project authors or reviews. + path_filters: + - "!sqlite3-binding.c" + - "!sqlite3-binding.h" + - "!sqlite3ext.h" + auto_review: + enabled: true + drafts: false +chat: + auto_reply: true diff --git a/vendor/github.com/mattn/go-sqlite3/README.md b/vendor/github.com/mattn/go-sqlite3/README.md index 5c4dd54326..16acdb4546 100644 --- a/vendor/github.com/mattn/go-sqlite3/README.md +++ b/vendor/github.com/mattn/go-sqlite3/README.md @@ -7,9 +7,18 @@ go-sqlite3 [![codecov](https://codecov.io/gh/mattn/go-sqlite3/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-sqlite3) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3) -Latest stable version is v1.14 or later, not v2. +## Sponsors + +This project is proudly sponsored by: -~~**NOTE:** The increase to v2 was an accident. There were no major changes or features.~~ + + + + CodeRabbit + + + +Latest stable version is v1.14 or later, not v2. # Description @@ -125,6 +134,7 @@ Boolean values can be one of: | Transaction Lock | `_txlock` |

  • immediate
  • deferred
  • exclusive
| Specify locking behavior for transactions. | | Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. | | Cache Size | `_cache_size` | `int` | Maximum cache size; default is 2000K (2M). See [PRAGMA cache_size](https://sqlite.org/pragma.html#pragma_cache_size) | +| Statement Cache Size | `_stmt_cache_size` | `int` | Maximum number of prepared statements cached per connection; default is 0 (disabled). Note that `sql.DB` is a connection pool, so each connection maintains its own independent cache. | ## DSN Examples @@ -181,6 +191,7 @@ go build -tags "icu json1 fts5 secure_delete" | Tracing / Debug | sqlite_trace | Activate trace functions | | User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. | | Virtual Tables | sqlite_vtable | SQLite Virtual Tables see [SQLite Official VTABLE Documentation](https://www.sqlite.org/vtab.html) for more information, and a [full example here](https://github.com/mattn/go-sqlite3/tree/master/_example/vtable) | +| The DBSTAT Virtual Table | sqlite_dbstat | The DBSTAT virtual table is a read-only virtual table that returns information about the amount of disk space used to store the content of an SQLite database. See [SQLite Official Documentation](https://www.sqlite.org/dbstat.html) for more information. | # Compilation diff --git a/vendor/github.com/mattn/go-sqlite3/SECURITY.md b/vendor/github.com/mattn/go-sqlite3/SECURITY.md new file mode 100644 index 0000000000..26d9c8b57e --- /dev/null +++ b/vendor/github.com/mattn/go-sqlite3/SECURITY.md @@ -0,0 +1,33 @@ +# Security Policy + +## Supported Versions + +Only the latest release on the `v1.14.x` line receives security fixes. + +| Version | Supported | +| -------- | ------------------ | +| 1.14.x | :white_check_mark: | +| < 1.14 | :x: | + +## Scope + +`go-sqlite3` is a CGo binding that bundles the SQLite amalgamation +(`sqlite3-binding.c` / `sqlite3-binding.h`). Please report issues to the +appropriate project: + +- Bugs in the Go binding layer, CGo glue, build tags, or this repository's + own code: report here. +- Vulnerabilities in SQLite itself: please report them upstream to the + SQLite developers at . Once a fix is released + upstream, this repository will update the bundled amalgamation. + +## Reporting a Vulnerability + +Please **do not** open a public GitHub issue for security problems. + +Use GitHub's private vulnerability reporting: + + +This project is maintained on a best-effort basis by volunteers, so please +allow reasonable time for investigation and a fix before any public +d diff --git a/vendor/github.com/mattn/go-sqlite3/callback.go b/vendor/github.com/mattn/go-sqlite3/callback.go index 0c518fa2c1..b7df2be7f6 100644 --- a/vendor/github.com/mattn/go-sqlite3/callback.go +++ b/vendor/github.com/mattn/go-sqlite3/callback.go @@ -18,7 +18,7 @@ package sqlite3 #endif #include -void _sqlite3_result_text(sqlite3_context* ctx, const char* s); +void _sqlite3_result_text(sqlite3_context* ctx, const char* s, int n); void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); */ import "C" @@ -29,12 +29,13 @@ import ( "math" "reflect" "sync" + "sync/atomic" "unsafe" ) //export callbackTrampoline -func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { - args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] +func callbackTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { + args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo) fi.Call(ctx, args) } @@ -59,9 +60,9 @@ func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, } //export commitHookTrampoline -func commitHookTrampoline(handle unsafe.Pointer) int { +func commitHookTrampoline(handle unsafe.Pointer) C.int { callback := lookupHandle(handle).(func() int) - return callback() + return C.int(callback()) } //export rollbackHookTrampoline @@ -71,23 +72,23 @@ func rollbackHookTrampoline(handle unsafe.Pointer) { } //export updateHookTrampoline -func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) { +func updateHookTrampoline(handle unsafe.Pointer, op C.int, db *C.char, table *C.char, rowid int64) { callback := lookupHandle(handle).(func(int, string, string, int64)) - callback(op, C.GoString(db), C.GoString(table), rowid) + callback(int(op), C.GoString(db), C.GoString(table), rowid) } //export authorizerTrampoline -func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int { +func authorizerTrampoline(handle unsafe.Pointer, op C.int, arg1 *C.char, arg2 *C.char, arg3 *C.char) C.int { callback := lookupHandle(handle).(func(int, string, string, string) int) - return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) + return C.int(callback(int(op), C.GoString(arg1), C.GoString(arg2), C.GoString(arg3))) } //export preUpdateHookTrampoline -func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { +func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op C.int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { hval := lookupHandleVal(handle) data := SQLitePreUpdateData{ Conn: hval.db, - Op: op, + Op: int(op), DatabaseName: C.GoString(db), TableName: C.GoString(table), OldRowID: oldrowid, @@ -104,39 +105,84 @@ type handleVal struct { } var handleLock sync.Mutex -var handleVals = make(map[unsafe.Pointer]handleVal) +var handleVals atomic.Value // stores map[unsafe.Pointer]handleVal func newHandle(db *SQLiteConn, v any) unsafe.Pointer { - handleLock.Lock() - defer handleLock.Unlock() val := handleVal{db: db, val: v} var p unsafe.Pointer = C.malloc(C.size_t(1)) if p == nil { panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") } - handleVals[p] = val + + handleLock.Lock() + defer handleLock.Unlock() + + next := cloneHandleVals(len(loadHandleVals()) + 1) + next[p] = val + handleVals.Store(next) return p } func lookupHandleVal(handle unsafe.Pointer) handleVal { - handleLock.Lock() - defer handleLock.Unlock() - return handleVals[handle] + return loadHandleVals()[handle] } func lookupHandle(handle unsafe.Pointer) any { return lookupHandleVal(handle).val } +// deleteHandle releases a single handle created by newHandle. It is a no-op +// if the handle is unknown (e.g. already released). +func deleteHandle(handle unsafe.Pointer) { + handleLock.Lock() + defer handleLock.Unlock() + + current := loadHandleVals() + if _, ok := current[handle]; !ok { + return + } + next := make(map[unsafe.Pointer]handleVal, len(current)-1) + for h, v := range current { + if h == handle { + continue + } + next[h] = v + } + handleVals.Store(next) + C.free(handle) +} + func deleteHandles(db *SQLiteConn) { handleLock.Lock() defer handleLock.Unlock() - for handle, val := range handleVals { + + current := loadHandleVals() + if len(current) == 0 { + return + } + + next := make(map[unsafe.Pointer]handleVal, len(current)) + for handle, val := range current { if val.db == db { - delete(handleVals, handle) C.free(handle) + continue } + next[handle] = val } + handleVals.Store(next) +} + +func loadHandleVals() map[unsafe.Pointer]handleVal { + m, _ := handleVals.Load().(map[unsafe.Pointer]handleVal) + return m +} + +func cloneHandleVals(size int) map[unsafe.Pointer]handleVal { + next := make(map[unsafe.Pointer]handleVal, size) + for handle, val := range loadHandleVals() { + next[handle] = val + } + return next } // This is only here so that tests can refer to it. @@ -204,12 +250,13 @@ func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { switch C.sqlite3_value_type(v) { case C.SQLITE_BLOB: - l := C.sqlite3_value_bytes(v) p := (*C.char)(C.sqlite3_value_blob(v)) + l := C.sqlite3_value_bytes(v) return reflect.ValueOf(C.GoStringN(p, l)), nil case C.SQLITE_TEXT: c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) - return reflect.ValueOf(C.GoString(c)), nil + l := C.sqlite3_value_bytes(v) + return reflect.ValueOf(C.GoStringN(c, l)), nil default: return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") } @@ -234,6 +281,16 @@ func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { } } +// callbackArgConvert returns conv as-is when the parameter type is the +// canonical type conv produces, and wraps it with a cast for named types +// (e.g. time.Duration), which reflect.Call would otherwise panic on. +func callbackArgConvert(conv callbackArgConverter, typ, canonical reflect.Type) callbackArgConverter { + if typ == canonical { + return conv + } + return callbackArgCast{conv, typ}.Run +} + func callbackArg(typ reflect.Type) (callbackArgConverter, error) { switch typ.Kind() { case reflect.Interface: @@ -245,18 +302,18 @@ func callbackArg(typ reflect.Type) (callbackArgConverter, error) { if typ.Elem().Kind() != reflect.Uint8 { return nil, errors.New("the only supported slice type is []byte") } - return callbackArgBytes, nil + return callbackArgConvert(callbackArgBytes, typ, reflect.TypeOf([]byte(nil))), nil case reflect.String: - return callbackArgString, nil + return callbackArgConvert(callbackArgString, typ, reflect.TypeOf("")), nil case reflect.Bool: - return callbackArgBool, nil + return callbackArgConvert(callbackArgBool, typ, reflect.TypeOf(false)), nil case reflect.Int64: - return callbackArgInt64, nil + return callbackArgConvert(callbackArgInt64, typ, reflect.TypeOf(int64(0))), nil case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: c := callbackArgCast{callbackArgInt64, typ} return c.Run, nil case reflect.Float64: - return callbackArgFloat64, nil + return callbackArgConvert(callbackArgFloat64, typ, reflect.TypeOf(float64(0))), nil case reflect.Float32: c := callbackArgCast{callbackArgFloat64, typ} return c.Run, nil @@ -300,8 +357,7 @@ func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: v = v.Convert(reflect.TypeOf(int64(0))) case reflect.Bool: - b := v.Interface().(bool) - if b { + if v.Bool() { v = reflect.ValueOf(int64(1)) } else { v = reflect.ValueOf(int64(0)) @@ -310,7 +366,7 @@ func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) } - C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) + C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Int())) return nil } @@ -323,7 +379,7 @@ func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) } - C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) + C.sqlite3_result_double(ctx, C.double(v.Float())) return nil } @@ -331,11 +387,14 @@ func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("cannot convert %s to BLOB", v.Type()) } - i := v.Interface() - if i == nil || len(i.([]byte)) == 0 { + bs := v.Bytes() + if len(bs) == 0 { C.sqlite3_result_null(ctx) } else { - bs := i.([]byte) + if i64 && len(bs) > math.MaxInt32 { + C.sqlite3_result_error_toobig(ctx) + return nil + } C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) } return nil @@ -345,8 +404,13 @@ func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { if v.Type().Kind() != reflect.String { return fmt.Errorf("cannot convert %s to TEXT", v.Type()) } - cstr := C.CString(v.Interface().(string)) - C._sqlite3_result_text(ctx, cstr) + s := v.String() + if i64 && len(s) > math.MaxInt32 { + C.sqlite3_result_error_toobig(ctx) + return nil + } + cstr := C.CString(s) + C._sqlite3_result_text(ctx, cstr, C.int(len(s))) return nil } diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c b/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c index 4c27973bda..4a3437f57d 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c @@ -1,7 +1,7 @@ #ifndef USE_LIBSQLITE3 /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.51.3. By combining all the individual C code files into this +** version 3.53.4. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -19,7 +19,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 737ae4a34738ffa0c3ff7f9bb18df914dd1c with changes in files: +** bf7c7f30031888f4e796e429ab3978879485 with changes in files: ** ** */ @@ -468,12 +468,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.51.3" -#define SQLITE_VERSION_NUMBER 3051003 -#define SQLITE_SOURCE_ID "2026-03-13 10:38:09 737ae4a34738ffa0c3ff7f9bb18df914dd1cad163f28fd6b6e114a344fe6d618" -#define SQLITE_SCM_BRANCH "branch-3.51" -#define SQLITE_SCM_TAGS "release version-3.51.3" -#define SQLITE_SCM_DATETIME "2026-03-13T10:38:09.694Z" +#define SQLITE_VERSION "3.53.4" +#define SQLITE_VERSION_NUMBER 3053004 +#define SQLITE_SOURCE_ID "2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc" +#define SQLITE_SCM_BRANCH "branch-3.53" +#define SQLITE_SCM_TAGS "release version-3.53.4" +#define SQLITE_SCM_DATETIME "2026-07-24T19:02:57.525Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -900,7 +900,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal only */ /* ** CAPI3REF: Flags For File Open Operations @@ -1612,6 +1612,12 @@ struct sqlite3_io_methods { #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO +/* reserved file-control numbers: +** 101 +** 102 +** 103 +*/ + /* ** CAPI3REF: Mutex Handle @@ -1812,7 +1818,7 @@ typedef const char *sqlite3_filename; ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. ** -** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** ^The xSetSystemCall(), xGetSystemCall(), and xNextSystemCall() interfaces ** are not used by the SQLite core. These optional interfaces are provided ** by some VFSes to facilitate testing of the VFS code. By overriding ** system calls with functions under its control, a test program can @@ -2033,7 +2039,8 @@ SQLITE_API int sqlite3_os_end(void); ** are called "anytime configuration options". ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ** [sqlite3_shutdown()] with a first argument that is not an anytime -** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. +** configuration option, then the sqlite3_config() call will +** return SQLITE_MISUSE. ** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** @@ -2599,9 +2606,10 @@ struct sqlite3_mem_methods { ** is less than 8. The "sz" argument should be a multiple of 8 less than ** 65536. If "sz" does not meet this constraint, it is reduced in size until ** it does. -**
  • The third argument ("cnt") is the number of slots. Lookaside is disabled -** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so -** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" +**

  • The third argument ("cnt") is the number of slots. +** Lookaside is disabled if "cnt"is less than 1. +* The "cnt" value will be reduced, if necessary, so +** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" ** parameter is usually chosen so that the product of "sz" and "cnt" is less ** than 1,000,000. ** @@ -2889,12 +2897,15 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] **

    SQLITE_DBCONFIG_STMT_SCANSTATUS
    **
    The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in -** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears -** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() -** statistics. For statistics to be collected, the flag must be set on -** the database handle both when the SQL statement is prepared and when it -** is stepped. The flag is set (collection of statistics is enabled) -** by default.

    This option takes two arguments: an integer and a pointer to +** [SQLITE_ENABLE_STMT_SCANSTATUS] builds. In this case, it sets or clears +** a flag that enables collection of run-time performance statistics +** used by [sqlite3_stmt_scanstatus_v2()] and the [nexec and ncycle] +** columns of the [bytecode virtual table]. +** For statistics to be collected, the flag must be set on +** the database handle both when the SQL statement is +** [sqlite3_prepare|prepared] and when it is [sqlite3_step|stepped]. +** The flag is set (collection of statistics is enabled) by default. +**

    This option takes two arguments: an integer and a pointer to ** an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after @@ -2967,16 +2978,34 @@ struct sqlite3_mem_methods { ** comments are allowed in SQL text after processing the first argument. **

    ** +** [[SQLITE_DBCONFIG_FP_DIGITS]] +**
    SQLITE_DBCONFIG_FP_DIGITS
    +**
    The SQLITE_DBCONFIG_FP_DIGITS setting is a small integer that determines +** the number of significant digits that SQLite will attempt to preserve when +** converting floating point numbers (IEEE 754 "doubles") into text. The +** default value 17, as of SQLite version 3.52.0. The value was 15 in all +** prior versions.

    +** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is a small integer, between 3 and 23, or +** zero. The FP_DIGITS setting is changed to that small integer, or left +** unaltered if the first argument is zero or out of range. The second argument +** is a pointer to an integer. If the pointer is not NULL, then the value of +** the FP_DIGITS setting, after possibly being modified by the first +** arguments, is written into the integer to which the second argument points. +**

    +** ** ** ** [[DBCONFIG arguments]]

    Arguments To SQLITE_DBCONFIG Options

    ** **

    Most of the SQLITE_DBCONFIG options take two arguments, so that the ** overall call to [sqlite3_db_config()] has a total of four parameters. -** The first argument (the third parameter to sqlite3_db_config()) is an integer. -** The second argument is a pointer to an integer. If the first argument is 1, -** then the option becomes enabled. If the first integer argument is 0, then the -** option is disabled. If the first argument is -1, then the option setting +** The first argument (the third parameter to sqlite3_db_config()) is +** an integer. +** The second argument is a pointer to an integer. If the first argument is 1, +** then the option becomes enabled. If the first integer argument is 0, +** then the option is disabled. +** If the first argument is -1, then the option setting ** is unchanged. The second argument, the pointer to an integer, may be NULL. ** If the second argument is not NULL, then a value of 0 or 1 is written into ** the integer to which the second argument points, depending on whether the @@ -2984,9 +3013,10 @@ struct sqlite3_mem_methods { ** the first argument. ** **

    While most SQLITE_DBCONFIG options use the argument format -** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] -** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the -** documentation of those exceptional options for details. +** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME], +** [SQLITE_DBCONFIG_LOOKASIDE], and [SQLITE_DBCONFIG_FP_DIGITS] options +** are different. See the documentation of those exceptional options for +** details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -3011,7 +3041,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_FP_DIGITS 1023 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1023 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -4493,6 +4524,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename); **

  • sqlite3_errmsg() **
  • sqlite3_errmsg16() **
  • sqlite3_error_offset() +**
  • sqlite3_db_handle() ** ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language @@ -4539,7 +4571,7 @@ SQLITE_API const char *sqlite3_errstr(int); SQLITE_API int sqlite3_error_offset(sqlite3 *db); /* -** CAPI3REF: Set Error Codes And Message +** CAPI3REF: Set Error Code And Message ** METHOD: sqlite3 ** ** Set the error code of the database handle passed as the first argument @@ -4656,7 +4688,12 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** or in an ORDER BY or GROUP BY clause.)^ ** ** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
    SQLITE_LIMIT_EXPR_DEPTH
    -**
    The maximum depth of the parse tree on any expression.
    )^ +**
    The maximum depth of the parse tree on any expression and +** the maximum nesting depth for subqueries and VIEWs
    )^ +** +** [[SQLITE_LIMIT_PARSER_DEPTH]] ^(
    SQLITE_LIMIT_PARSER_DEPTH
    +**
    The maximum depth of the LALR(1) parser stack used to analyze +** input SQL statements.
    )^ ** ** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    **
    The maximum number of terms in a compound SELECT statement.
    )^ @@ -4683,7 +4720,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); **
    The maximum index number of any [parameter] in an SQL statement.)^ ** ** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    -**
    The maximum depth of recursion for triggers.
    )^ +**
    The maximum depth of recursion for triggers, and the maximum +** nesting depth for separate triggers.
    )^ ** ** [[SQLITE_LIMIT_WORKER_THREADS]] ^(
    SQLITE_LIMIT_WORKER_THREADS
    **
    The maximum number of auxiliary worker threads that a single @@ -4702,6 +4740,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 +#define SQLITE_LIMIT_PARSER_DEPTH 12 /* ** CAPI3REF: Prepare Flags @@ -4746,12 +4785,29 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** fails, the sqlite3_prepare_v3() call returns the same error indications ** with or without this flag; it just omits the call to [sqlite3_log()] that ** logs the error. +** +** [[SQLITE_PREPARE_FROM_DDL]]
    SQLITE_PREPARE_FROM_DDL
    +**
    The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to enforce +** security constraints that would otherwise only be enforced when parsing +** the database schema. In other words, the SQLITE_PREPARE_FROM_DDL flag +** causes the SQL compiler to treat the SQL statement being prepared as if +** it had come from an attacker. When SQLITE_PREPARE_FROM_DDL is used and +** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] is off, SQL functions may only be called +** if they are tagged with [SQLITE_INNOCUOUS] and virtual tables may only +** be used if they are tagged with [SQLITE_VTAB_INNOCUOUS]. Best practice +** is to use the SQLITE_PREPARE_FROM_DDL option when preparing any SQL that +** is derived from parts of the database schema. In particular, virtual +** table implementations that run SQL statements that are derived from +** arguments to their CREATE VIRTUAL TABLE statement should always use +** [sqlite3_prepare_v3()] and set the SQLITE_PREPARE_FROM_DDL flag to +** prevent bypass of the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 #define SQLITE_PREPARE_NO_VTAB 0x04 #define SQLITE_PREPARE_DONT_LOG 0x10 +#define SQLITE_PREPARE_FROM_DDL 0x20 /* ** CAPI3REF: Compiling An SQL Statement @@ -4765,8 +4821,9 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** ** The preferred routine to use is [sqlite3_prepare_v2()]. The ** [sqlite3_prepare()] interface is legacy and should be avoided. -** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used -** for special purposes. +** [sqlite3_prepare_v3()] has an extra +** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is sometimes +** needed for special purpose or to pass along security restrictions. ** ** The use of the UTF-8 interfaces is preferred, as SQLite currently ** does all parsing using UTF-8. The UTF-16 interfaces are provided @@ -5171,8 +5228,8 @@ typedef struct sqlite3_context sqlite3_context; ** it should be a pointer to well-formed UTF16 text. ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then ** it should be a pointer to a well-formed unicode string that is -** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 -** otherwise. +** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, +** or UTF16 otherwise. ** ** [[byte-order determination rules]] ^The byte-order of ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) @@ -5218,10 +5275,15 @@ typedef struct sqlite3_context sqlite3_context; ** object and pointer to it must remain valid until then. ^SQLite will then ** manage the lifetime of its private copy. ** -** ^The sixth argument to sqlite3_bind_text64() must be one of -** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] -** to specify the encoding of the text in the third parameter. If -** the sixth argument to sqlite3_bind_text64() is not one of the +** ^The sixth argument (the E argument) +** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of +** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE] to specify the encoding of the text in the +** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the +** string argument is both UTF-8 encoded and is zero-terminated. In other +** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at +** least N+1 bytes and that the Z[N] byte is zero. If +** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the ** allowed values shown above, or if the text encoding is different ** from the encoding specified by the sixth parameter, then the behavior ** is undefined. @@ -6088,6 +6150,52 @@ SQLITE_API int sqlite3_create_window_function( ** ** These constants define integer codes that represent the various ** text encodings supported by SQLite. +** +**
    +** [[SQLITE_UTF8]]
    SQLITE_UTF8
    Text is encoding as UTF-8
    +** +** [[SQLITE_UTF16LE]]
    SQLITE_UTF16LE
    Text is encoding as UTF-16 +** with each code point being expressed "little endian" - the least significant +** byte first. This is the usual encoding, for example on Windows.
    +** +** [[SQLITE_UTF16BE]]
    SQLITE_UTF16BE
    Text is encoding as UTF-16 +** with each code point being expressed "big endian" - the most significant +** byte first. This encoding is less common, but is still sometimes seen, +** specially on older systems. +** +** [[SQLITE_UTF16]]
    SQLITE_UTF16
    Text is encoding as UTF-16 +** with each code point being expressed either little endian or as big +** endian, according to the native endianness of the host computer. +** +** [[SQLITE_ANY]]
    SQLITE_ANY
    This encoding value may only be used +** to declare the preferred text for [application-defined SQL functions] +** created using [sqlite3_create_function()] and similar. If the preferred +** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep +** parameter) is SQLITE_ANY, that indicates that the function does not have +** a preference regarding the text encoding of its parameters and can take +** any text encoding that the SQLite core find convenient to supply. This +** option is deprecated. Please do not use it in new applications. +** +** [[SQLITE_UTF16_ALIGNED]]
    SQLITE_UTF16_ALIGNED
    This encoding +** value may be used as the 3rd parameter (the eTextRep parameter) to +** [sqlite3_create_collation()] and similar. This encoding value means +** that the application-defined collating sequence created expects its +** input strings to be in UTF16 in native byte order, and that the start +** of the strings must be aligned to a 2-byte boundary. +** +** [[SQLITE_UTF8_ZT]]
    SQLITE_UTF8_ZT
    This option can only be +** used to specify the text encoding to strings input to +** [sqlite3_result_text64()] and [sqlite3_bind_text64()]. +** The SQLITE_UTF8_ZT encoding means that the input string (call it "z") +** is UTF-8 encoded and that it is zero-terminated. If the length parameter +** (call it "n") is non-negative, this encoding option means that the caller +** guarantees that z array contains at least n+1 bytes and that the z[n] +** byte has a value of zero. +** This option gives the same output as SQLITE_UTF8, but can be more efficient +** by avoiding the need to make a copy of the input string, in some cases. +** However, if z is allocated to hold fewer than n+1 bytes or if the +** z[n] byte is not zero, undefined behavior may result. +**
    */ #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ @@ -6095,6 +6203,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_UTF16 4 /* Use native byte order */ #define SQLITE_ANY 5 /* Deprecated */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ +#define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ /* ** CAPI3REF: Function Flags @@ -6329,26 +6438,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** the SQL function that supplied the [sqlite3_value*] parameters. ** ** As long as the input parameter is correct, these routines can only -** fail if an out-of-memory error occurs during a format conversion. -** Only the following subset of interfaces are subject to out-of-memory -** errors: -** -**
      -**
    • sqlite3_value_blob() -**
    • sqlite3_value_text() -**
    • sqlite3_value_text16() -**
    • sqlite3_value_text16le() -**
    • sqlite3_value_text16be() -**
    • sqlite3_value_bytes() -**
    • sqlite3_value_bytes16() -**
    -** +** fail if an out-of-memory error occurs while trying to do a +** UTF8→UTF16 or UTF16→UTF8 conversion. ** If an out-of-memory error occurs, then the return value from these ** routines is the same as if the column had contained an SQL NULL value. -** Valid SQL NULL returns can be distinguished from out-of-memory errors -** by invoking the [sqlite3_errcode()] immediately after the suspect +** If the input sqlite3_value was not obtained from [sqlite3_value_dup()], +** then valid SQL NULL returns can also be distinguished from +** out-of-memory errors after extracting the value +** by invoking the [sqlite3_errcode()] immediately after the suspicious ** return value is obtained and before any ** other SQLite interface is called on the same [database connection]. +** If the input sqlite3_value was obtained from sqlite3_value_dup() then +** it is disconnected from the database connection and so sqlite3_errcode() +** will not work. +** In that case, the only way to distinguish an out-of-memory +** condition from a true SQL NULL is to invoke sqlite3_value_type() on the +** input to see if it is NULL prior to trying to extract the value. */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API double sqlite3_value_double(sqlite3_value*); @@ -6375,7 +6480,8 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); ** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) ** returns something other than SQLITE_TEXT, then the return value from ** sqlite3_value_encoding(X) is meaningless. ^Calls to -** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], +** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], +** [sqlite3_value_text16be(X)], ** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or ** [sqlite3_value_bytes16(X)] might change the encoding of the value X and ** thus change the return from subsequent calls to sqlite3_value_encoding(X). @@ -6506,17 +6612,17 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** query execution, under some circumstances the associated auxiliary data ** might be preserved. An example of where this might be useful is in a ** regular-expression matching function. The compiled version of the regular -** expression can be stored as auxiliary data associated with the pattern string. -** Then as long as the pattern string remains the same, +** expression can be stored as auxiliary data associated with the pattern +** string. Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** -** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data -** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument -** value to the application-defined function. ^N is zero for the left-most -** function argument. ^If there is no auxiliary data -** associated with the function argument, the sqlite3_get_auxdata(C,N) interface -** returns a NULL pointer. +** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary +** data associated by the sqlite3_set_auxdata(C,N,P,X) function with the +** Nth argument value to the application-defined function. ^N is zero +** for the left-most function argument. ^If there is no auxiliary data +** associated with the function argument, the sqlite3_get_auxdata(C,N) +** interface returns a NULL pointer. ** ** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the ** N-th argument of the application-defined function. ^Subsequent @@ -6600,10 +6706,14 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi ** ** There is no limit (other than available memory) on the number of different ** client data pointers (with different names) that can be attached to a -** single database connection. However, the implementation is optimized -** for the case of having only one or two different client data names. -** Applications and wrapper libraries are discouraged from using more than -** one client data name each. +** single database connection. However, the current implementation stores +** the content on a linked list. Insert and retrieval performance will +** be proportional to the number of entries. The design use case, and +** the use case for which the implementation is optimized, is +** that an application will store only small number of client data names, +** typically just one or two. This interface is not intended to be a +** generalized key/value store for thousands or millions of keys. It +** will work for that, but performance might be disappointing. ** ** There is no way to enumerate the client data pointers ** associated with a database connection. The N parameter can be thought @@ -6711,10 +6821,14 @@ typedef void (*sqlite3_destructor_type)(void*); ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. -** ^The sqlite3_result_text64() interface sets the return value of an +** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an ** application-defined function to be a text string in an encoding -** specified by the fifth (and last) parameter, which must be one -** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. +** specified the E parameter, which must be one +** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that +** the result text is both UTF-8 and zero-terminated. In other words, +** SQLITE_UTF8_ZT means that the Z array holds at least N+1 bytes and that +** the Z[N] is zero. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces @@ -6801,7 +6915,7 @@ SQLITE_API void sqlite3_result_int(sqlite3_context*, int); SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void sqlite3_result_null(sqlite3_context*); SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, +SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, void(*)(void*), unsigned char encoding); SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); @@ -7740,7 +7854,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ^The sqlite3_load_extension() interface attempts to load an ** [SQLite extension] library contained in the file zFile. If ** the file cannot be loaded directly, attempts are made to load -** with various operating-system specific extensions added. +** with various operating-system specific filename extensions added. ** So for example, if "samplelib" cannot be loaded, then names like ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might ** be tried also. @@ -7748,10 +7862,10 @@ SQLITE_API int sqlite3_table_column_metadata( ** ^The entry point is zProc. ** ^(zProc may be 0, in which case SQLite will try to come up with an ** entry point name on its own. It first tries "sqlite3_extension_init". -** If that does not work, it constructs a name "sqlite3_X_init" where -** X consists of the lower-case equivalent of all ASCII alphabetic -** characters in the filename from the last "/" to the first following -** "." and omitting any initial "lib".)^ +** If that does not work, it tries names of the form "sqlite3_X_init" +** where X consists of the lower-case equivalent of all ASCII alphabetic +** characters or all ASCII alphanumeric characters in the filename from +** the last "/" to the first following "." and omitting any initial "lib".)^ ** ^The sqlite3_load_extension() interface returns ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. ** ^If an error occurs and pzErrMsg is not 0, then the @@ -7825,7 +7939,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); **
     **    int xEntryPoint(
     **      sqlite3 *db,
    -**      const char **pzErrMsg,
    +**      char **pzErrMsg,
     **      const struct sqlite3_api_routines *pThunk
     **    );
     ** 
    )^ @@ -8575,13 +8689,6 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix ** and Windows. ** -** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor -** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex -** implementation is included with the library. In this case the -** application must supply a custom mutex implementation using the -** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function -** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize(). ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() @@ -8936,6 +9043,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ +#define SQLITE_TESTCTRL_ATOF 34 #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* @@ -9044,17 +9152,22 @@ SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*); ** pass the returned value to [sqlite3_free()] to avoid a memory leak. ** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any ** errors were encountered during construction of the string. ^The -** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the +** [sqlite3_str_finish(X)] interface might also return a NULL pointer if the ** string in [sqlite3_str] object X is zero bytes long. +** +** ^The [sqlite3_str_free(X)] interface destroys both the sqlite3_str object +** X and the string content it contains. Calling sqlite3_str_free(X) is +** the equivalent of calling [sqlite3_free](sqlite3_str_finish(X)). */ SQLITE_API char *sqlite3_str_finish(sqlite3_str*); +SQLITE_API void sqlite3_str_free(sqlite3_str*); /* ** CAPI3REF: Add Content To A Dynamic String ** METHOD: sqlite3_str ** -** These interfaces add content to an sqlite3_str object previously obtained -** from [sqlite3_str_new()]. +** These interfaces add or remove content to an sqlite3_str object +** previously obtained from [sqlite3_str_new()]. ** ** ^The [sqlite3_str_appendf(X,F,...)] and ** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf] @@ -9077,6 +9190,10 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*); ** ^The [sqlite3_str_reset(X)] method resets the string under construction ** inside [sqlite3_str] object X back to zero bytes in length. ** +** ^The [sqlite3_str_truncate(X,N)] method changes the length of the string +** under construction to be N bytes or less. This routine is a no-op if +** N is negative or if the string is already N bytes or smaller in size. +** ** These methods do not return a result code. ^If an error occurs, that fact ** is recorded in the [sqlite3_str] object and can be recovered by a ** subsequent call to [sqlite3_str_errcode(X)]. @@ -9087,6 +9204,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N); SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn); SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C); SQLITE_API void sqlite3_str_reset(sqlite3_str*); +SQLITE_API void sqlite3_str_truncate(sqlite3_str*,int N); /* ** CAPI3REF: Status Of A Dynamic String @@ -10617,7 +10735,8 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** ** sqlite3_vtab_distinct() return value ** Rows are returned in aOrderBy order -** Rows with the same value in all aOrderBy columns are adjacent +** Rows with the same value in all aOrderBy columns are +** adjacent ** Duplicates over all colUsed columns may be omitted ** 0yesyesno ** 1noyesno @@ -10626,8 +10745,8 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** ** ** ^For the purposes of comparing virtual table output values to see if the -** values are the same value for sorting purposes, two NULL values are considered -** to be the same. In other words, the comparison operator is "IS" +** values are the same value for sorting purposes, two NULL values are +** considered to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements @@ -10920,9 +11039,9 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** a variable pointed to by the "pOut" parameter. ** ** The "flags" parameter must be passed a mask of flags. At present only -** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** one flag is defined - [SQLITE_SCANSTAT_COMPLEX]. If SQLITE_SCANSTAT_COMPLEX ** is specified, then status information is available for all elements -** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** of a query plan that are reported by "[EXPLAIN QUERY PLAN]" output. If ** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ** the EXPLAIN QUERY PLAN output) are available. Invoking API @@ -10936,7 +11055,8 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** elements used to implement the statement - a non-zero value is returned and ** the variable that pOut points to is unchanged. ** -** See also: [sqlite3_stmt_scanstatus_reset()] +** See also: [sqlite3_stmt_scanstatus_reset()] and the +** [nexec and ncycle] columns of the [bytecode virtual table]. */ SQLITE_API int sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ @@ -11478,19 +11598,42 @@ SQLITE_API int sqlite3_deserialize( /* ** CAPI3REF: Bind array values to the CARRAY table-valued function ** -** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to -** one of the first argument of the [carray() table-valued function]. The -** S parameter is a pointer to the [prepared statement] that uses the carray() -** functions. I is the parameter index to be bound. P is a pointer to the -** array to be bound, and N is the number of eements in the array. The -** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], -** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to -** indicate the datatype of the array being bound. The X argument is not a -** NULL pointer, then SQLite will invoke the function X on the P parameter -** after it has finished using P, even if the call to -** sqlite3_carray_bind() fails. The special-case finalizer -** SQLITE_TRANSIENT has no effect here. -*/ +** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to +** parameter that is the first argument of the [carray() table-valued function]. +** The S parameter is a pointer to the [prepared statement] that uses the +** carray() functions. I is the parameter index to be bound. I must be the +** index of the parameter that is the first argument to the carray() +** table-valued function. P is a pointer to the array to be bound, and N +** is the number of elements in the array. The F argument is one of +** constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], +** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], +** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. +** +** If the X argument is not a NULL pointer or one of the special +** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke +** the function X with argument D when it is finished using the data in P. +** The call to X(D) is a destructor for the array P. The destructor X(D) +** is invoked even if the call to sqlite3_carray_bind_v2() fails. If the X +** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes +** that the data static and the destructor is never invoked. If the X +** parameter is the special-case value [SQLITE_TRANSIENT], then +** sqlite3_carray_bind_v2() makes its own private copy of the data prior +** to returning and never invokes the destructor X. +** +** The sqlite3_carray_bind() function works the same as sqlite3_carray_bind_v2() +** with a D parameter set to P. In other words, +** sqlite3_carray_bind(S,I,P,N,F,X) is same as +** sqlite3_carray_bind_v2(S,I,P,N,F,X,P). +*/ +SQLITE_API int sqlite3_carray_bind_v2( + sqlite3_stmt *pStmt, /* Statement to be bound */ + int i, /* Parameter index */ + void *aData, /* Pointer to array data */ + int nData, /* Number of data elements */ + int mFlags, /* CARRAY flags */ + void (*xDel)(void*), /* Destructor for aData */ + void *pDel /* Optional argument to xDel() */ +); SQLITE_API int sqlite3_carray_bind( sqlite3_stmt *pStmt, /* Statement to be bound */ int i, /* Parameter index */ @@ -13034,11 +13177,23 @@ SQLITE_API int sqlite3changeset_apply_v3( ** database behave as if they were declared with "ON UPDATE NO ACTION ON ** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL ** or SET DEFAULT. +** +**
    SQLITE_CHANGESETAPPLY_NOUPDATELOOP
    +** Sometimes, a changeset contains two or more update statements such that +** although after applying all updates the database will contain no +** constraint violations, no single update can be applied before the others. +** The simplest example of this is a pair of UPDATEs that have "swapped" +** two column values with a UNIQUE constraint. +**

    +** Usually, sqlite3changeset_apply() and similar functions work hard to try +** to find a way to apply such a changeset. However, if this flag is set, +** then all such updates are considered CONSTRAINT conflicts. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 #define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 +#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP 0x0010 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -13521,6 +13676,232 @@ SQLITE_API int sqlite3session_config(int op, void *pArg); */ #define SQLITE_SESSION_CONFIG_STRMSIZE 1 +/* +** CAPI3REF: Configure a changegroup object +** +** Configure the changegroup object passed as the first argument. +** At present the only valid value for the second parameter is +** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET]. +*/ +SQLITE_API int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg); + +/* +** CAPI3REF: Options for sqlite3changegroup_config(). +** +** The following values may be passed as the 2nd parameter to +** sqlite3changegroup_config(). +** +**

    SQLITE_CHANGEGROUP_CONFIG_PATCHSET
    +** A changegroup object generates either a changeset or patchset. Usually, +** this is determined by whether the first call to sqlite3changegroup_add() +** is passed a changeset or a patchset. Or, if the first changes are added +** to the changegroup object using the sqlite3changegroup_change_xxx() +** APIs, then this option may be used to configure whether the changegroup +** object generates a changeset or patchset. +** +** When this option is invoked, parameter pArg must point to a value of +** type int. If the changegroup currently contains zero changes, and the +** value of the int variable is zero or greater than zero, then the +** changegroup is configured to generate a changeset or patchset, +** respectively. It is a no-op, not an error, if the changegroup is not +** configured because it has already started accumulating changes. +** +** Before returning, the int variable is set to 0 if the changegroup is +** configured to generate a changeset, or 1 if it is configured to generate +** a patchset. +*/ +#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1 + + +/* +** CAPI3REF: Begin adding a change to a changegroup +** +** This API is used, in concert with other sqlite3changegroup_change_xxx() +** APIs, to add changes to a changegroup object one at a time. To add a +** single change, the caller must: +** +** 1. Invoke sqlite3changegroup_change_begin() to indicate the type of +** change (INSERT, UPDATE or DELETE), the affected table and whether +** or not the change should be marked as indirect. +** +** 2. Invoke sqlite3changegroup_change_int64() or one of the other four +** value functions - _null(), _double(), _text() or _blob() - one or +** more times to specify old.* and new.* values for the change being +** constructed. +** +** 3. Invoke sqlite3changegroup_change_finish() to either finish adding +** the change to the group, or to discard the change altogether. +** +** The first argument to this function must be a pointer to the existing +** changegroup object that the change will be added to. The second argument +** must be SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE. The third is the +** name of the table that the change affects, and the fourth is a boolean +** flag specifying whether the change should be marked as "indirect" (if +** bIndirect is non-zero) or not indirect (if bIndirect is zero). +** +** Following a successful call to this function, this function may not be +** called again on the same changegroup object until after +** sqlite3changegroup_change_finish() has been called. Doing so is an +** SQLITE_MISUSE error. +** +** The changegroup object passed as the first argument must be already +** configured with schema data for the specified table. It may be configured +** either by calling sqlite3changegroup_schema() with a database that contains +** the table, or sqlite3changegroup_add() with a changeset that contains the +** table. If the changegroup object has not been configured with a schema for +** the specified table when this function is called, SQLITE_ERROR is returned. +** +** If successful, SQLITE_OK is returned. Otherwise, if an error occurs, an +** SQLite error code is returned. In this case, if argument pzErr is non-NULL, +** then (*pzErr) may be set to point to a buffer containing a utf-8 formated, +** nul-terminated, English language error message. It is the responsibility +** of the caller to eventually free this buffer using sqlite3_free(). +*/ +SQLITE_API int sqlite3changegroup_change_begin( + sqlite3_changegroup*, + int eOp, + const char *zTab, + int bIndirect, + char **pzErr +); + +/* +** CAPI3REF: Add a 64-bit integer to a changegroup +** +** This function may only be called between a successful call to +** sqlite3changegroup_change_begin() and its matching +** sqlite3changegroup_change_finish() call. If it is called at any +** other time, it is an SQLITE_MISUSE error. Calling this function +** specifies a 64-bit integer value to be used in the change currently being +** added to the changegroup object passed as the first argument. +** +** The second parameter, bNew, specifies whether the value is to be part of +** the new.* (if bNew is non-zero) or old.* (if bNew is zero) record of +** the change under construction. If this does not match the type of change +** specified by the preceding call to sqlite3changegroup_change_begin() (i.e. +** an old.* value for an SQLITE_INSERT change, or a new.* value for an +** SQLITE_DELETE), then SQLITE_ERROR is returned. +** +** The third parameter specifies the column of the old.* or new.* record that +** the value will be a part of. If the specified table has an explicit primary +** key, then this is the index of the table column, numbered from 0 in the order +** specified within the CREATE TABLE statement. Or, if the table uses an +** implicit rowid key, then the column 0 is the rowid and the explicit columns +** are numbered starting from 1. If the iCol parameter is less than 0 or greater +** than the index of the last column in the table, SQLITE_RANGE is returned. +** +** The fourth parameter is the integer value to use as part of the old.* or +** new.* record. +** +** If this call is successful, SQLITE_OK is returned. Otherwise, if an +** error occurs, an SQLite error code is returned. +*/ +SQLITE_API int sqlite3changegroup_change_int64( + sqlite3_changegroup*, + int bNew, + int iCol, + sqlite3_int64 iVal +); + +/* +** CAPI3REF: Add a NULL to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). Except that +** it configures the change currently under construction with a NULL value +** instead of a 64-bit integer. +*/ +SQLITE_API int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int); + +/* +** CAPI3REF: Add an double to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). Except that +** it configures the change currently being constructed with a real value +** instead of a 64-bit integer. +*/ +SQLITE_API int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double); + +/* +** CAPI3REF: Add a text value to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). It configures +** the currently accumulated change with a text value instead of a 64-bit +** integer. Parameter pVal points to a buffer containing the text encoded using +** utf-8. Parameter nVal may either be the size of the text value in bytes, or +** else a negative value, in which case the buffer pVal points to is assumed to +** be nul-terminated. +*/ +SQLITE_API int sqlite3changegroup_change_text( + sqlite3_changegroup*, int, int, const char *pVal, int nVal +); + +/* +** CAPI3REF: Add a blob to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). It configures +** the currently accumulated change with a blob value instead of a 64-bit +** integer. Parameter pVal points to a buffer containing the blob. Parameter +** nVal is the size of the blob in bytes. +*/ +SQLITE_API int sqlite3changegroup_change_blob( + sqlite3_changegroup*, int, int, const void *pVal, int nVal +); + +/* +** CAPI3REF: Finish adding one-at-at-time changes to a changegroup +** +** This function may only be called following a successful call to +** sqlite3changegroup_change_begin(). Otherwise, it is an SQLITE_MISUSE error. +** +** If parameter bDiscard is non-zero, then the current change is simply +** discarded. In this case this function is always successful and SQLITE_OK +** returned. +** +** If parameter bDiscard is zero, then an attempt is made to add the current +** change to the changegroup. Assuming the changegroup is configured to +** produce a changeset (not a patchset), this requires that: +** +** * If the change is an INSERT or DELETE, then a value must be specified +** for all columns of the new.* or old.* record, respectively. +** +** * If the change is an UPDATE record, then values must be provided for +** the PRIMARY KEY columns of the old.* record, but must not be provided +** for PRIMARY KEY columns of the new.* record. +** +** * If the change is an UPDATE record, then for each non-PRIMARY KEY +** column in the old.* record for which a value has been provided, a +** value must also be provided for the same column in the new.* record. +** Similarly, for each non-PK column in the old.* record for which +** a value is not provided, a value must not be provided for the same +** column in the new.* record. +** +** * All values specified for PRIMARY KEY columns must be non-NULL. +** +** Otherwise, it is an error. +** +** If the changegroup already contains a change for the same row (identified +** by PRIMARY KEY columns), then the current change is combined with the +** existing change in the same way as for sqlite3changegroup_add(). +** +** For a patchset, all of the above rules apply except that it doesn't matter +** whether or not values are provided for the non-PK old.* record columns +** for an UPDATE or DELETE change. This means that code used to produce +** a changeset using the sqlite3changegroup_change_xxx() APIs may also +** be used to produce patchsets. +** +** If the call is successful, SQLITE_OK is returned. Otherwise, if an error +** occurs, an SQLite error code is returned. If an error is returned and +** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer +** containing a nul-terminated, utf-8 encoded, English language error message. +** It is the responsibility of the caller to eventually free any such error +** message buffer using sqlite3_free(). +*/ +SQLITE_API int sqlite3changegroup_change_finish( + sqlite3_changegroup*, + int bDiscard, + char **pzErr +); + /* ** Make sure we can call this stuff from C++. */ @@ -14391,21 +14772,42 @@ struct fts5_api { ** It used to be the case that setting this value to zero would ** turn the limit off. That is no longer true. It is not possible ** to turn this limit off. +** +** The hard limit is the largest possible 32-bit signed integer less +** 1024, or 2147482624. */ #ifndef SQLITE_MAX_SQL_LENGTH # define SQLITE_MAX_SQL_LENGTH 1000000000 #endif /* -** The maximum depth of an expression tree. This is limited to -** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might -** want to place more severe limits on the complexity of an -** expression. A value of 0 means that there is no limit. +** The maximum depth of an expression tree. The expression tree depth +** is also limited indirectly by SQLITE_MAX_SQL_LENGTH and by +** SQLITE_MAX_PARSER_DEPTH. Reducing the maximum complexity of +** expressions can help prevent excess memory usage by hostile SQL. +** +** A value of 0 for this compile-time option causes all expression +** depth limiting code to be omitted. */ #ifndef SQLITE_MAX_EXPR_DEPTH # define SQLITE_MAX_EXPR_DEPTH 1000 #endif +/* +** The maximum depth of the LALR(1) stack used in the parser that +** interprets SQL inputs. The parser stack depth can also be limited +** indirectly by SQLITE_MAX_SQL_LENGTH. Limiting the parser stack +** depth can help prevent excess memory usage and excess CPU stack +** usage when processing hostile SQL. +** +** Prior to version 3.45.0 (2024-01-15), the parser stack was +** hard-coded to 100 entries, and that worked fine for almost all +** applications. So the upper bound on this limit need not be large. +*/ +#ifndef SQLITE_MAX_PARSER_DEPTH +# define SQLITE_MAX_PARSER_DEPTH 2500 +#endif + /* ** The maximum number of terms in a compound SELECT statement. ** The code generator for compound SELECT statements does one @@ -14521,6 +14923,10 @@ struct fts5_api { # undef SQLITE_MAX_DEFAULT_PAGE_SIZE # define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE #endif +#if SQLITE_MAX_DEFAULT_PAGE_SIZE -#endif #ifdef HAVE_INTTYPES_H #include #endif @@ -15291,6 +15695,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); # define float sqlite_int64 # define fabs(X) ((X)<0?-(X):(X)) # define sqlite3IsOverflow(X) 0 +# define INFINITY (9223372036854775807LL) # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif @@ -15398,6 +15803,13 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); # define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) #endif +/* +** sizeof64() is like sizeof(), but always returns a 64-bit value, even +** on 32-bit builds. This can help to avoid overflow by ensuring 64-bit +** arithmetic is used consistently in both 32-bit and 64-bit builds. +*/ +#define sizeof64(X) ((sqlite3_int64)sizeof(X)) + /* ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as ** to avoid complaints from -fsanitize=strict-bounds. @@ -15700,6 +16112,7 @@ typedef INT16_TYPE LogEst; #else # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) #endif +#define TWO_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&1)==0) /* ** Disable MMAP on platforms where it is known to not work @@ -16758,7 +17171,7 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); -SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); +SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree*, Btree*); SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); @@ -17158,6 +17571,7 @@ struct VdbeOp { SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Table *pTab; /* Used when p4type is P4_TABLE */ SubrtnSig *pSubrtnSig; /* Used when p4type is P4_SUBRTNSIG */ + Index *pIdx; /* Used when p4type is P4_INDEX */ #ifdef SQLITE_ENABLE_CURSOR_HINTS Expr *pExpr; /* Used when p4type is P4_EXPR */ #endif @@ -17212,20 +17626,21 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INT32 (-3) /* P4 is a 32-bit signed integer */ #define P4_SUBPROGRAM (-4) /* P4 is a pointer to a SubProgram structure */ #define P4_TABLE (-5) /* P4 is a pointer to a Table structure */ +#define P4_INDEX (-6) /* P4 is a pointer to an Index structure */ /* Above do not own any resources. Must free those below */ -#define P4_FREE_IF_LE (-6) -#define P4_DYNAMIC (-6) /* Pointer to memory from sqliteMalloc() */ -#define P4_FUNCDEF (-7) /* P4 is a pointer to a FuncDef structure */ -#define P4_KEYINFO (-8) /* P4 is a pointer to a KeyInfo structure */ -#define P4_EXPR (-9) /* P4 is a pointer to an Expr tree */ -#define P4_MEM (-10) /* P4 is a pointer to a Mem* structure */ -#define P4_VTAB (-11) /* P4 is a pointer to an sqlite3_vtab structure */ -#define P4_REAL (-12) /* P4 is a 64-bit floating point value */ -#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ -#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ -#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ -#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ -#define P4_SUBRTNSIG (-17) /* P4 is a SubrtnSig pointer */ +#define P4_FREE_IF_LE (-7) +#define P4_DYNAMIC (-7) /* Pointer to memory from sqliteMalloc() */ +#define P4_FUNCDEF (-8) /* P4 is a pointer to a FuncDef structure */ +#define P4_KEYINFO (-9) /* P4 is a pointer to a KeyInfo structure */ +#define P4_EXPR (-10) /* P4 is a pointer to an Expr tree */ +#define P4_MEM (-11) /* P4 is a pointer to a Mem* structure */ +#define P4_VTAB (-12) /* P4 is a pointer to an sqlite3_vtab structure */ +#define P4_REAL (-13) /* P4 is a 64-bit floating point value */ +#define P4_INT64 (-14) /* P4 is a 64-bit signed integer */ +#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +#define P4_FUNCCTX (-16) /* P4 is a pointer to an sqlite3_context object */ +#define P4_TABLEREF (-17) /* Like P4_TABLE, but reference counted */ +#define P4_SUBRTNSIG (-18) /* P4 is a SubrtnSig pointer */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -17314,10 +17729,10 @@ typedef struct VdbeOpList VdbeOpList; #define OP_And 44 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ #define OP_IdxLT 45 /* jump, synopsis: key=r[P3@P4] */ #define OP_IdxGE 46 /* jump, synopsis: key=r[P3@P4] */ -#define OP_RowSetRead 47 /* jump, synopsis: r[P3]=rowset(P1) */ -#define OP_RowSetTest 48 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 49 /* jump0 */ -#define OP_FkIfZero 50 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_IFindKey 47 /* jump */ +#define OP_RowSetRead 48 /* jump, synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 49 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 50 /* jump0 */ #define OP_IsNull 51 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 52 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ #define OP_Ne 53 /* jump, same as TK_NE, synopsis: IF r[P3]!=r[P1] */ @@ -17327,49 +17742,49 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Lt 57 /* jump, same as TK_LT, synopsis: IF r[P3]=r[P1] */ #define OP_ElseEq 59 /* jump, same as TK_ESCAPE */ -#define OP_IfPos 60 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ -#define OP_IfNotZero 61 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ -#define OP_DecrJumpZero 62 /* jump, synopsis: if (--r[P1])==0 goto P2 */ -#define OP_IncrVacuum 63 /* jump */ -#define OP_VNext 64 /* jump */ -#define OP_Filter 65 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ -#define OP_PureFunc 66 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Function 67 /* synopsis: r[P3]=func(r[P2@NP]) */ -#define OP_Return 68 -#define OP_EndCoroutine 69 -#define OP_HaltIfNull 70 /* synopsis: if r[P3]=null halt */ -#define OP_Halt 71 -#define OP_Integer 72 /* synopsis: r[P2]=P1 */ -#define OP_Int64 73 /* synopsis: r[P2]=P4 */ -#define OP_String 74 /* synopsis: r[P2]='P4' (len=P1) */ -#define OP_BeginSubrtn 75 /* synopsis: r[P2]=NULL */ -#define OP_Null 76 /* synopsis: r[P2..P3]=NULL */ -#define OP_SoftNull 77 /* synopsis: r[P1]=NULL */ -#define OP_Blob 78 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 79 /* synopsis: r[P2]=parameter(P1) */ -#define OP_Move 80 /* synopsis: r[P2@P3]=r[P1@P3] */ -#define OP_Copy 81 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ -#define OP_SCopy 82 /* synopsis: r[P2]=r[P1] */ -#define OP_IntCopy 83 /* synopsis: r[P2]=r[P1] */ -#define OP_FkCheck 84 -#define OP_ResultRow 85 /* synopsis: output=r[P1@P2] */ -#define OP_CollSeq 86 -#define OP_AddImm 87 /* synopsis: r[P1]=r[P1]+P2 */ -#define OP_RealAffinity 88 -#define OP_Cast 89 /* synopsis: affinity(r[P1]) */ -#define OP_Permutation 90 -#define OP_Compare 91 /* synopsis: r[P1@P3] <-> r[P2@P3] */ -#define OP_IsTrue 92 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ -#define OP_ZeroOrNull 93 /* synopsis: r[P2] = 0 OR NULL */ -#define OP_Offset 94 /* synopsis: r[P3] = sqlite_offset(P1) */ -#define OP_Column 95 /* synopsis: r[P3]=PX cursor P1 column P2 */ -#define OP_TypeCheck 96 /* synopsis: typecheck(r[P1@P2]) */ -#define OP_Affinity 97 /* synopsis: affinity(r[P1@P2]) */ -#define OP_MakeRecord 98 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_Count 99 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 100 -#define OP_SetCookie 101 -#define OP_ReopenIdx 102 /* synopsis: root=P2 iDb=P3 */ +#define OP_FkIfZero 60 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_IfPos 61 /* jump, synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */ +#define OP_IfNotZero 62 /* jump, synopsis: if r[P1]!=0 then r[P1]--, goto P2 */ +#define OP_DecrJumpZero 63 /* jump, synopsis: if (--r[P1])==0 goto P2 */ +#define OP_IncrVacuum 64 /* jump */ +#define OP_VNext 65 /* jump */ +#define OP_Filter 66 /* jump, synopsis: if key(P3@P4) not in filter(P1) goto P2 */ +#define OP_PureFunc 67 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Function 68 /* synopsis: r[P3]=func(r[P2@NP]) */ +#define OP_Return 69 +#define OP_EndCoroutine 70 +#define OP_HaltIfNull 71 /* synopsis: if r[P3]=null halt */ +#define OP_Halt 72 +#define OP_Integer 73 /* synopsis: r[P2]=P1 */ +#define OP_Int64 74 /* synopsis: r[P2]=P4 */ +#define OP_String 75 /* synopsis: r[P2]='P4' (len=P1) */ +#define OP_BeginSubrtn 76 /* synopsis: r[P2]=NULL */ +#define OP_Null 77 /* synopsis: r[P2..P3]=NULL */ +#define OP_SoftNull 78 /* synopsis: r[P1]=NULL */ +#define OP_Blob 79 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 80 /* synopsis: r[P2]=parameter(P1) */ +#define OP_Move 81 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 82 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 83 /* synopsis: r[P2]=r[P1] */ +#define OP_IntCopy 84 /* synopsis: r[P2]=r[P1] */ +#define OP_FkCheck 85 +#define OP_ResultRow 86 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 87 +#define OP_AddImm 88 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_RealAffinity 89 +#define OP_Cast 90 /* synopsis: affinity(r[P1]) */ +#define OP_Permutation 91 +#define OP_Compare 92 /* synopsis: r[P1@P3] <-> r[P2@P3] */ +#define OP_IsTrue 93 /* synopsis: r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4 */ +#define OP_ZeroOrNull 94 /* synopsis: r[P2] = 0 OR NULL */ +#define OP_Offset 95 /* synopsis: r[P3] = sqlite_offset(P1) */ +#define OP_Column 96 /* synopsis: r[P3]=PX cursor P1 column P2 */ +#define OP_TypeCheck 97 /* synopsis: typecheck(r[P1@P2]) */ +#define OP_Affinity 98 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 99 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 100 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 101 +#define OP_SetCookie 102 #define OP_BitAnd 103 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ #define OP_BitOr 104 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ #define OP_ShiftLeft 105 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 162 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 163 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 164 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 165 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 166 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 167 -#define OP_CursorLock 168 -#define OP_CursorUnlock 169 -#define OP_TableLock 170 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 171 -#define OP_VCreate 172 -#define OP_VDestroy 173 -#define OP_VOpen 174 -#define OP_VCheck 175 -#define OP_VInitIn 176 /* synopsis: r[P2]=ValueList(P1,P3) */ -#define OP_VColumn 177 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 178 -#define OP_Pagecount 179 -#define OP_MaxPgcnt 180 -#define OP_ClrSubtype 181 /* synopsis: r[P1].subtype = 0 */ -#define OP_GetSubtype 182 /* synopsis: r[P2] = r[P1].subtype */ -#define OP_SetSubtype 183 /* synopsis: r[P2].subtype = r[P1] */ -#define OP_FilterAdd 184 /* synopsis: filter(P1) += key(P3@P4) */ -#define OP_Trace 185 -#define OP_CursorHint 186 -#define OP_ReleaseReg 187 /* synopsis: release r[P1@P2] mask P3 */ -#define OP_Noop 188 -#define OP_Explain 189 -#define OP_Abortable 190 +#define OP_DropIndex 155 +#define OP_DropTrigger 156 +#define OP_IntegrityCk 157 +#define OP_RowSetAdd 158 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 159 +#define OP_FkCounter 160 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 161 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 162 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 163 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 164 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 165 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 166 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 167 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 168 +#define OP_CursorLock 169 +#define OP_CursorUnlock 170 +#define OP_TableLock 171 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 172 +#define OP_VCreate 173 +#define OP_VDestroy 174 +#define OP_VOpen 175 +#define OP_VCheck 176 +#define OP_VInitIn 177 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 178 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 179 +#define OP_Pagecount 180 +#define OP_MaxPgcnt 181 +#define OP_ClrSubtype 182 /* synopsis: r[P1].subtype = 0 */ +#define OP_GetSubtype 183 /* synopsis: r[P2] = r[P1].subtype */ +#define OP_SetSubtype 184 /* synopsis: r[P2].subtype = r[P1] */ +#define OP_FilterAdd 185 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 186 +#define OP_CursorHint 187 +#define OP_ReleaseReg 188 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 189 +#define OP_Explain 190 +#define OP_Abortable 191 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -17477,25 +17893,26 @@ typedef struct VdbeOpList VdbeOpList; /* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\ /* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ /* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x01, 0x41,\ -/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x41, 0x23,\ -/* 48 */ 0x0b, 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\ -/* 56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01,\ -/* 64 */ 0x41, 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00,\ -/* 72 */ 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10,\ -/* 80 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02,\ -/* 88 */ 0x02, 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40,\ -/* 96 */ 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26,\ +/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x41, 0x09,\ +/* 48 */ 0x23, 0x0b, 0x81, 0x03, 0x03, 0x0b, 0x0b, 0x0b,\ +/* 56 */ 0x0b, 0x0b, 0x0b, 0x01, 0x01, 0x03, 0x03, 0x03,\ +/* 64 */ 0x01, 0x41, 0x01, 0x00, 0x00, 0x02, 0x02, 0x08,\ +/* 72 */ 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10,\ +/* 80 */ 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00,\ +/* 88 */ 0x02, 0x02, 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20,\ +/* 96 */ 0x40, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x26,\ /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ -/* 112 */ 0x26, 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40,\ -/* 120 */ 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10,\ -/* 128 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,\ -/* 136 */ 0x50, 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50,\ -/* 144 */ 0x40, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\ -/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00,\ -/* 160 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10,\ -/* 176 */ 0x50, 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12,\ -/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} +/* 112 */ 0x26, 0x40, 0x40, 0x12, 0x00, 0x40, 0x10, 0x40,\ +/* 120 */ 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x40,\ +/* 128 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,\ +/* 136 */ 0x00, 0x50, 0x00, 0x40, 0x04, 0x04, 0x00, 0x40,\ +/* 144 */ 0x50, 0x40, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,\ +/* 152 */ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x10,\ +/* 160 */ 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,\ +/* 176 */ 0x10, 0x50, 0x40, 0x00, 0x10, 0x10, 0x02, 0x12,\ +/* 184 */ 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +} /* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -17503,7 +17920,7 @@ typedef struct VdbeOpList VdbeOpList; ** generated this include file strives to group all JUMP opcodes ** together near the beginning of the list. */ -#define SQLITE_MX_JUMP_OPCODE 65 /* Maximum JUMP opcode */ +#define SQLITE_MX_JUMP_OPCODE 66 /* Maximum JUMP opcode */ /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -17512,7 +17929,7 @@ typedef struct VdbeOpList VdbeOpList; ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ -#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ +#define SQLITE_PREPARE_MASK 0x3f /* Mask of public flags */ /* ** Prototypes for the VDBE interface. See comments on the implementation @@ -17794,10 +18211,10 @@ struct PgHdr { PCache *pCache; /* PRIVATE: Cache that owns this page */ PgHdr *pDirty; /* Transient list of dirty sorted by pgno */ Pager *pPager; /* The pager this page is part of */ - Pgno pgno; /* Page number for this page */ #ifdef SQLITE_CHECK_PAGES - u32 pageHash; /* Hash of page content */ + u64 pageHash; /* Hash of page content */ #endif + Pgno pgno; /* Page number for this page */ u16 flags; /* PGHDR flags defined below */ /********************************************************************** @@ -18137,7 +18554,7 @@ struct Schema { ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ -#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1) +#define SQLITE_N_LIMIT (SQLITE_LIMIT_PARSER_DEPTH+1) /* ** Lookaside malloc is a set of fixed-size buffers that can be used @@ -18291,6 +18708,7 @@ struct sqlite3 { u8 noSharedCache; /* True if no shared-cache backends */ u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */ u8 eOpenState; /* Current condition of the connection */ + u8 nFpDigit; /* Significant digits to keep on double->text */ int nextPagesize; /* Pagesize after VACUUM if >0 */ i64 nChange; /* Value returned by sqlite3_changes() */ i64 nTotalChange; /* Value returned by sqlite3_total_changes() */ @@ -20185,19 +20603,6 @@ struct Upsert { /* ** An instance of the following structure contains all information ** needed to generate code for a single SELECT statement. -** -** See the header comment on the computeLimitRegisters() routine for a -** detailed description of the meaning of the iLimit and iOffset fields. -** -** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes. -** These addresses must be stored so that we can go back and fill in -** the P4_KEYINFO and P2 parameters later. Neither the KeyInfo nor -** the number of columns in P2 can be computed at the same time -** as the OP_OpenEphm instruction is coded because not -** enough information about the compound query is known at that point. -** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences -** for the result set. The KeyInfo for addrOpenEphm[2] contains collating -** sequences for the ORDER BY clause. */ struct Select { u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ @@ -20205,7 +20610,6 @@ struct Select { u32 selFlags; /* Various SF_* values */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ u32 selId; /* Unique identifier number for this SELECT */ - int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */ ExprList *pEList; /* The fields of the result */ SrcList *pSrc; /* The FROM clause */ Expr *pWhere; /* The WHERE clause */ @@ -20237,7 +20641,7 @@ struct Select { #define SF_Resolved 0x0000004 /* Identifiers have been resolved */ #define SF_Aggregate 0x0000008 /* Contains agg functions or a GROUP BY */ #define SF_HasAgg 0x0000010 /* Contains aggregate functions */ -#define SF_UsesEphemeral 0x0000020 /* Uses the OpenEphemeral opcode */ +#define SF_ClonedRhsIn 0x0000020 /* Cloned RHS of an IN operator */ #define SF_Expanded 0x0000040 /* sqlite3SelectExpand() called on this */ #define SF_HasTypeInfo 0x0000080 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0000100 /* Part of a compound query */ @@ -20247,14 +20651,14 @@ struct Select { #define SF_MinMaxAgg 0x0001000 /* Aggregate containing min() or max() */ #define SF_Recursive 0x0002000 /* The recursive part of a recursive CTE */ #define SF_FixedLimit 0x0004000 /* nSelectRow set by a constant LIMIT */ -#define SF_MaybeConvert 0x0008000 /* Need convertCompoundSelectToSubquery() */ +/* 0x0008000 // available for reuse */ #define SF_Converted 0x0010000 /* By convertCompoundSelectToSubquery() */ #define SF_IncludeHidden 0x0020000 /* Include hidden columns in output */ #define SF_ComplexResult 0x0040000 /* Result contains subquery or function */ #define SF_WhereBegin 0x0080000 /* Really a WhereBegin() call. Debug Only */ #define SF_WinRewrite 0x0100000 /* Window function rewrite accomplished */ #define SF_View 0x0200000 /* SELECT statement is a view */ -#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ +/* 0x0400000 // available for reuse */ #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ #define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ @@ -20274,11 +20678,6 @@ struct Select { ** by one of the following macros. The "SRT" prefix means "SELECT Result ** Type". ** -** SRT_Union Store results as a key in a temporary index -** identified by pDest->iSDParm. -** -** SRT_Except Remove results from the temporary index pDest->iSDParm. -** ** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result ** set is not empty. ** @@ -20342,30 +20741,28 @@ struct Select { ** table. (pDest->iSDParm) is the number of key columns in ** each index record in this case. */ -#define SRT_Union 1 /* Store result as keys in an index */ -#define SRT_Except 2 /* Remove result from a UNION index */ -#define SRT_Exists 3 /* Store 1 if the result is not empty */ -#define SRT_Discard 4 /* Do not save the results anywhere */ -#define SRT_DistFifo 5 /* Like SRT_Fifo, but unique results only */ -#define SRT_DistQueue 6 /* Like SRT_Queue, but unique results only */ +#define SRT_Exists 1 /* Store 1 if the result is not empty */ +#define SRT_Discard 2 /* Do not save the results anywhere */ +#define SRT_DistFifo 3 /* Like SRT_Fifo, but unique results only */ +#define SRT_DistQueue 4 /* Like SRT_Queue, but unique results only */ /* The DISTINCT clause is ignored for all of the above. Not that ** IgnorableDistinct() implies IgnorableOrderby() */ #define IgnorableDistinct(X) ((X->eDest)<=SRT_DistQueue) -#define SRT_Queue 7 /* Store result in an queue */ -#define SRT_Fifo 8 /* Store result as data with an automatic rowid */ +#define SRT_Queue 5 /* Store result in an queue */ +#define SRT_Fifo 6 /* Store result as data with an automatic rowid */ /* The ORDER BY clause is ignored for all of the above */ #define IgnorableOrderby(X) ((X->eDest)<=SRT_Fifo) -#define SRT_Output 9 /* Output each row of result */ -#define SRT_Mem 10 /* Store result in a memory cell */ -#define SRT_Set 11 /* Store results as keys in an index */ -#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */ -#define SRT_Coroutine 13 /* Generate a single row of result */ -#define SRT_Table 14 /* Store result as data with an automatic rowid */ -#define SRT_Upfrom 15 /* Store result as data with rowid */ +#define SRT_Output 7 /* Output each row of result */ +#define SRT_Mem 8 /* Store result in a memory cell */ +#define SRT_Set 9 /* Store results as keys in an index */ +#define SRT_EphemTab 10 /* Create transient tab and store like SRT_Table */ +#define SRT_Coroutine 11 /* Generate a single row of result */ +#define SRT_Table 12 /* Store result as data with an automatic rowid */ +#define SRT_Upfrom 13 /* Store result as data with rowid */ /* ** An instance of this object describes where to put of the results of @@ -20501,17 +20898,12 @@ struct Parse { u8 nested; /* Number of nested calls to the parser/code generator */ u8 nTempReg; /* Number of temporary registers in aTempReg[] */ u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ - u8 mayAbort; /* True if statement may throw an ABORT exception */ - u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ - u8 bHasExists; /* Has a correlated "EXISTS (SELECT ....)" expression */ u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ - u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ - u8 disableTriggers; /* True to disable triggers */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif @@ -20520,10 +20912,15 @@ struct Parse { u8 isCreate; /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER) ** and ALTER TABLE ADD COLUMN. */ #endif - bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */ - bft bHasWith :1; /* True if statement contains WITH */ - bft okConstFactor :1; /* OK to factor out constants */ - bft checkSchema :1; /* Causes schema cookie check after an error */ + bft disableTriggers:1; /* True to disable triggers */ + bft mayAbort :1; /* True if statement may throw an ABORT exception */ + bft hasCompound :1; /* Need to invoke convertCompoundSelectToSubquery() */ + bft bReturning :1; /* Coding a RETURNING trigger */ + bft bHasExists :1; /* Has a correlated "EXISTS (SELECT ....)" expression */ + bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */ + bft bHasWith :1; /* True if statement contains WITH */ + bft okConstFactor:1; /* OK to factor out constants */ + bft checkSchema :1; /* Causes schema cookie check after an error */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ @@ -20532,6 +20929,7 @@ struct Parse { int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table associated with an index on expr, or negative ** of the base register during check-constraint eval */ + int nNestSel; /* Number of nested SELECT statements and/or VIEWs */ int nLabel; /* The *negative* of the number of labels used */ int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ @@ -20752,19 +21150,19 @@ struct Trigger { ** orconf -> stores the ON CONFLICT algorithm ** pSelect -> The content to be inserted - either a SELECT statement or ** a VALUES clause. -** zTarget -> Dequoted name of the table to insert into. +** pSrc -> Table to insert into. ** pIdList -> If this is an INSERT INTO ... () VALUES ... ** statement, then this stores the column-names to be ** inserted into. ** pUpsert -> The ON CONFLICT clauses for an Upsert ** ** (op == TK_DELETE) -** zTarget -> Dequoted name of the table to delete from. +** pSrc -> Table to delete from ** pWhere -> The WHERE clause of the DELETE statement if one is specified. ** Otherwise NULL. ** ** (op == TK_UPDATE) -** zTarget -> Dequoted name of the table to update. +** pSrc -> Table to update, followed by any FROM clause tables. ** pWhere -> The WHERE clause of the UPDATE statement if one is specified. ** Otherwise NULL. ** pExprList -> A list of the columns to update and the expressions to update @@ -20784,8 +21182,7 @@ struct TriggerStep { u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ - char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ - SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */ + SrcList *pSrc; /* Table to insert/update/delete */ Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */ IdList *pIdList; /* Column names for INSERT */ @@ -20868,10 +21265,11 @@ typedef struct { /* ** Allowed values for mInitFlags */ -#define INITFLAG_AlterMask 0x0003 /* Types of ALTER */ +#define INITFLAG_AlterMask 0x0007 /* Types of ALTER */ #define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */ #define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */ #define INITFLAG_AlterAdd 0x0003 /* Reparse after an ADD COLUMN */ +#define INITFLAG_AlterDropCons 0x0004 /* Reparse after an ADD COLUMN */ /* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled ** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning @@ -21001,6 +21399,7 @@ struct Walker { NameContext *pNC; /* Naming context */ int n; /* A counter */ int iCur; /* A cursor number */ + int sz; /* String literal length */ SrcList *pSrcList; /* FROM clause */ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */ struct RefSrcList *pRefSrcList; /* sqlite3ReferencesSrcList() */ @@ -21405,7 +21804,20 @@ SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3*,int*); SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void); SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void); -#if defined(SQLITE_ENABLE_MULTITHREADED_CHECKS) && !defined(SQLITE_MUTEX_OMIT) + +/* The SQLITE_THREAD_MISUSE_WARNINGS compile-time option used to be called +** SQLITE_ENABLE_MULTITHREADED_CHECKS. Keep that older macro for backwards +** compatibility, at least for a while... */ +#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS +# define SQLITE_THREAD_MISUSE_WARNINGS 1 +#endif + +/* SQLITE_THREAD_MISUSE_ABORT implies SQLITE_THREAD_MISUSE_WARNINGS */ +#ifdef SQLITE_THREAD_MISUSE_ABORT +# define SQLITE_THREAD_MISUSE_WARNINGS 1 +#endif + +#if defined(SQLITE_THREAD_MISUSE_WARNINGS) && !defined(SQLITE_MUTEX_OMIT) SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*); #else # define sqlite3MutexWarnOnContention(x) @@ -21434,17 +21846,22 @@ struct PrintfArguments { sqlite3_value **apArg; /* The argument values */ }; +/* +** Maxium number of base-10 digits in an unsigned 64-bit integer +*/ +#define SQLITE_U64_DIGITS 20 + /* ** An instance of this object receives the decoding of a floating point ** value into an approximate decimal representation. */ struct FpDecode { - char sign; /* '+' or '-' */ - char isSpecial; /* 1: Infinity 2: NaN */ - int n; /* Significant digits in the decode */ - int iDP; /* Location of the decimal point */ - char *z; /* Start of significant digits */ - char zBuf[24]; /* Storage for significant digits */ + int n; /* Significant digits in the decode */ + int iDP; /* Location of the decimal point */ + char *z; /* Start of significant digits */ + char zBuf[SQLITE_U64_DIGITS+1]; /* Storage for significant digits */ + char sign; /* '+' or '-' */ + char isSpecial; /* 1: Infinity 2: NaN */ }; SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int); @@ -21533,6 +21950,7 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); #endif SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*); +SQLITE_PRIVATE Expr *sqlite3ExprInt32(sqlite3*,int); SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); @@ -21784,6 +22202,7 @@ SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*, Parse*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); +SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr*); SQLITE_PRIVATE int sqlite3IsRowid(const char*); SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); SQLITE_PRIVATE void sqlite3GenerateRowDelete( @@ -21852,17 +22271,16 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, i SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*, const char*,const char*); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(Parse*,Token*, IdList*, +SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(Parse*,SrcList*, IdList*, Select*,u8,Upsert*, const char*,const char*); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(Parse*,Token*,SrcList*,ExprList*, +SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(Parse*,SrcList*,SrcList*,ExprList*, Expr*, u8, const char*,const char*); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(Parse*,Token*, Expr*, +SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(Parse*,SrcList*, Expr*, const char*,const char*); SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); -SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*); # define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) # define sqlite3IsToplevel(p) ((p)->pToplevel==0) #else @@ -21876,7 +22294,6 @@ SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc(Parse*, TriggerStep*); # define sqlite3ParseToplevel(p) p # define sqlite3IsToplevel(p) 1 # define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0 -# define sqlite3TriggerStepSrc(A,B) 0 #endif SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); @@ -21909,7 +22326,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); SQLITE_PRIVATE i64 sqlite3RealToI64(double); SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); +SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); SQLITE_PRIVATE int sqlite3Atoi(const char*); @@ -22053,10 +22470,21 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); +SQLITE_PRIVATE void sqlite3AlterDropConstraint(Parse*,SrcList*,Token*,Token*); +SQLITE_PRIVATE void sqlite3AlterAddConstraint( + Parse *pParse, /* Parse context */ + SrcList *pSrc, /* Table to add constraint to */ + Token *pFirst, /* First token of new constraint */ + Token *pName, /* Name of new constraint. NULL if name omitted. */ + const char *zExpr, /* Text of CHECK expression */ + int nExpr, /* Size of pExpr in bytes */ + Expr *pExpr /* The parsed CHECK expression */ +); +SQLITE_PRIVATE void sqlite3AlterSetNotNull(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE i64 sqlite3GetToken(const unsigned char *, int *); SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); -SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int, int); SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, SrcItem*); @@ -22129,6 +22557,7 @@ SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64); +SQLITE_PRIVATE int sqlite3StrAccumEnlargeIfNeeded(StrAccum*, i64); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8); SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); @@ -23356,6 +23785,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_STMTJRNL_SPILL "STMTJRNL_SPILL=" CTIMEOPT_VAL(SQLITE_STMTJRNL_SPILL), #endif +#ifdef SQLITE_STRICT_SUBTYPE + "STRICT_SUBTYPE", +#endif #ifdef SQLITE_SUBSTR_COMPATIBILITY "SUBSTR_COMPATIBILITY", #endif @@ -24471,6 +24903,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem*, Mem*); SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemSetStr(Mem*, const char*, i64, u8, void(*)(void*)); +SQLITE_PRIVATE int sqlite3VdbeMemSetText(Mem*, const char*, i64, void(*)(void*)); SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64); #ifdef SQLITE_OMIT_FLOATING_POINT # define sqlite3VdbeMemSetDouble sqlite3VdbeMemSetInt64 @@ -24489,13 +24922,14 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int); SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*); #endif SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*); -SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*); +SQLITE_PRIVATE int sqlite3VdbeMemZeroTerminateIfAble(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8); SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem*); SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*); SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*); +SQLITE_PRIVATE int sqlite3MemRealValueRC(Mem*, double*); SQLITE_PRIVATE int sqlite3VdbeBooleanValue(Mem*, int ifNull); SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*); @@ -24526,6 +24960,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook( Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int,int); #endif SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); +SQLITE_PRIVATE int sqlite3VdbeFindIndexKey(BtCursor*, Index*, UnpackedRecord*, int*, int); SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *); SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *); @@ -25455,7 +25890,7 @@ static int parseDateOrTime( return 0; }else if( sqlite3StrICmp(zDate,"now")==0 && sqlite3NotPureFunc(context) ){ return setDateTimeToCurrent(context, p); - }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ + }else if( sqlite3AtoF(zDate, &r)>0 ){ setRawDateNumber(p, r); return 0; }else if( (sqlite3StrICmp(zDate,"subsec")==0 @@ -25901,7 +26336,7 @@ static int parseModifier( ** date is already on the appropriate weekday, this is a no-op. */ if( sqlite3_strnicmp(z, "weekday ", 8)==0 - && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8)>0 + && sqlite3AtoF(&z[8], &r)>0 && r>=0.0 && r<7.0 && (n=(int)r)==r ){ sqlite3_int64 Z; computeYMD_HMS(p); @@ -25972,9 +26407,11 @@ static int parseModifier( case '8': case '9': { double rRounder; - int i; + int i, rx; int Y,M,D,h,m,x; const char *z2 = z; + char *zCopy; + sqlite3 *db = sqlite3_context_db_handle(pCtx); char z0 = z[0]; for(n=1; z[n]; n++){ if( z[n]==':' ) break; @@ -25984,7 +26421,11 @@ static int parseModifier( if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break; } } - if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ + zCopy = sqlite3DbStrNDup(db, z, n); + if( zCopy==0 ) break; + rx = sqlite3AtoF(zCopy, &r)<=0; + sqlite3DbFree(db, zCopy); + if( rx ){ assert( rc==1 ); break; } @@ -26804,7 +27245,7 @@ static void datedebugFunc( char *zJson; zJson = sqlite3_mprintf( "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," - "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," + "s:%.3f,validJD:%d,validYMD:%d,validHMS:%d," "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," "isUtc:%d,isLocal:%d}", x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, @@ -27151,7 +27592,7 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); - *pTimeOut = (sqlite3_int64)(r*86400000.0); + *pTimeOut = sqlite3RealToI64(r*86400000.0); } return rc; } @@ -29583,23 +30024,28 @@ static SQLITE_WSD int mutexIsInit = 0; #ifndef SQLITE_MUTEX_OMIT -#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS +#ifdef SQLITE_THREAD_MISUSE_WARNINGS /* -** This block (enclosed by SQLITE_ENABLE_MULTITHREADED_CHECKS) contains +** This block (enclosed by SQLITE_THREAD_MISUSE_WARNINGS) contains ** the implementation of a wrapper around the system default mutex ** implementation (sqlite3DefaultMutex()). ** ** Most calls are passed directly through to the underlying default ** mutex implementation. Except, if a mutex is configured by calling ** sqlite3MutexWarnOnContention() on it, then if contention is ever -** encountered within xMutexEnter() a warning is emitted via sqlite3_log(). +** encountered within xMutexEnter() then a warning is emitted via +** sqlite3_log(). Furthermore, if SQLITE_THREAD_MISUSE_ABORT is +** defined then abort() is called after the sqlite3_log() warning. ** -** This type of mutex is used as the database handle mutex when testing -** apps that usually use SQLITE_CONFIG_MULTITHREAD mode. +** This type of mutex is used on the database handle mutex when testing +** apps that usually use SQLITE_CONFIG_MULTITHREAD mode. A failure +** indicates that the app ought to be using SQLITE_OPEN_FULLMUTEX or +** similar because it is trying to use the same database handle from +** two different connections at the same time. */ /* -** Type for all mutexes used when SQLITE_ENABLE_MULTITHREADED_CHECKS +** Type for all mutexes used when SQLITE_THREAD_MISUSE_WARNINGS ** is defined. Variable CheckMutex.mutex is a pointer to the real mutex ** allocated by the system mutex implementation. Variable iType is usually set ** to the type of mutex requested - SQLITE_MUTEX_RECURSIVE, SQLITE_MUTEX_FAST @@ -29635,11 +30081,12 @@ static int checkMutexNotheld(sqlite3_mutex *p){ */ static int checkMutexInit(void){ pGlobalMutexMethods = sqlite3DefaultMutex(); - return SQLITE_OK; + return pGlobalMutexMethods->xMutexInit(); } static int checkMutexEnd(void){ + int rc = pGlobalMutexMethods->xMutexEnd(); pGlobalMutexMethods = 0; - return SQLITE_OK; + return rc; } /* @@ -29716,6 +30163,9 @@ static void checkMutexEnter(sqlite3_mutex *p){ sqlite3_log(SQLITE_MISUSE, "illegal multi-threaded access to database connection" ); +#if SQLITE_THREAD_MISUSE_ABORT + abort(); +#endif } pGlobalMutexMethods->xMutexEnter(pCheck->mutex); } @@ -29767,7 +30217,7 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex *p){ pCheck->iType = SQLITE_MUTEX_WARNONCONTENTION; } } -#endif /* ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS */ +#endif /* ifdef SQLITE_THREAD_MISUSE_WARNINGS */ /* ** Initialize the mutex system. @@ -29784,7 +30234,7 @@ SQLITE_PRIVATE int sqlite3MutexInit(void){ sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; if( sqlite3GlobalConfig.bCoreMutex ){ -#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS +#ifdef SQLITE_THREAD_MISUSE_WARNINGS pFrom = multiThreadedCheckMutex(); #else pFrom = sqlite3DefaultMutex(); @@ -30632,14 +31082,6 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ # define SQLITE_OS_WINCE 0 #endif -/* -** Determine if we are dealing with WinRT, which provides only a subset of -** the full Win32 API. -*/ -#if !defined(SQLITE_OS_WINRT) -# define SQLITE_OS_WINRT 0 -#endif - /* ** For WinCE, some API function parameters do not appear to be declared as ** volatile. @@ -30654,7 +31096,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ ** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() ** functions are not available (e.g. those not using MSVC, Cygwin, etc). */ -#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && \ SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) # define SQLITE_OS_WIN_THREADS 1 #else @@ -30771,11 +31213,7 @@ static int winMutexInit(void){ if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ int i; for(i=0; itrace = 1; #endif #endif -#if SQLITE_OS_WINRT - InitializeCriticalSectionEx(&p->mutex, 0, 0); -#else InitializeCriticalSection(&p->mutex); -#endif } break; } @@ -32110,7 +32544,7 @@ static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ sqlite3StrAccumSetError(pAccum, SQLITE_TOOBIG); return 0; } - z = sqlite3DbMallocRaw(pAccum->db, n); + z = sqlite3_malloc(n); if( z==0 ){ sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); } @@ -32441,9 +32875,11 @@ SQLITE_API void sqlite3_str_vappendf( }while( longvalue>0 ); } length = (int)(&zOut[nOut-1]-bufpt); - while( precision>length ){ - *(--bufpt) = '0'; /* Zero pad */ - length++; + if( precision>length ){ /* zero pad */ + int nn = precision-length; + bufpt -= nn; + memset(bufpt,'0',nn); + length = precision; } if( cThousand ){ int nn = (length - 1)/3; /* Number of "," to insert */ @@ -32474,6 +32910,7 @@ SQLITE_API void sqlite3_str_vappendf( FpDecode s; int iRound; int j; + i64 szBufNeeded; /* Size needed to hold the output */ if( bArgList ){ realvalue = getDoubleArg(pArgList); @@ -32494,7 +32931,7 @@ SQLITE_API void sqlite3_str_vappendf( }else{ iRound = precision+1; } - sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); + sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 20 : 16); if( s.isSpecial ){ if( s.isSpecial==2 ){ bufpt = flag_zeropad ? "null" : "NaN"; @@ -32562,17 +32999,31 @@ SQLITE_API void sqlite3_str_vappendf( }else{ e2 = s.iDP - 1; } - bufpt = buf; - { - i64 szBufNeeded; /* Size of a temporary buffer needed */ - szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; - if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; - if( szBufNeeded > etBUFSIZE ){ - bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); - if( bufpt==0 ) return; + + szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+10; + if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; + if( szBufNeeded + pAccum->nChar >= pAccum->nAlloc ){ + if( pAccum->mxAlloc==0 && pAccum->accError==0 ){ + /* Unable to allocate space in pAccum, perhaps because it + ** is coming from sqlite3_snprintf() or similar. We'll have + ** to render into temporary space and the memcpy() it over. */ + bufpt = sqlite3_malloc(szBufNeeded); + if( bufpt==0 ){ + sqlite3StrAccumSetError(pAccum, SQLITE_NOMEM); + return; + } + zExtra = bufpt; + }else if( sqlite3StrAccumEnlarge(pAccum, szBufNeeded)zText + pAccum->nChar; } + }else{ + bufpt = pAccum->zText + pAccum->nChar; } zOut = bufpt; + flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; /* The sign in front of the number */ if( prefix ){ @@ -32580,12 +33031,24 @@ SQLITE_API void sqlite3_str_vappendf( } /* Digits prior to the decimal point */ j = 0; + assert( s.n>0 ); if( e2<0 ){ *(bufpt++) = '0'; - }else{ + }else if( cThousand ){ for(; e2>=0; e2--){ *(bufpt++) = j1 ) *(bufpt++) = ','; + if( (e2%3)==0 && e2>1 ) *(bufpt++) = ','; + } + }else{ + j = e2+1; + if( j>s.n ) j = s.n; + memcpy(bufpt, s.z, j); + bufpt += j; + e2 -= j; + if( e2>=0 ){ + memset(bufpt, '0', e2+1); + bufpt += e2+1; + e2 = -1; } } /* The decimal point */ @@ -32594,12 +33057,26 @@ SQLITE_API void sqlite3_str_vappendf( } /* "0" digits after the decimal point but before the first ** significant digit of the number */ - for(e2++; e2<0 && precision>0; precision--, e2++){ - *(bufpt++) = '0'; + if( e2<(-1) && precision>0 ){ + int nn = -1-e2; + if( nn>precision ) nn = precision; + memset(bufpt, '0', nn); + bufpt += nn; + precision -= nn; } /* Significant digits after the decimal point */ - while( (precision--)>0 ){ - *(bufpt++) = j0 ){ + int nn = s.n - j; + if( NEVER(nn>precision) ) nn = precision; + if( nn>0 ){ + memcpy(bufpt, s.z+j, nn); + bufpt += nn; + precision -= nn; + } + if( precision>0 && !flag_rtz ){ + memset(bufpt, '0', precision); + bufpt += precision; + } } /* Remove trailing zeros and the "." if no digits follow the "." */ if( flag_rtz && flag_dp ){ @@ -32629,27 +33106,39 @@ SQLITE_API void sqlite3_str_vappendf( *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ } - *bufpt = 0; - /* The converted number is in buf[] and zero terminated. Output it. - ** Note that the number is in the usual order, not reversed as with - ** integer conversions. */ length = (int)(bufpt-zOut); - bufpt = zOut; - - /* Special case: Add leading zeros if the flag_zeropad flag is - ** set and we are not left justified */ - if( flag_zeropad && !flag_leftjustify && length < width){ - int i; - int nPad = width - length; - for(i=width; i>=nPad; i--){ - bufpt[i] = bufpt[i-nPad]; + assert( length <= szBufNeeded ); + if( lengthnChar += length; + zOut[length] = 0; + continue; + }else{ + /* We were unable to render directly into pAccum because we + ** couldn't allocate sufficient memory. We need to memcpy() + ** the rendering (or some prefix thereof) into the output + ** buffer. */ + bufpt[0] = 0; + bufpt = zExtra; + break; + } } case etSIZE: if( !bArgList ){ @@ -32693,10 +33182,9 @@ SQLITE_API void sqlite3_str_vappendf( i64 nCopyBytes; if( nPrior > precision-1 ) nPrior = precision - 1; nCopyBytes = length*nPrior; - if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){ - sqlite3StrAccumEnlarge(pAccum, nCopyBytes); + if( sqlite3StrAccumEnlargeIfNeeded(pAccum, nCopyBytes) ){ + break; } - if( pAccum->accError ) break; sqlite3_str_append(pAccum, &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes); precision -= nPrior; @@ -32800,8 +33288,8 @@ SQLITE_API void sqlite3_str_vappendf( ** all control characters, and for backslash itself. ** For %#Q, do the same but only if there is at least ** one control character. */ - u32 nBack = 0; - u32 nCtrl = 0; + i64 nBack = 0; + i64 nCtrl = 0; for(k=0; knChar >= p->nAlloc ){ + sqlite3StrAccumEnlarge(p, N); + } + return p->accError; +} + /* ** Append N copies of character c to the given string buffer. */ @@ -33173,6 +33668,14 @@ SQLITE_API int sqlite3_str_length(sqlite3_str *p){ return p ? p->nChar : 0; } +/* Truncate the text of the string to be no more than N bytes. */ +SQLITE_API void sqlite3_str_truncate(sqlite3_str *p, int N){ + if( p!=0 && N>=0 && (u32)NnChar ){ + p->nChar = N; + p->zText[p->nChar] = 0; + } +} + /* Return the current value for p */ SQLITE_API char *sqlite3_str_value(sqlite3_str *p){ if( p==0 || p->nChar==0 ) return 0; @@ -33193,6 +33696,17 @@ SQLITE_API void sqlite3_str_reset(StrAccum *p){ p->zText = 0; } +/* +** Destroy a dynamically allocate sqlite3_str object and all +** of its content, all in one call. +*/ +SQLITE_API void sqlite3_str_free(sqlite3_str *p){ + if( p!=0 && p!=&sqlite3OomStr ){ + sqlite3_str_reset(p); + sqlite3_free(p); + } +} + /* ** Initialize a string accumulator. ** @@ -34806,7 +35320,13 @@ SQLITE_PRIVATE void sqlite3TreeViewTrigger( SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); } SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);} SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); } -SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); } +SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ + TreeView *pView = 0; + sqlite3TreeViewPush(&pView, 0); + sqlite3TreeViewLine(pView, "SRCLIST"); + sqlite3TreeViewSrcList(pView,p); + sqlite3TreeViewPop(&pView); +} SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); } SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); } SQLITE_PRIVATE void sqlite3ShowUpsert(const Upsert *p){ sqlite3TreeViewUpsert(0,p,0); } @@ -35187,6 +35707,7 @@ SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ rc = sqlite3Win32Wait((HANDLE)p->tid); assert( rc!=WAIT_IO_COMPLETION ); bRc = CloseHandle((HANDLE)p->tid); + (void)bRc; /* Prevent warning when assert() is a no-op */ assert( bRc ); } if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; @@ -36326,266 +36847,618 @@ SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ return h; } -/* Double-Double multiplication. (x[0],x[1]) *= (y,yy) -** -** Reference: -** T. J. Dekker, "A Floating-Point Technique for Extending the -** Available Precision". 1971-07-26. +#if !defined(SQLITE_DISABLE_INTRINSIC) \ + && (defined(__GNUC__) || defined(__clang__)) \ + && (defined(__x86_64__) || defined(__aarch64__) || \ + (defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen>32))) +#define SQLITE_USE_UINT128 +#endif + +/* +** Two inputs are multiplied to get a 128-bit result. Write the +** lower 64-bits of the result into *pLo, and return the high-order +** 64 bits. */ -static void dekkerMul2(volatile double *x, double y, double yy){ - /* - ** The "volatile" keywords on parameter x[] and on local variables - ** below are needed force intermediate results to be truncated to - ** binary64 rather than be carried around in an extended-precision - ** format. The truncation is necessary for the Dekker algorithm to - ** work. Intel x86 floating point might omit the truncation without - ** the use of volatile. - */ - volatile double tx, ty, p, q, c, cc; - double hx, hy; - u64 m; - memcpy(&m, (void*)&x[0], 8); - m &= 0xfffffffffc000000LL; - memcpy(&hx, &m, 8); - tx = x[0] - hx; - memcpy(&m, &y, 8); - m &= 0xfffffffffc000000LL; - memcpy(&hy, &m, 8); - ty = y - hy; - p = hx*hy; - q = hx*ty + tx*hy; - c = p+q; - cc = p - c + q + tx*ty; - cc = x[0]*yy + x[1]*y + cc; - x[0] = c + cc; - x[1] = c - x[0]; - x[1] += cc; +static u64 sqlite3Multiply128(u64 a, u64 b, u64 *pLo){ +#if defined(SQLITE_USE_UINT128) + __uint128_t r = (__uint128_t)a * b; + *pLo = (u64)r; + return (u64)(r>>64); +#elif defined(_WIN64) && !defined(SQLITE_DISABLE_INTRINSIC) + *pLo = a*b; + return __umulh(a, b); +#else + u64 a0 = (u32)a; + u64 a1 = a >> 32; + u64 b0 = (u32)b; + u64 b1 = b >> 32; + u64 a0b0 = a0 * b0; + u64 a1b1 = a1 * b1; + u64 a0b1 = a0 * b1; + u64 a1b0 = a1 * b0; + u64 t = (a0b0 >> 32) + (u32)a0b1 + (u32)a1b0; + *pLo = (a0b0 & UINT64_C(0xffffffff)) | (t << 32); + return a1b1 + (a0b1>>32) + (a1b0>>32) + (t>>32); +#endif +} + +/* +** A is an unsigned 96-bit integer formed by (a<<32)+aLo. +** B is an unsigned 64-bit integer. +** +** Compute the upper 96 bits of 160-bit result of A*B. +** +** Write ((A*B)>>64 & 0xffffffff) (the middle 32 bits of A*B) +** into *pLo. Return the upper 64 bits of A*B. +** +** The lower 64 bits of A*B are discarded. +*/ +static u64 sqlite3Multiply160(u64 a, u32 aLo, u64 b, u32 *pLo){ +#if defined(SQLITE_USE_UINT128) + __uint128_t r = (__uint128_t)a * b; + r += ((__uint128_t)aLo * b) >> 32; + *pLo = (r>>32)&0xffffffff; + return r>>64; +#elif defined(_WIN64) && !defined(SQLITE_DISABLE_INTRINSIC) + u64 r1_hi = __umulh(a,b); + u64 r1_lo = a*b; + u64 r2 = (__umulh((u64)aLo,b)<<32) + ((aLo*b)>>32); + u64 t = r1_lo + r2; + if( t>32; + return r1_hi; +#else + u64 x2 = a>>32; + u64 x1 = a&0xffffffff; + u64 x0 = aLo; + u64 y1 = b>>32; + u64 y0 = b&0xffffffff; + u64 x2y1 = x2*y1; + u64 r4 = x2y1>>32; + u64 x2y0 = x2*y0; + u64 x1y1 = x1*y1; + u64 r3 = (x2y1 & 0xffffffff) + (x2y0 >>32) + (x1y1 >>32); + u64 x1y0 = x1*y0; + u64 x0y1 = x0*y1; + u64 r2 = (x2y0 & 0xffffffff) + (x1y1 & 0xffffffff) + + (x1y0 >>32) + (x0y1>>32); + u64 x0y0 = x0*y0; + u64 r1 = (x1y0 & 0xffffffff) + (x0y1 & 0xffffffff) + + (x0y0 >>32); + r2 += r1>>32; + r3 += r2>>32; + *pLo = r2&0xffffffff; + return (r4<<32) + r3; +#endif } +#undef SQLITE_USE_UINT128 + /* -** The string z[] is an text representation of a real number. -** Convert this string to a double and write it into *pResult. +** Return a u64 with the N-th bit set. +*/ +#define U64_BIT(N) (((u64)1)<<(N)) + +/* +** Range of powers of 10 that we need to deal with when converting +** IEEE754 doubles to and from decimal. +*/ +#define POWERSOF10_FIRST (-348) +#define POWERSOF10_LAST (+347) + +/* +** For any p between -348 and +347, return the integer part of ** -** The string z[] is length bytes in length (bytes, not characters) and -** uses the encoding enc. The string is not necessarily zero-terminated. +** pow(10,p) * pow(2,63-pow10to2(p)) ** -** Return TRUE if the result is a valid real number (or integer) and FALSE -** if the string is empty or contains extraneous text. More specifically -** return -** 1 => The input string is a pure integer -** 2 or more => The input has a decimal point or eNNN clause -** 0 or less => The input string is not a valid number -** -1 => Not a valid number, but has a valid prefix which -** includes a decimal point and/or an eNNN clause +** Or, in other words, for any p in range, return the most significant +** 64 bits of pow(10,p). The pow(10,p) value is shifted left or right, +** as appropriate so the most significant 64 bits fit exactly into a +** 64-bit unsigned integer. ** -** Valid numbers are in one of these formats: +** Write into *pLo the next 32 significant bits of the answer after +** the first 64. ** -** [+-]digits[E[+-]digits] -** [+-]digits.[digits][E[+-]digits] -** [+-].digits[E[+-]digits] +** Algorithm: ** -** Leading and trailing whitespace is ignored for the purpose of determining -** validity. +** (1) For p between 0 and 26, return the value directly from the aBase[] +** lookup table. +** +** (2) For p outside the range 0 to 26, use aScale[] for the initial value +** then refine that result (if necessary) by a single multiplication +** against aBase[]. +** +** The constant tables aBase[], aScale[], and aScaleLo[] are generated +** by the C program at ../tool/mkfptab.c run with the --round option. +*/ +static u64 powerOfTen(int p, u32 *pLo){ + static const u64 aBase[] = { + UINT64_C(0x8000000000000000), /* 0: 1.0e+0 << 63 */ + UINT64_C(0xa000000000000000), /* 1: 1.0e+1 << 60 */ + UINT64_C(0xc800000000000000), /* 2: 1.0e+2 << 57 */ + UINT64_C(0xfa00000000000000), /* 3: 1.0e+3 << 54 */ + UINT64_C(0x9c40000000000000), /* 4: 1.0e+4 << 50 */ + UINT64_C(0xc350000000000000), /* 5: 1.0e+5 << 47 */ + UINT64_C(0xf424000000000000), /* 6: 1.0e+6 << 44 */ + UINT64_C(0x9896800000000000), /* 7: 1.0e+7 << 40 */ + UINT64_C(0xbebc200000000000), /* 8: 1.0e+8 << 37 */ + UINT64_C(0xee6b280000000000), /* 9: 1.0e+9 << 34 */ + UINT64_C(0x9502f90000000000), /* 10: 1.0e+10 << 30 */ + UINT64_C(0xba43b74000000000), /* 11: 1.0e+11 << 27 */ + UINT64_C(0xe8d4a51000000000), /* 12: 1.0e+12 << 24 */ + UINT64_C(0x9184e72a00000000), /* 13: 1.0e+13 << 20 */ + UINT64_C(0xb5e620f480000000), /* 14: 1.0e+14 << 17 */ + UINT64_C(0xe35fa931a0000000), /* 15: 1.0e+15 << 14 */ + UINT64_C(0x8e1bc9bf04000000), /* 16: 1.0e+16 << 10 */ + UINT64_C(0xb1a2bc2ec5000000), /* 17: 1.0e+17 << 7 */ + UINT64_C(0xde0b6b3a76400000), /* 18: 1.0e+18 << 4 */ + UINT64_C(0x8ac7230489e80000), /* 19: 1.0e+19 >> 0 */ + UINT64_C(0xad78ebc5ac620000), /* 20: 1.0e+20 >> 3 */ + UINT64_C(0xd8d726b7177a8000), /* 21: 1.0e+21 >> 6 */ + UINT64_C(0x878678326eac9000), /* 22: 1.0e+22 >> 10 */ + UINT64_C(0xa968163f0a57b400), /* 23: 1.0e+23 >> 13 */ + UINT64_C(0xd3c21bcecceda100), /* 24: 1.0e+24 >> 16 */ + UINT64_C(0x84595161401484a0), /* 25: 1.0e+25 >> 20 */ + UINT64_C(0xa56fa5b99019a5c8), /* 26: 1.0e+26 >> 23 */ + }; + static const u64 aScale[] = { + UINT64_C(0x8049a4ac0c5811ae), /* 0: 1.0e-351 << 1229 */ + UINT64_C(0xcf42894a5dce35ea), /* 1: 1.0e-324 << 1140 */ + UINT64_C(0xa76c582338ed2621), /* 2: 1.0e-297 << 1050 */ + UINT64_C(0x873e4f75e2224e68), /* 3: 1.0e-270 << 960 */ + UINT64_C(0xda7f5bf590966848), /* 4: 1.0e-243 << 871 */ + UINT64_C(0xb080392cc4349dec), /* 5: 1.0e-216 << 781 */ + UINT64_C(0x8e938662882af53e), /* 6: 1.0e-189 << 691 */ + UINT64_C(0xe65829b3046b0afa), /* 7: 1.0e-162 << 602 */ + UINT64_C(0xba121a4650e4ddeb), /* 8: 1.0e-135 << 512 */ + UINT64_C(0x964e858c91ba2655), /* 9: 1.0e-108 << 422 */ + UINT64_C(0xf2d56790ab41c2a2), /* 10: 1.0e-81 << 333 */ + UINT64_C(0xc428d05aa4751e4c), /* 11: 1.0e-54 << 243 */ + UINT64_C(0x9e74d1b791e07e48), /* 12: 1.0e-27 << 153 */ + UINT64_C(0xcccccccccccccccc), /* 13: 1.0e-1 << 67 (special case) */ + UINT64_C(0xcecb8f27f4200f3a), /* 14: 1.0e+27 >> 26 */ + UINT64_C(0xa70c3c40a64e6c51), /* 15: 1.0e+54 >> 116 */ + UINT64_C(0x86f0ac99b4e8dafd), /* 16: 1.0e+81 >> 206 */ + UINT64_C(0xda01ee641a708de9), /* 17: 1.0e+108 >> 295 */ + UINT64_C(0xb01ae745b101e9e4), /* 18: 1.0e+135 >> 385 */ + UINT64_C(0x8e41ade9fbebc27d), /* 19: 1.0e+162 >> 475 */ + UINT64_C(0xe5d3ef282a242e81), /* 20: 1.0e+189 >> 564 */ + UINT64_C(0xb9a74a0637ce2ee1), /* 21: 1.0e+216 >> 654 */ + UINT64_C(0x95f83d0a1fb69cd9), /* 22: 1.0e+243 >> 744 */ + UINT64_C(0xf24a01a73cf2dccf), /* 23: 1.0e+270 >> 833 */ + UINT64_C(0xc3b8358109e84f07), /* 24: 1.0e+297 >> 923 */ + UINT64_C(0x9e19db92b4e31ba9), /* 25: 1.0e+324 >> 1013 */ + }; + static const unsigned int aScaleLo[] = { + 0x205b896d, /* 0: 1.0e-351 << 1229 */ + 0x52064cad, /* 1: 1.0e-324 << 1140 */ + 0xaf2af2b8, /* 2: 1.0e-297 << 1050 */ + 0x5a7744a7, /* 3: 1.0e-270 << 960 */ + 0xaf39a475, /* 4: 1.0e-243 << 871 */ + 0xbd8d794e, /* 5: 1.0e-216 << 781 */ + 0x547eb47b, /* 6: 1.0e-189 << 691 */ + 0x0cb4a5a3, /* 7: 1.0e-162 << 602 */ + 0x92f34d62, /* 8: 1.0e-135 << 512 */ + 0x3a6a07f9, /* 9: 1.0e-108 << 422 */ + 0xfae27299, /* 10: 1.0e-81 << 333 */ + 0xaa97e14c, /* 11: 1.0e-54 << 243 */ + 0x775ea265, /* 12: 1.0e-27 << 153 */ + 0xcccccccc, /* 13: 1.0e-1 << 67 (special case) */ + 0x00000000, /* 14: 1.0e+27 >> 26 */ + 0x999090b6, /* 15: 1.0e+54 >> 116 */ + 0x69a028bb, /* 16: 1.0e+81 >> 206 */ + 0xe80e6f48, /* 17: 1.0e+108 >> 295 */ + 0x5ec05dd0, /* 18: 1.0e+135 >> 385 */ + 0x14588f14, /* 19: 1.0e+162 >> 475 */ + 0x8f1668c9, /* 20: 1.0e+189 >> 564 */ + 0x6d953e2c, /* 21: 1.0e+216 >> 654 */ + 0x4abdaf10, /* 22: 1.0e+243 >> 744 */ + 0xbc633b39, /* 23: 1.0e+270 >> 833 */ + 0x0a862f81, /* 24: 1.0e+297 >> 923 */ + 0x6c07a2c2, /* 25: 1.0e+324 >> 1013 */ + }; + int g, n; + u64 s, x; + u32 lo; + + assert( p>=POWERSOF10_FIRST && p<=POWERSOF10_LAST ); + if( p<0 ){ + if( p==(-1) ){ + *pLo = aScaleLo[13]; + return aScale[13]; + } + g = p/27; + n = p%27; + if( n ){ + g--; + n += 27; + } + }else if( p<27 ){ + *pLo = 0; + return aBase[p]; + }else{ + g = p/27; + n = p%27; + } + s = aScale[g+13]; + if( n==0 ){ + *pLo = aScaleLo[g+13]; + return s; + } + x = sqlite3Multiply160(s,aScaleLo[g+13],aBase[n],&lo); + if( (U64_BIT(63) & x)==0 ){ + x = x<<1 | ((lo>>31)&1); + lo = (lo<<1) | 1; + } + *pLo = lo; + return x; +} + +/* +** pow10to2(x) computes floor(log2(pow(10,x))). +** pow2to10(y) computes floor(log10(pow(2,y))). +** +** Conceptually, pow10to2(p) converts a base-10 exponent p into +** a corresponding base-2 exponent, and pow2to10(e) converts a base-2 +** exponent into a base-10 exponent. ** -** If some prefix of the input string is a valid number, this routine -** returns FALSE but it still converts the prefix and writes the result -** into *pResult. +** The conversions are based on the observation that: +** +** ln(10.0)/ln(2.0) == 108853/32768 (approximately) +** ln(2.0)/ln(10.0) == 78913/262144 (approximately) +** +** These ratios are approximate, but they are accurate to 5 digits, +** which is close enough for the usage here. Right-shift is used +** for division so that rounding of negative numbers happens in the +** right direction. */ -#if defined(_MSC_VER) -#pragma warning(disable : 4756) -#endif -SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ -#ifndef SQLITE_OMIT_FLOATING_POINT - int incr; - const char *zEnd; - /* sign * significand * (10 ^ (esign * exponent)) */ - int sign = 1; /* sign of significand */ - u64 s = 0; /* significand */ - int d = 0; /* adjust exponent for shifting decimal point */ - int esign = 1; /* sign of exponent */ - int e = 0; /* exponent */ - int eValid = 1; /* True exponent is either not used or is well-formed */ - int nDigit = 0; /* Number of digits processed */ - int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ - u64 s2; /* round-tripped significand */ - double rr[2]; +static int pwr10to2(int p){ return (p*108853) >> 15; } +static int pwr2to10(int p){ return (p*78913) >> 18; } - assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); - *pResult = 0.0; /* Default return value, in case of an error */ - if( length==0 ) return 0; +/* +** Count leading zeros for a 64-bit unsigned integer. +*/ +static int countLeadingZeros(u64 m){ +#if (defined(__GNUC__) || defined(__clang__)) \ + && !defined(SQLITE_DISABLE_INTRINSIC) + return __builtin_clzll(m); +#else + int n = 0; + if( m <= 0x00000000ffffffffULL) { n += 32; m <<= 32; } + if( m <= 0x0000ffffffffffffULL) { n += 16; m <<= 16; } + if( m <= 0x00ffffffffffffffULL) { n += 8; m <<= 8; } + if( m <= 0x0fffffffffffffffULL) { n += 4; m <<= 4; } + if( m <= 0x3fffffffffffffffULL) { n += 2; m <<= 2; } + if( m <= 0x7fffffffffffffffULL) { n += 1; } + return n; +#endif +} - if( enc==SQLITE_UTF8 ){ - incr = 1; - zEnd = z + length; +/* +** Given m and e, which represent a quantity r == m*pow(2,e), +** return values *pD and *pP such that r == (*pD)*pow(10,*pP), +** approximately. *pD should contain at least n significant digits. +** +** The input m is required to have its highest bit set. In other words, +** m should be left-shifted, and e decremented, to maximize the value of m. +*/ +static void sqlite3Fp2Convert10(u64 m, int e, int n, u64 *pD, int *pP){ + int p; + u64 h, d1; + u32 d2; + assert( n>=1 && n<=18 ); + p = n - 1 - pwr2to10(e+63); + h = sqlite3Multiply128(m, powerOfTen(p,&d2), &d1); + assert( -(e + pwr10to2(p) + 2) >= 0 ); + assert( -(e + pwr10to2(p) + 1) <= 63 ); + if( n==18 ){ + h >>= -(e + pwr10to2(p) + 2); + *pD = (h + ((h<<1)&2))>>1; }else{ - int i; - incr = 2; - length &= ~1; - assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); - testcase( enc==SQLITE_UTF16LE ); - testcase( enc==SQLITE_UTF16BE ); - for(i=3-enc; i> -(e + pwr10to2(p) + 1); } + *pP = -p; +} - /* skip leading spaces */ - while( z=zEnd ) return 0; - - /* get sign of significand */ - if( *z=='-' ){ - sign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; - } +/* +** Return an IEEE754 floating point value that approximates d*pow(10,p). +** +** The (current) algorithm is adapted from the work of Ross Cox at +** https://github.com/rsc/fpfmt +*/ +static double sqlite3Fp10Convert2(u64 d, int p){ + int b, lp, e, adj, s; + u32 pwr10l, mid1; + u64 pwr10h, x, hi, lo, sticky, u, m; + double r; + if( pPOWERSOF10_LAST ) return INFINITY; + b = 64 - countLeadingZeros(d); + lp = pwr10to2(p); + e = 53 - b - lp; + if( e > 1074 ){ + if( e>=1130 ) return 0.0; + e = 1074; + } + s = -(e-(64-b) + lp + 3); + pwr10h = powerOfTen(p, &pwr10l); + if( pwr10l!=0 ){ + pwr10h++; + pwr10l = ~pwr10l; + } + x = d<<(64-b); + hi = sqlite3Multiply128(x,pwr10h,&lo); + mid1 = lo>>32; + sticky = 1; + if( (hi & (U64_BIT(s)-1))==0 ) { + u32 mid2 = sqlite3Multiply128(x,((u64)pwr10l)<<32,&lo)>>32; + sticky = (mid1-mid2 > 1); + hi -= mid1 < mid2; + } + u = (hi>>s) | sticky; + adj = (u >= U64_BIT(55)-2); + if( adj ){ + u = (u>>adj) | (u&1); + e -= adj; + } + m = (u + 1 + ((u>>2)&1)) >> 2; + if( e<=(-972) ) return INFINITY; + if((m & U64_BIT(52)) != 0){ + m = (m & ~U64_BIT(52)) | ((u64)(1075-e)<<52); + } + memcpy(&r,&m,8); + return r; +} - /* copy max significant digits to significand */ - while( z=((LARGEST_UINT64-9)/10) ){ - /* skip non-significant significand digits - ** (increase exponent by d to shift decimal left) */ - while( z Set if any prefix of the input is valid. Clear if +** there is no prefix of the input that can be seen as +** a valid floating point number. +** bit 1 => Set if the input contains a decimal point or eNNN +** clause. Zero if the input is an integer. +** bit 2 => The input is exactly 0.0, not an underflow from +** some value near zero. +** bit 3 => Set if there are more than about 19 significant +** digits in the input. +** +** If the input contains a syntax error but begins with text that might +** be a valid number of some kind, then the result is negative. The +** result is only zero if no prefix of the input could be interpreted as +** a number. +** +** Leading and trailing whitespace is ignored. Valid numbers are in +** one of the formats below: +** +** [+-]digits[E[+-]digits] +** [+-]digits.[digits][E[+-]digits] +** [+-].digits[E[+-]digits] +** +** Algorithm sketch: Compute an unsigned 64-bit integer s and a base-10 +** exponent d such that the value encoding by the input is s*pow(10,d). +** Then invoke sqlite3Fp10Convert2() to calculated the closest possible +** IEEE754 double. The sign is added back afterwards, if the input string +** starts with a "-". The use of an unsigned 64-bit s mantissa means that +** only about the first 19 significant digits of the input can contribute +** to the result. This can result in suboptimal rounding decisions when +** correct rounding requires more than 19 input digits. For example, +** this routine renders "3500000000000000.2500001" as +** 3500000000000000.0 instead of 3500000000000000.5 because the decision +** to round up instead of using banker's rounding to round down is determined +** by the 23rd significant digit, which this routine ignores. It is not +** possible to do better without some kind of BigNum. +*/ +SQLITE_PRIVATE int sqlite3AtoF(const char *zIn, double *pResult){ +#ifndef SQLITE_OMIT_FLOATING_POINT + const unsigned char *z = (const unsigned char*)zIn; + int neg = 0; /* True for a negative value */ + u64 s = 0; /* mantissa */ + int d = 0; /* Value is s * pow(10,d) */ + int mState = 0; /* 1: digit seen 2: fp 4: hard-zero */ + unsigned v; /* Value of a single digit */ + + start_of_text: + if( (v = (unsigned)z[0] - '0')<10 ){ + parse_integer_part: + mState = 1; + s = v; + z++; + while( (v = (unsigned)z[0] - '0')<10 ){ + s = s*10 + v; + z++; + if( s>=(LARGEST_UINT64-9)/10 ){ + mState = 9; + while( sqlite3Isdigit(z[0]) ){ z++; d++; } + break; + } } + }else if( z[0]=='-' ){ + neg = 1; + z++; + if( (v = (unsigned)z[0] - '0')<10 ) goto parse_integer_part; + }else if( z[0]=='+' ){ + z++; + if( (v = (unsigned)z[0] - '0')<10 ) goto parse_integer_part; + }else if( sqlite3Isspace(z[0]) ){ + do{ z++; }while( sqlite3Isspace(z[0]) ); + goto start_of_text; + }else{ + s = 0; } - if( z>=zEnd ) goto do_atof_calc; /* if decimal point is present */ if( *z=='.' ){ - z+=incr; - eType++; - /* copy digits from after decimal to significand - ** (decrease exponent by d to shift decimal right) */ - while( z=zEnd ) goto do_atof_calc; /* if exponent is present */ if( *z=='e' || *z=='E' ){ - z+=incr; - eValid = 0; - eType++; - - /* This branch is needed to avoid a (harmless) buffer overread. The - ** special comment alerts the mutation tester that the correct answer - ** is obtained even if the branch is omitted */ - if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/ + int esign; + z++; /* get sign of exponent */ if( *z=='-' ){ esign = -1; - z+=incr; - }else if( *z=='+' ){ - z+=incr; + z++; + }else{ + esign = +1; + if( *z=='+' ){ + z++; + } } /* copy digits to exponent */ - while( z0 && s<((LARGEST_UINT64-0x7ff)/10) ){ - s *= 10; - e--; - } - while( e<0 && (s%10)==0 ){ - s /= 10; - e++; - } - - rr[0] = (double)s; - assert( sizeof(s2)==sizeof(rr[0]) ); -#ifdef SQLITE_DEBUG - rr[1] = 18446744073709549568.0; - memcpy(&s2, &rr[1], sizeof(s2)); - assert( s2==0x43efffffffffffffLL ); -#endif - /* Largest double that can be safely converted to u64 - ** vvvvvvvvvvvvvvvvvvvvvv */ - if( rr[0]<=18446744073709549568.0 ){ - s2 = (u64)rr[0]; - rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); + *pResult = 0.0; + mState |= 4; }else{ - rr[1] = 0.0; + *pResult = sqlite3Fp10Convert2(s,d); } - assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */ - - if( e>0 ){ - while( e>=100 ){ - e -= 100; - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); - } - while( e>=10 ){ - e -= 10; - dekkerMul2(rr, 1.0e+10, 0.0); - } - while( e>=1 ){ - e -= 1; - dekkerMul2(rr, 1.0e+01, 0.0); - } - }else{ - while( e<=-100 ){ - e += 100; - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); - } - while( e<=-10 ){ - e += 10; - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); - } - while( e<=-1 ){ - e += 1; - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); - } - } - *pResult = rr[0]+rr[1]; - if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; - if( sign<0 ) *pResult = -*pResult; + if( neg ) *pResult = -*pResult; assert( !sqlite3IsNaN(*pResult) ); -atof_return: /* return true if number and no extra non-whitespace characters after */ - if( z==zEnd && nDigit>0 && eValid && eType>0 ){ - return eType; - }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ - return -1; - }else{ - return 0; + if( z[0]==0 ){ + return mState; } + if( sqlite3Isspace(z[0]) ){ + do{ z++; }while( sqlite3Isspace(*z) ); + if( z[0]==0 ){ + return mState; + } + } + return 0xfffffff0 | mState; #else - return !sqlite3Atoi64(z, pResult, length, enc); + return sqlite3Atoi64(z, pResult, strlen(z), SQLITE_UTF8)==0; #endif /* SQLITE_OMIT_FLOATING_POINT */ } -#if defined(_MSC_VER) -#pragma warning(default : 4756) + +/* +** Digit pairs used to convert a U64 or I64 into text, two digits +** at a time. +*/ +static const union { + char a[201]; + short int forceAlignment; +} sqlite3DigitPairs = { + "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899" +}; + +/* +** ARMv6, ARMv7, PPC32 are known to not support hardware u64 division. +*/ +#if (defined(__arm__) && !defined(__aarch64__)) || \ + (defined(__ppc__) && !defined(__ppc64__)) +# define SQLITE_AVOID_U64_DIVIDE 1 #endif +#ifdef SQLITE_AVOID_U64_DIVIDE +/* +** Render an unsigned 64-bit integer as text onto the end of a 2-byte +** aligned buffer that is SQLITE_U64_DIGIT+1 bytes long. The last byte +** of the buffer will be filled with a \000 byte. +** +** Return the index into the buffer of the first byte. +** +** This routine is used on platforms where u64-division is slow because +** it is not available in hardware and has to be emulated in software. +** It seeks to minimize the number of u64 divisions and use u32 divisions +** instead. It is slower on platforms that have hardware u64 division, +** but much faster on platforms that do not. +*/ +static int sqlite3UInt64ToText(u64 v, char *zOut){ + u32 x32, kk; + int i; + zOut[SQLITE_U64_DIGITS] = 0; + i = SQLITE_U64_DIGITS; + assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[0]) ); + assert( TWO_BYTE_ALIGNMENT(zOut) ); + while( (v>>32)!=0 ){ + u32 y, x0, x1, y0, y1; + x32 = v % 100000000; + v = v / 100000000; + y = x32 % 10000; + x32 /= 10000; + x1 = x32 / 100; + x0 = x32 % 100; + y1 = y / 100; + y0 = y % 100; + assert( i>=8 ); + i -= 8; + *(u16*)(&zOut[i]) = *(u16*)&sqlite3DigitPairs.a[x1*2]; + *(u16*)(&zOut[i+2]) = *(u16*)&sqlite3DigitPairs.a[x0*2]; + *(u16*)(&zOut[i+4]) = *(u16*)&sqlite3DigitPairs.a[y1*2]; + *(u16*)(&zOut[i+6]) = *(u16*)&sqlite3DigitPairs.a[y0*2]; + } + x32 = v; + while( x32>=10 ){ + kk = x32 % 100; + x32 = x32 / 100; + assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk*2]) ); + assert( i>=2 ); + i -= 2; + assert( TWO_BYTE_ALIGNMENT(&zOut[i]) ); + *(u16*)(&zOut[i]) = *(u16*)&sqlite3DigitPairs.a[kk*2]; + } + if( x32 ){ + assert( i>0 ); + zOut[--i] = x32 + '0'; + } + return i; +} +#endif /* defined(SQLITE_AVOID_U64_DIVIDE) */ + /* ** Render an signed 64-bit integer as text. Store the result in zOut[] and ** return the length of the string that was stored, in bytes. The value @@ -36597,23 +37470,39 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){ int i; u64 x; - char zTemp[22]; - if( v<0 ){ - x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; - }else{ + union { + char a[SQLITE_U64_DIGITS+1]; + u16 forceAlignment; + } u; + if( v>0 ){ x = v; + }else if( v==0 ){ + zOut[0] = '0'; + zOut[1] = 0; + return 1; + }else{ + x = (v==SMALLEST_INT64) ? ((u64)1)<<63 : (u64)-v; } - i = sizeof(zTemp)-2; - zTemp[sizeof(zTemp)-1] = 0; - while( 1 /*exit-by-break*/ ){ - zTemp[i] = (x%10) + '0'; - x = x/10; - if( x==0 ) break; - i--; - }; - if( v<0 ) zTemp[--i] = '-'; - memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); - return sizeof(zTemp)-1-i; +#ifdef SQLITE_AVOID_U64_DIVIDE + i = sqlite3UInt64ToText(x, u.a); +#else + i = sizeof(u.a)-1; + u.a[i] = 0; + while( x>=10 ){ + int kk = (x%100)*2; + assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk]) ); + assert( TWO_BYTE_ALIGNMENT(&u.a[i-2]) ); + *(u16*)(&u.a[i-2]) = *(u16*)&sqlite3DigitPairs.a[kk]; + i -= 2; + x /= 100; + } + if( x ){ + u.a[--i] = x + '0'; + } +#endif /* SQLITE_AVOID_U64_DIVIDE */ + if( v<0 ) u.a[--i] = '-'; + memcpy(zOut, &u.a[i], sizeof(u.a)-i); + return sizeof(u.a)-1-i; } /* @@ -36667,8 +37556,8 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc int incr; u64 u = 0; int neg = 0; /* assume positive */ - int i; - int c = 0; + int i, j; + unsigned int c = 0; int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */ int rc; /* Baseline return code */ const char *zStart; @@ -36696,8 +37585,8 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc } zStart = zNum; while( zNum='0' && c<='9'; i+=incr){ - u = u*10 + c - '0'; + for(i=0; &zNum[i]19*incr ? 1 : compare2pow63(zNum, incr); - if( c<0 ){ + j = i>19*incr ? 1 : compare2pow63(zNum, incr); + if( j<0 ){ /* zNum is less than 9223372036854775808 so it fits */ assert( u<=LARGEST_INT64 ); return rc; }else{ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; - if( c>0 ){ + if( j>0 ){ /* zNum is greater than 9223372036854775808 so it overflows */ return 2; }else{ @@ -36870,7 +37759,7 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ ** representation. ** ** If iRound<=0 then round to -iRound significant digits to the -** the left of the decimal point, or to a maximum of mxRound total +** the right of the decimal point, or to a maximum of mxRound total ** significant digits. ** ** If iRound>0 round to min(iRound,mxRound) significant digits total. @@ -36883,13 +37772,14 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ ** The p->z[] array is *not* zero-terminated. */ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ - int i; - u64 v; - int e, exp = 0; - double rr[2]; + int i; /* Index into zBuf[] where to put next character */ + int n; /* Number of digits */ + u64 v; /* mantissa */ + int e, exp = 0; /* Base-2 and base-10 exponent */ + char *zBuf; /* Local alias for p->zBuf */ + char *z; /* Local alias for p->z */ p->isSpecial = 0; - p->z = p->zBuf; assert( mxRound>0 ); /* Convert negative numbers to positive. Deal with Infinity, 0.0, and @@ -36907,78 +37797,100 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou p->sign = '+'; } memcpy(&v,&r,8); - e = v>>52; - if( (e&0x7ff)==0x7ff ){ + e = (v>>52)&0x7ff; + if( e==0x7ff ){ p->isSpecial = 1 + (v!=0x7ff0000000000000LL); p->n = 0; p->iDP = 0; + p->z = p->zBuf; return; } - - /* Multiply r by powers of ten until it lands somewhere in between - ** 1.0e+19 and 1.0e+17. - ** - ** Use Dekker-style double-double computation to increase the - ** precision. - ** - ** The error terms on constants like 1.0e+100 computed using the - ** decimal extension, for example as follows: - ** - ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); - */ - rr[0] = r; - rr[1] = 0.0; - if( rr[0]>9.223372036854774784e+18 ){ - while( rr[0]>9.223372036854774784e+118 ){ - exp += 100; - dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); - } - while( rr[0]>9.223372036854774784e+28 ){ - exp += 10; - dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); - } - while( rr[0]>9.223372036854774784e+18 ){ - exp += 1; - dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); - } + v &= 0x000fffffffffffffULL; + if( e==0 ){ + int nn = countLeadingZeros(v); + v <<= nn; + e = -1074 - nn; }else{ - while( rr[0]<9.223372036854774784e-83 ){ - exp -= 100; - dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); - } - while( rr[0]<9.223372036854774784e+07 ){ - exp -= 10; - dekkerMul2(rr, 1.0e+10, 0.0); - } - while( rr[0]<9.22337203685477478e+17 ){ - exp -= 1; - dekkerMul2(rr, 1.0e+01, 0.0); - } + v = (v<<11) | U64_BIT(63); + e -= 1086; } - v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; + sqlite3Fp2Convert10(v, e, (iRound<=0||iRound>=18)?18:iRound+1, &v, &exp); - /* Extract significant digits. */ - i = sizeof(p->zBuf)-1; + /* Extract significant digits, start at the right-most slot in p->zBuf + ** and working back to the right. "i" keeps track of the next slot in + ** which to store a digit. */ + assert( sizeof(p->zBuf)==SQLITE_U64_DIGITS+1 ); assert( v>0 ); - while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } - assert( i>=0 && izBuf)-1 ); - p->n = sizeof(p->zBuf) - 1 - i; - assert( p->n>0 ); - assert( p->nzBuf) ); - p->iDP = p->n + exp; + zBuf = p->zBuf; +#ifdef SQLITE_AVOID_U64_DIVIDE + i = sqlite3UInt64ToText(v, zBuf); +#else + i = SQLITE_U64_DIGITS; + while( v>=10 ){ + int kk = (v%100)*2; + assert( TWO_BYTE_ALIGNMENT(&sqlite3DigitPairs.a[kk]) ); + assert( TWO_BYTE_ALIGNMENT(&zBuf[i]) ); + assert( i-2>=0 ); + *(u16*)(&zBuf[i-2]) = *(u16*)&sqlite3DigitPairs.a[kk]; + i -= 2; + v /= 100; + } + if( v ){ + assert( v<10 ); + assert( i>0 ); + zBuf[--i] = v + '0'; + } +#endif /* SQLITE_AVOID_U64_DIVIDE */ + assert( i>=0 && i0 ); + assert( n<=SQLITE_U64_DIGITS ); + p->iDP = n + exp; if( iRound<=0 ){ iRound = p->iDP - iRound; - if( iRound==0 && p->zBuf[i+1]>='5' ){ + if( iRound==0 && zBuf[i]>='5' ){ iRound = 1; - p->zBuf[i--] = '0'; - p->n++; + zBuf[--i] = '0'; + n++; p->iDP++; } } - if( iRound>0 && (iRoundn || p->n>mxRound) ){ - char *z = &p->zBuf[i+1]; + z = &zBuf[i]; /* z points to the first digit */ + if( iRound>0 && (iRoundmxRound) ){ if( iRound>mxRound ) iRound = mxRound; - p->n = iRound; + if( iRound==17 ){ + /* If the precision is exactly 17, which only happens with the "!" + ** flag (ex: "%!.17g") then try to reduce the precision if that + ** yields text that will round-trip to the original floating-point. + ** value. Thus, for exaple, 49.47 will render as 49.47, rather than + ** as 49.469999999999999. */ + if( z[15]=='9' && z[14]=='9' ){ + int jj, kk; + u64 v2; + for(jj=14; jj>0 && z[jj-1]=='9'; jj--){} + if( jj==0 ){ + v2 = 1; + }else{ + v2 = z[0] - '0'; + for(kk=1; kkiDP>=n || (z[15]=='0' && z[14]=='0' && z[13]=='0') ){ + int jj, kk; + u64 v2; + assert( z[0]!='0' ); + for(jj=13; z[jj-1]=='0'; jj--){} + v2 = z[0] - '0'; + for(kk=1; kk='5' ){ int j = iRound-1; while( 1 /*exit-by-break*/ ){ @@ -36986,8 +37898,9 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou if( z[j]<='9' ) break; z[j] = '0'; if( j==0 ){ - p->z[i--] = '1'; - p->n++; + z--; + z[0] = '1'; + n++; p->iDP++; break; }else{ @@ -36996,13 +37909,13 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou } } } - p->z = &p->zBuf[i+1]; - assert( i+p->n < sizeof(p->zBuf) ); - assert( p->n>0 ); - while( p->z[p->n-1]=='0' ){ - p->n--; - assert( p->n>0 ); + assert( n>0 ); + while( z[n-1]=='0' ){ + n--; + assert( n>0 ); } + p->n = n; + p->z = z; } /* @@ -38067,10 +38980,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 44 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), /* 45 */ "IdxLT" OpHelp("key=r[P3@P4]"), /* 46 */ "IdxGE" OpHelp("key=r[P3@P4]"), - /* 47 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), - /* 48 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), - /* 49 */ "Program" OpHelp(""), - /* 50 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 47 */ "IFindKey" OpHelp(""), + /* 48 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 49 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 50 */ "Program" OpHelp(""), /* 51 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), /* 52 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), /* 53 */ "Ne" OpHelp("IF r[P3]!=r[P1]"), @@ -38080,49 +38993,49 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 57 */ "Lt" OpHelp("IF r[P3]=r[P1]"), /* 59 */ "ElseEq" OpHelp(""), - /* 60 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), - /* 61 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), - /* 62 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), - /* 63 */ "IncrVacuum" OpHelp(""), - /* 64 */ "VNext" OpHelp(""), - /* 65 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), - /* 66 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), - /* 67 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), - /* 68 */ "Return" OpHelp(""), - /* 69 */ "EndCoroutine" OpHelp(""), - /* 70 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), - /* 71 */ "Halt" OpHelp(""), - /* 72 */ "Integer" OpHelp("r[P2]=P1"), - /* 73 */ "Int64" OpHelp("r[P2]=P4"), - /* 74 */ "String" OpHelp("r[P2]='P4' (len=P1)"), - /* 75 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), - /* 76 */ "Null" OpHelp("r[P2..P3]=NULL"), - /* 77 */ "SoftNull" OpHelp("r[P1]=NULL"), - /* 78 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 79 */ "Variable" OpHelp("r[P2]=parameter(P1)"), - /* 80 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), - /* 81 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), - /* 82 */ "SCopy" OpHelp("r[P2]=r[P1]"), - /* 83 */ "IntCopy" OpHelp("r[P2]=r[P1]"), - /* 84 */ "FkCheck" OpHelp(""), - /* 85 */ "ResultRow" OpHelp("output=r[P1@P2]"), - /* 86 */ "CollSeq" OpHelp(""), - /* 87 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), - /* 88 */ "RealAffinity" OpHelp(""), - /* 89 */ "Cast" OpHelp("affinity(r[P1])"), - /* 90 */ "Permutation" OpHelp(""), - /* 91 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), - /* 92 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), - /* 93 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), - /* 94 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), - /* 95 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), - /* 96 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), - /* 97 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 98 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 99 */ "Count" OpHelp("r[P2]=count()"), - /* 100 */ "ReadCookie" OpHelp(""), - /* 101 */ "SetCookie" OpHelp(""), - /* 102 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), + /* 60 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 61 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), + /* 62 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]--, goto P2"), + /* 63 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), + /* 64 */ "IncrVacuum" OpHelp(""), + /* 65 */ "VNext" OpHelp(""), + /* 66 */ "Filter" OpHelp("if key(P3@P4) not in filter(P1) goto P2"), + /* 67 */ "PureFunc" OpHelp("r[P3]=func(r[P2@NP])"), + /* 68 */ "Function" OpHelp("r[P3]=func(r[P2@NP])"), + /* 69 */ "Return" OpHelp(""), + /* 70 */ "EndCoroutine" OpHelp(""), + /* 71 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), + /* 72 */ "Halt" OpHelp(""), + /* 73 */ "Integer" OpHelp("r[P2]=P1"), + /* 74 */ "Int64" OpHelp("r[P2]=P4"), + /* 75 */ "String" OpHelp("r[P2]='P4' (len=P1)"), + /* 76 */ "BeginSubrtn" OpHelp("r[P2]=NULL"), + /* 77 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 78 */ "SoftNull" OpHelp("r[P1]=NULL"), + /* 79 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 80 */ "Variable" OpHelp("r[P2]=parameter(P1)"), + /* 81 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 82 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 83 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 84 */ "IntCopy" OpHelp("r[P2]=r[P1]"), + /* 85 */ "FkCheck" OpHelp(""), + /* 86 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 87 */ "CollSeq" OpHelp(""), + /* 88 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 89 */ "RealAffinity" OpHelp(""), + /* 90 */ "Cast" OpHelp("affinity(r[P1])"), + /* 91 */ "Permutation" OpHelp(""), + /* 92 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), + /* 93 */ "IsTrue" OpHelp("r[P2] = coalesce(r[P1]==TRUE,P3) ^ P4"), + /* 94 */ "ZeroOrNull" OpHelp("r[P2] = 0 OR NULL"), + /* 95 */ "Offset" OpHelp("r[P3] = sqlite_offset(P1)"), + /* 96 */ "Column" OpHelp("r[P3]=PX cursor P1 column P2"), + /* 97 */ "TypeCheck" OpHelp("typecheck(r[P1@P2])"), + /* 98 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 99 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 100 */ "Count" OpHelp("r[P2]=count()"), + /* 101 */ "ReadCookie" OpHelp(""), + /* 102 */ "SetCookie" OpHelp(""), /* 103 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), /* 104 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), /* 105 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 162 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 163 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 164 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 165 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 166 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 167 */ "Expire" OpHelp(""), - /* 168 */ "CursorLock" OpHelp(""), - /* 169 */ "CursorUnlock" OpHelp(""), - /* 170 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 171 */ "VBegin" OpHelp(""), - /* 172 */ "VCreate" OpHelp(""), - /* 173 */ "VDestroy" OpHelp(""), - /* 174 */ "VOpen" OpHelp(""), - /* 175 */ "VCheck" OpHelp(""), - /* 176 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), - /* 177 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 178 */ "VRename" OpHelp(""), - /* 179 */ "Pagecount" OpHelp(""), - /* 180 */ "MaxPgcnt" OpHelp(""), - /* 181 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), - /* 182 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"), - /* 183 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"), - /* 184 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), - /* 185 */ "Trace" OpHelp(""), - /* 186 */ "CursorHint" OpHelp(""), - /* 187 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), - /* 188 */ "Noop" OpHelp(""), - /* 189 */ "Explain" OpHelp(""), - /* 190 */ "Abortable" OpHelp(""), + /* 155 */ "DropIndex" OpHelp(""), + /* 156 */ "DropTrigger" OpHelp(""), + /* 157 */ "IntegrityCk" OpHelp(""), + /* 158 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 159 */ "Param" OpHelp(""), + /* 160 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 161 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 162 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 163 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 164 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 165 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 166 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 167 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 168 */ "Expire" OpHelp(""), + /* 169 */ "CursorLock" OpHelp(""), + /* 170 */ "CursorUnlock" OpHelp(""), + /* 171 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 172 */ "VBegin" OpHelp(""), + /* 173 */ "VCreate" OpHelp(""), + /* 174 */ "VDestroy" OpHelp(""), + /* 175 */ "VOpen" OpHelp(""), + /* 176 */ "VCheck" OpHelp(""), + /* 177 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 178 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 179 */ "VRename" OpHelp(""), + /* 180 */ "Pagecount" OpHelp(""), + /* 181 */ "MaxPgcnt" OpHelp(""), + /* 182 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), + /* 183 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"), + /* 184 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"), + /* 185 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 186 */ "Trace" OpHelp(""), + /* 187 */ "CursorHint" OpHelp(""), + /* 188 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 189 */ "Noop" OpHelp(""), + /* 190 */ "Explain" OpHelp(""), + /* 191 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -38241,7 +39155,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ ** Debugging logic */ -/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ +/* SQLITE_KV_TRACE() is used for tracing calls to kvrecord routines. */ #if 0 #define SQLITE_KV_TRACE(X) printf X #else @@ -38255,7 +39169,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ #define SQLITE_KV_LOG(X) #endif - /* ** Forward declaration of objects used by this VFS implementation */ @@ -38263,6 +39176,11 @@ typedef struct KVVfsFile KVVfsFile; /* A single open file. There are only two files represented by this ** VFS - the database and the rollback journal. +** +** Maintenance reminder: if this struct changes in any way, the JSON +** rendering of its structure must be updated in +** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary +** compatibility concerns, so it does not need an iVersion member. */ struct KVVfsFile { sqlite3_file base; /* IO methods */ @@ -38312,7 +39230,7 @@ static int kvvfsCurrentTime(sqlite3_vfs*, double*); static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); static sqlite3_vfs sqlite3OsKvvfsObject = { - 1, /* iVersion */ + 2, /* iVersion */ sizeof(KVVfsFile), /* szOsFile */ 1024, /* mxPathname */ 0, /* pNext */ @@ -38388,23 +39306,37 @@ static sqlite3_io_methods kvvfs_jrnl_io_methods = { /* Forward declarations for the low-level storage engine */ -static int kvstorageWrite(const char*, const char *zKey, const char *zData); -static int kvstorageDelete(const char*, const char *zKey); -static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); -#define KVSTORAGE_KEY_SZ 32 +#ifndef SQLITE_WASM +/* In WASM builds these are implemented in JS. */ +static int kvrecordWrite(const char*, const char *zKey, const char *zData); +static int kvrecordDelete(const char*, const char *zKey); +static int kvrecordRead(const char*, const char *zKey, char *zBuf, int nBuf); +#endif +#ifndef KVRECORD_KEY_SZ +#define KVRECORD_KEY_SZ 32 +#endif /* Expand the key name with an appropriate prefix and put the result ** in zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least -** KVSTORAGE_KEY_SZ bytes. +** KVRECORD_KEY_SZ bytes. */ -static void kvstorageMakeKey( +static void kvrecordMakeKey( const char *zClass, const char *zKeyIn, char *zKeyOut ){ - sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); + assert( zKeyIn ); + assert( zKeyOut ); + assert( zClass ); + sqlite3_snprintf(KVRECORD_KEY_SZ, zKeyOut, "kvvfs-%s-%s", + zClass, zKeyIn); } +#ifndef SQLITE_WASM +/* In WASM builds do not define APIs which use fopen(), fwrite(), +** and the like because those APIs are a portability issue for +** WASM. +*/ /* Write content into a key. zClass is the particular namespace of the ** underlying key/value store to use - either "local" or "session". ** @@ -38412,14 +39344,14 @@ static void kvstorageMakeKey( ** ** Return the number of errors. */ -static int kvstorageWrite( +static int kvrecordWrite( const char *zClass, const char *zKey, const char *zData ){ FILE *fd; - char zXKey[KVSTORAGE_KEY_SZ]; - kvstorageMakeKey(zClass, zKey, zXKey); + char zXKey[KVRECORD_KEY_SZ]; + kvrecordMakeKey(zClass, zKey, zXKey); fd = fopen(zXKey, "wb"); if( fd ){ SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, @@ -38437,9 +39369,9 @@ static int kvstorageWrite( ** namespace given by zClass. If the key does not previously exist, ** this routine is a no-op. */ -static int kvstorageDelete(const char *zClass, const char *zKey){ - char zXKey[KVSTORAGE_KEY_SZ]; - kvstorageMakeKey(zClass, zKey, zXKey); +static int kvrecordDelete(const char *zClass, const char *zKey){ + char zXKey[KVRECORD_KEY_SZ]; + kvrecordMakeKey(zClass, zKey, zXKey); unlink(zXKey); SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); return 0; @@ -38460,7 +39392,7 @@ static int kvstorageDelete(const char *zClass, const char *zKey){ ** zero-terminates zBuf at zBuf[0] and returns the size of the data ** without reading it. */ -static int kvstorageRead( +static int kvrecordRead( const char *zClass, const char *zKey, char *zBuf, @@ -38468,8 +39400,8 @@ static int kvstorageRead( ){ FILE *fd; struct stat buf; - char zXKey[KVSTORAGE_KEY_SZ]; - kvstorageMakeKey(zClass, zKey, zXKey); + char zXKey[KVRECORD_KEY_SZ]; + kvrecordMakeKey(zClass, zKey, zXKey); if( access(zXKey, R_OK)!=0 || stat(zXKey, &buf)!=0 || !S_ISREG(buf.st_mode) @@ -38501,6 +39433,8 @@ static int kvstorageRead( return (int)n; } } +#endif /* #ifndef SQLITE_WASM */ + /* ** An internal level of indirection which enables us to replace the @@ -38508,17 +39442,27 @@ static int kvstorageRead( ** Maintenance reminder: if this struct changes in any way, the JSON ** rendering of its structure must be updated in ** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary -** compatibility concerns, so it does not need an iVersion -** member. +** compatibility concerns, so it does not need an iVersion member. */ typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods; struct sqlite3_kvvfs_methods { - int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf); - int (*xWrite)(const char *zClass, const char *zKey, const char *zData); - int (*xDelete)(const char *zClass, const char *zKey); + int (*xRcrdRead)(const char*, const char *zKey, char *zBuf, int nBuf); + int (*xRcrdWrite)(const char*, const char *zKey, const char *zData); + int (*xRcrdDelete)(const char*, const char *zKey); const int nKeySize; + const int nBufferSize; +#ifndef SQLITE_WASM +# define MAYBE_CONST const +#else +# define MAYBE_CONST +#endif + MAYBE_CONST sqlite3_vfs * pVfs; + MAYBE_CONST sqlite3_io_methods *pIoDb; + MAYBE_CONST sqlite3_io_methods *pIoJrnl; +#undef MAYBE_CONST }; + /* ** This object holds the kvvfs I/O methods which may be swapped out ** for JavaScript-side implementations in WASM builds. In such builds @@ -38533,10 +39477,20 @@ struct sqlite3_kvvfs_methods { const #endif SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = { -kvstorageRead, -kvstorageWrite, -kvstorageDelete, -KVSTORAGE_KEY_SZ +#ifndef SQLITE_WASM + .xRcrdRead = kvrecordRead, + .xRcrdWrite = kvrecordWrite, + .xRcrdDelete = kvrecordDelete, +#else + .xRcrdRead = 0, + .xRcrdWrite = 0, + .xRcrdDelete = 0, +#endif + .nKeySize = KVRECORD_KEY_SZ, + .nBufferSize = SQLITE_KVOS_SZ, + .pVfs = &sqlite3OsKvvfsObject, + .pIoDb = &kvvfs_db_io_methods, + .pIoJrnl = &kvvfs_jrnl_io_methods }; /****** Utility subroutines ************************************************/ @@ -38563,7 +39517,10 @@ KVSTORAGE_KEY_SZ ** of hexadecimal and base-26 numbers, it is always clear where ** one stops and the next begins. */ -static int kvvfsEncode(const char *aData, int nData, char *aOut){ +#ifndef SQLITE_WASM +static +#endif +int kvvfsEncode(const char *aData, int nData, char *aOut){ int i, j; const unsigned char *a = (const unsigned char*)aData; for(i=j=0; i='a' && c<='z' ){ n += (c - 'a')*mult; + if( n>nOut ) return -1 /* oversized/malformed input */; mult *= 26; c = aIn[++i]; } - if( j+n>nOut ) return -1; + if( j+n>nOut ) return -1 /* oversized/malformed input */; memset(&aOut[j], 0, n); j += n; if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */ }else{ aOut[j] = c<<4; c = kvvfsHexValue[aIn[++i]]; - if( c<0 ) break; + if( c<0 ) return -1 /* hex bytes are always in pairs */; aOut[j++] += c; i++; } @@ -38670,7 +39632,7 @@ static void kvvfsDecodeJournal( i = 0; mult = 1; while( (c = zTxt[i++])>='a' && c<='z' ){ - n += (zTxt[i] - 'a')*mult; + n += (c - 'a')*mult; mult *= 26; } sqlite3_free(pFile->aJrnl); @@ -38694,13 +39656,14 @@ static void kvvfsDecodeJournal( static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ char zData[50]; zData[0] = 0; - sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1); + sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "sz", zData, + sizeof(zData)-1); return strtoll(zData, 0, 0); } static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ char zData[50]; sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); - return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData); + return sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, "sz", zData); } /****** sqlite3_io_methods methods ******************************************/ @@ -38715,6 +39678,7 @@ static int kvvfsClose(sqlite3_file *pProtoFile){ pFile->isJournal ? "journal" : "db")); sqlite3_free(pFile->aJrnl); sqlite3_free(pFile->aData); + memset(pFile, 0, sizeof(*pFile)); return SQLITE_OK; } @@ -38731,16 +39695,23 @@ static int kvvfsReadJrnl( assert( pFile->isJournal ); SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); if( pFile->aJrnl==0 ){ - int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); + int rc; + int szTxt = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "jrnl", + 0, 0); char *aTxt; if( szTxt<=4 ){ return SQLITE_IOERR; } aTxt = sqlite3_malloc64( szTxt+1 ); if( aTxt==0 ) return SQLITE_NOMEM; - kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); - kvvfsDecodeJournal(pFile, aTxt, szTxt); + rc = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, "jrnl", + aTxt, szTxt+1); + if( rc>=0 ){ + kvvfsDecodeJournal(pFile, aTxt, szTxt); + rc = 0; + } sqlite3_free(aTxt); + if( rc ) return rc; if( pFile->aJrnl==0 ) return SQLITE_IOERR; } if( iOfst+iAmt>pFile->nJrnl ){ @@ -38780,8 +39751,8 @@ static int kvvfsReadDb( pgno = 1; } sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); - got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, - aData, SQLITE_KVOS_SZ-1); + got = sqlite3KvvfsMethods.xRcrdRead(pFile->zClass, zKey, + aData, SQLITE_KVOS_SZ-1); if( got<0 ){ n = 0; }else{ @@ -38849,6 +39820,7 @@ static int kvvfsWriteDb( unsigned int pgno; char zKey[30]; char *aData = pFile->aData; + int rc; SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); assert( iAmt>=512 && iAmt<=65536 ); assert( (iAmt & (iAmt-1))==0 ); @@ -38857,13 +39829,13 @@ static int kvvfsWriteDb( pgno = 1 + iOfst/iAmt; sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); kvvfsEncode(zBuf, iAmt, aData); - if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){ - return SQLITE_IOERR; - } - if( iOfst+iAmt > pFile->szDb ){ - pFile->szDb = iOfst + iAmt; + rc = sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, zKey, aData); + if( 0==rc ){ + if( iOfst+iAmt > pFile->szDb ){ + pFile->szDb = iOfst + iAmt; + } } - return SQLITE_OK; + return rc; } /* @@ -38873,7 +39845,7 @@ static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); assert( size==0 ); - sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl"); + sqlite3KvvfsMethods.xRcrdDelete(pFile->zClass, "jrnl"); sqlite3_free(pFile->aJrnl); pFile->aJrnl = 0; pFile->nJrnl = 0; @@ -38892,7 +39864,7 @@ static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ pgnoMax = 2 + pFile->szDb/pFile->szPage; while( pgno<=pgnoMax ){ sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); - sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey); + sqlite3KvvfsMethods.xRcrdDelete(pFile->zClass, zKey); pgno++; } pFile->szDb = size; @@ -38924,7 +39896,7 @@ static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ }while( n>0 ); zOut[i++] = ' '; kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); - i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut); + i = sqlite3KvvfsMethods.xRcrdWrite(pFile->zClass, "jrnl", zOut); sqlite3_free(zOut); return i ? SQLITE_IOERR : SQLITE_OK; } @@ -39038,33 +40010,32 @@ static int kvvfsOpen( KVVfsFile *pFile = (KVVfsFile*)pProtoFile; if( zName==0 ) zName = ""; SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); - if( strcmp(zName, "local")==0 - || strcmp(zName, "session")==0 - ){ - pFile->isJournal = 0; - pFile->base.pMethods = &kvvfs_db_io_methods; - }else - if( strcmp(zName, "local-journal")==0 - || strcmp(zName, "session-journal")==0 - ){ + assert(!pFile->zClass); + assert(!pFile->aData); + assert(!pFile->aJrnl); + assert(!pFile->nJrnl); + assert(!pFile->base.pMethods); + pFile->szPage = -1; + pFile->szDb = -1; + if( 0==sqlite3_strglob("*-journal", zName) ){ pFile->isJournal = 1; pFile->base.pMethods = &kvvfs_jrnl_io_methods; + if( 0==strcmp("session-journal",zName) ){ + pFile->zClass = "session"; + }else if( 0==strcmp("local-journal",zName) ){ + pFile->zClass = "local"; + } }else{ - return SQLITE_CANTOPEN; + pFile->isJournal = 0; + pFile->base.pMethods = &kvvfs_db_io_methods; } - if( zName[0]=='s' ){ - pFile->zClass = "session"; - }else{ - pFile->zClass = "local"; + if( !pFile->zClass ){ + pFile->zClass = zName; } pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ); if( pFile->aData==0 ){ return SQLITE_NOMEM; } - pFile->aJrnl = 0; - pFile->nJrnl = 0; - pFile->szPage = -1; - pFile->szDb = -1; return SQLITE_OK; } @@ -39074,13 +40045,17 @@ static int kvvfsOpen( ** returning. */ static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + int rc /* The JS impl can fail with OOM in argument conversion */; if( strcmp(zPath, "local-journal")==0 ){ - sqlite3KvvfsMethods.xDelete("local", "jrnl"); + rc = sqlite3KvvfsMethods.xRcrdDelete("local", "jrnl"); }else if( strcmp(zPath, "session-journal")==0 ){ - sqlite3KvvfsMethods.xDelete("session", "jrnl"); + rc = sqlite3KvvfsMethods.xRcrdDelete("session", "jrnl"); } - return SQLITE_OK; + else{ + rc = 0; + } + return rc; } /* @@ -39094,21 +40069,42 @@ static int kvvfsAccess( int *pResOut ){ SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); +#if 0 && defined(SQLITE_WASM) + /* + ** This is not having the desired effect in the JS bindings. + ** It's ostensibly the same logic as the #else block, but + ** it's not behaving that way. + ** + ** In JS we map all zPaths to Storage objects, and -journal files + ** are mapped to the storage for the main db (which is is exactly + ** what the mapping of "local-journal" -> "local" is doing). + */ + const char *zKey = (0==sqlite3_strglob("*-journal", zPath)) + ? "jrnl" : "sz"; + *pResOut = + sqlite3KvvfsMethods.xRcrdRead(zPath, zKey, 0, 0)>0; +#else if( strcmp(zPath, "local-journal")==0 ){ - *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0; + *pResOut = + sqlite3KvvfsMethods.xRcrdRead("local", "jrnl", 0, 0)>0; }else if( strcmp(zPath, "session-journal")==0 ){ - *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0; + *pResOut = + sqlite3KvvfsMethods.xRcrdRead("session", "jrnl", 0, 0)>0; }else if( strcmp(zPath, "local")==0 ){ - *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0; + *pResOut = + sqlite3KvvfsMethods.xRcrdRead("local", "sz", 0, 0)>0; }else if( strcmp(zPath, "session")==0 ){ - *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0; + *pResOut = + sqlite3KvvfsMethods.xRcrdRead("session", "sz", 0, 0)>0; }else { *pResOut = 0; } + /*all current JS tests avoid triggering: assert( *pResOut == 0 ); */ +#endif SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); return SQLITE_OK; } @@ -44342,9 +45338,9 @@ static int unixShmMap( nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; if( pShmNode->nRegionszRegion = szRegion; @@ -44375,7 +45371,7 @@ static int unixShmMap( */ else{ static const int pgsz = 4096; - int iPg; + i64 iPg; /* Write to the last byte of each newly allocated or extended page */ assert( (nByte % pgsz)==0 ); @@ -44392,7 +45388,7 @@ static int unixShmMap( } /* Map the requested memory region into this processes address space. */ - apNew = (char **)sqlite3_realloc( + apNew = (char **)sqlite3_realloc64( pShmNode->apRegion, nReqRegion*sizeof(char *) ); if( !apNew ){ @@ -44401,8 +45397,8 @@ static int unixShmMap( } pShmNode->apRegion = apNew; while( pShmNode->nRegionhShm>=0 ){ pMem = osMmap(0, nMap, @@ -47837,7 +48833,7 @@ SQLITE_API int sqlite3_os_end(void){ ** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions ** based on the sub-platform)? */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI) +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_ANSI) # define SQLITE_WIN32_HAS_ANSI #endif @@ -47845,7 +48841,7 @@ SQLITE_API int sqlite3_os_end(void){ ** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions ** based on the sub-platform)? */ -#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \ +#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT) && \ !defined(SQLITE_WIN32_NO_WIDE) # define SQLITE_WIN32_HAS_WIDE #endif @@ -47984,16 +48980,7 @@ SQLITE_API int sqlite3_os_end(void){ */ #if SQLITE_WIN32_FILEMAPPING_API && \ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) -/* -** Two of the file mapping APIs are different under WinRT. Figure out which -** set we need. -*/ -#if SQLITE_OS_WINRT -WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ - LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); -WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); -#else #if defined(SQLITE_WIN32_HAS_ANSI) WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ DWORD, DWORD, DWORD, LPCSTR); @@ -48005,7 +48992,6 @@ WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ #endif /* defined(SQLITE_WIN32_HAS_WIDE) */ WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); -#endif /* SQLITE_OS_WINRT */ /* ** These file mapping APIs are common to both Win32 and WinRT. @@ -48296,7 +49282,7 @@ static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; ** This function is not available on Windows CE or WinRT. */ -#if SQLITE_OS_WINCE || SQLITE_OS_WINRT +#if SQLITE_OS_WINCE # define osAreFileApisANSI() 1 #endif @@ -48311,7 +49297,7 @@ static struct win_syscall { sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, #else { "AreFileApisANSI", (SYSCALL)0, 0 }, @@ -48350,7 +49336,7 @@ static struct win_syscall { #define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) { "CreateFileW", (SYSCALL)CreateFileW, 0 }, #else { "CreateFileW", (SYSCALL)0, 0 }, @@ -48359,7 +49345,7 @@ static struct win_syscall { #define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \ +#if defined(SQLITE_WIN32_HAS_ANSI) && \ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \ SQLITE_WIN32_CREATEFILEMAPPINGA { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, @@ -48370,8 +49356,8 @@ static struct win_syscall { #define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) +#if (SQLITE_OS_WINCE || defined(SQLITE_WIN32_HAS_WIDE)) && \ + (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, #else { "CreateFileMappingW", (SYSCALL)0, 0 }, @@ -48380,7 +49366,7 @@ static struct win_syscall { #define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, #else { "CreateMutexW", (SYSCALL)0, 0 }, @@ -48466,7 +49452,7 @@ static struct win_syscall { #define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ LPDWORD))aSyscall[18].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, #else { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, @@ -48483,7 +49469,7 @@ static struct win_syscall { #define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, #else { "GetFileAttributesW", (SYSCALL)0, 0 }, @@ -48500,11 +49486,7 @@ static struct win_syscall { #define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ LPVOID))aSyscall[22].pCurrent) -#if !SQLITE_OS_WINRT { "GetFileSize", (SYSCALL)GetFileSize, 0 }, -#else - { "GetFileSize", (SYSCALL)0, 0 }, -#endif #define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) @@ -48517,7 +49499,7 @@ static struct win_syscall { #define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ LPSTR*))aSyscall[24].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, #else { "GetFullPathNameW", (SYSCALL)0, 0 }, @@ -48552,16 +49534,10 @@ static struct win_syscall { #define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ LPCSTR))aSyscall[27].pCurrent) -#if !SQLITE_OS_WINRT { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, -#else - { "GetSystemInfo", (SYSCALL)0, 0 }, -#endif - #define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, - #define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) #if !SQLITE_OS_WINCE @@ -48581,7 +49557,7 @@ static struct win_syscall { #define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, #else { "GetTempPathW", (SYSCALL)0, 0 }, @@ -48589,11 +49565,7 @@ static struct win_syscall { #define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) -#if !SQLITE_OS_WINRT { "GetTickCount", (SYSCALL)GetTickCount, 0 }, -#else - { "GetTickCount", (SYSCALL)0, 0 }, -#endif #define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) @@ -48606,7 +49578,7 @@ static struct win_syscall { #define osGetVersionExA ((BOOL(WINAPI*)( \ LPOSVERSIONINFOA))aSyscall[34].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ +#if defined(SQLITE_WIN32_HAS_WIDE) && \ SQLITE_WIN32_GETVERSIONEX { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, #else @@ -48621,20 +49593,12 @@ static struct win_syscall { #define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ SIZE_T))aSyscall[36].pCurrent) -#if !SQLITE_OS_WINRT { "HeapCreate", (SYSCALL)HeapCreate, 0 }, -#else - { "HeapCreate", (SYSCALL)0, 0 }, -#endif #define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ SIZE_T))aSyscall[37].pCurrent) -#if !SQLITE_OS_WINRT { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, -#else - { "HeapDestroy", (SYSCALL)0, 0 }, -#endif #define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent) @@ -48652,16 +49616,12 @@ static struct win_syscall { #define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ LPCVOID))aSyscall[41].pCurrent) -#if !SQLITE_OS_WINRT { "HeapValidate", (SYSCALL)HeapValidate, 0 }, -#else - { "HeapValidate", (SYSCALL)0, 0 }, -#endif #define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ LPCVOID))aSyscall[42].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE { "HeapCompact", (SYSCALL)HeapCompact, 0 }, #else { "HeapCompact", (SYSCALL)0, 0 }, @@ -48677,7 +49637,7 @@ static struct win_syscall { #define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent) -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ +#if defined(SQLITE_WIN32_HAS_WIDE) && \ !defined(SQLITE_OMIT_LOAD_EXTENSION) { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, #else @@ -48686,15 +49646,11 @@ static struct win_syscall { #define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent) -#if !SQLITE_OS_WINRT { "LocalFree", (SYSCALL)LocalFree, 0 }, -#else - { "LocalFree", (SYSCALL)0, 0 }, -#endif #define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE { "LockFile", (SYSCALL)LockFile, 0 }, #else { "LockFile", (SYSCALL)0, 0 }, @@ -48716,8 +49672,7 @@ static struct win_syscall { LPOVERLAPPED))aSyscall[48].pCurrent) #endif -#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \ - (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) +#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, #else { "MapViewOfFile", (SYSCALL)0, 0 }, @@ -48745,20 +49700,12 @@ static struct win_syscall { #define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent) -#if !SQLITE_OS_WINRT { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, -#else - { "SetFilePointer", (SYSCALL)0, 0 }, -#endif #define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ DWORD))aSyscall[54].pCurrent) -#if !SQLITE_OS_WINRT { "Sleep", (SYSCALL)Sleep, 0 }, -#else - { "Sleep", (SYSCALL)0, 0 }, -#endif #define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent) @@ -48767,7 +49714,7 @@ static struct win_syscall { #define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \ LPFILETIME))aSyscall[56].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE { "UnlockFile", (SYSCALL)UnlockFile, 0 }, #else { "UnlockFile", (SYSCALL)0, 0 }, @@ -48805,15 +49752,6 @@ static struct win_syscall { #define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ LPOVERLAPPED))aSyscall[61].pCurrent) -#if SQLITE_OS_WINRT - { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, -#else - { "CreateEventExW", (SYSCALL)0, 0 }, -#endif - -#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ - DWORD,DWORD))aSyscall[62].pCurrent) - /* ** For WaitForSingleObject(), MSDN says: ** @@ -48823,7 +49761,7 @@ static struct win_syscall { { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ - DWORD))aSyscall[63].pCurrent) + DWORD))aSyscall[62].pCurrent) #if !SQLITE_OS_WINCE { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, @@ -48832,69 +49770,10 @@ static struct win_syscall { #endif #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ - BOOL))aSyscall[64].pCurrent) + BOOL))aSyscall[63].pCurrent) -#if SQLITE_OS_WINRT - { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, -#else - { "SetFilePointerEx", (SYSCALL)0, 0 }, -#endif - -#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ - PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) - -#if SQLITE_OS_WINRT - { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, -#else - { "GetFileInformationByHandleEx", (SYSCALL)0, 0 }, -#endif - -#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ - FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) - -#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) - { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, -#else - { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, -#endif - -#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ - SIZE_T))aSyscall[67].pCurrent) - -#if SQLITE_OS_WINRT - { "CreateFile2", (SYSCALL)CreateFile2, 0 }, -#else - { "CreateFile2", (SYSCALL)0, 0 }, -#endif - -#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ - LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) - -#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) - { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, -#else - { "LoadPackagedLibrary", (SYSCALL)0, 0 }, -#endif - -#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ - DWORD))aSyscall[69].pCurrent) - -#if SQLITE_OS_WINRT - { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, -#else - { "GetTickCount64", (SYSCALL)0, 0 }, -#endif - -#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) - -#if SQLITE_OS_WINRT - { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, -#else { "GetNativeSystemInfo", (SYSCALL)0, 0 }, -#endif - -#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ - LPSYSTEM_INFO))aSyscall[71].pCurrent) + /* ^^^^^^^^^^^^^^^^^^^----------------^------- placeholder only */ #if defined(SQLITE_WIN32_HAS_ANSI) { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, @@ -48902,7 +49781,7 @@ static struct win_syscall { { "OutputDebugStringA", (SYSCALL)0, 0 }, #endif -#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) +#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[65].pCurrent) #if defined(SQLITE_WIN32_HAS_WIDE) { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, @@ -48910,20 +49789,11 @@ static struct win_syscall { { "OutputDebugStringW", (SYSCALL)0, 0 }, #endif -#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) +#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[66].pCurrent) { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, -#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) - -#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) - { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, -#else - { "CreateFileMappingFromApp", (SYSCALL)0, 0 }, -#endif - -#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ - LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent) +#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[67].pCurrent) /* ** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" @@ -48938,25 +49808,25 @@ static struct win_syscall { { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, #define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ - SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) + SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[68].pCurrent) #endif /* defined(InterlockedCompareExchange) */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID { "UuidCreate", (SYSCALL)UuidCreate, 0 }, #else { "UuidCreate", (SYSCALL)0, 0 }, #endif -#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) +#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[69].pCurrent) -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, #else { "UuidCreateSequential", (SYSCALL)0, 0 }, #endif #define osUuidCreateSequential \ - ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) + ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[70].pCurrent) #if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, @@ -48965,7 +49835,7 @@ static struct win_syscall { #endif #define osFlushViewOfFile \ - ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) + ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[71].pCurrent) /* ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() @@ -48982,7 +49852,7 @@ static struct win_syscall { #define osCreateEvent ( \ (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ - aSyscall[80].pCurrent \ + aSyscall[72].pCurrent \ ) /* @@ -48999,7 +49869,7 @@ static struct win_syscall { { "CancelIo", (SYSCALL)0, 0 }, #endif -#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) +#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[73].pCurrent) #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32) { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 }, @@ -49007,7 +49877,7 @@ static struct win_syscall { { "GetModuleHandleW", (SYSCALL)0, 0 }, #endif -#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent) +#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[74].pCurrent) #ifndef _WIN32 { "getenv", (SYSCALL)getenv, 0 }, @@ -49015,7 +49885,7 @@ static struct win_syscall { { "getenv", (SYSCALL)0, 0 }, #endif -#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent) +#define osGetenv ((const char *(*)(const char *))aSyscall[75].pCurrent) #ifndef _WIN32 { "getcwd", (SYSCALL)getcwd, 0 }, @@ -49023,7 +49893,7 @@ static struct win_syscall { { "getcwd", (SYSCALL)0, 0 }, #endif -#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent) +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[76].pCurrent) #ifndef _WIN32 { "readlink", (SYSCALL)readlink, 0 }, @@ -49031,7 +49901,7 @@ static struct win_syscall { { "readlink", (SYSCALL)0, 0 }, #endif -#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent) +#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[77].pCurrent) #ifndef _WIN32 { "lstat", (SYSCALL)lstat, 0 }, @@ -49039,7 +49909,7 @@ static struct win_syscall { { "lstat", (SYSCALL)0, 0 }, #endif -#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent) +#define osLstat ((int(*)(const char*,struct stat*))aSyscall[78].pCurrent) #ifndef _WIN32 { "__errno", (SYSCALL)__errno, 0 }, @@ -49047,7 +49917,7 @@ static struct win_syscall { { "__errno", (SYSCALL)0, 0 }, #endif -#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)()) +#define osErrno (*((int*(*)(void))aSyscall[79].pCurrent)()) #ifndef _WIN32 { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 }, @@ -49056,7 +49926,7 @@ static struct win_syscall { #endif #define osCygwin_conv_path ((size_t(*)(unsigned int, \ - const void *, void *, size_t))aSyscall[88].pCurrent) + const void *, void *, size_t))aSyscall[80].pCurrent) }; /* End of the overrideable system calls */ @@ -49160,10 +50030,10 @@ SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){ hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ DWORD lastErrno = osGetLastError(); if( lastErrno==NO_ERROR ){ @@ -49276,28 +50146,11 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ } #endif /* _WIN32 */ -/* -** The following routine suspends the current thread for at least ms -** milliseconds. This is equivalent to the Win32 Sleep() interface. -*/ -#if SQLITE_OS_WINRT -static HANDLE sleepObj = NULL; -#endif - SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){ -#if SQLITE_OS_WINRT - if ( sleepObj==NULL ){ - sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, - SYNCHRONIZE); - } - assert( sleepObj!=NULL ); - osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE); -#else osSleep(milliseconds); -#endif } -#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ +#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && \ SQLITE_THREADSAFE>0 SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ DWORD rc; @@ -49321,7 +50174,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ #if !SQLITE_WIN32_GETVERSIONEX # define osIsNT() (1) -#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) +#elif SQLITE_OS_WINCE || !defined(SQLITE_WIN32_HAS_ANSI) # define osIsNT() (1) #elif !defined(SQLITE_WIN32_HAS_WIDE) # define osIsNT() (0) @@ -49334,13 +50187,7 @@ SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ ** based on the NT kernel. */ SQLITE_API int sqlite3_win32_is_nt(void){ -#if SQLITE_OS_WINRT - /* - ** NOTE: The WinRT sub-platform is always assumed to be based on the NT - ** kernel. - */ - return 1; -#elif SQLITE_WIN32_GETVERSIONEX +#if SQLITE_WIN32_GETVERSIONEX if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){ #if defined(SQLITE_WIN32_HAS_ANSI) OSVERSIONINFOA sInfo; @@ -49382,7 +50229,7 @@ static void *winMemMalloc(int nBytes){ hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif assert( nBytes>=0 ); @@ -49404,7 +50251,7 @@ static void winMemFree(void *pPrior){ hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ @@ -49425,7 +50272,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){ hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); #endif assert( nBytes>=0 ); @@ -49453,7 +50300,7 @@ static int winMemSize(void *p){ hHeap = winMemGetHeap(); assert( hHeap!=0 ); assert( hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) ); #endif if( !p ) return 0; @@ -49483,7 +50330,7 @@ static int winMemInit(void *pAppData){ assert( pWinMemData->magic1==WINMEM_MAGIC1 ); assert( pWinMemData->magic2==WINMEM_MAGIC2 ); -#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE +#if SQLITE_WIN32_HEAP_CREATE if( !pWinMemData->hHeap ){ DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap; @@ -49516,7 +50363,7 @@ static int winMemInit(void *pAppData){ #endif assert( pWinMemData->hHeap!=0 ); assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif return SQLITE_OK; @@ -49534,7 +50381,7 @@ static void winMemShutdown(void *pAppData){ if( pWinMemData->hHeap ){ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); -#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) +#if defined(SQLITE_WIN32_MALLOC_VALIDATE) assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); #endif if( pWinMemData->bOwned ){ @@ -49915,17 +50762,6 @@ static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ char *zOut = 0; if( osIsNT() ){ -#if SQLITE_OS_WINRT - WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1]; - dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - lastErrno, - 0, - zTempWide, - SQLITE_WIN32_MAX_ERRMSG_CHARS, - 0); -#else LPWSTR zTempWide = NULL; dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | @@ -49936,16 +50772,13 @@ static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ (LPWSTR) &zTempWide, 0, 0); -#endif if( dwLen > 0 ){ /* allocate a buffer and convert to UTF8 */ sqlite3BeginBenignMalloc(); zOut = winUnicodeToUtf8(zTempWide); sqlite3EndBenignMalloc(); -#if !SQLITE_OS_WINRT /* free the system buffer allocated by FormatMessage */ osLocalFree(zTempWide); -#endif } } #ifdef SQLITE_WIN32_HAS_ANSI @@ -50606,7 +51439,6 @@ static int winHandleUnlock(HANDLE h, int iOff, int nByte){ static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ int rc = SQLITE_OK; /* Return value */ -#if !SQLITE_OS_WINRT LONG upperBits; /* Most sig. 32 bits of new offset */ LONG lowerBits; /* Least sig. 32 bits of new offset */ DWORD dwRet; /* Value returned by SetFilePointer() */ @@ -50628,20 +51460,7 @@ static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ rc = SQLITE_IOERR_SEEK; } } -#else - /* This implementation works for WinRT. */ - LARGE_INTEGER x; /* The new offset */ - BOOL bRet; /* Value returned by SetFilePointerEx() */ - - x.QuadPart = iOffset; - bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN); - - if(!bRet){ - rc = SQLITE_IOERR_SEEK; - } -#endif - - OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc))); + OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset,sqlite3ErrName(rc))); return rc; } @@ -50942,17 +51761,6 @@ static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){ */ static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ int rc = SQLITE_OK; - -#if SQLITE_OS_WINRT - FILE_STANDARD_INFO info; - BOOL b; - b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)); - if( b ){ - *pnByte = info.EndOfFile.QuadPart; - }else{ - rc = SQLITE_IOERR_FSTAT; - } -#else DWORD upperBits = 0; DWORD lowerBits = 0; @@ -50962,8 +51770,6 @@ static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){ rc = SQLITE_IOERR_FSTAT; } -#endif - return rc; } @@ -51162,20 +51968,6 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ assert( pSize!=0 ); SimulateIOError(return SQLITE_IOERR_FSTAT); OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize)); - -#if SQLITE_OS_WINRT - { - FILE_STANDARD_INFO info; - if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo, - &info, sizeof(info)) ){ - *pSize = info.EndOfFile.QuadPart; - }else{ - pFile->lastErrno = osGetLastError(); - rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, - "winFileSize", pFile->zPath); - } - } -#else { DWORD upperBits; DWORD lowerBits; @@ -51190,7 +51982,6 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ "winFileSize", pFile->zPath); } } -#endif OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n", pFile->h, pSize, *pSize, sqlite3ErrName(rc))); return rc; @@ -52152,20 +52943,6 @@ static int winHandleOpen( ** TODO: retry-on-ioerr. */ if( osIsNT() ){ -#if SQLITE_OS_WINRT - CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; - memset(&extendedParameters, 0, sizeof(extendedParameters)); - extendedParameters.dwSize = sizeof(extendedParameters); - extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; - extendedParameters.dwFileFlags = flag_overlapped; - extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; - h = osCreateFile2((LPCWSTR)zConverted, - (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */ - FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ - OPEN_ALWAYS, /* dwCreationDisposition */ - &extendedParameters - ); -#else h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ @@ -52174,7 +52951,6 @@ static int winHandleOpen( FILE_ATTRIBUTE_NORMAL|flag_overlapped, NULL ); -#endif }else{ /* Due to pre-processor directives earlier in this file, ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ @@ -52252,11 +53028,29 @@ SQLITE_API int sqlite3_win_test_unc_locking = 0; /* ** Return true if the string passed as the only argument is likely -** to be a UNC path. In other words, if it starts with "\\". +** to be a UNC path. Return false if note. +** +** Return true if: +** +** (1) The name begins with "\\" +** (2) But does not begin with "\\?\C:\" where C can be any alphabetic +** character. +** +** For testing, also return true in all cases if the global variable +** sqlite3_win_test_unc_locking is true. */ static int winIsUNCPath(const char *zFile){ if( zFile[0]=='\\' && zFile[1]=='\\' ){ - return 1; + if( zFile[2]=='?' + && zFile[3]=='\\' + && sqlite3Isalpha(zFile[4]) + && zFile[5]==':' + && winIsDirSep(zFile[6]) + ){ + return sqlite3_win_test_unc_locking; + }else{ + return 1; + } } return sqlite3_win_test_unc_locking; } @@ -52594,7 +53388,7 @@ static int winShmMap( if( pShmNode->nRegion<=iRegion ){ HANDLE hShared = pShmNode->hSharedShm; struct ShmRegion *apNew; /* New aRegion[] array */ - int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ + i64 nByte = ((i64)iRegion+1)*(i64)szRegion; /* Minimum file size */ sqlite3_int64 sz; /* Current size of wal-index file */ pShmNode->szRegion = szRegion; @@ -52625,7 +53419,7 @@ static int winShmMap( /* Map the requested memory region into this processes address space. */ apNew = (struct ShmRegion*)sqlite3_realloc64( - pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) + pShmNode->aRegion, ((i64)iRegion+1)*sizeof(apNew[0]) ); if( !apNew ){ rc = SQLITE_IOERR_NOMEM_BKPT; @@ -52642,29 +53436,20 @@ static int winShmMap( HANDLE hMap = NULL; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ -#if SQLITE_OS_WINRT - hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); -#elif defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); #endif - - OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%lld, rc=%s\n", osGetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); if( hMap ){ - int iOffset = pShmNode->nRegion*szRegion; + i64 iOffset = pShmNode->nRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; -#if SQLITE_OS_WINRT - pMap = osMapViewOfFileFromApp(hMap, flags, - iOffset - iOffsetShift, szRegion + iOffsetShift - ); -#else pMap = osMapViewOfFile(hMap, flags, - 0, iOffset - iOffsetShift, szRegion + iOffsetShift + 0, iOffset - iOffsetShift, (i64)szRegion + iOffsetShift ); -#endif OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n", osGetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, pMap ? "ok" : "failed")); @@ -52685,7 +53470,7 @@ static int winShmMap( shmpage_out: if( pShmNode->nRegion>iRegion ){ - int iOffset = iRegion*szRegion; + i64 iOffset = (i64)iRegion*(i64)szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; char *p = (char *)pShmNode->aRegion[iRegion].pMap; *pp = (void *)&p[iOffsetShift]; @@ -52797,9 +53582,7 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ flags |= FILE_MAP_WRITE; } #endif -#if SQLITE_OS_WINRT - pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); -#elif defined(SQLITE_WIN32_HAS_WIDE) +#if defined(SQLITE_WIN32_HAS_WIDE) pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, (DWORD)((nMap>>32) & 0xffffffff), (DWORD)(nMap & 0xffffffff), NULL); @@ -52819,11 +53602,7 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ } assert( (nMap % winSysInfo.dwPageSize)==0 ); assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff ); -#if SQLITE_OS_WINRT - pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap); -#else pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap); -#endif if( pNew==NULL ){ osCloseHandle(pFd->hMap); pFd->hMap = NULL; @@ -53158,7 +53937,6 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ } #endif -#if !SQLITE_OS_WINRT && defined(_WIN32) else if( osIsNT() ){ char *zMulti; LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) ); @@ -53212,7 +53990,6 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ } } #endif /* SQLITE_WIN32_HAS_ANSI */ -#endif /* !SQLITE_OS_WINRT */ /* ** Check to make sure the temporary directory ends with an appropriate @@ -53387,13 +54164,6 @@ static int winOpen( memset(pFile, 0, sizeof(winFile)); pFile->h = INVALID_HANDLE_VALUE; -#if SQLITE_OS_WINRT - if( !zUtf8Name && !sqlite3_temp_directory ){ - sqlite3_log(SQLITE_ERROR, - "sqlite3_temp_directory variable should be set for WinRT"); - } -#endif - /* If the second argument to this function is NULL, generate a ** temporary file name to use */ @@ -53476,31 +54246,6 @@ static int winOpen( #endif if( osIsNT() ){ -#if SQLITE_OS_WINRT - CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; - extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); - extendedParameters.dwFileAttributes = - dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; - extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; - extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; - extendedParameters.lpSecurityAttributes = NULL; - extendedParameters.hTemplateFile = NULL; - do{ - h = osCreateFile2((LPCWSTR)zConverted, - dwDesiredAccess, - dwShareMode, - dwCreationDisposition, - &extendedParameters); - if( h!=INVALID_HANDLE_VALUE ) break; - if( isReadWrite ){ - int rc2; - sqlite3BeginBenignMalloc(); - rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO); - sqlite3EndBenignMalloc(); - if( rc2==SQLITE_OK && isRO ) break; - } - }while( winRetryIoerr(&cnt, &lastErrno) ); -#else do{ h = osCreateFileW((LPCWSTR)zConverted, dwDesiredAccess, @@ -53517,7 +54262,6 @@ static int winOpen( if( rc2==SQLITE_OK && isRO ) break; } }while( winRetryIoerr(&cnt, &lastErrno) ); -#endif } #ifdef SQLITE_WIN32_HAS_ANSI else{ @@ -53654,25 +54398,7 @@ static int winDelete( } if( osIsNT() ){ do { -#if SQLITE_OS_WINRT - WIN32_FILE_ATTRIBUTE_DATA sAttrData; - memset(&sAttrData, 0, sizeof(sAttrData)); - if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard, - &sAttrData) ){ - attr = sAttrData.dwFileAttributes; - }else{ - lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND - || lastErrno==ERROR_PATH_NOT_FOUND ){ - rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ - }else{ - rc = SQLITE_ERROR; - } - break; - } -#else attr = osGetFileAttributesW(zConverted); -#endif if ( attr==INVALID_FILE_ATTRIBUTES ){ lastErrno = osGetLastError(); if( lastErrno==ERROR_FILE_NOT_FOUND @@ -53795,6 +54521,7 @@ static int winAccess( attr = sAttrData.dwFileAttributes; } }else{ + if( noRetry ) lastErrno = osGetLastError(); winLogIoerr(cnt, __LINE__); if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ sqlite3_free(zConverted); @@ -53963,7 +54690,7 @@ static int winFullPathnameNoMutex( int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */ ){ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE int nByte; void *zConverted; char *zOut; @@ -54052,7 +54779,7 @@ static int winFullPathnameNoMutex( } #endif /* __CYGWIN__ */ -#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32) +#if SQLITE_OS_WINCE && defined(_WIN32) SimulateIOError( return SQLITE_ERROR ); /* WinCE has no concept of a relative pathname, or so I am told. */ /* WinRT has no way to convert a relative path to an absolute one. */ @@ -54071,7 +54798,7 @@ static int winFullPathnameNoMutex( return SQLITE_OK; #endif -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +#if !SQLITE_OS_WINCE #if defined(_WIN32) /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this @@ -54203,11 +54930,7 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ return 0; } if( osIsNT() ){ -#if SQLITE_OS_WINRT - h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); -#else h = osLoadLibraryW((LPCWSTR)zConverted); -#endif } #ifdef SQLITE_WIN32_HAS_ANSI else{ @@ -54289,23 +55012,16 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ DWORD pid = osGetCurrentProcessId(); xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD)); } -#if SQLITE_OS_WINRT - { - ULONGLONG cnt = osGetTickCount64(); - xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG)); - } -#else { DWORD cnt = osGetTickCount(); xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD)); } -#endif /* SQLITE_OS_WINRT */ { LARGE_INTEGER i; osQueryPerformanceCounter(&i); xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER)); } -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID +#if !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID { UUID id; memset(&id, 0, sizeof(UUID)); @@ -54315,7 +55031,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ osUuidCreateSequential(&id); xorMemory(&e, (unsigned char*)&id, sizeof(UUID)); } -#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */ +#endif /* !SQLITE_OS_WINCE && SQLITE_WIN32_USE_UUID */ return e.nXor>nBuf ? nBuf : e.nXor; #endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */ } @@ -54546,15 +55262,16 @@ SQLITE_API int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==89 ); + assert( ArraySize(aSyscall)==81 ); + assert( strcmp(aSyscall[0].zName,"AreFileApisANSI")==0 ); + assert( strcmp(aSyscall[20].zName,"GetFileAttributesA")==0 ); + assert( strcmp(aSyscall[40].zName,"HeapReAlloc")==0 ); + assert( strcmp(aSyscall[60].zName,"WideCharToMultiByte")==0 ); + assert( strcmp(aSyscall[80].zName,"cygwin_conv_path")==0 ); /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); -#if SQLITE_OS_WINRT - osGetNativeSystemInfo(&winSysInfo); -#else osGetSystemInfo(&winSysInfo); -#endif assert( winSysInfo.dwAllocationGranularity>0 ); assert( winSysInfo.dwPageSize>0 ); @@ -54578,17 +55295,9 @@ SQLITE_API int sqlite3_os_init(void){ } SQLITE_API int sqlite3_os_end(void){ -#if SQLITE_OS_WINRT - if( sleepObj!=NULL ){ - osCloseHandle(sleepObj); - sleepObj = NULL; - } -#endif - #ifndef SQLITE_OMIT_WAL winBigLock = 0; #endif - return SQLITE_OK; } @@ -55357,7 +56066,7 @@ SQLITE_API unsigned char *sqlite3_serialize( sqlite3_int64 sz; int szPage = 0; sqlite3_stmt *pStmt = 0; - unsigned char *pOut; + unsigned char *pOut = 0; char *zSql; int rc; @@ -55367,12 +56076,13 @@ SQLITE_API unsigned char *sqlite3_serialize( return 0; } #endif + sqlite3_mutex_enter(db->mutex); if( zSchema==0 ) zSchema = db->aDb[0].zDbSName; p = memdbFromDbSchema(db, zSchema); iDb = sqlite3FindDbName(db, zSchema); if( piSize ) *piSize = -1; - if( iDb<0 ) return 0; + if( iDb<0 ) goto serialize_out; if( p ){ MemStore *pStore = p->pStore; assert( pStore->pMutex==0 ); @@ -55383,19 +56093,17 @@ SQLITE_API unsigned char *sqlite3_serialize( pOut = sqlite3_malloc64( pStore->sz ); if( pOut ) memcpy(pOut, pStore->aData, pStore->sz); } - return pOut; + goto serialize_out; } pBt = db->aDb[iDb].pBt; - if( pBt==0 ) return 0; + if( pBt==0 ) goto serialize_out; szPage = sqlite3BtreeGetPageSize(pBt); zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema); rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM; sqlite3_free(zSql); - if( rc ) return 0; + if( rc ) goto serialize_out; rc = sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ){ - pOut = 0; - }else{ + if( rc==SQLITE_ROW ){ sz = sqlite3_column_int64(pStmt, 0)*szPage; if( sz==0 ){ sqlite3_reset(pStmt); @@ -55429,6 +56137,9 @@ SQLITE_API unsigned char *sqlite3_serialize( } } sqlite3_finalize(pStmt); + + serialize_out: + sqlite3_mutex_leave(db->mutex); return pOut; } @@ -55474,10 +56185,10 @@ SQLITE_API int sqlite3_deserialize( if( rc ) goto end_deserialize; db->init.iDb = (u8)iDb; db->init.reopenMemdb = 1; - rc = sqlite3_step(pStmt); + sqlite3_step(pStmt); db->init.reopenMemdb = 0; - if( rc!=SQLITE_DONE ){ - rc = SQLITE_ERROR; + rc = sqlite3_finalize(pStmt); + if( rc!=SQLITE_OK ){ goto end_deserialize; } p = memdbFromDbSchema(db, zSchema); @@ -55498,7 +56209,6 @@ SQLITE_API int sqlite3_deserialize( } end_deserialize: - sqlite3_finalize(pStmt); if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){ sqlite3_free(pData); } @@ -57283,22 +57993,24 @@ static int pcache1InitBulk(PCache1 *pCache){ if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ szBulk = pCache->szAlloc*(i64)pCache->nMax; } - zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); - sqlite3EndBenignMalloc(); - if( zBulk ){ - int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; - do{ - PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; - pX->page.pBuf = zBulk; - pX->page.pExtra = (u8*)pX + ROUND8(sizeof(*pX)); - assert( EIGHT_BYTE_ALIGNMENT( pX->page.pExtra ) ); - pX->isBulkLocal = 1; - pX->isAnchor = 0; - pX->pNext = pCache->pFree; - pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ - pCache->pFree = pX; - zBulk += pCache->szAlloc; - }while( --nBulk ); + if( szBulk>=pCache->szAlloc ){ + zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); + sqlite3EndBenignMalloc(); + if( zBulk ){ + int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; + do{ + PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; + pX->page.pBuf = zBulk; + pX->page.pExtra = (u8*)pX + ROUND8(sizeof(*pX)); + assert( EIGHT_BYTE_ALIGNMENT( pX->page.pExtra ) ); + pX->isBulkLocal = 1; + pX->isAnchor = 0; + pX->pNext = pCache->pFree; + pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ + pCache->pFree = pX; + zBulk += pCache->szAlloc; + }while( --nBulk ); + } } return pCache->pFree!=0; } @@ -59737,6 +60449,8 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); if( iRead ) return 0; /* Case (4) */ } +#else + UNUSED_PARAMETER(pgno); #endif assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 ); if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd) @@ -60157,17 +60871,17 @@ static int jrnlBufferSize(Pager *pPager){ */ #ifdef SQLITE_CHECK_PAGES /* -** Return a 32-bit hash of the page data for pPage. +** Return a 64-bit hash of the page data for pPage. */ -static u32 pager_datahash(int nByte, unsigned char *pData){ - u32 hash = 0; +static u64 pager_datahash(int nByte, unsigned char *pData){ + u64 hash = 0; int i; for(i=0; ipPager->pageSize, (unsigned char *)pPage->pData); } static void pager_set_pagehash(PgHdr *pPage){ @@ -60194,39 +60908,43 @@ static void checkPage(PgHdr *pPg){ #endif /* SQLITE_CHECK_PAGES */ /* -** When this is called the journal file for pager pPager must be open. -** This function attempts to read a super-journal file name from the -** end of the file and, if successful, copies it into memory supplied -** by the caller. See comments above writeSuperJournal() for the format -** used to store a super-journal file name at the end of a journal file. -** -** zSuper must point to a buffer of at least nSuper bytes allocated by -** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is -** enough space to write the super-journal name). If the super-journal -** name in the journal is longer than nSuper bytes (including a -** nul-terminator), then this is handled as if no super-journal name -** were present in the journal. +** Free a buffer allocated by the readSuperJournal() function. +*/ +static void freeSuperJournal(char *zSuper){ + if( zSuper ){ + sqlite3_free(&zSuper[-4]); + } +} + +/* +** Parameter pJrnl is a file-handle open on a journal file. This function +** attempts to read a super-journal file name from the end of the journal +** file. If successful, it sets output parameter (*pzSuper) to point to a +** buffer containing the super-journal name as a nul-terminated string. +** The caller is responsible for freeing the buffer using freeSuperJournal(). ** -** If a super-journal file name is present at the end of the journal -** file, then it is copied into the buffer pointed to by zSuper. A -** nul-terminator byte is appended to the buffer following the -** super-journal file name. +** Refer to comments above writeSuperJournal() for the format used to store +** a super-journal file name at the end of a journal file. ** -** If it is determined that no super-journal file name is present -** zSuper[0] is set to 0 and SQLITE_OK returned. +** Parameter nSuper is passed the maximum allowable size of the super journal +** name in bytes. If the super-journal name in the journal is longer than +** nSuper bytes (including a nul-terminator), then this is handled as if no +** super-journal name were present in the journal. ** -** If an error occurs while reading from the journal file, an SQLite -** error code is returned. +** If there is no super-journal name at the end of pJrnl, (*pzSuper) is +** set to 0 and SQLITE_OK is returned. Or, if an error occurs while reading +** the super-journal name, an SQLite error code is returned and (*pzSuper) +** is set to 0. */ -static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u64 nSuper){ +static int readSuperJournal(sqlite3_file *pJrnl, u64 nSuper, char **pzSuper){ int rc; /* Return code */ u32 len; /* Length in bytes of super-journal name */ i64 szJ; /* Total size in bytes of journal file pJrnl */ u32 cksum; /* MJ checksum value read from journal */ - u32 u; /* Unsigned loop counter */ unsigned char aMagic[8]; /* A buffer to hold the magic header */ - zSuper[0] = '\0'; + char *zOut = 0; + *pzSuper = 0; if( SQLITE_OK!=(rc = sqlite3OsFileSize(pJrnl, &szJ)) || szJ<16 || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len)) @@ -60236,27 +60954,34 @@ static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u64 nSuper){ || SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum)) || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8)) || memcmp(aMagic, aJournalMagic, 8) - || SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, zSuper, len, szJ-16-len)) ){ return rc; } - /* See if the checksum matches the super-journal name */ - for(u=0; uzJournal */ + + /* Check if this looks like a real super-journal name. If it does not, + ** return SQLITE_OK without attempting to delete it. This is to limit + ** the degree to which a crafted journal file can be used to cause + ** SQLite to delete arbitrary files. */ + if( pagerIsSuperJrnlName(zSuper)==0 ){ + return SQLITE_OK; + } /* Allocate space for both the pJournal and pSuper file descriptors. ** If successful, open the super-journal file for reading. @@ -61486,9 +62252,8 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ */ rc = sqlite3OsFileSize(pSuper, &nSuperJournal); if( rc!=SQLITE_OK ) goto delsuper_out; - nSuperPtr = 1 + (i64)pVfs->mxPathname; - assert( nSuperJournal>=0 && nSuperPtr>0 ); - zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2); + assert( nSuperJournal>=0 ); + zFree = sqlite3Malloc(4 + nSuperJournal + 2); if( !zFree ){ rc = SQLITE_NOMEM_BKPT; goto delsuper_out; @@ -61497,7 +62262,6 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ } zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0; zSuperJournal = &zFree[4]; - zSuperPtr = &zSuperJournal[nSuperJournal+2]; rc = sqlite3OsRead(pSuper, zSuperJournal, (int)nSuperJournal, 0); if( rc!=SQLITE_OK ) goto delsuper_out; zSuperJournal[nSuperJournal] = 0; @@ -61505,43 +62269,56 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){ zJournal = zSuperJournal; while( (zJournal-zSuperJournal)zJournal)==0 ){ + bSeen = 1; + }else{ + int exists; + rc = sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS, &exists); if( rc!=SQLITE_OK ){ goto delsuper_out; } + if( exists ){ + char *zSuperPtr = 0; - rc = readSuperJournal(pJournal, zSuperPtr, nSuperPtr); - sqlite3OsClose(pJournal); - if( rc!=SQLITE_OK ){ - goto delsuper_out; - } + /* One of the journals pointed to by the super-journal exists. + ** Open it and check if it points at the super-journal. If + ** so, return without deleting the super-journal file. + ** NB: zJournal is really a MAIN_JOURNAL. But call it a + ** SUPER_JOURNAL here so that the VFS will not send the zJournal + ** name into sqlite3_database_file_object(). + */ + int c; + int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_SUPER_JOURNAL); + rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0); + if( rc!=SQLITE_OK ){ + goto delsuper_out; + } - c = zSuperPtr[0]!=0 && strcmp(zSuperPtr, zSuper)==0; - if( c ){ - /* We have a match. Do not delete the super-journal file. */ - goto delsuper_out; + rc = readSuperJournal(pJournal, 1+(u64)pVfs->mxPathname, &zSuperPtr); + sqlite3OsClose(pJournal); + if( rc!=SQLITE_OK ){ + assert( zSuperPtr==0 ); + goto delsuper_out; + } + + c = zSuperPtr!=0 && strcmp(zSuperPtr, zSuper)==0; + freeSuperJournal(zSuperPtr); + if( c ){ + /* We have a match. Do not delete the super-journal file. */ + goto delsuper_out; + } } } zJournal += (sqlite3Strlen30(zJournal)+1); } sqlite3OsClose(pSuper); - rc = sqlite3OsDelete(pVfs, zSuper, 0); + if( bSeen ){ + /* Only delete the super-journal if bSeen is true - indicating that + ** the super-journal contained a pointer to this database's journal + ** file. */ + rc = sqlite3OsDelete(pVfs, zSuper, 0); + } delsuper_out: sqlite3_free(zFree); @@ -61746,19 +62523,11 @@ static int pager_playback(Pager *pPager, int isHot){ ** If a super-journal file name is specified, but the file is not ** present on disk, then the journal is not hot and does not need to be ** played back. - ** - ** TODO: Technically the following is an error because it assumes that - ** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that - ** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize. */ - zSuper = pPager->pTmpSpace; - rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); - if( rc==SQLITE_OK && zSuper[0] ){ + rc = readSuperJournal(pPager->jfd, 1+(i64)pPager->pVfs->mxPathname, &zSuper); + if( rc==SQLITE_OK && zSuper ){ rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res); } - zSuper = 0; if( rc!=SQLITE_OK || !res ){ goto end_playback; } @@ -61887,30 +62656,20 @@ static int pager_playback(Pager *pPager, int isHot){ */ pPager->changeCountDone = pPager->tempFile; - if( rc==SQLITE_OK ){ - /* Leave 4 bytes of space before the super-journal filename in memory. - ** This is because it may end up being passed to sqlite3OsOpen(), in - ** which case it requires 4 0x00 bytes in memory immediately before - ** the filename. */ - zSuper = &pPager->pTmpSpace[4]; - rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname); - testcase( rc!=SQLITE_OK ); - } if( rc==SQLITE_OK && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ rc = sqlite3PagerSync(pPager, 0); } if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zSuper[0]!='\0', 0); + rc = pager_end_transaction(pPager, zSuper!=0, 0); testcase( rc!=SQLITE_OK ); } - if( rc==SQLITE_OK && zSuper[0] && res ){ + if( rc==SQLITE_OK && zSuper && res ){ /* If there was a super-journal and this routine will return success, ** see if it is possible to delete the super-journal. */ - assert( zSuper==&pPager->pTmpSpace[4] ); - memset(pPager->pTmpSpace, 0, 4); + assert( memcmp(&zSuper[-4], "\0\0\0\0", 4)==0 ); rc = pager_delsuper(pPager, zSuper); testcase( rc!=SQLITE_OK ); } @@ -61923,6 +62682,7 @@ static int pager_playback(Pager *pPager, int isHot){ ** back a journal created by a process with a different sector size ** value. Reset it to the correct value for this process. */ + freeSuperJournal(zSuper); setSectorSize(pPager); return rc; } @@ -63116,6 +63876,8 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); pPager->pWal = 0; } +#else + UNUSED_PARAMETER(db); #endif pager_reset(pPager); if( MEMDB ){ @@ -67780,6 +68542,12 @@ static int walDecodeFrame( return 0; } + /* Need a valid page size + */ + if( !pWal->szPage ){ + return 0; + } + /* A frame is only valid if a checksum of the WAL header, ** all prior frames, the first 16 bytes of this frame-header, ** and the frame-data matches the checksum in the last 8 @@ -67883,7 +68651,7 @@ static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){ /* ** Compute a hash on a page number. The resulting hash value must land -** between 0 and (HASHTABLE_NSLOT-1). The walHashNext() function advances +** between 0 and (HASHTABLE_NSLOT-1). The walNextHash() function advances ** the hash to the next value in the event of a collision. */ static int walHash(u32 iPage){ @@ -68091,7 +68859,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ if( (nCollide--)==0 ) return SQLITE_CORRUPT_BKPT; } - sLoc.aPgno[idx-1] = iPage; + sLoc.aPgno[(idx-1)&(HASHTABLE_NPAGE-1)] = iPage; AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT @@ -69634,7 +70402,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ /* Allocate a buffer to read frames into */ assert( (pWal->szPage & (pWal->szPage-1))==0 ); - assert( pWal->szPage>=512 && pWal->szPage<=65536 ); + assert( (pWal->szPage>=512 && pWal->szPage<=65536) || pWal->szPage==0 ); szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc64(szFrame); if( aFrame==0 ){ @@ -70339,7 +71107,10 @@ static int walFindFrame( SEH_INJECT_FAULT; while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){ u32 iFrame = iH + sLoc.iZero; - if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){ + if( iFrame<=iLast + && iFrame>=pWal->minFrame + && sLoc.aPgno[(iH-1)&(HASHTABLE_NPAGE-1)]==pgno + ){ assert( iFrame>iRead || CORRUPT_DB ); iRead = iFrame; } @@ -72129,6 +72900,9 @@ struct IntegrityCk { u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ i64 nRow; /* Number of rows visited in current tree */ +#ifdef SQLITE_DEBUG + u32 mxHeap; /* Maximum number of entries in the Min-heap */ +#endif }; /* @@ -73714,7 +74488,7 @@ static void btreeParseCellPtr( CellInfo *pInfo /* Fill in this structure */ ){ u8 *pIter; /* For scanning through pCell */ - u32 nPayload; /* Number of bytes of cell payload */ + u64 nPayload; /* Number of bytes of cell payload */ u64 iKey; /* Extracted Key value */ assert( sqlite3_mutex_held(pPage->pBt->mutex) ); @@ -73736,6 +74510,7 @@ static void btreeParseCellPtr( do{ nPayload = (nPayload<<7) | (*++pIter & 0x7f); }while( (*pIter)>=0x80 && pIternKey = *(i64*)&iKey; - pInfo->nPayload = nPayload; + pInfo->nPayload = (u32)nPayload; pInfo->pPayload = pIter; testcase( nPayload==pPage->maxLocal ); testcase( nPayload==(u32)pPage->maxLocal+1 ); - assert( nPayload>=0 ); assert( pPage->maxLocal <= BT_MAX_LOCAL ); if( nPayload<=pPage->maxLocal ){ /* This is the (easy) common case where the entire payload fits @@ -74590,8 +75364,12 @@ static int btreeComputeFreeSpace(MemPage *pPage){ } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); + if( size<4 ){ + /* Minimum freeblock size is 4 */ + return SQLITE_CORRUPT_PAGE(pPage); + } nFree = nFree + size; - if( next<=pc+size+3 ) break; + if( next0 ){ @@ -76125,6 +76903,30 @@ static SQLITE_NOINLINE int btreeBeginTrans( } #endif +#ifdef SQLITE_EXPERIMENTAL_PRAGMA_20251114 + /* If both a read and write transaction will be opened by this call, + ** then issue a file-control as if the following pragma command had + ** been evaluated: + ** + ** PRAGMA experimental_pragma_20251114 = 1|2 + ** + ** where the RHS is "1" if wrflag is 1 (RESERVED lock), or "2" if wrflag + ** is 2 (EXCLUSIVE lock). Ignore any result or error returned by the VFS. + ** + ** WARNING: This code will likely remain part of SQLite only temporarily - + ** it exists to allow users to experiment with certain types of blocking + ** locks in custom VFS implementations. It MAY BE REMOVED AT ANY TIME. */ + if( pBt->pPage1==0 && wrflag ){ + sqlite3_file *fd = sqlite3PagerFile(pPager); + char *aFcntl[3] = {0,0,0}; + aFcntl[1] = "experimental_pragma_20251114"; + assert( wrflag==1 || wrflag==2 ); + aFcntl[2] = (wrflag==1 ? "1" : "2"); + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_PRAGMA, (void*)aFcntl); + sqlite3_free(aFcntl[0]); + } +#endif + /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after @@ -77613,7 +78415,9 @@ static int accessPayload( ** means "not yet known" (the cache is lazily populated). */ if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){ - int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; + i64 nOvfl = pCur->info.nPayload; + testcase( nOvfl - pCur->info.nLocal + ovflSize - 1 > 0xffffffffU ); + nOvfl = (nOvfl - pCur->info.nLocal + ovflSize-1)/ovflSize; if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ @@ -77718,6 +78522,12 @@ static int accessPayload( (eOp==0 ? PAGER_GET_READONLY : 0) ); if( rc==SQLITE_OK ){ + if( eOp!=0 + && (sqlite3PagerPageRefcount(pDbPage)!=1 + || NEVER(((MemPage*)sqlite3PagerGetExtra(pDbPage))->isInit)) ){ + sqlite3PagerUnref(pDbPage); + return SQLITE_CORRUPT_PAGE(pPage); + } aPayload = sqlite3PagerGetData(pDbPage); nextPage = get4byte(aPayload); rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage); @@ -78129,7 +78939,7 @@ SQLITE_PRIVATE int sqlite3BtreeIsEmpty(BtCursor *pCur, int *pRes){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - if( pCur->eState==CURSOR_VALID ){ + if( NEVER(pCur->eState==CURSOR_VALID) ){ *pRes = 0; return SQLITE_OK; } @@ -78392,14 +79202,14 @@ static int indexCellCompare( /* This branch runs if the record-size field of the cell is a ** single byte varint and the record fits entirely on the main ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); + if( pCell + nCell >= pPage->aDataEnd ) return 99; c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); + if( pCell + nCell >= pPage->aDataEnd ) return 99; c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); }else{ /* If the record extends into overflow pages, do not attempt @@ -78561,14 +79371,17 @@ SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( /* This branch runs if the record-size field of the cell is a ** single byte varint and the record fits entirely on the main ** b-tree page. */ - testcase( pCell+nCell+1==pPage->aDataEnd ); + if( pCell + nCell >= pPage->aDataEnd ){ + rc = SQLITE_CORRUPT_PAGE(pPage); + goto moveto_index_finish; + } c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal + && pCell + nCell < pPage->aDataEnd ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ - testcase( pCell+nCell+2==pPage->aDataEnd ); c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); }else{ /* The record flows over onto one or more overflow pages. In @@ -82210,7 +83023,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 }while( rc==SQLITE_OK && nOut>0 ); if( rc==SQLITE_OK && nRem>0 && ALWAYS(pPgnoOut) ){ - Pgno pgnoNew; + Pgno pgnoNew = 0; /* Prevent harmless static-analyzer warning */ MemPage *pNew = 0; rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0); put4byte(pPgnoOut, pgnoNew); @@ -83433,6 +84246,7 @@ static int checkTreePage( } }else{ /* Populate the coverage-checking heap for leaf pages */ + assert( heap[0] < pCheck->mxHeap ); btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1)); } } @@ -83452,6 +84266,7 @@ static int checkTreePage( u32 size; pc = get2byteAligned(&data[cellStart+i*2]); size = pPage->xCellSize(pPage, &data[pc]); + assert( heap[0] < pCheck->mxHeap ); btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } @@ -83468,6 +84283,7 @@ static int checkTreePage( assert( (u32)i<=usableSize-4 ); /* Enforced by btreeComputeFreeSpace() */ size = get2byte(&data[i+2]); assert( (u32)(i+size)<=usableSize ); /* due to btreeComputeFreeSpace() */ + assert( heap[0] < pCheck->mxHeap ); btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1)); /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a ** big-endian integer which is the offset in the b-tree page of the next @@ -83602,6 +84418,9 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( goto integrity_ck_cleanup; } sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize ); +#ifdef SQLITE_DEBUG + sCheck.mxHeap = pBt->pageSize/4 - 1; +#endif if( sCheck.heap==0 ){ checkOom(&sCheck); goto integrity_ck_cleanup; @@ -84019,6 +84838,7 @@ SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree *p){ */ struct sqlite3_backup { sqlite3* pDestDb; /* Destination database handle */ + char *zDestDb; Btree *pDest; /* Destination b-tree file */ u32 iDestSchema; /* Original schema cookie in destination */ int bDestLocked; /* True once a write-transaction is open on pDest */ @@ -84108,10 +84928,8 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ ** Attempt to set the page size of the destination to match the page size ** of the source. */ -static int setDestPgsz(sqlite3_backup *p){ - int rc; - rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0); - return rc; +static int setDestPgsz(Btree *pDest, Btree *pSrc){ + return sqlite3BtreeSetPageSize(pDest, sqlite3BtreeGetPageSize(pSrc), 0, 0); } /* @@ -84168,27 +84986,37 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init( ); p = 0; }else { + int nDest = sqlite3Strlen30(zDestDb); + /* Allocate space for a new sqlite3_backup object... ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ - p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup)); + p = (sqlite3_backup*)sqlite3MallocZero(sizeof(sqlite3_backup)+nDest+1); if( !p ){ sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT); + }else{ + p->zDestDb = (char*)&p[1]; + memcpy(p->zDestDb, zDestDb, nDest); } } /* If the allocation succeeded, populate the new object. */ if( p ){ + /* Do not store the pointer to the destination b-tree at this point. + ** This is because there is nothing preventing it from being detached + ** or otherwise freed before the first call to sqlite3_backup_step() + ** on this object. The source b-tree does not have this problem, as + ** incrementing Btree.nBackup (see below) effectively locks the object. */ + Btree *pDest = findBtree(pDestDb, pDestDb, zDestDb); p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb); - p->pDest = findBtree(pDestDb, pDestDb, zDestDb); p->pDestDb = pDestDb; p->pSrcDb = pSrcDb; p->iNext = 1; p->isAttached = 0; - if( 0==p->pSrc || 0==p->pDest - || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK + if( 0==p->pSrc || 0==pDest + || checkReadTransaction(pDestDb, pDest)!=SQLITE_OK ){ /* One (or both) of the named databases did not exist or an OOM ** error was hit. Or there is a transaction open on the destination @@ -84312,7 +85140,7 @@ static void attachBackupObject(sqlite3_backup *p){ */ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int rc; - int destMode; /* Destination journal mode */ + int destMode = 0; /* Destination journal mode */ int pgszSrc = 0; /* Source page size */ int pgszDest = 0; /* Destination page size */ @@ -84328,7 +85156,8 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ rc = p->rc; if( !isFatalError(rc) ){ Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */ - Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */ + Btree * pDest = 0; /* Dest btree */ + Pager * pDestPager = 0; /* Dest pager */ int ii; /* Iterator variable */ int nSrcPage = -1; /* Size of source db in pages */ int bCloseTrans = 0; /* True if src db requires unlocking */ @@ -84342,6 +85171,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ rc = SQLITE_OK; } + /* If there is no open read-transaction on the source database, open ** one now. If a transaction is opened here, then it will be closed ** before this function exits. @@ -84351,34 +85181,48 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ bCloseTrans = 1; } + /* Locate the destination btree and pager. */ + if( (pDest = p->pDest)==0 ){ + pDest = findBtree(p->pDestDb, p->pDestDb, p->zDestDb); + } + if( pDest==0 ){ + rc = SQLITE_ERROR; + }else{ + pDestPager = sqlite3BtreePager(pDest); + } + /* If the destination database has not yet been locked (i.e. if this ** is the first call to backup_step() for the current backup operation), ** try to set its page size to the same as the source database. This ** is especially important on ZipVFS systems, as in that case it is ** not possible to create a database file that uses one page size by ** writing to it with another. */ - if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){ + if( p->bDestLocked==0 && rc==SQLITE_OK + && setDestPgsz(pDest, p->pSrc)==SQLITE_NOMEM + ){ rc = SQLITE_NOMEM; } /* Lock the destination database, if it is not locked already. */ if( SQLITE_OK==rc && p->bDestLocked==0 - && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2, + && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(pDest, 2, (int*)&p->iDestSchema)) ){ p->bDestLocked = 1; + p->pDest = pDest; } /* Do not allow backup if the destination database is in WAL mode ** and the page sizes are different between source and destination */ - pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); - pgszDest = sqlite3BtreeGetPageSize(p->pDest); - destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); - if( SQLITE_OK==rc - && (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) - && pgszSrc!=pgszDest - ){ - rc = SQLITE_READONLY; + if( rc==SQLITE_OK ){ + pgszSrc = sqlite3BtreeGetPageSize(p->pSrc); + pgszDest = sqlite3BtreeGetPageSize(p->pDest); + destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest)); + if( (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager)) + && pgszSrc!=pgszDest + ){ + rc = SQLITE_READONLY; + } } /* Now that there is a read-lock on the source database, query the @@ -84596,7 +85440,9 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){ } /* If a transaction is still open on the Btree, roll it back. */ - sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0); + if( p->pDest ){ + sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0); + } /* Set the error code of the destination database handle. */ rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; @@ -84876,21 +85722,27 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){ StrAccum acc; assert( p->flags & (MEM_Int|MEM_Real|MEM_IntReal) ); assert( sz>22 ); - if( p->flags & MEM_Int ){ -#if GCC_VERSION>=7000000 - /* Work-around for GCC bug - ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 */ + if( p->flags & (MEM_Int|MEM_IntReal) ){ +#if GCC_VERSION>=7000000 && GCC_VERSION<15000000 && defined(__i386__) + /* Work-around for GCC bug or bugs: + ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96270 + ** https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114659 + ** The problem appears to be fixed in GCC 15 */ i64 x; - assert( (p->flags&MEM_Int)*2==sizeof(x) ); - memcpy(&x, (char*)&p->u, (p->flags&MEM_Int)*2); + assert( (MEM_Str&~p->flags)*4==sizeof(x) ); + memcpy(&x, (char*)&p->u, (MEM_Str&~p->flags)*4); p->n = sqlite3Int64ToText(x, zBuf); #else p->n = sqlite3Int64ToText(p->u.i, zBuf); #endif + if( p->flags & MEM_IntReal ){ + memcpy(zBuf+p->n,".0", 3); + p->n += 2; + } }else{ sqlite3StrAccumInit(&acc, 0, zBuf, sz, 0); - sqlite3_str_appendf(&acc, "%!.15g", - (p->flags & MEM_IntReal)!=0 ? (double)p->u.i : p->u.r); + sqlite3_str_appendf(&acc, "%!.*g", + (p->db ? p->db->nFpDigit : 17), p->u.r); assert( acc.zText==zBuf && acc.mxAlloc<=0 ); zBuf[acc.nChar] = 0; /* Fast version of sqlite3StrAccumFinish(&acc) */ p->n = acc.nChar; @@ -84939,6 +85791,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemValidStrRep(Mem *p){ assert( p->enc==SQLITE_UTF8 || p->z[((p->n+1)&~1)+1]==0 ); } if( (p->flags & (MEM_Int|MEM_Real|MEM_IntReal))==0 ) return 1; + if( p->db==0 ){ + return 1; /* db->nFpDigit required to validate p->z[] */ + } memcpy(&tmp, p, sizeof(tmp)); vdbeMemRenderNum(sizeof(zBuf), zBuf, &tmp); z = p->z; @@ -85089,13 +85944,16 @@ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ ** ** This is an optimization. Correct operation continues even if ** this routine is a no-op. +** +** Return true if the strig is zero-terminated after this routine is +** called and false if it is not. */ -SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ +SQLITE_PRIVATE int sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ if( (pMem->flags & (MEM_Str|MEM_Term|MEM_Ephem|MEM_Static))!=MEM_Str ){ /* pMem must be a string, and it cannot be an ephemeral or static string */ - return; + return 0; } - if( pMem->enc!=SQLITE_UTF8 ) return; + if( pMem->enc!=SQLITE_UTF8 ) return 0; assert( pMem->z!=0 ); if( pMem->flags & MEM_Dyn ){ if( pMem->xDel==sqlite3_free @@ -85103,18 +85961,19 @@ SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ ){ pMem->z[pMem->n] = 0; pMem->flags |= MEM_Term; - return; + return 1; } if( pMem->xDel==sqlite3RCStrUnref ){ /* Blindly assume that all RCStr objects are zero-terminated */ pMem->flags |= MEM_Term; - return; + return 1; } }else if( pMem->szMalloc >= pMem->n+1 ){ pMem->z[pMem->n] = 0; pMem->flags |= MEM_Term; - return; + return 1; } + return 0; } /* @@ -85412,18 +86271,117 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(const Mem *pMem){ } } +/* +** This routine implements the uncommon and slower path for +** sqlite3MemRealValueRC() that has to deal with input strings +** that are not UTF8 or that are not zero-terminated. It is +** broken out into a separate no-inline routine so that the +** main sqlite3MemRealValueRC() routine can avoid unnecessary +** stack pushes. +** +** A text->float translation of pMem->z is written into *pValue. +** +** Result code invariants: +** +** rc==0 => ERROR: Input string not well-formed, or OOM +** rc<0 => Some prefix of the input is well-formed +** rc>0 => All of the input is well-formed +** (rc&2)==0 => The number is expressed as an integer, with no +** decimal point or eNNN suffix. +*/ +static SQLITE_NOINLINE int sqlite3MemRealValueRCSlowPath( + Mem *pMem, + double *pValue +){ + int rc = SQLITE_OK; + *pValue = 0.0; + if( pMem->enc==SQLITE_UTF8 ){ + char *zCopy = sqlite3DbStrNDup(pMem->db, pMem->z, pMem->n); + if( zCopy ){ + rc = sqlite3AtoF(zCopy, pValue); + sqlite3DbFree(pMem->db, zCopy); + } + return rc; + }else{ + int n, i, j; + char *zCopy; + const char *z; + + n = pMem->n & ~1; + zCopy = sqlite3DbMallocRaw(pMem->db, n/2 + 2); + if( zCopy ){ + z = pMem->z; + if( pMem->enc==SQLITE_UTF16LE ){ + for(i=j=0; idb, zCopy); + } + return rc; + } +} + +/* +** Invoke sqlite3AtoF() on the text value of pMem. Write the +** translation of the text input into *pValue. +** +** The caller must ensure that pMem->db!=0 and that pMem is in +** mode MEM_Str or MEM_Blob. +** +** Result code invariants: +** +** rc==0 => ERROR: Input string not well-formed, or OOM +** rc<0 => Some prefix of the input is well-formed +** rc>0 => All of the input is well-formed +** (rc&2)==0 => The number is expressed as an integer, with no +** decimal point or eNNN suffix. +*/ +SQLITE_PRIVATE int sqlite3MemRealValueRC(Mem *pMem, double *pValue){ + testcase( pMem->db==0 ); + assert( pMem->flags & (MEM_Str|MEM_Blob) ); + if( pMem->z==0 ){ + *pValue = 0.0; + return 0; + }else if( pMem->enc==SQLITE_UTF8 + && ((pMem->flags & MEM_Term)!=0 || sqlite3VdbeMemZeroTerminateIfAble(pMem)) + ){ + return sqlite3AtoF(pMem->z, pValue); + }else if( pMem->n==0 ){ + *pValue = 0.0; + return 0; + }else{ + return sqlite3MemRealValueRCSlowPath(pMem, pValue); + } +} + +/* +** This routine acts as a bridge from sqlite3VdbeRealValue() to +** sqlite3VdbeRealValueRC, allowing sqlite3VdbeRealValue() to avoid +** stuffing values onto the stack. +*/ +static SQLITE_NOINLINE double sqlite3MemRealValueNoRC(Mem *pMem){ + double r; + (void)sqlite3MemRealValueRC(pMem, &r); + return r; +} + /* ** Return the best representation of pMem that we can get into a ** double. If pMem is already a double or an integer, return its ** value. If it is a string or blob, try to convert it to a double. ** If it is a NULL, return 0.0. */ -static SQLITE_NOINLINE double memRealValue(Mem *pMem){ - /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ - double val = (double)0; - sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); - return val; -} SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){ assert( pMem!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); @@ -85434,7 +86392,7 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){ testcase( pMem->flags & MEM_IntReal ); return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ - return memRealValue(pMem); + return sqlite3MemRealValueNoRC(pMem); }else{ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ return (double)0; @@ -85558,8 +86516,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){ sqlite3_int64 ix; assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); - rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); - if( ((rc==0 || rc==1) && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1) + rc = sqlite3MemRealValueRC(pMem, &pMem->u.r); + if( ((rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<2) || sqlite3RealSameAsInt(pMem->u.r, (ix = sqlite3RealToI64(pMem->u.r))) ){ pMem->u.i = ix; @@ -86023,6 +86981,84 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( return SQLITE_OK; } +/* Like sqlite3VdbeMemSetStr() except: +** +** enc is always SQLITE_UTF8 +** pMem->db is always non-NULL +*/ +SQLITE_PRIVATE int sqlite3VdbeMemSetText( + Mem *pMem, /* Memory cell to set to string value */ + const char *z, /* String pointer */ + i64 n, /* Bytes in string, or negative */ + void (*xDel)(void*) /* Destructor function */ +){ + i64 nByte = n; /* New value for pMem->n */ + u16 flags; + + assert( pMem!=0 ); + assert( pMem->db!=0 ); + assert( sqlite3_mutex_held(pMem->db->mutex) ); + assert( !sqlite3VdbeMemIsRowSet(pMem) ); + + /* If z is a NULL pointer, set pMem to contain an SQL NULL. */ + if( !z ){ + sqlite3VdbeMemSetNull(pMem); + return SQLITE_OK; + } + + if( nByte<0 ){ + nByte = strlen(z); + flags = MEM_Str|MEM_Term; + }else{ + flags = MEM_Str; + } + if( nByte>(i64)pMem->db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( xDel && xDel!=SQLITE_TRANSIENT ){ + if( xDel==SQLITE_DYNAMIC ){ + sqlite3DbFree(pMem->db, (void*)z); + }else{ + xDel((void*)z); + } + } + sqlite3VdbeMemSetNull(pMem); + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); + } + + /* The following block sets the new values of Mem.z and Mem.xDel. It + ** also sets a flag in local variable "flags" to indicate the memory + ** management (one of MEM_Dyn or MEM_Static). + */ + if( xDel==SQLITE_TRANSIENT ){ + i64 nAlloc = nByte + 1; + testcase( nAlloc==31 ); + testcase( nAlloc==32 ); + if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ + return SQLITE_NOMEM_BKPT; + } + assert( pMem->z!=0 ); + memcpy(pMem->z, z, nByte); + pMem->z[nByte] = 0; + }else{ + sqlite3VdbeMemRelease(pMem); + pMem->z = (char *)z; + if( xDel==SQLITE_DYNAMIC ){ + pMem->zMalloc = pMem->z; + pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); + pMem->xDel = 0; + }else if( xDel==SQLITE_STATIC ){ + pMem->xDel = xDel; + flags |= MEM_Static; + }else{ + pMem->xDel = xDel; + flags |= MEM_Dyn; + } + } + pMem->flags = flags; + pMem->n = (int)(nByte & 0x7fffffff); + pMem->enc = SQLITE_UTF8; + return SQLITE_OK; +} + /* ** Move data out of a btree key or data field and into a Mem structure. ** The data is payload from the entry that pCur is currently pointing @@ -86451,7 +87487,7 @@ static int valueFromExpr( if( affinity==SQLITE_AFF_BLOB ){ if( op==TK_FLOAT ){ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); - sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); + sqlite3AtoF(pVal->z, &pVal->u.r); pVal->flags = MEM_Real; }else if( op==TK_INTEGER ){ /* This case is required by -9223372036854775808 and other strings @@ -86719,6 +87755,11 @@ SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr( ** ** If *ppVal is initially NULL then the caller is responsible for ** ensuring that the value written into *ppVal is eventually freed. +** +** If the buffer does not contain a well-formed record, this routine may +** read several bytes past the end of the buffer. Callers must therefore +** ensure that any buffer which may contain a corrupt record is padded +** with at least 8 bytes of addressable memory. */ SQLITE_PRIVATE int sqlite3Stat4Column( sqlite3 *db, /* Database handle */ @@ -88837,6 +89878,10 @@ SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){ zP4 = pOp->p4.pTab->zName; break; } + case P4_INDEX: { + zP4 = pOp->p4.pIdx->zName; + break; + } case P4_SUBRTNSIG: { SubrtnSig *pSig = pOp->p4.pSubrtnSig; sqlite3_str_appendf(&x, "subrtnsig:%d,%s", pSig->selId, pSig->zAff); @@ -89735,7 +90780,7 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName( } assert( p->aColName!=0 ); pColName = &(p->aColName[idx+var*p->nResAlloc]); - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); + rc = sqlite3VdbeMemSetText(pColName, zName, -1, xDel); assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); return rc; } @@ -92227,6 +93272,223 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ } } +/* +** Helper function for vdbeIsMatchingIndexKey(). Return true if column +** iCol should be ignored when comparing a record with a record from +** an index on disk. The field should be ignored if: +** +** * the corresponding bit in mask is set, and +** * either: +** - bIntegrity is false, or +** - the two Mem values are both real values that differ by +** BTREE_ULPDISTORTION or fewer ULPs. +*/ +static int vdbeSkipField( + Bitmask mask, /* Mask of indexed expression fields */ + int iCol, /* Column of index being considered */ + Mem *pMem1, /* Expected index value */ + Mem *pMem2, /* Actual indexed value */ + int bIntegrity /* True if running PRAGMA integrity_check */ +){ +#define BTREE_ULPDISTORTION 2 + if( iCol>=BMS || (mask & MASKBIT(iCol))==0 ) return 0; + if( bIntegrity==0 ) return 1; + if( (pMem1->flags & MEM_Real) && (pMem2->flags & MEM_Real) ){ + u64 m1, m2; + memcpy(&m1,&pMem1->u.r,8); + memcpy(&m2,&pMem2->u.r,8); + if( (m1pKeyInfo->enc; + mem.db = p->pKeyInfo->db; + nRec = sqlite3BtreePayloadSize(pCur); + if( nRec>0x7fffffff ){ + return SQLITE_CORRUPT_BKPT; + } + + /* Allocate 5 extra bytes at the end of the buffer. This allows the + ** getVarint32() call below to read slightly past the end of the buffer + ** if the record is corrupt. */ + aRec = sqlite3MallocZero(nRec+5); + if( aRec==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + rc = sqlite3BtreePayload(pCur, 0, nRec, aRec); + } + + if( rc==SQLITE_OK ){ + u32 szHdr = 0; /* Size of record header in bytes */ + u32 idxHdr = 0; /* Current index in header */ + + idxHdr = getVarint32(aRec, szHdr); + if( szHdr>98307 ){ + rc = SQLITE_CORRUPT; + }else{ + int res = 0; /* Result of this function call */ + u32 idxRec = szHdr; /* Index of next field in record body */ + int ii = 0; /* Iterator variable */ + + int nCol = p->pKeyInfo->nAllField; + for(ii=0; ii=szHdr ){ + rc = SQLITE_CORRUPT_BKPT; + break; + } + idxHdr += getVarint32(&aRec[idxHdr], iSerial); + nSerial = sqlite3VdbeSerialTypeLen(iSerial); + if( (idxRec+nSerial)>nRec ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + sqlite3VdbeSerialGet(&aRec[idxRec], iSerial, &mem); + if( vdbeSkipField(mask, ii, &p->aMem[ii], &mem, bInt)==0 ){ + res = sqlite3MemCompare(&mem, &p->aMem[ii], p->pKeyInfo->aColl[ii]); + if( res!=0 ) break; + } + } + idxRec += sqlite3VdbeSerialTypeLen(iSerial); + } + + *piRes = res; + } + } + + sqlite3_free(aRec); + return rc; +} + +/* +** This is called when the record in (*p) should be found in the index +** opened by cursor pCur, but was not. This may happen as part of a DELETE +** operation or an integrity check. +** +** One reason that an exact match was not found may be the EIIB bug - that +** a text-to-float conversion may have caused a real value in record (*p) +** to be slightly different from its counterpart on disk. This function +** attempts to find the right index record. If it does find the right +** record, it leaves *pCur pointing to it and sets (*pRes) to 0 before +** returning. Otherwise, (*pRes) is set to non-zero and an SQLite error +** code returned. +** +** The algorithm used to find the correct record is: +** +** * Scan up to BTREE_FDK_RANGE entries either side of the current entry. +** If parameter bIntegrity is false, then all fields that are indexed +** expressions or virtual table columns are omitted from the comparison. +** If bIntegrity is true, then small differences in real values in +** such fields are overlooked, but they are not omitted from the comparison +** altogether. +** +** * If the above fails to find an entry and bIntegrity is false, search +** the entire index. +*/ +SQLITE_PRIVATE int sqlite3VdbeFindIndexKey( + BtCursor *pCur, + Index *pIdx, + UnpackedRecord *p, + int *pRes, + int bIntegrity +){ +#define BTREE_FDK_RANGE 10 + int nStep = 0; + int res = 1; + int rc = SQLITE_OK; + int ii = 0; + + /* Calculate a mask based on the first 64 columns of the index. The mask + ** bit is set if the corresponding index field is either an expression + ** or a virtual column of the table. */ + Bitmask mask = 0; + for(ii=0; iinColumn, BMS); ii++){ + int iCol = pIdx->aiColumn[ii]; + if( (iCol==XN_EXPR) + || (iCol>=0 && (pIdx->pTable->aCol[iCol].colFlags & COLFLAG_VIRTUAL)) + ){ + mask |= MASKBIT(ii); + } + } + + /* If the mask is 0 at this point, then the index contains no expressions + ** or virtual columns. So do not search for a match - return so that the + ** caller may declare the db corrupt immediately. Or, if mask is non-zero, + ** proceed. */ + if( mask!=0 ){ + + /* Move the cursor back BTREE_FDK_RANGE entries. If this hits an EOF, + ** position the cursor at the first entry in the index and set nStep + ** to -1 so that the first loop below scans the entire index. Otherwise, + ** set nStep to BTREE_FDK_RANGE*2 so that the first loop below scans + ** just that many entries. */ + for(ii=0; sqlite3BtreeEof(pCur)==0 && ii=0), or the entire index if (nStep<0). */ + while( sqlite3BtreeCursorIsValidNN(pCur) ){ + for(ii=0; rc==SQLITE_OK && (iiexpired; + int iRet = 1; + if( pStmt ){ + Vdbe *p = (Vdbe*)pStmt; + sqlite3_mutex_enter(p->db->mutex); + iRet = p->expired; + sqlite3_mutex_leave(p->db->mutex); + } + return iRet; } #endif @@ -92813,7 +94081,23 @@ static void setResultStrOrError( void (*xDel)(void*) /* Destructor function */ ){ Mem *pOut = pCtx->pOut; - int rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); + int rc; + if( enc==SQLITE_UTF8 ){ + rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); + }else if( enc==SQLITE_UTF8_ZT ){ + /* It is usually considered improper to assert() on an input. However, + ** the following assert() is checking for inputs that are documented + ** to result in undefined behavior. */ + assert( z==0 + || n<0 + || n>pOut->db->aLimit[SQLITE_LIMIT_LENGTH] + || z[n]==0 + ); + rc = sqlite3VdbeMemSetText(pOut, z, n, xDel); + pOut->flags |= MEM_Term; + }else{ + rc = sqlite3VdbeMemSetStr(pOut, z, n, enc, xDel); + } if( rc ){ if( rc==SQLITE_TOOBIG ){ sqlite3_result_error_toobig(pCtx); @@ -93006,7 +94290,7 @@ SQLITE_API void sqlite3_result_text64( #endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); - if( enc!=SQLITE_UTF8 ){ + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; n &= ~(u64)1; } @@ -93157,6 +94441,8 @@ static int doWalCallbacks(sqlite3 *db){ } } } +#else + UNUSED_PARAMETER(db); #endif return rc; } @@ -94113,13 +95399,25 @@ static int bindText( assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */ if( zData!=0 ){ pVar = &p->aVar[i-1]; - rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); - if( rc==SQLITE_OK ){ - if( encoding==0 ){ - pVar->enc = ENC(p->db); - }else{ - rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); - } + if( encoding==SQLITE_UTF8 ){ + rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); + }else if( encoding==SQLITE_UTF8_ZT ){ + /* It is usually consider improper to assert() on an input. + ** However, the following assert() is checking for inputs + ** that are documented to result in undefined behavior. */ + assert( zData==0 + || nData<0 + || nData>pVar->db->aLimit[SQLITE_LIMIT_LENGTH] + || ((u8*)zData)[nData]==0 + ); + rc = sqlite3VdbeMemSetText(pVar, zData, nData, xDel); + pVar->flags |= MEM_Term; + }else{ + rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel); + if( encoding==0 ) pVar->enc = ENC(p->db); + } + if( rc==SQLITE_OK && encoding!=0 ){ + rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } if( rc ){ sqlite3Error(p->db, rc); @@ -94231,7 +95529,7 @@ SQLITE_API int sqlite3_bind_text64( unsigned char enc ){ assert( xDel!=SQLITE_DYNAMIC ); - if( enc!=SQLITE_UTF8 ){ + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF8_ZT ){ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; nData &= ~(u64)1; } @@ -95268,17 +96566,19 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( #ifndef SQLITE_HWTIME_H #define SQLITE_HWTIME_H -/* -** The following routine only works on Pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) +#if defined(_MSC_VER) && defined(_WIN32) + +/* #include "windows.h" */ + #include - #if defined(__GNUC__) + __inline sqlite3_uint64 sqlite3Hwtime(void){ + LARGE_INTEGER tm; + QueryPerformanceCounter(&tm); + return (sqlite3_uint64)tm.QuadPart; + } + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && \ + (defined(i386) || defined(__i386__) || defined(_M_IX86)) __inline__ sqlite_uint64 sqlite3Hwtime(void){ unsigned int lo, hi; @@ -95286,17 +96586,6 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( return (sqlite_uint64)hi << 32 | lo; } - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) __inline__ sqlite_uint64 sqlite3Hwtime(void){ @@ -95305,6 +96594,14 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( return (sqlite_uint64)hi << 32 | lo; } +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__aarch64__) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + sqlite3_uint64 cnt; + __asm__ __volatile__ ("mrs %0, cntvct_el0" : "=r" (cnt)); + return cnt; + } + #elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) __inline__ sqlite_uint64 sqlite3Hwtime(void){ @@ -95663,12 +96960,11 @@ static int alsoAnInt(Mem *pRec, double rValue, i64 *piValue){ */ static void applyNumericAffinity(Mem *pRec, int bTryForInt){ double rValue; - u8 enc = pRec->enc; int rc; assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real|MEM_IntReal))==MEM_Str ); - rc = sqlite3AtoF(pRec->z, &rValue, pRec->n, enc); + rc = sqlite3MemRealValueRC(pRec, &rValue); if( rc<=0 ) return; - if( rc==1 && alsoAnInt(pRec, rValue, &pRec->u.i) ){ + if( (rc&2)==0 && alsoAnInt(pRec, rValue, &pRec->u.i) ){ pRec->flags |= MEM_Int; }else{ pRec->u.r = rValue; @@ -95748,7 +97044,10 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ int eType = sqlite3_value_type(pVal); if( eType==SQLITE_TEXT ){ Mem *pMem = (Mem*)pVal; + assert( pMem->db!=0 ); + sqlite3_mutex_enter(pMem->db->mutex); applyNumericAffinity(pMem, 0); + sqlite3_mutex_leave(pMem->db->mutex); eType = sqlite3_value_type(pVal); } return eType; @@ -95781,15 +97080,15 @@ static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ pMem->u.i = 0; return MEM_Int; } - rc = sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc); + rc = sqlite3MemRealValueRC(pMem, &pMem->u.r); if( rc<=0 ){ - if( rc==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ + if( (rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)<=1 ){ pMem->u.i = ix; return MEM_Int; }else{ return MEM_Real; } - }else if( rc==1 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){ + }else if( (rc&2)==0 && sqlite3Atoi64(pMem->z, &ix, pMem->n, pMem->enc)==0 ){ pMem->u.i = ix; return MEM_Int; } @@ -101930,20 +103229,17 @@ case OP_SorterInsert: { /* in2 */ break; } -/* Opcode: IdxDelete P1 P2 P3 * P5 +/* Opcode: IdxDelete P1 P2 P3 P4 * ** Synopsis: key=r[P2@P3] ** ** The content of P3 registers starting at register P2 form ** an unpacked index key. This opcode removes that entry from the ** index opened by cursor P1. ** -** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error -** if no matching index entry is found. This happens when running -** an UPDATE or DELETE statement and the index entry to be updated -** or deleted is not found. For some uses of IdxDelete -** (example: the EXCEPT operator) it does not matter that no matching -** entry is found. For those cases, P5 is zero. Also, do not raise -** this (self-correcting and non-critical) error if in writable_schema mode. +** P4 is a pointer to an Index structure. +** +** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found +** and not in writable_schema mode. */ case OP_IdxDelete: { VdbeCursor *pC; @@ -101966,13 +103262,22 @@ case OP_IdxDelete: { r.aMem = &aMem[pOp->p2]; rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res); if( rc ) goto abort_due_to_error; - if( res==0 ){ - rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); - if( rc ) goto abort_due_to_error; - }else if( pOp->p5 && !sqlite3WritableSchema(db) ){ - rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); - goto abort_due_to_error; + if( res!=0 ){ + rc = sqlite3VdbeFindIndexKey(pCrsr, pOp->p4.pIdx, &r, &res, 0); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + if( res!=0 ){ + if( !sqlite3WritableSchema(db) ){ + rc = sqlite3ReportError( + SQLITE_CORRUPT_INDEX, __LINE__, "index corruption"); + goto abort_due_to_error; + } + pC->cacheStatus = CACHE_STALE; + pC->seekResult = 0; + break; + } } + rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE); + if( rc ) goto abort_due_to_error; assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; pC->seekResult = 0; @@ -102599,6 +103904,58 @@ case OP_IntegrityCk: { sqlite3VdbeChangeEncoding(pIn1, encoding); goto check_for_interrupt; } + +/* Opcode: IFindKey P1 P2 P3 P4 * +** +** This instruction always follows an OP_Found with the same P1, P2 and P3 +** values as this instruction and a non-zero P4 value. The P4 value to +** this opcode is of type P4_INDEX and contains a pointer to the Index +** object of for the index being searched. +** +** This opcode uses sqlite3VdbeFindIndexKey() to search around the current +** cursor location for an index key that exactly matches all fields that +** are not indexed expressions or references to VIRTUAL generated columns, +** and either exactly match or are real numbers that are within 2 ULPs of +** each other if the don't match. +** +** To put it another way, this opcode looks for nearby index entries that +** are very close to the search key, but which might have small differences +** in floating-point values that come via an expression. +** +** If no nearby alternative entry is found in cursor P1, then jump to P2. +** But if a close match is found, fall through. +** +** This opcode is used by PRAGMA integrity_check to help distinguish +** between truely corrupt indexes and expression indexes that are holding +** floating-point values that are off by one or two ULPs. +*/ +case OP_IFindKey: { /* jump, in3 */ + VdbeCursor *pC; + int res; + UnpackedRecord r; + + assert( pOp[-1].opcode==OP_Found ); + assert( pOp[-1].p1==pOp->p1 ); + assert( pOp[-1].p3==pOp->p3 ); + pC = p->apCsr[pOp->p1]; + assert( pOp->p4type==P4_INDEX ); + assert( pC->eCurType==CURTYPE_BTREE ); + assert( pC->uc.pCursor!=0 ); + assert( pC->isTable==0 ); + + memset(&r, 0, sizeof(r)); + r.aMem = &aMem[pOp->p3]; + r.nField = pOp->p4.pIdx->nColumn; + r.pKeyInfo = pC->pKeyInfo; + + rc = sqlite3VdbeFindIndexKey(pC->uc.pCursor, pOp->p4.pIdx, &r, &res, 1); + if( rc || res!=0 ){ + rc = SQLITE_OK; + goto jump_to_p2; + } + pC->nullRow = 0; + break; +}; #endif /* SQLITE_OMIT_INTEGRITY_CHECK */ /* Opcode: RowSetAdd P1 P2 * * * @@ -109901,6 +111258,7 @@ static int lookupName( pExpr->op = TK_FUNCTION; pExpr->u.zToken = "coalesce"; pExpr->x.pList = pFJMatch; + pExpr->affExpr = SQLITE_AFF_DEFER; cnt = 1; goto lookupname_end; }else{ @@ -110063,12 +111421,41 @@ static int exprProbability(Expr *p){ double r = -1.0; if( p->op!=TK_FLOAT ) return -1; assert( !ExprHasProperty(p, EP_IntValue) ); - sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8); + sqlite3AtoF(p->u.zToken, &r); assert( r>=0.0 ); if( r>1.0 ) return -1; return (int)(r*134217728.0); } +/* +** Set the EP_SubtArg property on every expression inside of +** pList. If any subexpression is actually a subquery, then +** also set the EP_SubtArg property on the first result-set +** column of that subquery. +*/ +static SQLITE_NOINLINE void resolveSetExprSubtypeArg(ExprList *pList){ + int nn, ii; + nn = pList ? pList->nExpr : 0; + for(ii=0; iia[ii].pExpr; + while( 1 /*exit-by-break*/ ){ + ExprSetProperty(pExpr, EP_SubtArg); + if( pExpr->op==TK_SELECT ){ + assert( ExprUseXSelect(pExpr) ); + assert( pExpr->x.pSelect!=0 ); + resolveSetExprSubtypeArg(pExpr->x.pSelect->pEList); + break; + } + if( pExpr->op==TK_UPLUS ){ + pExpr = pExpr->pLeft; + assert( pExpr!=0 ); + }else{ + break; + } + } + } +} + /* ** This routine is callback for sqlite3WalkExpr(). ** @@ -110313,10 +111700,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ if( (pDef->funcFlags & SQLITE_SUBTYPE) || ExprHasProperty(pExpr, EP_SubtArg) ){ - int ii; - for(ii=0; iia[ii].pExpr, EP_SubtArg); - } + resolveSetExprSubtypeArg(pList); } if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){ @@ -110783,10 +112167,8 @@ static int resolveCompoundOrderBy( /* Convert the ORDER BY term into an integer column number iCol, ** taking care to preserve the COLLATE clause if it exists. */ if( !IN_RENAME_OBJECT ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pNew = sqlite3ExprInt32(db, iCol); if( pNew==0 ) return 1; - pNew->flags |= EP_IntValue; - pNew->u.iValue = iCol; if( pItem->pExpr==pE ){ pItem->pExpr = pNew; }else{ @@ -111140,10 +112522,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } #endif - /* The ORDER BY and GROUP BY clauses may not refer to terms in - ** outer queries - */ - sNC.pNext = 0; sNC.ncFlags |= NC_AllowAgg|NC_AllowWin; /* If this is a converted compound query, move the ORDER BY clause from @@ -112390,34 +113768,22 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( int dequote /* True to dequote */ ){ Expr *pNew; - int nExtra = 0; - int iValue = 0; + int nExtra = pToken ? pToken->n+1 : 0; assert( db!=0 ); - if( pToken ){ - if( op!=TK_INTEGER || pToken->z==0 - || sqlite3GetInt32(pToken->z, &iValue)==0 ){ - nExtra = pToken->n+1; /* tag-20240227-a */ - assert( iValue>=0 ); - } - } pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra); if( pNew ){ memset(pNew, 0, sizeof(Expr)); pNew->op = (u8)op; pNew->iAgg = -1; - if( pToken ){ - if( nExtra==0 ){ - pNew->flags |= EP_IntValue|EP_Leaf|(iValue?EP_IsTrue:EP_IsFalse); - pNew->u.iValue = iValue; - }else{ - pNew->u.zToken = (char*)&pNew[1]; - assert( pToken->z!=0 || pToken->n==0 ); - if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); - pNew->u.zToken[pToken->n] = 0; - if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ - sqlite3DequoteExpr(pNew); - } + if( nExtra ){ + assert( pToken!=0 ); + pNew->u.zToken = (char*)&pNew[1]; + assert( pToken->z!=0 || pToken->n==0 ); + if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); + pNew->u.zToken[pToken->n] = 0; + if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ + sqlite3DequoteExpr(pNew); } } #if SQLITE_MAX_EXPR_DEPTH>0 @@ -112442,6 +113808,24 @@ SQLITE_PRIVATE Expr *sqlite3Expr( return sqlite3ExprAlloc(db, op, &x, 0); } +/* +** Allocate an expression for a 32-bit signed integer literal. +*/ +SQLITE_PRIVATE Expr *sqlite3ExprInt32(sqlite3 *db, int iVal){ + Expr *pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)); + if( pNew ){ + memset(pNew, 0, sizeof(Expr)); + pNew->op = TK_INTEGER; + pNew->iAgg = -1; + pNew->flags = EP_IntValue|EP_Leaf|(iVal?EP_IsTrue:EP_IsFalse); + pNew->u.iValue = iVal; +#if SQLITE_MAX_EXPR_DEPTH>0 + pNew->nHeight = 1; +#endif + } + return pNew; +} + /* ** Attach subtrees pLeft and pRight to the Expr node pRoot. ** @@ -112604,7 +113988,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){ ){ sqlite3ExprDeferredDelete(pParse, pLeft); sqlite3ExprDeferredDelete(pParse, pRight); - return sqlite3Expr(db, TK_INTEGER, "0"); + return sqlite3ExprInt32(db, 0); }else{ return sqlite3PExpr(pParse, TK_AND, pLeft, pRight); } @@ -112729,7 +114113,9 @@ SQLITE_PRIVATE void sqlite3ExprFunctionUsable( ){ assert( !IN_RENAME_OBJECT ); assert( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 ); - if( ExprHasProperty(pExpr, EP_FromDDL) ){ + if( ExprHasProperty(pExpr, EP_FromDDL) + || pParse->prepFlags & SQLITE_PREPARE_FROM_DDL + ){ if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0 || (pParse->db->flags & SQLITE_TrustedSchema)==0 ){ @@ -113425,9 +114811,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int fla pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); pNew->iLimit = 0; pNew->iOffset = 0; - pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; + pNew->selFlags = p->selFlags; pNew->nSelectRow = p->nSelectRow; pNew->pWith = sqlite3WithDup(db, p->pWith); #ifndef SQLITE_OMIT_WINDOWFUNC @@ -114079,7 +115463,7 @@ static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ /* ** Walk an expression tree. Return non-zero if the expression is constant -** and 0 if it involves variables or function calls. +** or return zero if the expression involves variables or function calls. ** ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is @@ -114869,6 +116253,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( */ u32 savedNQueryLoop = pParse->nQueryLoop; int rMayHaveNull = 0; + int bloomOk = (inFlags & IN_INDEX_MEMBERSHIP)!=0; eType = IN_INDEX_EPH; if( inFlags & IN_INDEX_LOOP ){ pParse->nQueryLoop = 0; @@ -114876,7 +116261,13 @@ SQLITE_PRIVATE int sqlite3FindInIndex( *prRhsHasNull = rMayHaveNull = ++pParse->nMem; } assert( pX->op==TK_IN ); - sqlite3CodeRhsOfIN(pParse, pX, iTab); + if( !bloomOk + && ExprUseXSelect(pX) + && (pX->x.pSelect->selFlags & SF_ClonedRhsIn)!=0 + ){ + bloomOk = 1; + } + sqlite3CodeRhsOfIN(pParse, pX, iTab, bloomOk); if( rMayHaveNull ){ sqlite3SetHasNullFlag(v, iTab, rMayHaveNull); } @@ -115034,7 +116425,8 @@ static int findCompatibleInRhsSubrtn( SQLITE_PRIVATE void sqlite3CodeRhsOfIN( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The IN operator */ - int iTab /* Use this cursor number */ + int iTab, /* Use this cursor number */ + int allowBloom /* True to allow the use of a Bloom filter */ ){ int addrOnce = 0; /* Address of the OP_Once instruction at top */ int addr; /* Address of OP_OpenEphemeral instruction */ @@ -115156,7 +116548,10 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( sqlite3SelectDestInit(&dest, SRT_Set, iTab); dest.zAffSdst = exprINAffinity(pParse, pExpr); pSelect->iLimit = 0; - if( addrOnce && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ){ + if( addrOnce + && allowBloom + && OptimizationEnabled(pParse->db, SQLITE_BloomFilter) + ){ int regBloom = ++pParse->nMem; addrBloom = sqlite3VdbeAddOp2(v, OP_Blob, 10000, regBloom); VdbeComment((v, "Bloom filter")); @@ -115377,7 +116772,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ || (pLeft->u.iValue!=1 && pLeft->u.iValue!=0) ){ sqlite3 *db = pParse->db; - pLimit = sqlite3Expr(db, TK_INTEGER, "0"); + pLimit = sqlite3ExprInt32(db, 0); if( pLimit ){ pLimit->affExpr = SQLITE_AFF_NUMERIC; pLimit = sqlite3PExpr(pParse, TK_NE, @@ -115388,7 +116783,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ } }else{ /* If there is no pre-existing limit add a limit of 1 */ - pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1"); + pLimit = sqlite3ExprInt32(pParse->db, 1); pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); } pSel->iLimit = 0; @@ -115626,7 +117021,7 @@ static void sqlite3ExprCodeIN( Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i); if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error; if( sqlite3ExprCanBeNull(p) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); + sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+aiMap[i], destStep2); VdbeCoverage(v); } } @@ -115649,8 +117044,9 @@ static void sqlite3ExprCodeIN( if( ExprHasProperty(pExpr, EP_Subrtn) ){ const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); assert( pOp->opcode==OP_Once || pParse->nErr ); - if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */ - assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); + if( pOp->p3>0 ){ /* tag-202407032019 */ + assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) + || pParse->nErr ); sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, rLhs, nVector); VdbeCoverage(v); } @@ -115699,9 +117095,18 @@ static void sqlite3ExprCodeIN( CollSeq *pColl; int r3 = sqlite3GetTempReg(pParse); p = sqlite3VectorFieldSubexpr(pLeft, i); - pColl = sqlite3ExprCollSeq(pParse, p); - sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3); - sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3, + if( ExprUseXSelect(pExpr) ){ + Expr *pRhs = pExpr->x.pSelect->pEList->a[i].pExpr; + pColl = sqlite3BinaryCompareCollSeq(pParse, p, pRhs); + }else{ + /* If the RHS of the IN(...) expression are scalar expressions, do + ** not consider their collation sequences. The documentation says + ** "The collating sequence used for expressions of the form "x IN (y, z, + ** ...)" is the collating sequence of x.". */ + pColl = sqlite3ExprCollSeq(pParse, p); + } + sqlite3VdbeAddOp3(v, OP_Column, iTab, aiMap[i], r3); + sqlite3VdbeAddOp4(v, OP_Ne, rLhs+aiMap[i], destNotNull, r3, (void*)pColl, P4_COLLSEQ); VdbeCoverage(v); sqlite3ReleaseTempReg(pParse, r3); @@ -115740,7 +117145,7 @@ static void sqlite3ExprCodeIN( static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; - sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); + sqlite3AtoF(z, &value); assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ if( negateFlag ) value = -value; sqlite3VdbeAddOp4Dup8(v, OP_Real, 0, iMem, 0, (u8*)&value, P4_REAL); @@ -116122,26 +117527,37 @@ static int exprCodeInlineFunction( } /* -** Expression Node callback for sqlite3ExprCanReturnSubtype(). +** Expression Node callback for sqlite3ExprCanReturnSubtype(). If +** pExpr is able to return a subtype, set pWalker->eCode and abort +** the search. If pExpr can never return a subtype, prune search. +** +** The only expressions that can return a subtype are: +** +** 1. A function +** 2. The no-op "+" operator +** 3. A CASE...END expression +** 4. A CAST() expression +** 5. A "expr COLLATE colseq" expression. ** -** Only a function call is able to return a subtype. So if the node -** is not a function call, return WRC_Prune immediately. +** For any other kind of expression, prune the search. ** -** A function call is able to return a subtype if it has the -** SQLITE_RESULT_SUBTYPE property. +** For case 1, the expression can yield a subtype if the function has +** the SQLITE_RESULT_SUBTYPE property. Functions can also return +** a subtype (via sqlite3_result_value()) if any of the arguments can +** return a subtype. ** -** Assume that every function is able to pass-through a subtype from -** one of its argument (using sqlite3_result_value()). Most functions -** are not this way, but we don't have a mechanism to distinguish those -** that are from those that are not, so assume they all work this way. -** That means that if one of its arguments is another function and that -** other function is able to return a subtype, then this function is -** able to return a subtype. +** In all cases 1 through 5, the expression might also return a subtype +** if any operand can return a subtype. */ static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ int n; FuncDef *pDef; sqlite3 *db; + if( pExpr->op==TK_CASE || pExpr->op==TK_UPLUS + || pExpr->op==TK_COLLATE || pExpr->op==TK_CAST + ){ + return WRC_Continue; + } if( pExpr->op!=TK_FUNCTION ){ return WRC_Prune; } @@ -116151,7 +117567,7 @@ static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ pWalker->eCode = 1; - return WRC_Prune; + return WRC_Abort; } return WRC_Continue; } @@ -116599,7 +118015,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_ISNOT: op = (op==TK_IS) ? TK_EQ : TK_NE; p5 = SQLITE_NULLEQ; - /* fall-through */ + /* no break */ deliberate_fall_through case TK_LT: case TK_LE: case TK_GT: @@ -118841,7 +120257,10 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ if( pIEpr==0 ) break; if( NEVER(!ExprUseYTab(pExpr)) ) break; for(i=0; inSrc; i++){ - if( pSrcList->a[0].iCursor==pIEpr->iDataCur ) break; + if( pSrcList->a[i].iCursor==pIEpr->iDataCur ){ + testcase( i>0 ); + break; + } } if( i>=pSrcList->nSrc ) break; if( NEVER(pExpr->pAggInfo!=0) ) break; /* Resolved by outer context */ @@ -119630,7 +121049,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ /* Look up the table being altered. */ assert( pParse->pNewTable==0 ); assert( sqlite3BtreeHoldsAllMutexes(db) ); - if( db->mallocFailed ) goto exit_begin_add_column; + if( NEVER(db->mallocFailed) ) goto exit_begin_add_column; pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); if( !pTab ) goto exit_begin_add_column; @@ -119702,7 +121121,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ ** Or, if pTab is not a view or virtual table, zero is returned. */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) -static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ +static int isRealTable(Parse *pParse, Table *pTab, int iOp){ const char *zType = 0; #ifndef SQLITE_OMIT_VIEW if( IsView(pTab) ){ @@ -119715,9 +121134,12 @@ static int isRealTable(Parse *pParse, Table *pTab, int bDrop){ } #endif if( zType ){ + const char *azMsg[] = { + "rename columns of", "drop column from", "edit constraints of" + }; + assert( iOp>=0 && iOpzName + azMsg[iOp], zType, pTab->zName ); return 1; } @@ -120188,6 +121610,25 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){ return pBest; } +/* +** Set the error message of the context passed as the first argument to +** the result of formatting zFmt using printf() style formatting. +*/ +static void errorMPrintf(sqlite3_context *pCtx, const char *zFmt, ...){ + sqlite3 *db = sqlite3_context_db_handle(pCtx); + char *zErr = 0; + va_list ap; + va_start(ap, zFmt); + zErr = sqlite3VMPrintf(db, zFmt, ap); + va_end(ap); + if( zErr ){ + sqlite3_result_error(pCtx, zErr, -1); + sqlite3DbFree(db, zErr); + }else{ + sqlite3_result_error_nomem(pCtx); + } +} + /* ** An error occurred while parsing or otherwise processing a database ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an @@ -120485,8 +121926,8 @@ static int renameResolveTrigger(Parse *pParse){ sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); if( pParse->nErr ) rc = pParse->rc; } - if( rc==SQLITE_OK && pStep->zTarget ){ - SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); + if( rc==SQLITE_OK && pStep->pSrc ){ + SrcList *pSrc = sqlite3SrcListDup(db, pStep->pSrc, 0); if( pSrc ){ Select *pSel = sqlite3SelectNew( pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 @@ -120514,10 +121955,10 @@ static int renameResolveTrigger(Parse *pParse){ pSel->pSrc = 0; sqlite3SelectDelete(db, pSel); } - if( pStep->pFrom ){ + if( ALWAYS(pStep->pSrc) ){ int i; - for(i=0; ipFrom->nSrc && rc==SQLITE_OK; i++){ - SrcItem *p = &pStep->pFrom->a[i]; + for(i=0; ipSrc->nSrc && rc==SQLITE_OK; i++){ + SrcItem *p = &pStep->pSrc->a[i]; if( p->fg.isSubquery ){ assert( p->u4.pSubq!=0 ); sqlite3SelectPrep(pParse, p->u4.pSubq->pSelect, 0); @@ -120586,13 +122027,13 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere); sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere); } - if( pStep->pFrom ){ + if( pStep->pSrc ){ int i; - SrcList *pFrom = pStep->pFrom; - for(i=0; inSrc; i++){ - if( pFrom->a[i].fg.isSubquery ){ - assert( pFrom->a[i].u4.pSubq!=0 ); - sqlite3WalkSelect(pWalker, pFrom->a[i].u4.pSubq->pSelect); + SrcList *pSrc = pStep->pSrc; + for(i=0; inSrc; i++){ + if( pSrc->a[i].fg.isSubquery ){ + assert( pSrc->a[i].u4.pSubq!=0 ); + sqlite3WalkSelect(pWalker, pSrc->a[i].u4.pSubq->pSelect); } } } @@ -120763,8 +122204,8 @@ static void renameColumnFunc( if( rc!=SQLITE_OK ) goto renameColumnFunc_done; for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){ - if( pStep->zTarget ){ - Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb); + if( pStep->pSrc ){ + Table *pTarget = sqlite3LocateTableItem(&sParse, 0, &pStep->pSrc->a[0]); if( pTarget==pTab ){ if( pStep->pUpsert ){ ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet; @@ -120776,7 +122217,6 @@ static void renameColumnFunc( } } - /* Find tokens to edit in UPDATE OF clause */ if( sParse.pTriggerTab==pTab ){ renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld); @@ -120978,13 +122418,10 @@ static void renameTableFunc( if( rc==SQLITE_OK ){ renameWalkTrigger(&sWalker, pTrigger); for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ - if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ - renameTokenFind(&sParse, &sCtx, pStep->zTarget); - } - if( pStep->pFrom ){ + if( pStep->pSrc ){ int i; - for(i=0; ipFrom->nSrc; i++){ - SrcItem *pItem = &pStep->pFrom->a[i]; + for(i=0; ipSrc->nSrc; i++){ + SrcItem *pItem = &pStep->pSrc->a[i]; if( 0==sqlite3_stricmp(pItem->zName, zOld) ){ renameTokenFind(&sParse, &sCtx, pItem->zName); } @@ -121231,6 +122668,57 @@ static void renameTableTest( #endif } + +/* +** Return the number of bytes until the end of the next non-whitespace and +** non-comment token. For the purpose of this function, a "(" token includes +** all of the bytes through and including the matching ")", or until the +** first illegal token, whichever comes first. +** +** Write the token type into *piToken. +** +** The value returned is the number of bytes in the token itself plus +** the number of bytes of leading whitespace and comments skipped plus +** all bytes through the next matching ")" if the token is TK_LP. +** +** Example: (Note: '.' used in place of '*' in the example z[] text) +** +** ,--------- *piToken := TK_RP +** v +** z[] = " /.comment./ --comment\n (two three four) five" +** | | +** |<-------------------------------------->| +** | +** `--- return value +*/ +static int getConstraintToken(const u8 *z, int *piToken){ + int iOff = 0; + int t = 0; + do { + iOff += sqlite3GetToken(&z[iOff], &t); + }while( t==TK_SPACE || t==TK_COMMENT ); + + *piToken = t; + + if( t==TK_LP ){ + int nNest = 1; + while( nNest>0 ){ + iOff += sqlite3GetToken(&z[iOff], &t); + if( t==TK_LP ){ + nNest++; + }else if( t==TK_RP ){ + t = TK_LP; + nNest--; + }else if( t==TK_ILLEGAL ){ + break; + } + } + } + + *piToken = t; + return iOff; +} + /* ** The implementation of internal UDF sqlite_drop_column(). ** @@ -121275,15 +122763,24 @@ static void dropColumnFunc( goto drop_column_done; } - pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); if( iColnCol-1 ){ RenameToken *pEnd; + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zCnName); pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zCnName); zEnd = (const char*)pEnd->t.z; }else{ + int eTok; assert( IsOrdinaryTable(pTab) ); + assert( iCol!=0 ); + /* Point pCol->t.z at the "," immediately preceding the definition of + ** the column being dropped. To do this, start at the name of the + ** previous column, and tokenize until the next ",". */ + pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol-1].zCnName); + do { + pCol->t.z += getConstraintToken((const u8*)pCol->t.z, &eTok); + }while( eTok!=TK_COMMA ); + pCol->t.z--; zEnd = (const char*)&zSql[pTab->u.tab.addColOffset]; - while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--; } zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd); @@ -121452,6 +122949,665 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T sqlite3SrcListDelete(db, pSrc); } +/* +** Return the number of bytes of leading whitespace/comments in string z[]. +*/ +static int getWhitespace(const u8 *z){ + int nRet = 0; + while( 1 ){ + int t = 0; + int n = sqlite3GetToken(&z[nRet], &t); + if( t!=TK_SPACE && t!=TK_COMMENT ) break; + nRet += n; + } + return nRet; +} + + +/* +** Argument z points into the body of a constraint - specifically the +** second token of the constraint definition. For a named constraint, +** z points to the first token past the CONSTRAINT keyword. For an +** unnamed NOT NULL constraint, z points to the first byte past the NOT +** keyword. +** +** Return the number of bytes until the end of the constraint. +*/ +static int getConstraint(const u8 *z){ + int iOff = 0; + int t = 0; + + /* Now, the current constraint proceeds until the next occurence of one + ** of the following tokens: + ** + ** CONSTRAINT, PRIMARY, NOT, UNIQUE, CHECK, DEFAULT, + ** COLLATE, REFERENCES, FOREIGN, GENERATED, AS, RP, or COMMA + ** + ** Also exit the loop if ILLEGAL turns up. + */ + while( 1 ){ + int n = getConstraintToken(&z[iOff], &t); + if( t==TK_CONSTRAINT || t==TK_PRIMARY || t==TK_NOT || t==TK_UNIQUE + || t==TK_CHECK || t==TK_DEFAULT || t==TK_COLLATE || t==TK_REFERENCES + || t==TK_FOREIGN || t==TK_RP || t==TK_COMMA || t==TK_ILLEGAL + || t==TK_AS || t==TK_GENERATED + ){ + break; + } + iOff += n; + } + + return iOff; +} + +/* +** Compare two constraint names. +** +** Summary: *pRes := zQuote != zCmp +** +** Details: +** Compare the (possibly quoted) constraint name zQuote[0..nQuote-1] +** against zCmp[]. Write zero into *pRes if they are the same and +** non-zero if they differ. Normally return SQLITE_OK, except if there +** is an OOM, set the OOM error condition on ctx and return SQLITE_NOMEM. +*/ +static int quotedCompare( + sqlite3_context *ctx, /* Function context on which to report errors */ + int t, /* Token type */ + const u8 *zQuote, /* Possibly quoted text. Not zero-terminated. */ + int nQuote, /* Length of zQuote in bytes */ + const u8 *zCmp, /* Zero-terminated, unquoted name to compare against */ + int *pRes /* OUT: Set to 0 if equal, non-zero if unequal */ +){ + char *zCopy = 0; /* De-quoted, zero-terminated copy of zQuote[] */ + + if( t==TK_ILLEGAL ){ + *pRes = 1; + return SQLITE_OK; + } + zCopy = sqlite3MallocZero(nQuote+1); + if( zCopy==0 ){ + sqlite3_result_error_nomem(ctx); + return SQLITE_NOMEM_BKPT; + } + memcpy(zCopy, zQuote, nQuote); + sqlite3Dequote(zCopy); + *pRes = sqlite3_stricmp((const char*)zCopy, (const char*)zCmp); + sqlite3_free(zCopy); + return SQLITE_OK; +} + +/* +** zSql[] is a CREATE TABLE statement, supposedly. Find the offset +** into zSql[] of the first character past the first "(" and write +** that offset into *piOff and return SQLITE_OK. Or, if not found, +** set the SQLITE_CORRUPT error code and return SQLITE_ERROR. +*/ +static int skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff){ + int iOff = 0; + + if( zSql==0 ) return SQLITE_ERROR; + + /* Jump past the "CREATE TABLE" bit. */ + while( 1 ){ + int t = 0; + iOff += sqlite3GetToken(&zSql[iOff], &t); + if( t==TK_LP ) break; + if( t==TK_ILLEGAL ){ + sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT); + return SQLITE_ERROR; + } + } + + *piOff = iOff; + return SQLITE_OK; +} + +/* +** Internal SQL function sqlite3_drop_constraint(): Given an input +** CREATE TABLE statement, return a revised CREATE TABLE statement +** with a constraint removed. Two forms, depending on the datatype +** of argv[2]: +** +** sqlite_drop_constraint(SQL, INT) -- Omit NOT NULL from the INT-th column +** sqlite_drop_constraint(SQL, TEXT) -- OMIT constraint with name TEXT +** +** In the first case, the left-most column is 0. +*/ +static void dropConstraintFunc( + sqlite3_context *ctx, + int NotUsed, + sqlite3_value **argv +){ + const u8 *zSql = sqlite3_value_text(argv[0]); + const u8 *zCons = 0; + int iNotNull = -1; + int ii; + int iOff = 0; + int iStart = 0; + int iEnd = 0; + char *zNew = 0; + int t = 0; + sqlite3 *db; + UNUSED_PARAMETER(NotUsed); + + if( zSql==0 ) return; + + /* Jump past the "CREATE TABLE" bit. */ + if( skipCreateTable(ctx, zSql, &iOff) ) return; + + if( sqlite3_value_type(argv[1])==SQLITE_INTEGER ){ + iNotNull = sqlite3_value_int(argv[1]); + }else{ + zCons = sqlite3_value_text(argv[1]); + } + + /* Search for the named constraint within column definitions. */ + for(ii=0; iEnd==0; ii++){ + + /* Now parse the column or table constraint definition. Search + ** for the token CONSTRAINT if this is a DROP CONSTRAINT command, or + ** NOT in the right column if this is a DROP NOT NULL. */ + while( 1 ){ + iStart = iOff; + iOff += getConstraintToken(&zSql[iOff], &t); + if( t==TK_CONSTRAINT && (zCons || iNotNull==ii) ){ + /* Check if this is the constraint we are searching for. */ + int nTok = 0; + int cmp = 1; + + /* Skip past any whitespace. */ + iOff += getWhitespace(&zSql[iOff]); + + /* Compare the next token - which may be quoted - with the name of + ** the constraint being dropped. */ + nTok = getConstraintToken(&zSql[iOff], &t); + if( zCons ){ + if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return; + } + iOff += nTok; + + /* The next token is usually the first token of the constraint + ** definition. This is enough to tell the type of the constraint - + ** TK_NOT means it is a NOT NULL, TK_CHECK a CHECK constraint etc. + ** + ** There is also the chance that the next token is TK_CONSTRAINT + ** (or TK_DEFAULT or TK_COLLATE), for example if a table has been + ** created as follows: + ** + ** CREATE TABLE t1(cols, CONSTRAINT one CONSTRAINT two NOT NULL); + ** + ** In this case, allow the "CONSTRAINT one" bit to be dropped by + ** this command if that is what is requested, or to advance to + ** the next iteration of the loop with &zSql[iOff] still pointing + ** to the CONSTRAINT keyword. */ + nTok = getConstraintToken(&zSql[iOff], &t); + if( t==TK_CONSTRAINT || t==TK_DEFAULT || t==TK_COLLATE + || t==TK_COMMA || t==TK_RP || t==TK_GENERATED || t==TK_AS + ){ + t = TK_CHECK; + }else{ + iOff += nTok; + iOff += getConstraint(&zSql[iOff]); + } + + if( cmp==0 || (iNotNull>=0 && t==TK_NOT) ){ + if( t!=TK_NOT && t!=TK_CHECK ){ + errorMPrintf(ctx, "constraint may not be dropped: %s", zCons); + return; + } + iEnd = iOff; + break; + } + + }else if( t==TK_NOT && iNotNull==ii ){ + iEnd = iOff + getConstraint(&zSql[iOff]); + break; + }else if( t==TK_RP || t==TK_ILLEGAL ){ + iEnd = -1; + break; + }else if( t==TK_COMMA ){ + break; + } + } + } + + /* If the constraint has not been found it is an error. */ + if( iEnd<=0 ){ + if( zCons ){ + errorMPrintf(ctx, "no such constraint: %s", zCons); + }else{ + /* SQLite follows postgres in that a DROP NOT NULL on a column that is + ** not NOT NULL is not an error. So just return the original SQL here. */ + sqlite3_result_text(ctx, (const char*)zSql, -1, SQLITE_TRANSIENT); + } + }else{ + + /* Figure out if an extra space should be inserted after the constraint + ** is removed. And if an additional comma preceding the constraint + ** should be removed. */ + const char *zSpace = " "; + iEnd += getWhitespace(&zSql[iEnd]); + sqlite3GetToken(&zSql[iEnd], &t); + if( t==TK_RP || t==TK_COMMA ){ + zSpace = ""; + if( zSql[iStart-1]==',' ) iStart--; + } + + db = sqlite3_context_db_handle(ctx); + zNew = sqlite3MPrintf(db, "%.*s%s%s", iStart, zSql, zSpace, &zSql[iEnd]); + sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC); + } +} + +/* +** Internal SQL function: +** +** sqlite_add_constraint(SQL, CONSTRAINT-TEXT, ICOL) +** +** SQL is a CREATE TABLE statement. Return a modified version of +** SQL that adds CONSTRAINT-TEXT at the end of the ICOL-th column +** definition. (The left-most column defintion is 0.) +*/ +static void addConstraintFunc( + sqlite3_context *ctx, + int NotUsed, + sqlite3_value **argv +){ + const u8 *zSql = sqlite3_value_text(argv[0]); + const char *zCons = (const char*)sqlite3_value_text(argv[1]); + int iCol = sqlite3_value_int(argv[2]); + int iOff = 0; + int ii; + char *zNew = 0; + int t = 0; + sqlite3 *db; + UNUSED_PARAMETER(NotUsed); + + if( skipCreateTable(ctx, zSql, &iOff) ) return; + + for(ii=0; ii<=iCol || (iCol<0 && t!=TK_RP); ii++){ + iOff += getConstraintToken(&zSql[iOff], &t); + while( 1 ){ + int nTok = getConstraintToken(&zSql[iOff], &t); + if( t==TK_COMMA || t==TK_RP ) break; + if( t==TK_ILLEGAL ){ + sqlite3_result_error_code(ctx, SQLITE_CORRUPT_BKPT); + return; + } + iOff += nTok; + } + } + + iOff += getWhitespace(&zSql[iOff]); + + db = sqlite3_context_db_handle(ctx); + if( iCol<0 ){ + zNew = sqlite3MPrintf(db, "%.*s, %s%s", iOff, zSql, zCons, &zSql[iOff]); + }else{ + zNew = sqlite3MPrintf(db, "%.*s %s%s", iOff, zSql, zCons, &zSql[iOff]); + } + sqlite3_result_text(ctx, zNew, -1, SQLITE_DYNAMIC); +} + +/* +** Find a column named pCol in table pTab. If successful, set output +** parameter *piCol to the index of the column in the table and return +** SQLITE_OK. Otherwise, set *piCol to -1 and return an SQLite error +** code. +*/ +static int alterFindCol(Parse *pParse, Table *pTab, Token *pCol, int *piCol){ + sqlite3 *db = pParse->db; + char *zName = sqlite3NameFromToken(db, pCol); + int rc = SQLITE_NOMEM; + int iCol = -1; + + if( zName ){ + iCol = sqlite3ColumnIndex(pTab, zName); + if( iCol<0 ){ + sqlite3ErrorMsg(pParse, "no such column: %s", zName); + rc = SQLITE_ERROR; + }else{ + rc = SQLITE_OK; + } + } + +#ifndef SQLITE_OMIT_AUTHORIZATION + if( rc==SQLITE_OK ){ + const char *zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; + const char *zCol = pTab->aCol[iCol].zCnName; + if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ + pTab = 0; + } + } +#endif + + sqlite3DbFree(db, zName); + *piCol = iCol; + return rc; +} + + +/* +** Find the table named by the first entry in source list pSrc. If successful, +** return a pointer to the Table structure and set output variable (*pzDb) +** to point to the name of the database containin the table (i.e. "main", +** "temp" or the name of an attached database). +** +** If the table cannot be located, return NULL. The value of the two output +** parameters is undefined in this case. +*/ +static Table *alterFindTable( + Parse *pParse, /* Parsing context */ + SrcList *pSrc, /* Name of the table to look for */ + int *piDb, /* OUT: write the iDb here */ + const char **pzDb, /* OUT: write name of schema here */ + int bAuth /* Do ALTER TABLE authorization checks if true */ +){ + sqlite3 *db = pParse->db; + Table *pTab = 0; + assert( sqlite3BtreeHoldsAllMutexes(db) ); + pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]); + if( pTab ){ + int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + *pzDb = db->aDb[iDb].zDbSName; + *piDb = iDb; + + if( SQLITE_OK!=isRealTable(pParse, pTab, 2) + || SQLITE_OK!=isAlterableTable(pParse, pTab) + ){ + pTab = 0; + } + } +#ifndef SQLITE_OMIT_AUTHORIZATION + if( pTab && bAuth ){ + if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, *pzDb, pTab->zName, 0) ){ + pTab = 0; + } + } +#endif + sqlite3SrcListDelete(db, pSrc); + return pTab; +} + +/* +** Generate bytecode for one of: +** +** (1) ALTER TABLE pSrc DROP CONSTRAINT pCons +** (2) ALTER TABLE pSrc ALTER pCol DROP NOT NULL +** +** One of pCons and pCol must be NULL and the other non-null. +*/ +SQLITE_PRIVATE void sqlite3AlterDropConstraint( + Parse *pParse, /* Parsing context */ + SrcList *pSrc, /* The table being altered */ + Token *pCons, /* Name of the constraint to drop */ + Token *pCol /* Name of the column from which to remove the NOT NULL */ +){ + sqlite3 *db = pParse->db; + Table *pTab = 0; + int iDb = 0; + const char *zDb = 0; + char *zArg = 0; + + assert( (pCol==0)!=(pCons==0) ); + assert( pSrc->nSrc==1 ); + pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, pCons!=0); + if( !pTab ) return; + + if( pCons ){ + char *z = sqlite3NameFromToken(db, pCons); + zArg = sqlite3MPrintf(db, "%Q", z); + sqlite3DbFree(db, z); + }else{ + int iCol; + if( alterFindCol(pParse, pTab, pCol, &iCol) ) return; + zArg = sqlite3MPrintf(db, "%d", iCol); + } + + /* Edit the SQL for the named table. */ + sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_drop_constraint(sql, %s) " + "WHERE type='table' AND tbl_name=%Q COLLATE nocase" + , zDb, zArg, pTab->zName + ); + sqlite3DbFree(db, zArg); + + /* Finally, reload the database schema. */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); +} + +/* +** The implementation of SQL function sqlite_fail(MSG). This takes a single +** argument, and returns it as an error message with the error code set to +** SQLITE_CONSTRAINT. +*/ +static void failConstraintFunc( + sqlite3_context *ctx, + int NotUsed, + sqlite3_value **argv +){ + const char *zText = (const char*)sqlite3_value_text(argv[0]); + int err = sqlite3_value_int(argv[1]); + (void)NotUsed; + sqlite3_result_error(ctx, zText, -1); + sqlite3_result_error_code(ctx, err); +} + +/* +** Buffer pCons, which is nCons bytes in size, contains the text of a +** NOT NULL or CHECK constraint that will be inserted into a CREATE TABLE +** statement. If successful, this function returns the size of the buffer in +** bytes not including any trailing whitespace or "--" style comments. Or, +** if an OOM occurs, it returns 0 and sets db->mallocFailed to true. +** +** C-style comments at the end are preserved. "--" style comments are +** removed because the comment terminator might be \000, and we are about +** to insert the pCons[] text into the middle of a larger string, and that +** will have the effect of removing the comment terminator and messing up +** the syntax. +*/ +static int alterRtrimConstraint( + sqlite3 *db, /* used to record OOM error */ + const char *pCons, /* Buffer containing constraint */ + int nCons /* Size of pCons in bytes */ +){ + u8 *zTmp = (u8*)sqlite3MPrintf(db, "%.*s", nCons, pCons); + int iOff = 0; + int iEnd = 0; + + if( zTmp==0 ) return 0; + + while( 1 ){ + int t = 0; + int nToken = sqlite3GetToken(&zTmp[iOff], &t); + if( t==TK_ILLEGAL ) break; + if( t!=TK_SPACE && (t!=TK_COMMENT || zTmp[iOff]!='-') ){ + iEnd = iOff+nToken; + } + iOff += nToken; + } + + sqlite3DbFree(db, zTmp); + return iEnd; +} + +/* +** Prepare a statement of the form: +** +** ALTER TABLE pSrc ALTER pCol SET NOT NULL +*/ +SQLITE_PRIVATE void sqlite3AlterSetNotNull( + Parse *pParse, /* Parsing context */ + SrcList *pSrc, /* Name of the table being altered */ + Token *pCol, /* Name of the column to add a NOT NULL constraint to */ + Token *pFirst /* The NOT token of the NOT NULL constraint text */ +){ + Table *pTab = 0; + int iCol = 0; + int iDb = 0; + const char *zDb = 0; + const char *pCons = 0; + int nCons = 0; + + /* Look up the table being altered. */ + assert( pSrc->nSrc==1 ); + pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 0); + if( !pTab ) return; + + /* Find the column being altered. */ + if( alterFindCol(pParse, pTab, pCol, &iCol) ){ + return; + } + + /* Find the length in bytes of the constraint definition */ + pCons = pFirst->z; + nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons); + + /* Search for a constraint violation. Throw an exception if one is found. */ + sqlite3NestedParse(pParse, + "SELECT sqlite_fail('constraint failed', %d) " + "FROM %Q.%Q AS x WHERE x.%.*s IS NULL", + SQLITE_CONSTRAINT, zDb, pTab->zName, (int)pCol->n, pCol->z + ); + + /* Edit the SQL for the named table. */ + sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_add_constraint(sqlite_drop_constraint(sql, %d), %.*Q, %d) " + "WHERE type='table' AND tbl_name=%Q COLLATE nocase" + , zDb, iCol, nCons, pCons, iCol, pTab->zName + ); + + /* Finally, reload the database schema. */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); +} + +/* +** Implementation of internal SQL function: +** +** sqlite_find_constraint(SQL, CONSTRAINT-NAME) +** +** This function returns true if the SQL passed as the first argument is a +** CREATE TABLE that contains a constraint with the name CONSTRAINT-NAME, +** or false otherwise. +*/ +static void findConstraintFunc( + sqlite3_context *ctx, + int NotUsed, + sqlite3_value **argv +){ + const u8 *zSql = 0; + const u8 *zCons = 0; + int iOff = 0; + int t = 0; + + (void)NotUsed; + zSql = sqlite3_value_text(argv[0]); + zCons = sqlite3_value_text(argv[1]); + + if( zSql==0 || zCons==0 ) return; + while( t!=TK_LP && t!=TK_ILLEGAL ){ + iOff += sqlite3GetToken(&zSql[iOff], &t); + } + + while( 1 ){ + iOff += getConstraintToken(&zSql[iOff], &t); + if( t==TK_CONSTRAINT ){ + int nTok = 0; + int cmp = 0; + iOff += getWhitespace(&zSql[iOff]); + nTok = getConstraintToken(&zSql[iOff], &t); + if( quotedCompare(ctx, t, &zSql[iOff], nTok, zCons, &cmp) ) return; + if( cmp==0 ){ + sqlite3_result_int(ctx, 1); + return; + } + }else if( t==TK_ILLEGAL ){ + break; + } + } + + sqlite3_result_int(ctx, 0); +} + +/* +** Generate bytecode to implement: +** +** ALTER TABLE pSrc ADD [CONSTRAINT pName] CHECK(pExpr) +** +** Any "ON CONFLICT" text that occurs after the "CHECK(...)", up +** until pParse->sLastToken, is included as part of the new constraint. +*/ +SQLITE_PRIVATE void sqlite3AlterAddConstraint( + Parse *pParse, /* Parse context */ + SrcList *pSrc, /* Table to add constraint to */ + Token *pFirst, /* First token of new constraint */ + Token *pName, /* Name of new constraint. NULL if name omitted. */ + const char *zExpr, /* Text of CHECK expression */ + int nExpr, /* Size of pExpr in bytes */ + Expr *pExpr /* The parsed CHECK expression */ +){ + Table *pTab = 0; /* Table identified by pSrc */ + int iDb = 0; /* Which schema does pTab live in */ + const char *zDb = 0; /* Name of the schema in which pTab lives */ + const char *pCons = 0; /* Text of the constraint */ + int nCons; /* Bytes of text to use from pCons[] */ + int rc; /* Result from error checking pExpr */ + + /* Look up the table being altered. */ + assert( pSrc->nSrc==1 ); + pTab = alterFindTable(pParse, pSrc, &iDb, &zDb, 1); + if( !pTab ){ + sqlite3ExprDelete(pParse->db, pExpr); + return; + } + + /* Verify that the new CHECK constraint does not contain any + ** internal-use-only function. Forum post 2026-05-10T01:11:28Z + */ + rc = sqlite3ResolveSelfReference(pParse, pTab, NC_IsCheck, pExpr, 0); + sqlite3ExprDelete(pParse->db, pExpr); + if( rc ) return; + + /* If this new constraint has a name, check that it is not a duplicate of + ** an existing constraint. It is an error if it is. */ + if( pName ){ + char *zName = sqlite3NameFromToken(pParse->db, pName); + + sqlite3NestedParse(pParse, + "SELECT sqlite_fail('constraint %q already exists', %d) " + "FROM \"%w\"." LEGACY_SCHEMA_TABLE " " + "WHERE type='table' AND tbl_name=%Q COLLATE nocase " + "AND sqlite_find_constraint(sql, %Q)", + zName, SQLITE_ERROR, zDb, pTab->zName, zName + ); + sqlite3DbFree(pParse->db, zName); + } + + /* Search for a constraint violation. Throw an exception if one is found. */ + sqlite3NestedParse(pParse, + "SELECT sqlite_fail('constraint failed', %d) " + "FROM %Q.%Q WHERE (%.*s) IS NOT TRUE", + SQLITE_CONSTRAINT, zDb, pTab->zName, nExpr, zExpr + ); + + /* Edit the SQL for the named table. */ + pCons = pFirst->z; + nCons = alterRtrimConstraint(pParse->db, pCons, pParse->sLastToken.z - pCons); + + sqlite3NestedParse(pParse, + "UPDATE \"%w\"." LEGACY_SCHEMA_TABLE " SET " + "sql = sqlite_add_constraint(sql, %.*Q, -1) " + "WHERE type='table' AND tbl_name=%Q COLLATE nocase" + , zDb, nCons, pCons, pTab->zName + ); + + /* Finally, reload the database schema. */ + renameReloadSchema(pParse, iDb, INITFLAG_AlterDropCons); +} + /* ** Register built-in functions used to help implement ALTER TABLE */ @@ -121462,6 +123618,10 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){ INTERNAL_FUNCTION(sqlite_rename_test, 7, renameTableTest), INTERNAL_FUNCTION(sqlite_drop_column, 3, dropColumnFunc), INTERNAL_FUNCTION(sqlite_rename_quotefix,2, renameQuotefixFunc), + INTERNAL_FUNCTION(sqlite_drop_constraint,2, dropConstraintFunc), + INTERNAL_FUNCTION(sqlite_fail, 2, failConstraintFunc), + INTERNAL_FUNCTION(sqlite_add_constraint, 3, addConstraintFunc), + INTERNAL_FUNCTION(sqlite_find_constraint,2, findConstraintFunc), }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } @@ -123285,9 +125445,9 @@ static int loadStatTbl( } pIdx->nSampleCol = nIdxCol; pIdx->mxSample = nSample; - nByte = ROUND8(sizeof(IndexSample) * nSample); - nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; - nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ + nByte = ROUND8(sizeof64(IndexSample) * nSample); + nByte += sizeof64(tRowcnt) * nIdxCol * 3 * nSample; + nByte += nIdxCol * sizeof64(tRowcnt); /* Space for Index.aAvgEq[] */ pIdx->aSample = sqlite3DbMallocZero(db, nByte); if( pIdx->aSample==0 ){ @@ -123295,7 +125455,7 @@ static int loadStatTbl( return SQLITE_NOMEM_BKPT; } pPtr = (u8*)pIdx->aSample; - pPtr += ROUND8(nSample*sizeof(pIdx->aSample[0])); + pPtr += ROUND8(nSample*sizeof64(pIdx->aSample[0])); pSpace = (tRowcnt*)pPtr; assert( EIGHT_BYTE_ALIGNMENT( pSpace ) ); pIdx->aAvgEq = pSpace; pSpace += nIdxCol; @@ -123592,6 +125752,16 @@ static void attachFunc( ** from sqlite3_deserialize() to close database db->init.iDb and ** reopen it as a MemDB */ Btree *pNewBt = 0; + + pNew = &db->aDb[db->init.iDb]; + assert( pNew->pBt!=0 ); + if( sqlite3BtreeTxnState(pNew->pBt)!=SQLITE_TXN_NONE + || sqlite3BtreeIsInBackup(pNew->pBt) + ){ + rc = SQLITE_BUSY; + goto attach_error; + } + pVfs = sqlite3_vfs_find("memdb"); if( pVfs==0 ) return; rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB); @@ -123601,8 +125771,7 @@ static void attachFunc( /* Both the Btree and the new Schema were allocated successfully. ** Close the old db and update the aDb[] slot with the new memdb ** values. */ - pNew = &db->aDb[db->init.iDb]; - if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt); + sqlite3BtreeClose(pNew->pBt); pNew->pBt = pNewBt; pNew->pSchema = pNewSchema; }else{ @@ -124082,7 +126251,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep( if( sqlite3WalkSelect(&pFix->w, pStep->pSelect) || sqlite3WalkExpr(&pFix->w, pStep->pWhere) || sqlite3WalkExprList(&pFix->w, pStep->pExprList) - || sqlite3FixSrcList(pFix, pStep->pFrom) + || sqlite3FixSrcList(pFix, pStep->pSrc) ){ return 1; } @@ -124189,7 +126358,7 @@ SQLITE_API int sqlite3_set_authorizer( sqlite3_mutex_enter(db->mutex); db->xAuth = (sqlite3_xauth)xAuth; db->pAuthArg = pArg; - if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1); + sqlite3ExpirePreparedStatements(db, 1); sqlite3_mutex_leave(db->mutex); return SQLITE_OK; } @@ -124860,6 +127029,7 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem( const char *zDb; if( p->fg.fixedSchema ){ int iDb = sqlite3SchemaToIndex(pParse->db, p->u4.pSchema); + assert( iDb>=0 && iDbdb->nDb ); zDb = pParse->db->aDb[iDb].zDbSName; }else{ assert( !p->fg.isSubquery ); @@ -126433,8 +128603,8 @@ SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){ ** The estimate is conservative. It might be larger that what is ** really needed. */ -static int identLength(const char *z){ - int n; +static i64 identLength(const char *z){ + i64 n; for(n=0; *z; n++, z++){ if( *z=='"' ){ n++; } } @@ -126867,9 +129037,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ if( !hasColumn(pPk->aiColumn, j, i) && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){ + const char *zColl = sqlite3ColumnColl(&pTab->aCol[i]); assert( jnColumn ); pPk->aiColumn[j] = i; - pPk->azColl[j] = sqlite3StrBINARY; + pPk->azColl[j] = zColl ? zColl : sqlite3StrBINARY; j++; } } @@ -126944,13 +129115,14 @@ SQLITE_PRIVATE void sqlite3MarkAllShadowTablesOf(sqlite3 *db, Table *pTab){ ** restored to its original value prior to this routine returning. */ SQLITE_PRIVATE int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ - char *zTail; /* Pointer to the last "_" in zName */ + const char *zTail; /* Pointer to the last "_" in zName */ Table *pTab; /* Table that zName is a shadow of */ + char *zCopy; zTail = strrchr(zName, '_'); if( zTail==0 ) return 0; - *zTail = 0; - pTab = sqlite3FindTable(db, zName, 0); - *zTail = '_'; + zCopy = sqlite3DbStrNDup(db, zName, (int)(zTail-zName)); + pTab = zCopy ? sqlite3FindTable(db, zCopy, 0) : 0; + sqlite3DbFree(db, zCopy); if( pTab==0 ) return 0; if( !IsVirtual(pTab) ) return 0; return sqlite3IsShadowTableOf(db, pTab, zName); @@ -127103,6 +129275,7 @@ SQLITE_PRIVATE void sqlite3EndTable( convertToWithoutRowidTable(pParse, p); } iDb = sqlite3SchemaToIndex(db, p->pSchema); + assert( iDb>=0 && iDb<=db->nDb ); #ifndef SQLITE_OMIT_CHECK /* Resolve names in all CHECK constraint expressions. @@ -127398,6 +129571,7 @@ SQLITE_PRIVATE void sqlite3CreateView( sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); + assert( iDb>=0 && iDbnDb ); sqlite3FixInit(&sFix, pParse, iDb, "view", pName); if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail; @@ -128994,6 +131168,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists goto exit_drop_index; } iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); + assert( iDb>=0 && iDbnDb ); #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_DROP_INDEX; @@ -129891,8 +132066,7 @@ SQLITE_PRIVATE void sqlite3RowidConstraint( } /* -** Check to see if pIndex uses the collating sequence pColl. Return -** true if it does and false if it does not. +** Return true if any column of pIndex uses the zColl collation */ #ifndef SQLITE_OMIT_REINDEX static int collationMatch(const char *zColl, Index *pIndex){ @@ -129900,8 +132074,8 @@ static int collationMatch(const char *zColl, Index *pIndex){ assert( zColl!=0 ); for(i=0; inColumn; i++){ const char *z = pIndex->azColl[i]; - assert( z!=0 || pIndex->aiColumn[i]<0 ); - if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){ + assert( z!=0 ); + if( 0==sqlite3StrICmp(z, zColl) ){ return 1; } } @@ -129909,73 +132083,39 @@ static int collationMatch(const char *zColl, Index *pIndex){ } #endif -/* -** Recompute all indices of pTab that use the collating sequence pColl. -** If pColl==0 then recompute all indices of pTab. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ - if( !IsVirtual(pTab) ){ - Index *pIndex; /* An index associated with pTab */ - - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( zColl==0 || collationMatch(zColl, pIndex) ){ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - } - } - } -} -#endif - -/* -** Recompute all indices of all tables in all databases where the -** indices use the collating sequence pColl. If pColl==0 then recompute -** all indices everywhere. -*/ -#ifndef SQLITE_OMIT_REINDEX -static void reindexDatabases(Parse *pParse, char const *zColl){ - Db *pDb; /* A single database */ - int iDb; /* The database index number */ - sqlite3 *db = pParse->db; /* The database connection */ - HashElem *k; /* For looping over tables in pDb */ - Table *pTab; /* A table in the database */ - - assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ - for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ - assert( pDb!=0 ); - for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ - pTab = (Table*)sqliteHashData(k); - reindexTable(pParse, pTab, zColl); - } - } -} -#endif - /* ** Generate code for the REINDEX command. ** ** REINDEX -- 1 ** REINDEX -- 2 -** REINDEX ?.? -- 3 -** REINDEX ?.? -- 4 +** REINDEX ?.? -- 3 +** REINDEX ?.? -- 4 +** REINDEX EXPRESSIONS -- 5 ** -** Form 1 causes all indices in all attached databases to be rebuilt. -** Form 2 rebuilds all indices in all databases that use the named +** Form 1 causes all indexes in all attached databases to be rebuilt. +** Form 2 rebuilds all indexes in all databases that use the named ** collating function. Forms 3 and 4 rebuild the named index or all -** indices associated with the named table. +** indexes associated with the named table, respectively. Form 5 +** rebuilds all expression indexes in addition to all collations, +** indexes, or tables named "EXPRESSIONS". +** +** If the name is ambiguous such that it matches two or more of +** forms 2 through 5, then rebuild the union of all matching indexes, +** taken care to avoid rebuilding the same index more than once. */ #ifndef SQLITE_OMIT_REINDEX SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ - CollSeq *pColl; /* Collating sequence to be reindexed, or NULL */ - char *z; /* Name of a table or index */ - const char *zDb; /* Name of the database */ - Table *pTab; /* A table in the database */ - Index *pIndex; /* An index associated with pTab */ - int iDb; /* The database index number */ + char *z = 0; /* Name of a table or index or collation */ + const char *zDb = 0; /* Name of the database */ + int iReDb = -1; /* The database index number */ sqlite3 *db = pParse->db; /* The database connection */ Token *pObjName; /* Name of the table or index to be reindexed */ + int bMatch = 0; /* At least one name match */ + const char *zColl = 0; /* Rebuild indexes using this collation */ + Table *pReTab = 0; /* Rebuild all indexes of this table */ + Index *pReIndex = 0; /* Rebuild this index */ + int isExprIdx = 0; /* Rebuild all expression indexes */ + int bAll = 0; /* Rebuild all indexes */ /* Read the database schema. If an error occurs, leave an error message ** and code in pParse and return NULL. */ @@ -129984,41 +132124,66 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ } if( pName1==0 ){ - reindexDatabases(pParse, 0); - return; + /* rebuild all indexes */ + bMatch = 1; + bAll = 1; }else if( NEVER(pName2==0) || pName2->z==0 ){ - char *zColl; assert( pName1->z ); - zColl = sqlite3NameFromToken(pParse->db, pName1); - if( !zColl ) return; - pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); - if( pColl ){ - reindexDatabases(pParse, zColl); - sqlite3DbFree(db, zColl); - return; + z = sqlite3NameFromToken(pParse->db, pName1); + if( z==0 ) return; + }else{ + iReDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); + if( iReDb<0 ) return; + z = sqlite3NameFromToken(db, pObjName); + if( z==0 ) return; + zDb = db->aDb[iReDb].zDbSName; + } + if( !bAll ){ + if( zDb==0 && sqlite3StrICmp(z, "expressions")==0 ){ + isExprIdx = 1; + bMatch = 1; + } + if( zDb==0 && sqlite3FindCollSeq(db, ENC(db), z, 0)!=0 ){ + zColl = z; + bMatch = 1; + } + if( zColl==0 && (pReTab = sqlite3FindTable(db, z, zDb))!=0 ){ + bMatch = 1; + } + if( zColl==0 && (pReIndex = sqlite3FindIndex(db, z, zDb))!=0 ){ + bMatch = 1; } - sqlite3DbFree(db, zColl); } - iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName); - if( iDb<0 ) return; - z = sqlite3NameFromToken(db, pObjName); - if( z==0 ) return; - zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; - pTab = sqlite3FindTable(db, z, zDb); - if( pTab ){ - reindexTable(pParse, pTab, 0); - sqlite3DbFree(db, z); - return; + if( bMatch ){ + int iDb; + HashElem *k; + Table *pTab; + Index *pIdx; + Db *pDb; + for(iDb=0, pDb=db->aDb; iDbnDb; iDb++, pDb++){ + assert( pDb!=0 ); + if( iReDb>=0 && iReDb!=iDb ) continue; + for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ + pTab = (Table*)sqliteHashData(k); + if( IsVirtual(pTab) ) continue; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( bAll + || pTab==pReTab + || pIdx==pReIndex + || (isExprIdx && pIdx->bHasExpr) + || (zColl!=0 && collationMatch(zColl,pIdx)) + ){ + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIdx, -1); + } + } /* End loop over indexes of pTab */ + } /* End loop over tables of iDb */ + } /* End loop over databases */ + }else{ + sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); } - pIndex = sqlite3FindIndex(db, z, zDb); sqlite3DbFree(db, z); - if( pIndex ){ - iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); - return; - } - sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); + return; } #endif @@ -130828,7 +132993,7 @@ static int vtabIsReadOnly(Parse *pParse, Table *pTab){ ** * Only allow DELETE, INSERT, or UPDATE of non-SQLITE_VTAB_INNOCUOUS ** virtual tables if PRAGMA trusted_schema=ON. */ - if( pParse->pToplevel!=0 + if( (pParse->pToplevel!=0 || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) && pTab->u.vtab.p->eVtabRisk > ((pParse->db->flags & SQLITE_TrustedSchema)!=0) ){ @@ -131665,8 +133830,9 @@ SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete( r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1, &iPartIdxLabel, pPrior, r1); sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, - pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn); - sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */ + pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn + ); + sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX); sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel); pPrior = pIdx; } @@ -132101,9 +134267,18 @@ static void printfFunc( sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); str.printfFlags = SQLITE_PRINTF_SQLFUNC; sqlite3_str_appendf(&str, zFormat, &x); - n = str.nChar; - sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, - SQLITE_DYNAMIC); + if( str.accError==SQLITE_OK ){ + n = str.nChar; + sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n, + SQLITE_DYNAMIC); + }else{ + if( str.accError==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(context); + }else{ + sqlite3_result_error_toobig(context); + } + sqlite3_str_reset(&str); + } } } @@ -132241,7 +134416,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ sqlite3_result_error_nomem(context); return; } - sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8); + sqlite3AtoF(zBuf, &r); sqlite3_free(zBuf); } sqlite3_result_double(context, r); @@ -132873,18 +135048,11 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int switch( sqlite3_value_type(pValue) ){ case SQLITE_FLOAT: { - double r1, r2; - const char *zVal; - r1 = sqlite3_value_double(pValue); - sqlite3_str_appendf(pStr, "%!0.15g", r1); - zVal = sqlite3_str_value(pStr); - if( zVal ){ - sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8); - if( r1!=r2 ){ - sqlite3_str_reset(pStr); - sqlite3_str_appendf(pStr, "%!0.20e", r1); - } - } + /* ,--- Show infinity as 9.0e+999 + ** | + ** | ,--- 17 precision guarantees round-trip + ** v v */ + sqlite3_str_appendf(pStr, "%!0.17g", sqlite3_value_double(pValue)); break; } case SQLITE_INTEGER: { @@ -132976,7 +135144,7 @@ static void unistrFunc( } i = j = 0; while( icnt>0 ); p->cnt--; if( !p->approx ){ - if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){ - p->ovrfl = 1; - p->approx = 1; + i64 x = p->iSum; + if( sqlite3SubInt64(&x, sqlite3_value_int64(argv[0]))==0 ){ + p->iSum = x; + return; } - }else if( type==SQLITE_INTEGER ){ + p->ovrfl = 1; + p->approx = 1; + kahanBabuskaNeumaierInit(p, p->iSum); + } + if( type==SQLITE_INTEGER ){ i64 iVal = sqlite3_value_int64(argv[0]); if( iVal!=SMALLEST_INT64 ){ kahanBabuskaNeumaierStepInt64(p, -iVal); @@ -134139,6 +136312,8 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) sqlite3CreateFunc(db, "like", nArg, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0, 0, 0); pDef = sqlite3FindFunction(db, "like", nArg, SQLITE_UTF8, 0); + assert( pDef!=0 ); /* The sqlite3CreateFunc() call above cannot fail + ** because the "like" SQL-function already exists */ pDef->funcFlags |= flags; pDef->funcFlags &= ~SQLITE_FUNC_UNSAFE; } @@ -134723,47 +136898,46 @@ static void percentSort(double *a, unsigned int n){ 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]iLt ) SWAP_DOUBLE(a[i],a[iLt]) - iLt++; - i++; - }else if( a[i]>rPivot ){ - do{ - iGt--; - }while( iGt>i && a[iGt]>rPivot ); + while( 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]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(int)(n/2) ){ + if( n-iGt>=2 ) percentSort(a+iGt, n-iGt); + n = iLt; }else{ - i++; + if( iLt>=2 ) percentSort(a, iLt); + a += iGt; + n -= iGt; } - }while( i=2 ) percentSort(a, iLt); - if( n-iGt>=2 ) percentSort(a+iGt, n-iGt); - -/* Uncomment for testing */ -#if 0 - for(i=0; istep_list; + sqlite3SrcListDelete(dbMem, pStep->pSrc); sqlite3ExprDelete(dbMem, pStep->pWhere); sqlite3ExprListDelete(dbMem, pStep->pExprList); sqlite3SelectDelete(dbMem, pStep->pSelect); @@ -136146,6 +138321,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( if( !IsOrdinaryTable(pTab) ) return; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + assert( iDb>=00 && iDbnDb ); zDb = db->aDb[iDb].zDbSName; /* Loop through all the foreign key constraints for which pTab is the @@ -136563,7 +138739,6 @@ static Trigger *fkActionTrigger( nFrom = sqlite3Strlen30(zFrom); if( action==OE_Restrict ){ - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); SrcList *pSrc; Expr *pRaise; @@ -136574,10 +138749,10 @@ static Trigger *fkActionTrigger( } 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); + SrcItem *pItem = &pSrc->a[0]; + pItem->zName = sqlite3DbStrDup(db, zFrom); + pItem->fg.fixedSchema = 1; + pItem->u4.pSchema = pTab->pSchema; } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), @@ -136593,14 +138768,17 @@ static Trigger *fkActionTrigger( pTrigger = (Trigger *)sqlite3DbMallocZero(db, sizeof(Trigger) + /* struct Trigger */ - sizeof(TriggerStep) + /* Single step in trigger program */ - nFrom + 1 /* Space for pStep->zTarget */ + sizeof(TriggerStep) /* Single step in trigger program */ ); if( pTrigger ){ pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; - pStep->zTarget = (char *)&pStep[1]; - memcpy((char *)pStep->zTarget, zFrom, nFrom); - + pStep->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + if( pStep->pSrc ){ + SrcItem *pItem = &pStep->pSrc->a[0]; + pItem->zName = sqlite3DbStrNDup(db, zFrom, nFrom); + pItem->u4.pSchema = pTab->pSchema; + pItem->fg.fixedSchema = 1; + } pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); @@ -140657,7 +142835,11 @@ struct sqlite3_api_routines { /* Version 3.51.0 and later */ int (*set_errmsg)(sqlite3*,int,const char*); int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); - + /* Version 3.52.0 and later */ + void (*str_truncate)(sqlite3_str*,int); + void (*str_free)(sqlite3_str*); + int (*carray_bind)(sqlite3_stmt*,int,void*,int,int,void(*)(void*)); + int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*); }; /* @@ -140996,6 +143178,11 @@ typedef int (*sqlite3_loadext_entry)( /* Version 3.51.0 and later */ #define sqlite3_set_errmsg sqlite3_api->set_errmsg #define sqlite3_db_status64 sqlite3_api->db_status64 +/* Version 3.52.0 and later */ +#define sqlite3_str_truncate sqlite3_api->str_truncate +#define sqlite3_str_free sqlite3_api->str_free +#define sqlite3_carray_bind sqlite3_api->carray_bind +#define sqlite3_carray_bind_v2 sqlite3_api->carray_bind_v2 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -141522,7 +143709,17 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_setlk_timeout, /* Version 3.51.0 and later */ sqlite3_set_errmsg, - sqlite3_db_status64 + sqlite3_db_status64, + /* Version 3.52.0 and later */ + sqlite3_str_truncate, + sqlite3_str_free, +#ifdef SQLITE_ENABLE_CARRAY + sqlite3_carray_bind, + sqlite3_carray_bind_v2 +#else + 0, + 0 +#endif }; /* True if x is the directory separator character @@ -141624,33 +143821,42 @@ static int sqlite3LoadExtension( ** entry point name "sqlite3_extension_init" was not found, then ** construct an entry point name "sqlite3_X_init" where the X is ** replaced by the lowercase value of every ASCII alphabetic - ** character in the filename after the last "/" upto the first ".", - ** and eliding the first three characters if they are "lib". + ** character in the filename after the last "/" up to the first ".", + ** and skipping the first three characters if they are "lib". ** Examples: ** ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init + ** + ** If that still finds no entry point, repeat a second time but this + ** time include both alphabetic and numeric characters up to the first + ** ".". Example: + ** + ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example5_init */ if( xInit==0 && zProc==0 ){ int iFile, iEntry, c; int ncFile = sqlite3Strlen30(zFile); + int cnt = 0; zAltEntry = sqlite3_malloc64(ncFile+30); if( zAltEntry==0 ){ sqlite3OsDlClose(pVfs, handle); return SQLITE_NOMEM_BKPT; } - memcpy(zAltEntry, "sqlite3_", 8); - for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} - iFile++; - if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; - for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ - if( sqlite3Isalpha(c) ){ - zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; + do{ + memcpy(zAltEntry, "sqlite3_", 8); + for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){} + iFile++; + if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; + for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ + if( sqlite3Isalpha(c) || (cnt && sqlite3Isdigit(c)) ){ + zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; + } } - } - memcpy(zAltEntry+iEntry, "_init", 6); - zEntry = zAltEntry; - xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); + memcpy(zAltEntry+iEntry, "_init", 6); + zEntry = zAltEntry; + xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry); + }while( xInit==0 && (++cnt)<2 ); } if( xInit==0 ){ if( pzErrMsg ){ @@ -144660,8 +146866,20 @@ SQLITE_PRIVATE void sqlite3Pragma( pPrior = pIdx; sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);/* increment entry count */ /* Verify that an index entry exists for the current table row */ - jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1, + sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1, pIdx->nColumn); VdbeCoverage(v); + jmp2 = sqlite3VdbeAddOp3(v, OP_IFindKey, iIdxCur+j, ckUniq, r1); + VdbeCoverage(v); + sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX); + sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, + sqlite3MPrintf(db, "index %s stores an imprecise floating-point " + "value for row ", pIdx->zName), + P4_DYNAMIC); + sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); + integrityCheckResultRow(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, ckUniq); + + sqlite3VdbeJumpHere(v, jmp2); sqlite3VdbeLoadString(v, 3, "row "); sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); sqlite3VdbeLoadString(v, 4, " missing from index "); @@ -144669,7 +146887,7 @@ SQLITE_PRIVATE void sqlite3Pragma( jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName); sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); jmp4 = integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, jmp2); + sqlite3VdbeResolveLabel(v, ckUniq); /* The OP_IdxRowid opcode is an optimized version of OP_Column ** that extracts the rowid off the end of the index record. @@ -145722,7 +147940,8 @@ static void corruptSchema( static const char *azAlterType[] = { "rename", "drop column", - "add column" + "add column", + "drop constraint" }; *pData->pzErrMsg = sqlite3MPrintf(db, "error in %s %s after %s: %s", azObj[0], azObj[1], @@ -146805,7 +149024,7 @@ SQLITE_API int sqlite3_prepare16_v3( */ typedef struct DistinctCtx DistinctCtx; struct DistinctCtx { - u8 isTnct; /* 0: Not distinct. 1: DISTICT 2: DISTINCT and ORDER BY */ + u8 isTnct; /* 0: Not distinct. 1: DISTINCT 2: DISTINCT and ORDER BY */ u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */ int tabTnct; /* Ephemeral table used for DISTINCT processing */ int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */ @@ -146935,8 +149154,6 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( pNew->iLimit = 0; pNew->iOffset = 0; pNew->selId = ++pParse->nSelect; - pNew->addrOpenEphm[0] = -1; - pNew->addrOpenEphm[1] = -1; pNew->nSelectRow = 0; if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); pNew->pSrc = pSrc; @@ -147445,6 +149662,10 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){ pRight->fg.isOn = 1; p->selFlags |= SF_OnToWhere; } + + if( IsVirtual(pRightTab) && joinType==EP_OuterON && pRight->u1.pFuncArg ){ + p->selFlags |= SF_OnToWhere; + } } return 0; } @@ -148084,29 +150305,6 @@ static void selectInnerLoop( } switch( eDest ){ - /* In this mode, write each query result to the key of the temporary - ** table iParm. - */ -#ifndef SQLITE_OMIT_COMPOUND_SELECT - case SRT_Union: { - int r1; - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, regResult, nResultCol); - sqlite3ReleaseTempReg(pParse, r1); - break; - } - - /* Construct a record from the query result, but instead of - ** saving that record, use it as a key to delete elements from - ** the temporary table iParm. - */ - case SRT_Except: { - sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol); - break; - } -#endif /* SQLITE_OMIT_COMPOUND_SELECT */ - /* Store the result as data using a unique key. */ case SRT_Fifo: @@ -149219,8 +151417,8 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( } } if( zType ){ - const i64 k = sqlite3Strlen30(zType); - n = sqlite3Strlen30(pCol->zCnName); + const i64 k = strlen(zType); + n = strlen(pCol->zCnName); pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); if( pCol->zCnName ){ @@ -149246,6 +151444,13 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c sqlite3 *db = pParse->db; u64 savedFlags; + pParse->nNestSel++; +#if SQLITE_MAX_EXPR_DEPTH>0 + if( pParse->nNestSel >= db->aLimit[SQLITE_LIMIT_EXPR_DEPTH] ){ + sqlite3ErrorMsg(pParse, "VIEWs and/or subqueries nested too deep"); + return 0; + } +#endif savedFlags = db->flags; db->flags &= ~(u64)SQLITE_FullColNames; db->flags |= SQLITE_ShortColNames; @@ -149267,6 +151472,8 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c sqlite3DeleteTable(db, pTab); return 0; } + pParse->nNestSel--; + assert( pParse->nNestSel>=0 ); return pTab; } @@ -149393,9 +151600,9 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){ ** function is responsible for ensuring that this structure is eventually ** freed. */ -static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){ +static KeyInfo *multiSelectByMergeKeyInfo(Parse *pParse, Select *p, int nExtra){ ExprList *pOrderBy = p->pOrderBy; - int nOrderBy = ALWAYS(pOrderBy!=0) ? pOrderBy->nExpr : 0; + int nOrderBy = (pOrderBy!=0) ? pOrderBy->nExpr : 0; sqlite3 *db = pParse->db; KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1); if( pRet ){ @@ -149528,7 +151735,7 @@ static void generateWithRecursiveQuery( regCurrent = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol); if( pOrderBy ){ - KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1); + KeyInfo *pKeyInfo = multiSelectByMergeKeyInfo(pParse, p, 1); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0, (char*)pKeyInfo, P4_KEYINFO); destQueue.pOrderBy = pOrderBy; @@ -149537,8 +151744,28 @@ static void generateWithRecursiveQuery( } VdbeComment((v, "Queue table")); if( iDistinct ){ - p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0); - p->selFlags |= SF_UsesEphemeral; + /* Generate an ephemeral table used to enforce distinctness on the + ** output of the recursive part of the CTE. + */ + KeyInfo *pKeyInfo; /* Collating sequence for the result set */ + CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ + + assert( p->pNext==0 ); + assert( p->pEList!=0 ); + nCol = p->pEList->nExpr; + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nCol, 1); + if( pKeyInfo ){ + for(i=0, apColl=pKeyInfo->aColl; idb->pDfltColl; + } + } + sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iDistinct, nCol, 0, + (void*)pKeyInfo, P4_KEYINFO); + }else{ + assert( pParse->nErr>0 ); + } } /* Detach the ORDER BY clause from the compound SELECT */ @@ -149613,7 +151840,7 @@ static void generateWithRecursiveQuery( #endif /* SQLITE_OMIT_CTE */ /* Forward references */ -static int multiSelectOrderBy( +static int multiSelectByMerge( Parse *pParse, /* Parsing context */ Select *p, /* The right-most of SELECTs to be coded */ SelectDest *pDest /* What to do with query results */ @@ -149762,12 +151989,26 @@ static int multiSelect( generateWithRecursiveQuery(pParse, p, &dest); }else #endif - - /* Compound SELECTs that have an ORDER BY clause are handled separately. - */ if( p->pOrderBy ){ - return multiSelectOrderBy(pParse, p, pDest); - }else{ + /* If the compound has an ORDER BY clause, then always use the merge + ** algorithm. */ + return multiSelectByMerge(pParse, p, pDest); + }else if( p->op!=TK_ALL ){ + /* If the compound is EXCEPT, INTERSECT, or UNION (anything other than + ** UNION ALL) then also always use the merge algorithm. However, the + ** multiSelectByMerge() routine requires that the compound have an + ** ORDER BY clause, and it doesn't right now. So invent one first. */ + Expr *pOne = sqlite3ExprInt32(db, 1); + p->pOrderBy = sqlite3ExprListAppend(pParse, 0, pOne); + if( pParse->nErr ) goto multi_select_end; + assert( p->pOrderBy!=0 ); + p->pOrderBy->a[0].u.x.iOrderByCol = 1; + return multiSelectByMerge(pParse, p, pDest); + }else{ + /* For a UNION ALL compound without ORDER BY, simply run the left + ** query, then run the right query */ + int addr = 0; + int nLimit = 0; /* Initialize to suppress harmless compiler warning */ #ifndef SQLITE_OMIT_EXPLAIN if( pPrior->pPrior==0 ){ @@ -149775,300 +152016,49 @@ static int multiSelect( ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY")); } #endif - - /* Generate code for the left and right SELECT statements. - */ - switch( p->op ){ - case TK_ALL: { - int addr = 0; - int nLimit = 0; /* Initialize to suppress harmless compiler warning */ - assert( !pPrior->pLimit ); - pPrior->iLimit = p->iLimit; - pPrior->iOffset = p->iOffset; - pPrior->pLimit = p->pLimit; - TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); - rc = sqlite3Select(pParse, pPrior, &dest); - pPrior->pLimit = 0; - if( rc ){ - goto multi_select_end; - } - p->pPrior = 0; - p->iLimit = pPrior->iLimit; - p->iOffset = pPrior->iOffset; - if( p->iLimit ){ - addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); - VdbeComment((v, "Jump ahead if LIMIT reached")); - if( p->iOffset ){ - sqlite3VdbeAddOp3(v, OP_OffsetLimit, - p->iLimit, p->iOffset+1, p->iOffset); - } - } - ExplainQueryPlan((pParse, 1, "UNION ALL")); - TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); - rc = sqlite3Select(pParse, p, &dest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; - p->pPrior = pPrior; - p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); - if( p->pLimit - && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) - && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) - ){ - p->nSelectRow = sqlite3LogEst((u64)nLimit); - } - if( addr ){ - sqlite3VdbeJumpHere(v, addr); - } - break; - } - case TK_EXCEPT: - case TK_UNION: { - int unionTab; /* Cursor number of the temp table holding result */ - u8 op = 0; /* One of the SRT_ operations to apply to self */ - int priorOp; /* The SRT_ operation to apply to prior selects */ - Expr *pLimit; /* Saved values of p->nLimit */ - int addr; - int emptyBypass = 0; /* IfEmpty opcode to bypass RHS */ - SelectDest uniondest; - - - testcase( p->op==TK_EXCEPT ); - testcase( p->op==TK_UNION ); - priorOp = SRT_Union; - if( dest.eDest==priorOp ){ - /* We can reuse a temporary table generated by a SELECT to our - ** right. - */ - assert( p->pLimit==0 ); /* Not allowed on leftward elements */ - unionTab = dest.iSDParm; - }else{ - /* We will need to create our own temporary table to hold the - ** intermediate results. - */ - unionTab = pParse->nTab++; - assert( p->pOrderBy==0 ); - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0); - assert( p->addrOpenEphm[0] == -1 ); - p->addrOpenEphm[0] = addr; - findRightmost(p)->selFlags |= SF_UsesEphemeral; - assert( p->pEList ); - } - - - /* Code the SELECT statements to our left - */ - assert( !pPrior->pOrderBy ); - sqlite3SelectDestInit(&uniondest, priorOp, unionTab); - TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION left...\n")); - rc = sqlite3Select(pParse, pPrior, &uniondest); - if( rc ){ - goto multi_select_end; - } - - /* Code the current SELECT statement - */ - if( p->op==TK_EXCEPT ){ - op = SRT_Except; - emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, unionTab); - VdbeCoverage(v); - }else{ - assert( p->op==TK_UNION ); - op = SRT_Union; - } - p->pPrior = 0; - pLimit = p->pLimit; - p->pLimit = 0; - uniondest.eDest = op; - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - sqlite3SelectOpName(p->op))); - TREETRACE(0x200, pParse, p, ("multiSelect EXCEPT/UNION right...\n")); - rc = sqlite3Select(pParse, p, &uniondest); - testcase( rc!=SQLITE_OK ); - assert( p->pOrderBy==0 ); - pDelete = p->pPrior; - p->pPrior = pPrior; - p->pOrderBy = 0; - if( p->op==TK_UNION ){ - p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); - } - if( emptyBypass ) sqlite3VdbeJumpHere(v, emptyBypass); - sqlite3ExprDelete(db, p->pLimit); - p->pLimit = pLimit; - p->iLimit = 0; - p->iOffset = 0; - - /* Convert the data in the temporary table into whatever form - ** it is that we currently need. - */ - assert( unionTab==dest.iSDParm || dest.eDest!=priorOp ); - assert( p->pEList || db->mallocFailed ); - if( dest.eDest!=priorOp && db->mallocFailed==0 ){ - int iCont, iBreak, iStart; - iBreak = sqlite3VdbeMakeLabel(pParse); - iCont = sqlite3VdbeMakeLabel(pParse); - computeLimitRegisters(pParse, p, iBreak); - sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); - iStart = sqlite3VdbeCurrentAddr(v); - selectInnerLoop(pParse, p, unionTab, - 0, 0, &dest, iCont, iBreak); - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); - } - break; - } - default: assert( p->op==TK_INTERSECT ); { - int tab1, tab2; - int iCont, iBreak, iStart; - Expr *pLimit; - int addr, iLimit, iOffset; - SelectDest intersectdest; - int r1; - int emptyBypass; - - /* INTERSECT is different from the others since it requires - ** two temporary tables. Hence it has its own case. Begin - ** by allocating the tables we will need. - */ - tab1 = pParse->nTab++; - tab2 = pParse->nTab++; - assert( p->pOrderBy==0 ); - - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0); - assert( p->addrOpenEphm[0] == -1 ); - p->addrOpenEphm[0] = addr; - findRightmost(p)->selFlags |= SF_UsesEphemeral; - assert( p->pEList ); - - /* Code the SELECTs to our left into temporary table "tab1". - */ - sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); - TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT left...\n")); - rc = sqlite3Select(pParse, pPrior, &intersectdest); - if( rc ){ - goto multi_select_end; - } - - /* Initialize LIMIT counters before checking to see if the LHS - ** is empty, in case the jump is taken */ - iBreak = sqlite3VdbeMakeLabel(pParse); - computeLimitRegisters(pParse, p, iBreak); - emptyBypass = sqlite3VdbeAddOp1(v, OP_IfEmpty, tab1); VdbeCoverage(v); - - /* Code the current SELECT into temporary table "tab2" - */ - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); - assert( p->addrOpenEphm[1] == -1 ); - p->addrOpenEphm[1] = addr; - - /* Disable prior SELECTs and the LIMIT counters during the computation - ** of the RHS select */ - pLimit = p->pLimit; - iLimit = p->iLimit; - iOffset = p->iOffset; - p->pPrior = 0; - p->pLimit = 0; - p->iLimit = 0; - p->iOffset = 0; - - intersectdest.iSDParm = tab2; - ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE", - sqlite3SelectOpName(p->op))); - TREETRACE(0x400, pParse, p, ("multiSelect INTERSECT right...\n")); - rc = sqlite3Select(pParse, p, &intersectdest); - testcase( rc!=SQLITE_OK ); - pDelete = p->pPrior; - p->pPrior = pPrior; - if( p->nSelectRow>pPrior->nSelectRow ){ - p->nSelectRow = pPrior->nSelectRow; - } - sqlite3ExprDelete(db, p->pLimit); - - /* Reinstate the LIMIT counters prior to running the final intersect */ - p->pLimit = pLimit; - p->iLimit = iLimit; - p->iOffset = iOffset; - - /* Generate code to take the intersection of the two temporary - ** tables. - */ - if( rc ) break; - assert( p->pEList ); - sqlite3VdbeAddOp1(v, OP_Rewind, tab1); - r1 = sqlite3GetTempReg(pParse); - iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1); - iCont = sqlite3VdbeMakeLabel(pParse); - sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); - VdbeCoverage(v); - sqlite3ReleaseTempReg(pParse, r1); - selectInnerLoop(pParse, p, tab1, - 0, 0, &dest, iCont, iBreak); - sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); - sqlite3VdbeResolveLabel(v, iBreak); - sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); - sqlite3VdbeJumpHere(v, emptyBypass); - sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); - break; - } - } - - #ifndef SQLITE_OMIT_EXPLAIN - if( p->pNext==0 ){ - ExplainQueryPlanPop(pParse); - } - #endif - } - if( pParse->nErr ) goto multi_select_end; - - /* Compute collating sequences used by - ** temporary tables needed to implement the compound select. - ** Attach the KeyInfo structure to all temporary tables. - ** - ** This section is run by the right-most SELECT statement only. - ** SELECT statements to the left always skip this part. The right-most - ** SELECT might also skip this part if it has no ORDER BY clause and - ** no temp tables are required. - */ - if( p->selFlags & SF_UsesEphemeral ){ - int i; /* Loop counter */ - KeyInfo *pKeyInfo; /* Collating sequence for the result set */ - Select *pLoop; /* For looping through SELECT statements */ - CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ - int nCol; /* Number of columns in result set */ - - assert( p->pNext==0 ); - assert( p->pEList!=0 ); - nCol = p->pEList->nExpr; - pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); - if( !pKeyInfo ){ - rc = SQLITE_NOMEM_BKPT; + assert( !pPrior->pLimit ); + pPrior->iLimit = p->iLimit; + pPrior->iOffset = p->iOffset; + pPrior->pLimit = sqlite3ExprDup(db, p->pLimit, 0); + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL left...\n")); + rc = sqlite3Select(pParse, pPrior, &dest); + sqlite3ExprDelete(db, pPrior->pLimit); + pPrior->pLimit = 0; + if( rc ){ goto multi_select_end; } - for(i=0, apColl=pKeyInfo->aColl; ipDfltColl; - } + p->pPrior = 0; + p->iLimit = pPrior->iLimit; + p->iOffset = pPrior->iOffset; + if( p->iLimit ){ + addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v); + VdbeComment((v, "Jump ahead if LIMIT reached")); + if( p->iOffset ){ + sqlite3VdbeAddOp3(v, OP_OffsetLimit, + p->iLimit, p->iOffset+1, p->iOffset); + } + } + ExplainQueryPlan((pParse, 1, "UNION ALL")); + TREETRACE(0x200, pParse, p, ("multiSelect UNION ALL right...\n")); + rc = sqlite3Select(pParse, p, &dest); + testcase( rc!=SQLITE_OK ); + pDelete = p->pPrior; + p->pPrior = pPrior; + p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); + if( p->pLimit + && sqlite3ExprIsInteger(p->pLimit->pLeft, &nLimit, pParse) + && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit) + ){ + p->nSelectRow = sqlite3LogEst((u64)nLimit); } - - for(pLoop=p; pLoop; pLoop=pLoop->pPrior){ - for(i=0; i<2; i++){ - int addr = pLoop->addrOpenEphm[i]; - if( addr<0 ){ - /* If [0] is unused then [1] is also unused. So we can - ** always safely abort as soon as the first unused slot is found */ - assert( pLoop->addrOpenEphm[1]<0 ); - break; - } - sqlite3VdbeChangeP2(v, addr, nCol); - sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo), - P4_KEYINFO); - pLoop->addrOpenEphm[i] = -1; - } + if( addr ){ + sqlite3VdbeJumpHere(v, addr); + } +#ifndef SQLITE_OMIT_EXPLAIN + if( p->pNext==0 ){ + ExplainQueryPlanPop(pParse); } - sqlite3KeyInfoUnref(pKeyInfo); +#endif } multi_select_end: @@ -150100,8 +152090,8 @@ SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){ ** Code an output subroutine for a coroutine implementation of a ** SELECT statement. ** -** The data to be output is contained in pIn->iSdst. There are -** pIn->nSdst columns to be output. pDest is where the output should +** The data to be output is contained in an array of pIn->nSdst registers +** starting at register pIn->iSdst. pDest is where the output should ** be sent. ** ** regReturn is the number of the register holding the subroutine @@ -150130,6 +152120,8 @@ static int generateOutputSubroutine( int iContinue; int addr; + assert( pIn->eDest==SRT_Coroutine ); + addr = sqlite3VdbeCurrentAddr(v); iContinue = sqlite3VdbeMakeLabel(pParse); @@ -150151,23 +152143,60 @@ static int generateOutputSubroutine( */ codeOffset(v, p->iOffset, iContinue); - assert( pDest->eDest!=SRT_Exists ); - assert( pDest->eDest!=SRT_Table ); switch( pDest->eDest ){ /* Store the result as data using a unique key. */ + case SRT_Fifo: + case SRT_DistFifo: + case SRT_Table: case SRT_EphemTab: { int r1 = sqlite3GetTempReg(pParse); int r2 = sqlite3GetTempReg(pParse); + int iParm = pDest->iSDParm; + testcase( pDest->eDest==SRT_Table ); + testcase( pDest->eDest==SRT_EphemTab ); + testcase( pDest->eDest==SRT_Fifo ); + testcase( pDest->eDest==SRT_DistFifo ); sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1); - sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2); - sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2); +#if !defined(SQLITE_ENABLE_NULL_TRIM) && defined(SQLITE_DEBUG) + /* A destination of SRT_Table and a non-zero iSDParm2 parameter means + ** that this is an "UPDATE ... FROM" on a virtual table or view. In this + ** case set the p5 parameter of the OP_MakeRecord to OPFLAG_NOCHNG_MAGIC. + ** This does not affect operation in any way - it just allows MakeRecord + ** to process OPFLAG_NOCHANGE values without an assert() failing. */ + if( pDest->eDest==SRT_Table && pDest->iSDParm2 ){ + sqlite3VdbeChangeP5(v, OPFLAG_NOCHNG_MAGIC); + } +#endif +#ifndef SQLITE_OMIT_CTE + if( pDest->eDest==SRT_DistFifo ){ + /* If the destination is DistFifo, then cursor (iParm+1) is open + ** on an ephemeral index that is used to enforce uniqueness on the + ** total result. At this point, we are processing the setup portion + ** of the recursive CTE using the merge algorithm, so the results are + ** guaranteed to be unique anyhow. But we still need to populate the + ** (iParm+1) cursor for use by the subsequent recursive phase. + */ + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm+1, r1, + pIn->iSdst, pIn->nSdst); + } +#endif + sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); + sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3ReleaseTempReg(pParse, r2); sqlite3ReleaseTempReg(pParse, r1); break; } + /* If any row exist in the result set, record that fact and abort. + */ + case SRT_Exists: { + sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iSDParm); + /* The LIMIT clause will terminate the loop for us */ + break; + } + #ifndef SQLITE_OMIT_SUBQUERY /* If we are creating a set for an "expr IN (SELECT ...)". */ @@ -150214,9 +152243,51 @@ static int generateOutputSubroutine( break; } +#ifndef SQLITE_OMIT_CTE + /* Write the results into a priority queue that is order according to + ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an + ** index with pSO->nExpr+2 columns. Build a key using pSO for the first + ** pSO->nExpr columns, then make sure all keys are unique by adding a + ** final OP_Sequence column. The last column is the record as a blob. + */ + case SRT_DistQueue: + case SRT_Queue: { + int nKey; + int r1, r2, r3, ii; + ExprList *pSO; + int iParm = pDest->iSDParm; + pSO = pDest->pOrderBy; + assert( pSO ); + nKey = pSO->nExpr; + r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3GetTempRange(pParse, nKey+2); + r3 = r2+nKey+1; + + sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r3); + if( pDest->eDest==SRT_DistQueue ){ + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); + } + for(ii=0; iiiSdst + pSO->a[ii].u.x.iOrderByCol - 1, + r2+ii); + } + sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey); + sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iParm, r1, r2, nKey+2); + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempRange(pParse, r2, nKey+2); + break; + } +#endif /* SQLITE_OMIT_CTE */ + + /* Ignore the output */ + case SRT_Discard: { + break; + } + /* If none of the above, then the result destination must be - ** SRT_Output. This routine is never called with any other - ** destination other than the ones handled above or SRT_Output. + ** SRT_Output. ** ** For SRT_Output, results are stored in a sequence of registers. ** Then the OP_ResultRow opcode is used to cause sqlite3_step() to @@ -150244,8 +152315,9 @@ static int generateOutputSubroutine( } /* -** Alternative compound select code generator for cases when there -** is an ORDER BY clause. +** Generate code for a compound SELECT statement using a merge +** algorithm. The compound must have an ORDER BY clause for this +** to work. ** ** We assume a query of the following form: ** @@ -150262,7 +152334,7 @@ static int generateOutputSubroutine( ** ** outB: Move the output of the selectB coroutine into the output ** of the compound query. (Only generated for UNION and -** UNION ALL. EXCEPT and INSERTSECT never output a row that +** UNION ALL. EXCEPT and INTERSECT never output a row that ** appears only in B.) ** ** AltB: Called when there is data from both coroutines and Au.x.iOrderByCol==i ) break; } if( j==nOrderBy ){ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pNew = sqlite3ExprInt32(db, i); if( pNew==0 ) return SQLITE_NOMEM_BKPT; - pNew->flags |= EP_IntValue; - pNew->u.iValue = i; p->pOrderBy = pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i; } @@ -150412,26 +152480,29 @@ static int multiSelectOrderBy( } /* Compute the comparison permutation and keyinfo that is used with - ** the permutation used to determine if the next - ** row of results comes from selectA or selectB. Also add explicit - ** collations to the ORDER BY clause terms so that when the subqueries - ** to the right and the left are evaluated, they use the correct - ** collation. + ** the permutation to determine if the next row of results comes + ** from selectA or selectB. Also add literal collations to the + ** ORDER BY clause terms so that when selectA and selectB are + ** evaluated, they use the correct collation. */ aPermute = sqlite3DbMallocRawNN(db, sizeof(u32)*(nOrderBy + 1)); if( aPermute ){ struct ExprList_item *pItem; + int bKeep = 0; aPermute[0] = nOrderBy; for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){ assert( pItem!=0 ); assert( pItem->u.x.iOrderByCol>0 ); assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr ); aPermute[i] = pItem->u.x.iOrderByCol - 1; + if( aPermute[i]!=(u32)i-1 ) bKeep = 1; + } + if( bKeep==0 ){ + sqlite3DbFreeNN(db, aPermute); + aPermute = 0; } - pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1); - }else{ - pKeyMerge = 0; } + pKeyMerge = multiSelectByMergeKeyInfo(pParse, p, 1); /* Allocate a range of temporary registers and the KeyInfo needed ** for the logic that removes duplicate result rows when the @@ -150510,7 +152581,7 @@ static int multiSelectOrderBy( */ addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); - VdbeComment((v, "left SELECT")); + VdbeComment((v, "SUBR: next-A")); pPrior->iLimit = regLimitA; ExplainQueryPlan((pParse, 1, "LEFT")); sqlite3Select(pParse, pPrior, &destA); @@ -150522,7 +152593,7 @@ static int multiSelectOrderBy( */ addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); - VdbeComment((v, "right SELECT")); + VdbeComment((v, "SUBR: next-B")); savedLimit = p->iLimit; savedOffset = p->iOffset; p->iLimit = regLimitB; @@ -150536,7 +152607,7 @@ static int multiSelectOrderBy( /* Generate a subroutine that outputs the current row of the A ** select as the next output row of the compound select. */ - VdbeNoopComment((v, "Output routine for A")); + VdbeNoopComment((v, "SUBR: out-A")); addrOutA = generateOutputSubroutine(pParse, p, &destA, pDest, regOutA, regPrev, pKeyDup, labelEnd); @@ -150545,7 +152616,7 @@ static int multiSelectOrderBy( ** select as the next output row of the compound select. */ if( op==TK_ALL || op==TK_UNION ){ - VdbeNoopComment((v, "Output routine for B")); + VdbeNoopComment((v, "SUBR: out-B")); addrOutB = generateOutputSubroutine(pParse, p, &destB, pDest, regOutB, regPrev, pKeyDup, labelEnd); @@ -150558,10 +152629,12 @@ static int multiSelectOrderBy( if( op==TK_EXCEPT || op==TK_INTERSECT ){ addrEofA_noB = addrEofA = labelEnd; }else{ - VdbeNoopComment((v, "eof-A subroutine")); + VdbeNoopComment((v, "SUBR: eof-A")); addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); + VdbeComment((v, "out-B")); addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); VdbeCoverage(v); + VdbeComment((v, "next-B")); sqlite3VdbeGoto(v, addrEofA); p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow); } @@ -150573,17 +152646,20 @@ static int multiSelectOrderBy( addrEofB = addrEofA; if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; }else{ - VdbeNoopComment((v, "eof-B subroutine")); + VdbeNoopComment((v, "SUBR: eof-B")); addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); + VdbeComment((v, "out-A")); sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v); + VdbeComment((v, "next-A")); sqlite3VdbeGoto(v, addrEofB); } /* Generate code to handle the case of AB */ - VdbeNoopComment((v, "A-gt-B subroutine")); addrAgtB = sqlite3VdbeCurrentAddr(v); if( op==TK_ALL || op==TK_UNION ){ sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); + VdbeComment((v, "out-B")); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); + VdbeComment((v, "next-B")); + sqlite3VdbeGoto(v, labelCmpr); + }else{ + addrAgtB++; /* Just do next-B. Might as well use the next-B call + ** in the next code block */ } - sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); - sqlite3VdbeGoto(v, labelCmpr); /* This code runs once to initialize everything. */ sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); + VdbeComment((v, "next-A")); + /* v--- Also the A>B case for EXCEPT and INTERSECT */ sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); + VdbeComment((v, "next-B")); /* Implement the main merge loop */ + if( aPermute!=0 ){ + sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); + } sqlite3VdbeResolveLabel(v, labelCmpr); - sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY); sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, (char*)pKeyMerge, P4_KEYINFO); - sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); - sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); + if( aPermute!=0 ){ + sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); + } + sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); + VdbeCoverageIf(v, op==TK_ALL); + VdbeCoverageIf(v, op==TK_UNION); + VdbeCoverageIf(v, op==TK_EXCEPT); + VdbeCoverageIf(v, op==TK_INTERSECT); /* Jump to the this point in order to terminate the query. */ @@ -151540,7 +153628,7 @@ static int flattenSubquery( } pSubitem->fg.jointype |= jointype; - /* Now begin substituting subquery result set expressions for + /* Begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** ** Example: @@ -151552,7 +153640,7 @@ static int flattenSubquery( ** We look at every expression in the outer query and every place we see ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10". */ - if( pSub->pOrderBy && (pParent->selFlags & SF_NoopOrderBy)==0 ){ + if( pSub->pOrderBy ){ /* At this point, any non-zero iOrderByCol values indicate that the ** ORDER BY column expression is identical to the iOrderByCol'th ** expression returned by SELECT statement pSub. Since these values @@ -151560,9 +153648,9 @@ static int flattenSubquery( ** zero them before transferring the ORDER BY clause. ** ** Not doing this may cause an error if a subsequent call to this - ** function attempts to flatten a compound sub-query into pParent - ** (the only way this can happen is if the compound sub-query is - ** currently part of pSub->pSrc). See ticket [d11a6e908f]. */ + ** function attempts to flatten a compound sub-query into pParent. + ** See ticket [d11a6e908f]. + */ ExprList *pOrderBy = pSub->pOrderBy; for(i=0; inExpr; i++){ pOrderBy->a[i].u.x.iOrderByCol = 0; @@ -152180,6 +154268,16 @@ static int pushDownWhereTerms( x.pEList = pSubq->pEList; x.pCList = findLeftmostExprlist(pSubq); pNew = substExpr(&x, pNew); + assert( pNew!=0 || pParse->nErr!=0 ); + if( pParse->nErr==0 && pNew->op==TK_IN && ExprUseXSelect(pNew) ){ + assert( pNew->x.pSelect!=0 ); + pNew->x.pSelect->selFlags |= SF_ClonedRhsIn; + assert( pWhere!=0 ); + assert( pWhere->op==TK_IN ); + assert( ExprUseXSelect(pWhere) ); + assert( pWhere->x.pSelect!=0 ); + pWhere->x.pSelect->selFlags |= SF_ClonedRhsIn; + } #ifndef SQLITE_OMIT_WINDOWFUNC if( pSubq->pWin && 0==pushDownWindowCheck(pParse, pSubq, pNew) ){ /* Restriction 6c has prevented push-down in this case */ @@ -152414,14 +154512,14 @@ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){ ** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2) ** ORDER BY ... COLLATE ... ** -** This transformation is necessary because the multiSelectOrderBy() routine +** This transformation is necessary because the multiSelectByMerge() routine ** above that generates the code for a compound SELECT with an ORDER BY clause ** uses a merge algorithm that requires the same collating sequence on the ** result columns as on the ORDER BY clause. See ticket ** http://sqlite.org/src/info/6709574d2a ** ** This transformation is only needed for EXCEPT, INTERSECT, and UNION. -** The UNION ALL operator works fine with multiSelectOrderBy() even when +** The UNION ALL operator works fine with multiSelectByMerge() even when ** there are COLLATE terms in the ORDER BY. */ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ @@ -152967,7 +155065,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( ALWAYS(IsVirtual(pTab)) - && pFrom->fg.fromDDL + && (pFrom->fg.fromDDL || (pParse->prepFlags & SQLITE_PREPARE_FROM_DDL)) && ALWAYS(pTab->u.vtab.p!=0) && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0) ){ @@ -153920,7 +156018,7 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){ && pExpr->pAggInfo==0 ){ sqlite3 *db = pWalker->pParse->db; - Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1"); + Expr *pNew = sqlite3ExprInt32(db, 1); if( pNew ){ Expr *pWhere = pS->pWhere; SWAP(Expr, *pNew, *pExpr); @@ -154238,6 +156336,7 @@ static SQLITE_NOINLINE void existsToJoin( && !ExprHasProperty(pWhere, EP_OuterON|EP_InnerON) && ALWAYS(p->pSrc!=0) && p->pSrc->nSrcpLimit==0 || p->pLimit->pRight==0) ){ if( pWhere->op==TK_AND ){ Expr *pRight = pWhere->pRight; @@ -154271,7 +156370,6 @@ static SQLITE_NOINLINE void existsToJoin( ExprSetProperty(pWhere, EP_IntValue); assert( p->pWhere!=0 ); pSub->pSrc->a[0].fg.fromExists = 1; - pSub->pSrc->a[0].fg.jointype |= JT_CROSS; p->pSrc = sqlite3SrcListAppendList(pParse, p->pSrc, pSub->pSrc); if( pSubWhere ){ p->pWhere = sqlite3PExpr(pParse, TK_AND, p->pWhere, pSubWhere); @@ -154286,7 +156384,6 @@ static SQLITE_NOINLINE void existsToJoin( sqlite3TreeViewSelect(0, p, 0); } #endif - existsToJoin(pParse, p, pSubWhere); } } } @@ -154299,6 +156396,7 @@ typedef struct CheckOnCtx CheckOnCtx; struct CheckOnCtx { SrcList *pSrc; /* SrcList for this context */ int iJoin; /* Cursor numbers must be =< than this */ + int bFuncArg; /* True for table-function arg */ CheckOnCtx *pParent; /* Parent context */ }; @@ -154346,11 +156444,16 @@ static int selectCheckOnClausesExpr(Walker *pWalker, Expr *pExpr){ ** does not refer to a table to the right of CheckOnCtx.iJoin. */ do { SrcList *pSrc = pCtx->pSrc; + int nSrc = pSrc->nSrc; int iTab = pExpr->iTable; - if( iTab>=pSrc->a[0].iCursor && iTab<=pSrc->a[pSrc->nSrc-1].iCursor ){ + int ii; + for(ii=0; iia[ii].iCursor!=iTab; ii++){} + if( iiiJoin && iTab>pCtx->iJoin ){ sqlite3ErrorMsg(pWalker->pParse, - "ON clause references tables to its right"); + "%s references tables to its right", + (pCtx->bFuncArg ? "table-function argument" : "ON clause") + ); return WRC_Abort; } break; @@ -154388,6 +156491,7 @@ static int selectCheckOnClausesSelect(Walker *pWalker, Select *pSelect){ SQLITE_PRIVATE void sqlite3SelectCheckOnClauses(Parse *pParse, Select *pSelect){ Walker w; CheckOnCtx sCtx; + int ii; assert( pSelect->selFlags & SF_OnToWhere ); assert( pSelect->pSrc!=0 && pSelect->pSrc->nSrc>=2 ); memset(&w, 0, sizeof(w)); @@ -154397,8 +156501,46 @@ SQLITE_PRIVATE void sqlite3SelectCheckOnClauses(Parse *pParse, Select *pSelect){ w.u.pCheckOnCtx = &sCtx; memset(&sCtx, 0, sizeof(sCtx)); sCtx.pSrc = pSelect->pSrc; - sqlite3WalkExprNN(&w, pSelect->pWhere); + sqlite3WalkExpr(&w, pSelect->pWhere); pSelect->selFlags &= ~SF_OnToWhere; + + /* Check for any table-function args that are attached to virtual tables + ** on the RHS of an outer join. They are subject to the same constraints + ** as ON clauses. */ + sCtx.bFuncArg = 1; + for(ii=0; iipSrc->nSrc; ii++){ + SrcItem *pItem = &pSelect->pSrc->a[ii]; + if( pItem->fg.isTabFunc + && (pItem->fg.jointype & JT_OUTER) + ){ + sCtx.iJoin = pItem->iCursor; + sqlite3WalkExprList(&w, pItem->u1.pFuncArg); + } + } +} + +/* +** If p2 exists and p1 and p2 have the same number of terms, then change +** every term of p1 to have the same sort order as p2 and return true. +** +** If p2 is NULL or p1 and p2 are different lengths, then make no changes +** and return false. +** +** p1 must be non-NULL. +*/ +static int sqlite3CopySortOrder(ExprList *p1, ExprList *p2){ + assert( p1 ); + if( p2 && p1->nExpr==p2->nExpr ){ + int ii; + for(ii=0; iinExpr; ii++){ + u8 sortFlags; + sortFlags = p2->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; + p1->a[ii].fg.sortFlags = sortFlags; + } + return 1; + }else{ + return 0; + } } /* @@ -154496,8 +156638,7 @@ SQLITE_PRIVATE int sqlite3Select( 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 || + assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Discard || pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_DistFifo ); /* All of these destinations are also able to ignore the ORDER BY clause */ if( p->pOrderBy ){ @@ -154513,7 +156654,6 @@ SQLITE_PRIVATE int sqlite3Select( p->pOrderBy = 0; } p->selFlags &= ~(u32)SF_Distinct; - p->selFlags |= SF_NoopOrderBy; } sqlite3SelectPrep(pParse, p, 0); if( pParse->nErr ){ @@ -155041,7 +157181,8 @@ SQLITE_PRIVATE int sqlite3Select( ** BY and DISTINCT, and an index or separate temp-table for the other. */ if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct - && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0 + && sqlite3CopySortOrder(pEList, sSort.pOrderBy) + && sqlite3ExprListCompare(pEList, sSort.pOrderBy, -1)==0 && OptimizationEnabled(db, SQLITE_GroupByOrder) #ifndef SQLITE_OMIT_WINDOWFUNC && p->pWin==0 @@ -155255,21 +157396,10 @@ SQLITE_PRIVATE int sqlite3Select( ** but not actually sorted. Either way, record the fact that the ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp ** variable. */ - if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){ - int ii; - /* The GROUP BY processing doesn't care whether rows are delivered in - ** ASC or DESC order - only that each group is returned contiguously. - ** So set the ASC/DESC flags in the GROUP BY to match those in the - ** ORDER BY to maximize the chances of rows being delivered in an - ** order that makes the ORDER BY redundant. */ - for(ii=0; iinExpr; ii++){ - u8 sortFlags; - sortFlags = sSort.pOrderBy->a[ii].fg.sortFlags & KEYINFO_ORDER_DESC; - pGroupBy->a[ii].fg.sortFlags = sortFlags; - } - if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){ - orderByGrp = 1; - } + if( sqlite3CopySortOrder(pGroupBy, sSort.pOrderBy) + && sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 + ){ + orderByGrp = 1; } }else{ assert( 0==sqlite3LogEst(1) ); @@ -156079,7 +158209,7 @@ SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerS sqlite3SelectDelete(db, pTmp->pSelect); sqlite3IdListDelete(db, pTmp->pIdList); sqlite3UpsertDelete(db, pTmp->pUpsert); - sqlite3SrcListDelete(db, pTmp->pFrom); + sqlite3SrcListDelete(db, pTmp->pSrc); sqlite3DbFree(db, pTmp->zSpan); sqlite3DbFree(db, pTmp); @@ -156268,11 +158398,16 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( } } + /* NB: The SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES compile-time option is + ** experimental and unsupported. Do not use it unless understand the + ** implications and you cannot get by without this capability. */ +#if !defined(SQLITE_ALLOW_TRIGGERS_ON_SYSTEM_TABLES) /* Experimental */ /* Do not create a trigger on a system table */ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); goto trigger_cleanup; } +#endif /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. @@ -156384,6 +158519,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; zName = pTrig->zName; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); + assert( iDb>=00 && iDbnDb ); pTrig->step_list = pStepList; while( pStepList ){ pStepList->pTrig = pTrig; @@ -156418,12 +158554,12 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( if( sqlite3ReadOnlyShadowTables(db) ){ TriggerStep *pStep; for(pStep=pTrig->step_list; pStep; pStep=pStep->pNext){ - if( pStep->zTarget!=0 - && sqlite3ShadowTableName(db, pStep->zTarget) + if( pStep->pSrc!=0 + && sqlite3ShadowTableName(db, pStep->pSrc->a[0].zName) ){ sqlite3ErrorMsg(pParse, "trigger \"%s\" may not write to shadow table \"%s\"", - pTrig->zName, pStep->zTarget); + pTrig->zName, pStep->pSrc->a[0].zName); goto triggerfinish_cleanup; } } @@ -156514,26 +158650,39 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep( static TriggerStep *triggerStepAllocate( Parse *pParse, /* Parser context */ u8 op, /* Trigger opcode */ - Token *pName, /* The target name */ + SrcList *pTabList, /* Target table */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ + Trigger *pNew = pParse->pNewTrigger; sqlite3 *db = pParse->db; - TriggerStep *pTriggerStep; + TriggerStep *pTriggerStep = 0; - if( pParse->nErr ) return 0; - pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1); - if( pTriggerStep ){ - char *z = (char*)&pTriggerStep[1]; - memcpy(z, pName->z, pName->n); - sqlite3Dequote(z); - pTriggerStep->zTarget = z; - pTriggerStep->op = op; - pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd); - if( IN_RENAME_OBJECT ){ - sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName); + if( pParse->nErr==0 ){ + if( pNew + && pNew->pSchema!=db->aDb[1].pSchema + && pTabList->a[0].u4.zDatabase + ){ + sqlite3ErrorMsg(pParse, + "qualified table names are not allowed on INSERT, UPDATE, and DELETE " + "statements within triggers"); + }else{ + pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep)); + if( pTriggerStep ){ + pTriggerStep->pSrc = sqlite3SrcListDup(db, pTabList, EXPRDUP_REDUCE); + pTriggerStep->op = op; + pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd); + if( pTriggerStep->pSrc && IN_RENAME_OBJECT ){ + sqlite3RenameTokenRemap(pParse, + pTriggerStep->pSrc->a[0].zName, + pTabList->a[0].zName + ); + } + } } } + + sqlite3SrcListDelete(db, pTabList); return pTriggerStep; } @@ -156546,7 +158695,7 @@ static TriggerStep *triggerStepAllocate( */ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( Parse *pParse, /* Parser */ - Token *pTableName, /* Name of the table into which we insert */ + SrcList *pTabList, /* Table to INSERT into */ IdList *pColumn, /* List of columns in pTableName to insert into */ Select *pSelect, /* A SELECT statement that supplies values */ u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ @@ -156559,7 +158708,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( assert(pSelect != 0 || db->mallocFailed); - pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTabList, zStart, zEnd); if( pTriggerStep ){ if( IN_RENAME_OBJECT ){ pTriggerStep->pSelect = pSelect; @@ -156591,7 +158740,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep( */ SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ - Token *pTableName, /* Name of the table to be updated */ + SrcList *pTabList, /* Name of the table to be updated */ SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ @@ -156602,21 +158751,36 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; - pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTabList, zStart, zEnd); if( pTriggerStep ){ + SrcList *pFromDup = 0; if( IN_RENAME_OBJECT ){ pTriggerStep->pExprList = pEList; pTriggerStep->pWhere = pWhere; - pTriggerStep->pFrom = pFrom; + pFromDup = pFrom; pEList = 0; pWhere = 0; pFrom = 0; }else{ pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE); pTriggerStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); - pTriggerStep->pFrom = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE); + pFromDup = sqlite3SrcListDup(db, pFrom, EXPRDUP_REDUCE); } pTriggerStep->orconf = orconf; + + if( pFromDup && !IN_RENAME_OBJECT){ + Select *pSub; + Token as = {0, 0}; + pSub = sqlite3SelectNew(pParse, 0, pFromDup, 0,0,0,0, SF_NestedFrom, 0); + pFromDup = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, &as, pSub ,0); + } + if( pFromDup && pTriggerStep->pSrc ){ + pTriggerStep->pSrc = sqlite3SrcListAppendList( + pParse, pTriggerStep->pSrc, pFromDup + ); + }else{ + sqlite3SrcListDelete(db, pFromDup); + } } sqlite3ExprListDelete(db, pEList); sqlite3ExprDelete(db, pWhere); @@ -156631,7 +158795,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep( */ SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep( Parse *pParse, /* Parser */ - Token *pTableName, /* The table from which rows are deleted */ + SrcList *pTabList, /* The table from which rows are deleted */ Expr *pWhere, /* The WHERE clause */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ @@ -156639,7 +158803,7 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep( sqlite3 *db = pParse->db; TriggerStep *pTriggerStep; - pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd); + pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTabList, zStart, zEnd); if( pTriggerStep ){ if( IN_RENAME_OBJECT ){ pTriggerStep->pWhere = pWhere; @@ -156839,6 +159003,7 @@ static SQLITE_NOINLINE Trigger *triggersReallyExist( p = pList; if( (pParse->db->flags & SQLITE_EnableTrigger)==0 && pTab->pTrigger!=0 + && sqlite3SchemaToIndex(pParse->db, pTab->pTrigger->pSchema)!=1 ){ /* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that ** only TEMP triggers are allowed. Truncate the pList so that it @@ -156901,52 +159066,6 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( return triggersReallyExist(pParse,pTab,op,pChanges,pMask); } -/* -** Convert the pStep->zTarget string into a SrcList and return a pointer -** to that SrcList. -** -** This routine adds a specific database name, if needed, to the target when -** forming the SrcList. This prevents a trigger in one database from -** referring to a target in another database. An exception is when the -** trigger is in TEMP in which case it can refer to any other database it -** wants. -*/ -SQLITE_PRIVATE SrcList *sqlite3TriggerStepSrc( - Parse *pParse, /* The parsing context */ - TriggerStep *pStep /* The trigger containing the target token */ -){ - sqlite3 *db = pParse->db; - SrcList *pSrc; /* SrcList to be returned */ - char *zName = sqlite3DbStrDup(db, pStep->zTarget); - 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); - as.n = 0; - as.z = 0; - pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); - } - pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); - } - }else{ - sqlite3DbFree(db, zName); - } - return pSrc; -} - /* ** Return true if the pExpr term from the RETURNING clause argument ** list is of the form "*". Raise an error if the terms if of the @@ -157212,7 +159331,7 @@ static int codeTriggerProgram( switch( pStep->op ){ case TK_UPDATE: { sqlite3Update(pParse, - sqlite3TriggerStepSrc(pParse, pStep), + sqlite3SrcListDup(db, pStep->pSrc, 0), sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3ExprDup(db, pStep->pWhere, 0), pParse->eOrconf, 0, 0, 0 @@ -157222,7 +159341,7 @@ static int codeTriggerProgram( } case TK_INSERT: { sqlite3Insert(pParse, - sqlite3TriggerStepSrc(pParse, pStep), + sqlite3SrcListDup(db, pStep->pSrc, 0), sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3IdListDup(db, pStep->pIdList), pParse->eOrconf, @@ -157233,7 +159352,7 @@ static int codeTriggerProgram( } case TK_DELETE: { sqlite3DeleteFrom(pParse, - sqlite3TriggerStepSrc(pParse, pStep), + sqlite3SrcListDup(db, pStep->pSrc, 0), sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0 ); sqlite3VdbeAddOp0(v, OP_ResetCount); @@ -157298,7 +159417,7 @@ static TriggerPrg *codeRowTrigger( Table *pTab, /* The table pTrigger is attached to */ int orconf /* ON CONFLICT policy to code trigger program with */ ){ - Parse *pTop = sqlite3ParseToplevel(pParse); + Parse *pTop; /* Top level Parse object */ sqlite3 *db = pParse->db; /* Database handle */ TriggerPrg *pPrg; /* Value to return */ Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ @@ -157307,10 +159426,24 @@ static TriggerPrg *codeRowTrigger( SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ Parse sSubParse; /* Parse context for sub-vdbe */ + int nDepth; /* Trigger depth */ + /* Ensure that triggers are not chained too deep. This test is linear + ** in the chaining depth, but sensible code ought not be chaining + ** triggers excessively, so that shouldn't be a problem. + */ + pTop = pParse; + for(nDepth=0; pTop->pOuterParse; pTop = pTop->pOuterParse, nDepth++){} + if( nDepth>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ + sqlite3ErrorMsg(pParse, "triggers nested too deep"); + return 0; + } + + pTop = sqlite3ParseToplevel(pParse); assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); assert( pTop->pVdbe ); + /* Allocate the TriggerPrg and SubProgram objects. To ensure that they ** are freed if an error occurs, link them into the Parse.pTriggerPrg ** list of the top-level Parse object sooner rather than later. */ @@ -159311,7 +161444,8 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate( /* excluded.* columns of type REAL need to be converted to a hard real */ for(i=0; inCol; i++){ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i); + int iStorage = pTop->regData + sqlite3TableColumnToStorage(pTab, i); + sqlite3VdbeAddOp1(v, OP_RealAffinity, iStorage); } } sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0), @@ -159555,9 +161689,11 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( pDb = &db->aDb[nDb]; assert( strcmp(pDb->zDbSName,zDbVacuum)==0 ); pTemp = pDb->pBt; + nRes = sqlite3BtreeGetRequestedReserve(pMain); if( pOut ){ sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); i64 sz = 0; + const char *zFilename; if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ rc = SQLITE_ERROR; sqlite3SetString(pzErrMsg, db, "output file already exists"); @@ -159569,8 +161705,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( ** they are for the database being vacuumed, except that PAGER_CACHESPILL ** is always set. */ pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK); + + /* If the VACUUM INTO target file is a URI filename and if the + ** "reserve=N" query parameter is present, reset the reserve to the + ** amount specified, if the amount is within range */ + zFilename = sqlite3BtreeGetFilename(pTemp); + if( ALWAYS(zFilename) ){ + int nNew = (int)sqlite3_uri_int64(zFilename, "reserve", nRes); + if( nNew>=0 && nNew<=255 ) nRes = nNew; + } } - nRes = sqlite3BtreeGetRequestedReserve(pMain); sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); @@ -159889,6 +162033,7 @@ SQLITE_API int sqlite3_drop_modules(sqlite3 *db, const char** azNames){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif + sqlite3_mutex_enter(db->mutex); for(pThis=sqliteHashFirst(&db->aModule); pThis; pThis=pNext){ Module *pMod = (Module*)sqliteHashData(pThis); pNext = sqliteHashNext(pThis); @@ -159899,6 +162044,7 @@ SQLITE_API int sqlite3_drop_modules(sqlite3 *db, const char** azNames){ } createModule(db, pMod->zName, 0, 0, 0); } + sqlite3_mutex_leave(db->mutex); return SQLITE_OK; } @@ -163375,7 +165521,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( if( SMASKBIT32(j) & pLoop->u.vtab.mHandleIn ){ int iTab = pParse->nTab++; int iCache = ++pParse->nMem; - sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab); + sqlite3CodeRhsOfIN(pParse, pTerm->pExpr, iTab, 0); sqlite3VdbeAddOp3(v, OP_VInitIn, iTab, iTarget, iCache); }else{ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget); @@ -164099,7 +166245,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). */ - if( pWInfo->nLevel>1 ){ + if( pWInfo->nLevel>1 || pTabItem->fg.fromExists ){ int nNotReady; /* The number of notReady tables */ SrcItem *origSrc; /* Original list of tables */ nNotReady = pWInfo->nLevel - iLevel - 1; @@ -164112,6 +166258,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( for(k=1; k<=nNotReady; k++){ memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k])); } + + /* Clear the fromExists flag on the OR-optimized table entry so that + ** the calls to sqlite3WhereEnd() do not code early-exits after the + ** first row is visited. The early exit applies to this table's + ** overall loop - including the multiple OR branches and any WHERE + ** conditions not passed to the sub-loops - not to the sub-loops. */ + pOrTab->a[0].fg.fromExists = 0; }else{ pOrTab = pWInfo->pTabList; } @@ -164355,7 +166508,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( assert( pLevel->op==OP_Return ); pLevel->p2 = sqlite3VdbeCurrentAddr(v); - if( pWInfo->nLevel>1 ){ sqlite3DbFreeNN(db, pOrTab); } + if( pWInfo->pTabList!=pOrTab ){ sqlite3DbFreeNN(db, pOrTab); } if( !untestedTerms ) disableTerm(pLevel, pTerm); }else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -164512,6 +166665,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; + if( ExprHasProperty(pAlt->pExpr, EP_Collate) ) continue; if( (pAlt->eOperator & WO_IN) && ExprUseXSelect(pAlt->pExpr) && (pAlt->pExpr->x.pSelect->pEList->nExpr>1) @@ -165043,13 +167197,14 @@ static int isLikeOrGlob( ){ int isNum; double rDummy; - isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8); + assert( zNew[iTo]==0 ); + isNum = sqlite3AtoF(zNew, &rDummy); if( isNum<=0 ){ if( iTo==1 && zNew[0]=='-' ){ isNum = +1; }else{ zNew[iTo-1]++; - isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8); + isNum = sqlite3AtoF(zNew, &rDummy); zNew[iTo-1]--; } } @@ -165092,6 +167247,34 @@ static int isLikeOrGlob( } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ +/* +** If pExpr is one of "like", "glob", "match", or "regexp", then +** return the corresponding SQLITE_INDEX_CONSTRAINT_xxxx value. +** If not, return 0. +** +** pExpr is guaranteed to be a TK_FUNCTION. +*/ +SQLITE_PRIVATE int sqlite3ExprIsLikeOperator(const Expr *pExpr){ + static const struct { + const char *zOp; + unsigned char eOp; + } aOp[] = { + { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, + { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, + { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, + { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } + }; + int i; + assert( pExpr->op==TK_FUNCTION ); + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + for(i=0; iu.zToken, aOp[i].zOp)==0 ){ + return aOp[i].eOp; + } + } + return 0; +} + #ifndef SQLITE_OMIT_VIRTUALTABLE /* @@ -165128,15 +167311,6 @@ static int isAuxiliaryVtabOperator( Expr **ppRight /* Expression to left of MATCH/op2 */ ){ if( pExpr->op==TK_FUNCTION ){ - static const struct Op2 { - const char *zOp; - unsigned char eOp2; - } aOp[] = { - { "match", SQLITE_INDEX_CONSTRAINT_MATCH }, - { "glob", SQLITE_INDEX_CONSTRAINT_GLOB }, - { "like", SQLITE_INDEX_CONSTRAINT_LIKE }, - { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP } - }; ExprList *pList; Expr *pCol; /* Column reference */ int i; @@ -165156,16 +167330,11 @@ static int isAuxiliaryVtabOperator( */ pCol = pList->a[1].pExpr; assert( pCol->op!=TK_COLUMN || (ExprUseYTab(pCol) && pCol->y.pTab!=0) ); - if( ExprIsVtab(pCol) ){ - for(i=0; iu.zToken, aOp[i].zOp)==0 ){ - *peOp2 = aOp[i].eOp2; - *ppRight = pList->a[0].pExpr; - *ppLeft = pCol; - return 1; - } - } + if( ExprIsVtab(pCol) && (i = sqlite3ExprIsLikeOperator(pExpr))!=0 ){ + *peOp2 = i; + *ppRight = pList->a[0].pExpr; + *ppLeft = pCol; + return 1; } /* We can also match against the first column of overloaded @@ -165250,7 +167419,10 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){ pWC->a[iChild].iParent = iParent; pWC->a[iChild].truthProb = pWC->a[iParent].truthProb; + assert( pWC->a[iParent].nChild < UMXV(pWC->a[0].nChild) ); pWC->a[iParent].nChild++; + testcase( pWC->a[iParent].nChild == UMXV(pWC->a[0].nChild) ); + } /* @@ -165299,16 +167471,22 @@ static void whereCombineDisjuncts( Expr *pNew; /* New virtual expression */ int op; /* Operator for the combined expression */ int idxNew; /* Index in pWC of the next virtual term */ + Expr *pA, *pB; /* Expressions associated with pOne and pTwo */ if( (pOne->wtFlags | pTwo->wtFlags) & TERM_VNULL ) return; if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return; if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return; - assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 ); - assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 ); - if( sqlite3ExprCompare(0,pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return; - if( sqlite3ExprCompare(0,pOne->pExpr->pRight, pTwo->pExpr->pRight,-1) )return; + pA = pOne->pExpr; + pB = pTwo->pExpr; + assert( pA->pLeft!=0 && pA->pRight!=0 ); + assert( pB->pLeft!=0 && pB->pRight!=0 ); + if( sqlite3ExprCompare(0,pA->pLeft, pB->pLeft, -1) ) return; + if( sqlite3ExprCompare(0,pA->pRight, pB->pRight,-1) ) return; + if( ExprHasProperty(pA,EP_Commuted)!=ExprHasProperty(pB,EP_Commuted) ){ + return; + } /* If we reach this point, it means the two subterms can be combined */ if( (eOp & (eOp-1))!=0 ){ if( eOp & (WO_LT|WO_LE) ){ @@ -165319,7 +167497,7 @@ static void whereCombineDisjuncts( } } db = pWC->pWInfo->pParse->db; - pNew = sqlite3ExprDup(db, pOne->pExpr, 0); + pNew = sqlite3ExprDup(db, pA, 0); if( pNew==0 ) return; for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( opop = op; @@ -165683,8 +167861,8 @@ static void exprAnalyzeOrTerm( ** 3. Not originating in the ON clause of an OUTER JOIN ** 4. The operator is not IS or else the query does not contain RIGHT JOIN ** 5. The affinities of A and B must be compatible -** 6a. Both operands use the same collating sequence OR -** 6b. The overall collating sequence is BINARY +** 6. Both operands use the same collating sequence, and they must not +** use explicit COLLATE clauses. ** If this routine returns TRUE, that means that the RHS can be substituted ** for the LHS anyplace else in the WHERE clause where the LHS column occurs. ** This is an optimization. No harm comes from returning 0. But if 1 is @@ -165692,10 +167870,9 @@ static void exprAnalyzeOrTerm( */ static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){ char aff1, aff2; - CollSeq *pColl; if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; /* (1) */ if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; /* (2) */ - if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* (3) */ + if( ExprHasProperty(pExpr, EP_OuterON|EP_Collate) ) return 0; /* (3) */ assert( pSrc!=0 ); if( pExpr->op==TK_IS && pSrc->nSrc>=2 @@ -165710,10 +167887,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){ ){ return 0; /* (5) */ } - pColl = sqlite3ExprCompareCollSeq(pParse, pExpr); - if( !sqlite3IsBinary(pColl) - && !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) - ){ + if( !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) ){ return 0; /* (6) */ } return 1; @@ -166025,6 +168199,7 @@ static void exprAnalyze( pList = pExpr->x.pList; assert( pList!=0 ); assert( pList->nExpr==2 ); + assert( pWC->a[idxTerm].nChild==0 ); for(i=0; i<2; i++){ Expr *pNewExpr; int idxNew; @@ -166045,7 +168220,7 @@ static void exprAnalyze( /* Analyze a term that is composed of two or more subterms connected by ** an OR operator. */ - else if( pExpr->op==TK_OR ){ + else if( pExpr->op==TK_OR && !ExprHasProperty(pExpr, EP_Collate) ){ assert( pWC->op==TK_AND ); exprAnalyzeOrTerm(pSrc, pWC, idxTerm); pTerm = &pWC->a[idxTerm]; @@ -166235,8 +168410,11 @@ static void exprAnalyze( && pExpr->x.pSelect->pWin==0 #endif && pWC->op==TK_AND + && pExpr->x.pSelect->pEList->nExpr <= UMXV(pTerm->nChild) + /* ^-- See bug 2026-06-04T10:00:49Z */ ){ int i; + assert( pTerm->nChild==0 ); for(i=0; ipLeft); i++){ int idxNew; idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL|TERM_SLICE); @@ -166359,13 +168537,11 @@ static void whereAddLimitExpr( int iVal = 0; if( sqlite3ExprIsInteger(pExpr, &iVal, pParse) && iVal>=0 ){ - Expr *pVal = sqlite3Expr(db, TK_INTEGER, 0); + Expr *pVal = sqlite3ExprInt32(db, iVal); if( pVal==0 ) return; - ExprSetProperty(pVal, EP_IntValue); - pVal->u.iValue = iVal; pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); }else{ - Expr *pVal = sqlite3Expr(db, TK_REGISTER, 0); + Expr *pVal = sqlite3ExprAlloc(db, TK_REGISTER, 0, 0); if( pVal==0 ) return; pVal->iTable = iReg; pNew = sqlite3PExpr(pParse, TK_MATCH, 0, pVal); @@ -168192,11 +170368,14 @@ static sqlite3_index_info *allocateIndexInfo( break; } if( i==n ){ + int bSortByGroup = (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0; nOrderBy = n; if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ - eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); + eDistinct = 2 + bSortByGroup; }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ - eDistinct = 1; + eDistinct = 1 - bSortByGroup; + }else if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){ + eDistinct = 3; } } } @@ -169124,11 +171303,16 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ WhereInfo *pWInfo; if( pWC ){ + int nb; + SrcItem *pItem; + Table *pTab; + Bitmask mAll; + 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; + nb = 1+(pWInfo->pTabList->nSrc+3)/4; + pItem = pWInfo->pTabList->a + p->iTab; + pTab = pItem->pSTab; + 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", @@ -169607,6 +171791,67 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ return rc; } +/* +** Callback for estLikePatternLength(). +** +** If this node is a string literal that is longer pWalker->sz, then set +** pWalker->sz to the byte length of that string literal. +** +** pWalker->eCode indicates how to count characters: +** +** eCode==0 Count as a GLOB pattern +** eCode==1 Count as a LIKE pattern +*/ +static int exprNodePatternLengthEst(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_STRING ){ + int sz = 0; /* Pattern size in bytes */ + u8 *z = (u8*)pExpr->u.zToken; /* The pattern */ + u8 c; /* Next character of the pattern */ + u8 c1, c2, c3; /* Wildcards */ + if( pWalker->eCode ){ + c1 = '%'; + c2 = '_'; + c3 = 0; + }else{ + c1 = '*'; + c2 = '?'; + c3 = '['; + } + while( (c = *(z++))!=0 ){ + if( c==c3 ){ + if( *z ) z++; + while( *z && *z!=']' ) z++; + }else if( c!=c1 && c!=c2 ){ + sz++; + } + } + if( sz>pWalker->u.sz ) pWalker->u.sz = sz; + } + return WRC_Continue; +} + +/* +** Return the length of the longest string literal in the given +** expression. +** +** eCode indicates how to count characters: +** +** eCode==0 Count as a GLOB pattern +** eCode==1 Count as a LIKE pattern +*/ +static int estLikePatternLength(Expr *p, u16 eCode){ + Walker w; + w.u.sz = 0; + w.eCode = eCode; + w.xExprCallback = exprNodePatternLengthEst; + w.xSelectCallback = sqlite3SelectWalkFail; +#ifdef SQLITE_DEBUG + w.xSelectCallback2 = sqlite3SelectWalkAssert2; +#endif + sqlite3WalkExpr(&w, p); + return w.u.sz; +} + /* ** Adjust the WhereLoop.nOut value downward to account for terms of the ** WHERE clause that reference the loop but which are not used by an @@ -169635,6 +171880,13 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ ** "x" column is boolean or else -1 or 0 or 1 is a common default value ** on the "x" column and so in that case only cap the output row estimate ** at 1/2 instead of 1/4. +** +** Heuristic 3: If there is a LIKE or GLOB (or REGEXP or MATCH) operator +** with a large constant pattern, then reduce the size of the search +** space according to the length of the pattern, under the theory that +** longer patterns are less likely to match. This heuristic was added +** to give better output-row count estimates when preparing queries for +** the Join-Order Benchmarks. See forum thread 2026-01-30T09:57:54z */ static void whereLoopOutputAdjust( WhereClause *pWC, /* The WHERE clause */ @@ -169684,13 +171936,14 @@ static void whereLoopOutputAdjust( }else{ /* In the absence of explicit truth probabilities, use heuristics to ** guess a reasonable truth probability. */ + Expr *pOpExpr = pTerm->pExpr; pLoop->nOut--; if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && (pTerm->wtFlags & TERM_HIGHTRUTH)==0 /* tag-20200224-1 */ ){ - Expr *pRight = pTerm->pExpr->pRight; + Expr *pRight = pOpExpr->pRight; int k = 0; - testcase( pTerm->pExpr->op==TK_IS ); + testcase( pOpExpr->op==TK_IS ); if( sqlite3ExprIsInteger(pRight, &k, 0) && k>=(-1) && k<=1 ){ k = 10; }else{ @@ -169700,6 +171953,23 @@ static void whereLoopOutputAdjust( pTerm->wtFlags |= TERM_HEURTRUTH; iReduce = k; } + }else + if( ExprHasProperty(pOpExpr, EP_InfixFunc) + && pOpExpr->op==TK_FUNCTION + ){ + int eOp; + assert( ExprUseXList(pOpExpr) ); + assert( pOpExpr->x.pList->nExpr>=2 ); + eOp = sqlite3ExprIsLikeOperator(pOpExpr); + if( ALWAYS(eOp>0) ){ + int szPattern; + Expr *pRHS = pOpExpr->x.pList->a[0].pExpr; + eOp = eOp==SQLITE_INDEX_CONSTRAINT_LIKE; + szPattern = estLikePatternLength(pRHS, eOp); + if( szPattern>0 ){ + pLoop->nOut -= szPattern*2; + } + } } } } @@ -169771,6 +172041,7 @@ static int whereRangeVectorLen( idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn); if( aff!=idxaff ) break; + if( ExprHasProperty(pTerm->pExpr, EP_Commuted) ) SWAP(Expr*, pRhs, pLhs); pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); if( pColl==0 ) break; if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break; @@ -170160,6 +172431,7 @@ static int whereLoopAddBtreeIndex( pNew->rRun += nInMul + nIn; pNew->nOut += nInMul + nIn; whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize); + if( pSrc->fg.fromExists ) pNew->nOut = 0; rc = whereLoopInsert(pBuilder, pNew); if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ @@ -170755,7 +173027,14 @@ static int whereLoopAddBtree( 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; + /* Do not set btree.pOrderBy for a recursive CTE. In this case + ** the ORDER BY clause does not determine the overall order that + ** rows are emitted from the CTE in. */ + if( (pSrc->u4.pSubq->pSelect->selFlags & SF_Recursive)==0 ){ + pNew->u.btree.pOrderBy = pSrc->u4.pSubq->pSelect->pOrderBy; + } + }else if( pSrc->fg.fromExists ){ + pNew->nOut = 0; } rc = whereLoopInsert(pBuilder, pNew); pNew->nOut = rSize; @@ -170858,6 +173137,7 @@ static int whereLoopAddBtree( ** positioned to the correct row during the right-join no-match ** loop. */ }else{ + if( pSrc->fg.fromExists ) pNew->nOut = 0; rc = whereLoopInsert(pBuilder, pNew); } pNew->nOut = rSize; @@ -171520,7 +173800,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; int bFirstPastRJ = 0; - int hasRightJoin = 0; + int hasRightCrossJoin = 0; WhereLoop *pNew; @@ -171547,15 +173827,34 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){ ** prevents the right operand of a RIGHT JOIN from being swapped with ** other elements even further to the right. ** - ** The JT_LTORJ case and the hasRightJoin flag work together to - ** prevent FROM-clause terms from moving from the right side of - ** a LEFT JOIN over to the left side of that join if the LEFT JOIN - ** is itself on the left side of a RIGHT JOIN. + ** The hasRightCrossJoin flag prevent FROM-clause terms from moving + ** from the right side of a LEFT JOIN or CROSS JOIN over to the + ** left side of that same join. This is a required restriction in + ** the case of LEFT JOIN - an incorrect answer may results if it is + ** not enforced. This restriction is not required for CROSS JOIN. + ** It is provided merely as a means of controlling join order, under + ** the theory that no real-world queries that care about performance + ** actually use the CROSS JOIN syntax. */ - if( pItem->fg.jointype & JT_LTORJ ) hasRightJoin = 1; + if( pItem->fg.jointype & (JT_LTORJ|JT_CROSS) ){ + testcase( pItem->fg.jointype & JT_LTORJ ); + testcase( pItem->fg.jointype & JT_CROSS ); + hasRightCrossJoin = 1; + } mPrereq |= mPrior; bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; - }else if( !hasRightJoin ){ + }else if( pItem->fg.fromExists ){ + /* joins that result from the EXISTS-to-JOIN optimization should not + ** be moved to the left of any of their dependencies */ + WhereClause *pWC = &pWInfo->sWC; + WhereTerm *pTerm; + int i; + for(i=pWC->nBase, pTerm=pWC->a; i>0; i--, pTerm++){ + if( (pNew->maskSelf & pTerm->prereqAll)!=0 ){ + mPrereq |= (pTerm->prereqAll & (pNew->maskSelf-1)); + } + } + }else if( !hasRightCrossJoin ){ mPrereq = 0; } #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -171778,9 +174077,7 @@ static i8 wherePathSatisfiesOrderBy( pLoop = pLast; } if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){ - if( pLoop->u.vtab.isOrdered - && ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY) - ){ + if( pLoop->u.vtab.isOrdered && pWInfo->pOrderBy==pOrderBy ){ obSat = obDone; }else{ /* No further ORDER BY terms may be matched. So this call should @@ -172156,12 +174453,21 @@ static LogEst whereSortingCost( ** 12 otherwise ** ** For the purposes of this heuristic, a star-query is defined as a query -** with a large central table that is joined using an INNER JOIN, -** not CROSS or OUTER JOINs, against four or more smaller tables. -** The central table is called the "fact" table. The smaller tables -** that get joined are "dimension tables". Also, any table that is -** self-joined cannot be a dimension table; we assume that dimension -** tables may only be joined against fact tables. +** with a central "fact" table that is joined against multiple +** "dimension" tables, subject to the following constraints: +** +** (aa) Only a five-way or larger join is considered for this +** optimization. If there are fewer than four terms in the FROM +** clause, this heuristic does not apply. +** +** (bb) The join between the fact table and the dimension tables must +** be an INNER join. CROSS and OUTER JOINs do not qualify. +** +** (cc) A table must have 3 or more dimension tables in order to be +** considered a fact table. (Was 4 prior to 2026-02-10.) +** +** (dd) A table that is a self-join cannot be a dimension table. +** Dimension tables are joined against fact tables. ** ** SIDE EFFECT: (and really the whole point of this subroutine) ** @@ -172214,7 +174520,7 @@ static int computeMxChoice(WhereInfo *pWInfo){ } #endif /* SQLITE_DEBUG */ - if( nLoop>=5 + if( nLoop>=4 /* Constraint (aa) */ && !pWInfo->bStarDone && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery) ){ @@ -172226,7 +174532,7 @@ static int computeMxChoice(WhereInfo *pWInfo){ pWInfo->bStarDone = 1; /* Only do this computation once */ - /* Look for fact tables with four or more dimensions where the + /* Look for fact tables with three or more dimensions where the ** dimension tables are not separately from the fact tables by an outer ** or cross join. Adjust cost weights if found. */ @@ -172243,18 +174549,17 @@ static int computeMxChoice(WhereInfo *pWInfo){ if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ /* If the candidate fact-table is the right table of an outer join ** restrict the search for dimension-tables to be tables to the right - ** of the fact-table. */ - if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */ + ** of the fact-table. Constraint (bb) */ + if( iFromIdx+3 > nLoop ){ + break; /* ^-- Impossible to reach nDep>=2 - Constraint (cc) */ + } while( pStart && pStart->iTab<=iFromIdx ){ pStart = pStart->pNextLoop; } } for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ - /* Fact-tables and dimension-tables cannot be separated by an - ** outer join (at least for the definition of fact- and dimension- - ** used by this heuristic). */ - break; + break; /* Constraint (bb) */ } if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */ && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */ @@ -172268,7 +174573,9 @@ static int computeMxChoice(WhereInfo *pWInfo){ } } } - if( nDep<=3 ) continue; + if( nDep<=2 ){ + continue; /* Constraint (cc) */ + } /* If we reach this point, it means that pFactTab is a fact table ** with four or more dimensions connected by inner joins. Proceed @@ -172281,6 +174588,23 @@ static int computeMxChoice(WhereInfo *pWInfo){ pWLoop->rStarDelta = 0; } } +#endif +#ifdef WHERETRACE_ENABLED /* 0x80000 */ + if( sqlite3WhereTrace & 0x80000 ){ + Bitmask mShow = mSeen; + sqlite3DebugPrintf("Fact table %s(%d), dimensions:", + pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, + iFromIdx); + for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){ + if( mShow & pWLoop->maskSelf ){ + SrcItem *pDim = aFromTabs + pWLoop->iTab; + mShow &= ~pWLoop->maskSelf; + sqlite3DebugPrintf(" %s(%d)", + pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab); + } + } + sqlite3DebugPrintf("\n"); + } #endif pWInfo->bStarUsed = 1; @@ -172304,10 +174628,8 @@ static int computeMxChoice(WhereInfo *pWInfo){ if( sqlite3WhereTrace & 0x80000 ){ SrcItem *pDim = aFromTabs + pWLoop->iTab; sqlite3DebugPrintf( - "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n", - pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab, - pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName, - iFromIdx, mxRun + "Increase SCAN cost of %s to %d\n", + pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, mxRun ); } pWLoop->rStarDelta = mxRun - pWLoop->rRun; @@ -173121,6 +175443,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin( for(pTerm=pWInfo->sWC.a; pTermprereqAll & pLoop->maskSelf)!=0 ){ pTerm->wtFlags |= TERM_CODED; + pTerm->prereqAll = 0; } } if( i!=pWInfo->nLevel-1 ){ @@ -174096,6 +176419,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ ){ int r1 = pParse->nMem+1; int j, op; + int addrIfNull = 0; /* Init to avoid false-positive compiler warning */ + if( pLevel->iLeftJoin ){ + addrIfNull = sqlite3VdbeAddOp2(v, OP_IfNullRow, pLevel->iIdxCur, r1); + } for(j=0; jiIdxCur, j, r1+j); } @@ -174105,25 +176432,17 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ VdbeCoverageIf(v, op==OP_SeekLT); VdbeCoverageIf(v, op==OP_SeekGT); sqlite3VdbeAddOp2(v, OP_Goto, 1, pLevel->p2); + if( pLevel->iLeftJoin ){ + sqlite3VdbeJumpHere(v, addrIfNull); + } } #endif /* SQLITE_DISABLE_SKIPAHEAD_DISTINCT */ } - if( pTabList->a[pLevel->iFrom].fg.fromExists && i==pWInfo->nLevel-1 ){ - /* If the EXISTS-to-JOIN optimization was applied, then the EXISTS - ** loop(s) will be the inner-most loops of the join. There might be - ** multiple EXISTS loops, but they will all be nested, and the join - ** order will not have been changed by the query planner. If the - ** inner-most EXISTS loop sees a single successful row, it should - ** break out of *all* EXISTS loops. But only the inner-most of the - ** nested EXISTS loops should do this breakout. */ - int nOuter = 0; /* Nr of outer EXISTS that this one is nested within */ - while( nOutera[pLevel[-nOuter-1].iFrom].fg.fromExists ) break; - nOuter++; - } - testcase( nOuter>0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel[-nOuter].addrBrk); - VdbeComment((v, "EXISTS break")); + if( pTabList->a[pLevel->iFrom].fg.fromExists ){ + /* This is an EXISTS-to-JOIN optimization loop. If this loop sees a + ** successful row, it should break out of itself. */ + sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); + VdbeComment((v, "EXISTS break %d", i)); } sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ @@ -174645,7 +176964,7 @@ static void nth_valueStepFunc( break; case SQLITE_FLOAT: { double fVal = sqlite3_value_double(apArg[1]); - if( ((i64)fVal)!=fVal ) goto error_out; + if( sqlite3RealToI64(fVal)!=fVal ) goto error_out; iVal = (i64)fVal; break; } @@ -175140,7 +177459,7 @@ SQLITE_PRIVATE void sqlite3WindowUpdate( pWin->eEnd = aUp[i].eEnd; pWin->eExclude = 0; if( pWin->eStart==TK_FOLLOWING ){ - pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1"); + pWin->pStart = sqlite3ExprInt32(db, 1); } break; } @@ -175485,9 +177804,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ ** keep everything legal in this case. */ if( pSublist==0 ){ - pSublist = sqlite3ExprListAppend(pParse, 0, - sqlite3Expr(db, TK_INTEGER, "0") - ); + pSublist = sqlite3ExprListAppend(pParse, 0, sqlite3ExprInt32(db, 0)); } pSub = sqlite3SelectNew( @@ -177711,8 +180028,23 @@ static void updateDeleteLimitError( ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate ** testing. */ - static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ - return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); + static void *parserStackRealloc( + void *pOld, /* Prior allocation */ + sqlite3_uint64 newSize, /* Requested new alloation size */ + Parse *pParse /* Parsing context */ + ){ + void *p = sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); + if( p==0 ) sqlite3OomFault(pParse->db); + return p; + } + static void parserStackFree(void *pOld, Parse *pParse){ + (void)pParse; + sqlite3_free(pOld); + } + + /* Return an integer that is the maximum allowed stack size */ + static int parserStackSizeLimit(Parse *pParse){ + return pParse->db->aLimit[SQLITE_LIMIT_PARSER_DEPTH]; } @@ -177751,15 +180083,46 @@ static void updateDeleteLimitError( } - /* A routine to convert a binary TK_IS or TK_ISNOT expression into a - ** unary TK_ISNULL or TK_NOTNULL expression. */ - static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){ - sqlite3 *db = pParse->db; - if( pA && pY && pY->op==TK_NULL && !IN_RENAME_OBJECT ){ - pA->op = (u8)op; - sqlite3ExprDelete(db, pA->pRight); - pA->pRight = 0; + /* Create a TK_ISNULL or TK_NOTNULL expression, perhaps optimized to + ** to TK_TRUEFALSE, if possible */ + static Expr *sqlite3PExprIsNull( + Parse *pParse, /* Parsing context */ + int op, /* TK_ISNULL or TK_NOTNULL */ + Expr *pLeft /* Operand */ + ){ + Expr *p = pLeft; + assert( op==TK_ISNULL || op==TK_NOTNULL ); + assert( pLeft!=0 ); + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ + p = p->pLeft; + assert( p!=0 ); } + switch( p->op ){ + case TK_INTEGER: + case TK_STRING: + case TK_FLOAT: + case TK_BLOB: + sqlite3ExprDeferredDelete(pParse, pLeft); + return sqlite3ExprInt32(pParse->db, op==TK_NOTNULL); + default: + break; + } + return sqlite3PExpr(pParse, op, pLeft, 0); + } + + /* Create a TK_IS or TK_ISNOT operator, perhaps optimized to + ** TK_ISNULL or TK_NOTNULL or TK_TRUEFALSE. */ + static Expr *sqlite3PExprIs( + Parse *pParse, /* Parsing context */ + int op, /* TK_IS or TK_ISNOT */ + Expr *pLeft, /* Left operand */ + Expr *pRight /* Right operand */ + ){ + if( pRight && pRight->op==TK_NULL ){ + sqlite3ExprDeferredDelete(pParse, pRight); + return sqlite3PExprIsNull(pParse, op==TK_IS ? TK_ISNULL : TK_NOTNULL, pLeft); + } + return sqlite3PExpr(pParse, op, pLeft, pRight); } /* Add a single new term to an ExprList that is used to store a @@ -178042,63 +180405,72 @@ static void updateDeleteLimitError( #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 323 +#define YYNOCODE 322 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 102 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - u32 yy9; - struct TrigEvent yy28; - With* yy125; - IdList* yy204; - struct FrameBound yy205; - TriggerStep* yy319; - const char* yy342; - Cte* yy361; - ExprList* yy402; - Upsert* yy403; - OnOrUsing yy421; - u8 yy444; - struct {int value; int mask;} yy481; - Window* yy483; - int yy502; - SrcList* yy563; - Expr* yy590; - Select* yy637; + ExprList* yy14; + With* yy59; + Cte* yy67; + Upsert* yy122; + IdList* yy132; + int yy144; + const char* yy168; + SrcList* yy203; + Window* yy211; + OnOrUsing yy269; + struct TrigEvent yy286; + struct {int value; int mask;} yy383; + u32 yy391; + TriggerStep* yy427; + Expr* yy454; + u8 yy462; + struct FrameBound yy509; + Select* yy555; } YYMINORTYPE; #ifndef YYSTACKDEPTH -#define YYSTACKDEPTH 100 +#define YYSTACKDEPTH 50 #endif #define sqlite3ParserARG_SDECL #define sqlite3ParserARG_PDECL #define sqlite3ParserARG_PARAM #define sqlite3ParserARG_FETCH #define sqlite3ParserARG_STORE +#undef YYREALLOC #define YYREALLOC parserStackRealloc -#define YYFREE sqlite3_free +#undef YYFREE +#define YYFREE parserStackFree +#undef YYDYNSTACK #define YYDYNSTACK 1 +#undef YYSIZELIMIT +#define YYSIZELIMIT parserStackSizeLimit +#define sqlite3ParserCTX(P) ((P)->pParse) #define sqlite3ParserCTX_SDECL Parse *pParse; #define sqlite3ParserCTX_PDECL ,Parse *pParse #define sqlite3ParserCTX_PARAM ,pParse #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; +#undef YYERRORSYMBOL +#undef YYERRSYMDT +#undef YYFALLBACK #define YYFALLBACK 1 -#define YYNSTATE 583 -#define YYNRULE 409 -#define YYNRULE_WITH_ACTION 344 +#define YYNSTATE 600 +#define YYNRULE 412 +#define YYNRULE_WITH_ACTION 348 #define YYNTOKEN 187 -#define YY_MAX_SHIFT 582 -#define YY_MIN_SHIFTREDUCE 845 -#define YY_MAX_SHIFTREDUCE 1253 -#define YY_ERROR_ACTION 1254 -#define YY_ACCEPT_ACTION 1255 -#define YY_NO_ACTION 1256 -#define YY_MIN_REDUCE 1257 -#define YY_MAX_REDUCE 1665 +#define YY_MAX_SHIFT 599 +#define YY_MIN_SHIFTREDUCE 867 +#define YY_MAX_SHIFTREDUCE 1278 +#define YY_ERROR_ACTION 1279 +#define YY_ACCEPT_ACTION 1280 +#define YY_NO_ACTION 1281 +#define YY_MIN_REDUCE 1282 +#define YY_MAX_REDUCE 1693 #define YY_MIN_DSTRCTR 206 -#define YY_MAX_DSTRCTR 320 +#define YY_MAX_DSTRCTR 319 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -178181,643 +180553,680 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2207) +#define YY_ACTTAB_COUNT (2379) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 130, 127, 234, 282, 282, 1328, 576, 1307, 460, 289, - /* 10 */ 289, 576, 1622, 381, 576, 1328, 573, 576, 562, 413, - /* 20 */ 1300, 1542, 573, 481, 562, 524, 460, 459, 558, 82, - /* 30 */ 82, 983, 294, 375, 51, 51, 498, 61, 61, 984, - /* 40 */ 82, 82, 1577, 137, 138, 91, 7, 1228, 1228, 1063, - /* 50 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 413, - /* 60 */ 288, 288, 182, 288, 288, 481, 536, 288, 288, 130, - /* 70 */ 127, 234, 432, 573, 525, 562, 573, 557, 562, 1290, - /* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063, - /* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296, - /* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132, - /* 110 */ 132, 132, 131, 128, 451, 451, 1050, 1050, 1064, 1067, - /* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174, - /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 481, 1341, - /* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132, - /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 498, 1228, - /* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, - /* 170 */ 136, 1204, 862, 1281, 288, 288, 283, 288, 288, 523, - /* 180 */ 523, 1250, 139, 578, 7, 578, 1345, 573, 1169, 562, - /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 573, 547, - /* 200 */ 562, 1169, 245, 1541, 1169, 245, 133, 133, 132, 132, - /* 210 */ 132, 131, 128, 451, 302, 134, 134, 134, 134, 133, - /* 220 */ 133, 132, 132, 132, 131, 128, 451, 1575, 1204, 1205, - /* 230 */ 1204, 7, 470, 550, 455, 413, 550, 455, 130, 127, - /* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132, - /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 483, 137, - /* 260 */ 138, 91, 1019, 1228, 1228, 1063, 1066, 1053, 1053, 135, - /* 270 */ 135, 136, 136, 136, 136, 1085, 576, 1204, 132, 132, - /* 280 */ 132, 131, 128, 451, 93, 214, 134, 134, 134, 134, - /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 401, 19, - /* 300 */ 19, 134, 134, 134, 134, 133, 133, 132, 132, 132, - /* 310 */ 131, 128, 451, 1498, 426, 267, 344, 467, 332, 134, - /* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128, - /* 330 */ 451, 1281, 576, 6, 1204, 1205, 1204, 257, 576, 413, - /* 340 */ 511, 508, 507, 1279, 94, 1019, 464, 1204, 551, 551, - /* 350 */ 506, 1224, 1571, 44, 38, 51, 51, 411, 576, 413, - /* 360 */ 45, 51, 51, 137, 138, 91, 530, 1228, 1228, 1063, - /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 398, - /* 380 */ 1148, 82, 82, 137, 138, 91, 39, 1228, 1228, 1063, - /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 344, - /* 400 */ 44, 288, 288, 375, 1204, 1205, 1204, 209, 1204, 1224, - /* 410 */ 320, 567, 471, 576, 573, 576, 562, 576, 316, 264, - /* 420 */ 231, 46, 160, 134, 134, 134, 134, 133, 133, 132, - /* 430 */ 132, 132, 131, 128, 451, 303, 82, 82, 82, 82, - /* 440 */ 82, 82, 442, 134, 134, 134, 134, 133, 133, 132, - /* 450 */ 132, 132, 131, 128, 451, 1582, 544, 320, 567, 1250, - /* 460 */ 874, 1582, 380, 382, 413, 1204, 1205, 1204, 360, 182, - /* 470 */ 288, 288, 1576, 557, 1339, 557, 7, 557, 1277, 472, - /* 480 */ 346, 526, 531, 573, 556, 562, 439, 1511, 137, 138, - /* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, - /* 500 */ 136, 136, 136, 136, 465, 1511, 1513, 532, 413, 288, - /* 510 */ 288, 423, 512, 288, 288, 411, 288, 288, 874, 130, - /* 520 */ 127, 234, 573, 1107, 562, 1204, 573, 1107, 562, 573, - /* 530 */ 560, 562, 137, 138, 91, 1293, 1228, 1228, 1063, 1066, - /* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134, - /* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, - /* 560 */ 493, 503, 1292, 1204, 257, 288, 288, 511, 508, 507, - /* 570 */ 1204, 1628, 1169, 123, 568, 275, 4, 506, 573, 1511, - /* 580 */ 562, 331, 1204, 1205, 1204, 1169, 548, 548, 1169, 261, - /* 590 */ 571, 7, 134, 134, 134, 134, 133, 133, 132, 132, - /* 600 */ 132, 131, 128, 451, 108, 533, 130, 127, 234, 1204, - /* 610 */ 448, 447, 413, 1451, 452, 983, 886, 96, 1598, 1233, - /* 620 */ 1204, 1205, 1204, 984, 1235, 1450, 565, 1204, 1205, 1204, - /* 630 */ 229, 522, 1234, 534, 1333, 1333, 137, 138, 91, 1449, - /* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 650 */ 136, 136, 373, 1595, 971, 1040, 413, 1236, 418, 1236, - /* 660 */ 879, 121, 121, 948, 373, 1595, 1204, 1205, 1204, 122, - /* 670 */ 1204, 452, 577, 452, 363, 417, 1028, 882, 373, 1595, - /* 680 */ 137, 138, 91, 462, 1228, 1228, 1063, 1066, 1053, 1053, - /* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134, - /* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028, - /* 710 */ 1030, 1031, 35, 570, 570, 570, 197, 423, 1040, 198, - /* 720 */ 1204, 123, 568, 1204, 4, 320, 567, 1204, 1205, 1204, - /* 730 */ 40, 388, 576, 384, 882, 1029, 423, 1188, 571, 1028, - /* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 750 */ 128, 451, 529, 1568, 1204, 19, 19, 1204, 575, 492, - /* 760 */ 413, 157, 452, 489, 1187, 1331, 1331, 5, 1204, 949, - /* 770 */ 431, 1028, 1028, 1030, 565, 22, 22, 1204, 1205, 1204, - /* 780 */ 1204, 1205, 1204, 477, 137, 138, 91, 212, 1228, 1228, - /* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 800 */ 1188, 48, 111, 1040, 413, 1204, 213, 970, 1041, 121, - /* 810 */ 121, 1204, 1205, 1204, 1204, 1205, 1204, 122, 221, 452, - /* 820 */ 577, 452, 44, 487, 1028, 1204, 1205, 1204, 137, 138, - /* 830 */ 91, 378, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, - /* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133, - /* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031, - /* 860 */ 35, 461, 1204, 1205, 1204, 1569, 1040, 377, 214, 1149, - /* 870 */ 1657, 535, 1657, 437, 902, 320, 567, 1568, 364, 320, - /* 880 */ 567, 412, 329, 1029, 519, 1188, 3, 1028, 134, 134, - /* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451, - /* 900 */ 1659, 399, 1169, 307, 893, 307, 515, 576, 413, 214, - /* 910 */ 498, 944, 1024, 540, 903, 1169, 943, 392, 1169, 1028, - /* 920 */ 1028, 1030, 406, 298, 1204, 50, 1149, 1658, 413, 1658, - /* 930 */ 145, 145, 137, 138, 91, 293, 1228, 1228, 1063, 1066, - /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 1188, 1147, - /* 950 */ 514, 1568, 137, 138, 91, 1505, 1228, 1228, 1063, 1066, - /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 323, - /* 970 */ 435, 539, 111, 1506, 274, 291, 372, 517, 367, 516, - /* 980 */ 262, 1204, 1205, 1204, 1574, 481, 363, 576, 7, 1569, - /* 990 */ 1568, 377, 134, 134, 134, 134, 133, 133, 132, 132, - /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 576, 232, 576, - /* 1010 */ 19, 19, 134, 134, 134, 134, 133, 133, 132, 132, - /* 1020 */ 132, 131, 128, 451, 1169, 433, 576, 1207, 19, 19, - /* 1030 */ 19, 19, 19, 19, 1627, 576, 911, 1169, 47, 120, - /* 1040 */ 1169, 117, 413, 306, 498, 438, 1125, 206, 336, 19, - /* 1050 */ 19, 1435, 49, 449, 449, 449, 1368, 315, 81, 81, - /* 1060 */ 576, 304, 413, 1570, 207, 377, 137, 138, 91, 115, - /* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 1080 */ 136, 136, 576, 82, 82, 1207, 137, 138, 91, 1340, - /* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, - /* 1100 */ 136, 136, 1569, 386, 377, 82, 82, 463, 1126, 1552, - /* 1110 */ 333, 463, 335, 131, 128, 451, 1569, 161, 377, 16, - /* 1120 */ 317, 387, 428, 1127, 448, 447, 134, 134, 134, 134, - /* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576, - /* 1140 */ 1105, 10, 445, 267, 576, 1554, 134, 134, 134, 134, - /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 532, 576, - /* 1160 */ 922, 576, 19, 19, 576, 1573, 576, 147, 147, 7, - /* 1170 */ 923, 1236, 498, 1236, 576, 487, 413, 552, 285, 1224, - /* 1180 */ 969, 215, 82, 82, 66, 66, 1435, 67, 67, 21, - /* 1190 */ 21, 1110, 1110, 495, 334, 297, 413, 53, 53, 297, - /* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 1336, 1311, 446, - /* 1220 */ 137, 138, 91, 227, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1230 */ 135, 135, 136, 136, 136, 136, 574, 1224, 936, 936, - /* 1240 */ 137, 126, 91, 141, 1228, 1228, 1063, 1066, 1053, 1053, - /* 1250 */ 135, 135, 136, 136, 136, 136, 533, 429, 472, 346, - /* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1270 */ 128, 451, 576, 457, 233, 343, 1435, 403, 498, 1550, - /* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1290 */ 128, 451, 576, 324, 576, 82, 82, 487, 576, 969, - /* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131, - /* 1310 */ 128, 451, 288, 288, 546, 68, 68, 54, 54, 553, - /* 1320 */ 413, 69, 69, 351, 6, 573, 944, 562, 410, 409, - /* 1330 */ 1435, 943, 450, 545, 260, 259, 258, 576, 158, 576, - /* 1340 */ 413, 222, 1180, 479, 969, 138, 91, 430, 1228, 1228, - /* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 1360 */ 70, 70, 71, 71, 576, 1126, 91, 576, 1228, 1228, - /* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, - /* 1380 */ 1127, 166, 850, 851, 852, 1282, 419, 72, 72, 108, - /* 1390 */ 73, 73, 1310, 358, 1180, 1128, 576, 305, 576, 123, - /* 1400 */ 568, 494, 4, 488, 134, 134, 134, 134, 133, 133, - /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 55, - /* 1420 */ 55, 56, 56, 576, 134, 134, 134, 134, 133, 133, - /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 1104, 233, 1104, - /* 1440 */ 452, 1602, 582, 2, 1259, 576, 57, 57, 576, 321, - /* 1450 */ 576, 155, 565, 1435, 485, 353, 576, 356, 1341, 59, - /* 1460 */ 59, 576, 44, 969, 569, 419, 576, 238, 60, 60, - /* 1470 */ 261, 74, 74, 75, 75, 287, 231, 576, 1366, 76, - /* 1480 */ 76, 1040, 420, 184, 20, 20, 576, 121, 121, 77, - /* 1490 */ 77, 97, 218, 288, 288, 122, 125, 452, 577, 452, - /* 1500 */ 143, 143, 1028, 576, 520, 576, 573, 576, 562, 144, - /* 1510 */ 144, 474, 227, 1244, 478, 123, 568, 576, 4, 320, - /* 1520 */ 567, 245, 411, 576, 443, 411, 78, 78, 62, 62, - /* 1530 */ 79, 79, 571, 319, 1028, 1028, 1030, 1031, 35, 418, - /* 1540 */ 63, 63, 576, 290, 411, 9, 80, 80, 1144, 576, - /* 1550 */ 400, 576, 486, 455, 576, 1223, 452, 576, 325, 342, - /* 1560 */ 576, 111, 576, 1188, 242, 64, 64, 473, 565, 576, - /* 1570 */ 23, 576, 170, 170, 171, 171, 576, 87, 87, 328, - /* 1580 */ 65, 65, 542, 83, 83, 146, 146, 541, 123, 568, - /* 1590 */ 341, 4, 84, 84, 168, 168, 576, 1040, 576, 148, - /* 1600 */ 148, 576, 1380, 121, 121, 571, 1021, 576, 266, 576, - /* 1610 */ 424, 122, 576, 452, 577, 452, 576, 553, 1028, 142, - /* 1620 */ 142, 169, 169, 576, 162, 162, 528, 889, 371, 452, - /* 1630 */ 152, 152, 151, 151, 1379, 149, 149, 109, 370, 150, - /* 1640 */ 150, 565, 576, 480, 576, 266, 86, 86, 576, 1092, - /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 482, 576, 266, 466, - /* 1660 */ 543, 123, 568, 1616, 4, 88, 88, 85, 85, 475, - /* 1670 */ 1040, 52, 52, 222, 901, 900, 121, 121, 571, 1188, - /* 1680 */ 58, 58, 244, 1032, 122, 889, 452, 577, 452, 908, - /* 1690 */ 909, 1028, 300, 347, 504, 111, 263, 361, 165, 111, - /* 1700 */ 111, 1088, 452, 263, 974, 1153, 266, 1092, 986, 987, - /* 1710 */ 942, 939, 125, 125, 565, 1103, 872, 1103, 159, 941, - /* 1720 */ 1309, 125, 1557, 1028, 1028, 1030, 1031, 35, 542, 337, - /* 1730 */ 1530, 205, 1529, 541, 499, 1589, 490, 348, 1376, 352, - /* 1740 */ 355, 1032, 357, 1040, 359, 1324, 1308, 366, 563, 121, - /* 1750 */ 121, 376, 1188, 1389, 1434, 1362, 280, 122, 1374, 452, - /* 1760 */ 577, 452, 167, 1439, 1028, 1289, 1280, 1268, 1267, 1269, - /* 1770 */ 1609, 1359, 312, 313, 314, 397, 12, 237, 224, 1421, - /* 1780 */ 295, 1416, 1409, 1426, 339, 484, 340, 509, 1371, 1612, - /* 1790 */ 1372, 1425, 1244, 404, 301, 228, 1028, 1028, 1030, 1031, - /* 1800 */ 35, 1601, 1192, 454, 345, 1307, 292, 369, 1502, 1501, - /* 1810 */ 270, 396, 396, 395, 277, 393, 1370, 1369, 859, 1549, - /* 1820 */ 186, 123, 568, 235, 4, 1188, 391, 210, 211, 223, - /* 1830 */ 1547, 239, 1241, 327, 422, 96, 220, 195, 571, 180, - /* 1840 */ 188, 326, 468, 469, 190, 191, 502, 192, 193, 566, - /* 1850 */ 247, 109, 1430, 491, 199, 251, 102, 281, 402, 476, - /* 1860 */ 405, 1496, 452, 497, 253, 1422, 13, 1428, 14, 1427, - /* 1870 */ 203, 1507, 241, 500, 565, 354, 407, 92, 95, 1270, - /* 1880 */ 175, 254, 518, 43, 1327, 255, 1326, 1325, 436, 1518, - /* 1890 */ 350, 1318, 104, 229, 893, 1626, 440, 441, 1625, 408, - /* 1900 */ 240, 1296, 268, 1040, 310, 269, 1297, 527, 444, 121, - /* 1910 */ 121, 368, 1295, 1594, 1624, 311, 1394, 122, 1317, 452, - /* 1920 */ 577, 452, 374, 1580, 1028, 1393, 140, 553, 11, 90, - /* 1930 */ 568, 385, 4, 116, 318, 414, 1579, 110, 1483, 537, - /* 1940 */ 320, 567, 1350, 555, 42, 579, 571, 1349, 1198, 383, - /* 1950 */ 276, 390, 216, 389, 278, 279, 1028, 1028, 1030, 1031, - /* 1960 */ 35, 172, 580, 1265, 458, 1260, 415, 416, 185, 156, - /* 1970 */ 452, 1534, 1535, 173, 1533, 1532, 89, 308, 225, 226, - /* 1980 */ 846, 174, 565, 453, 217, 1188, 322, 236, 1102, 154, - /* 1990 */ 1100, 330, 187, 176, 1223, 243, 189, 925, 338, 246, - /* 2000 */ 1116, 194, 177, 425, 178, 427, 98, 196, 99, 100, - /* 2010 */ 101, 1040, 179, 1119, 1115, 248, 249, 121, 121, 163, - /* 2020 */ 24, 250, 349, 1238, 496, 122, 1108, 452, 577, 452, - /* 2030 */ 1192, 454, 1028, 266, 292, 200, 252, 201, 861, 396, - /* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256, - /* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239, - /* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326, - /* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309, - /* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155, - /* 2090 */ 17, 1154, 230, 1188, 284, 286, 265, 204, 125, 1171, - /* 2100 */ 241, 28, 978, 972, 29, 41, 1175, 1179, 175, 1173, - /* 2110 */ 30, 43, 31, 8, 241, 1178, 32, 1160, 208, 549, - /* 2120 */ 33, 111, 175, 1083, 1070, 43, 1068, 1072, 240, 113, - /* 2130 */ 114, 34, 561, 118, 1124, 271, 1073, 36, 18, 572, - /* 2140 */ 1033, 873, 240, 124, 37, 935, 272, 273, 1617, 183, - /* 2150 */ 153, 394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256, - /* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567, - /* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256, - /* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256, - /* 2190 */ 1256, 1256, 458, 1256, 1256, 1256, 1256, 1256, 1256, 1256, - /* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458, + /* 0 */ 134, 131, 238, 290, 290, 1353, 593, 1332, 478, 1606, + /* 10 */ 593, 1315, 593, 7, 593, 1353, 590, 593, 579, 424, + /* 20 */ 1566, 134, 131, 238, 1318, 541, 478, 477, 575, 84, + /* 30 */ 84, 1005, 303, 84, 84, 51, 51, 63, 63, 1006, + /* 40 */ 84, 84, 498, 141, 142, 93, 442, 1254, 1254, 1085, + /* 50 */ 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, 424, + /* 60 */ 296, 296, 498, 296, 296, 567, 553, 296, 296, 1306, + /* 70 */ 574, 1358, 1358, 590, 542, 579, 590, 574, 579, 548, + /* 80 */ 590, 1304, 579, 141, 142, 93, 576, 1254, 1254, 1085, + /* 90 */ 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, 399, + /* 100 */ 478, 395, 6, 138, 138, 138, 138, 137, 137, 136, + /* 110 */ 136, 136, 135, 132, 463, 44, 342, 593, 305, 1127, + /* 120 */ 1280, 1, 1, 599, 2, 1284, 598, 1200, 1284, 1200, + /* 130 */ 330, 424, 158, 330, 1613, 158, 390, 116, 308, 1366, + /* 140 */ 51, 51, 1366, 138, 138, 138, 138, 137, 137, 136, + /* 150 */ 136, 136, 135, 132, 463, 141, 142, 93, 515, 1254, + /* 160 */ 1254, 1085, 1088, 1075, 1075, 139, 139, 140, 140, 140, + /* 170 */ 140, 1230, 329, 584, 296, 296, 212, 296, 296, 568, + /* 180 */ 568, 488, 143, 1072, 1072, 1086, 1089, 590, 1195, 579, + /* 190 */ 590, 340, 579, 140, 140, 140, 140, 133, 392, 564, + /* 200 */ 536, 1195, 250, 425, 1195, 250, 137, 137, 136, 136, + /* 210 */ 136, 135, 132, 463, 291, 138, 138, 138, 138, 137, + /* 220 */ 137, 136, 136, 136, 135, 132, 463, 966, 1230, 1231, + /* 230 */ 1230, 412, 965, 467, 412, 424, 467, 489, 357, 1611, + /* 240 */ 391, 138, 138, 138, 138, 137, 137, 136, 136, 136, + /* 250 */ 135, 132, 463, 463, 134, 131, 238, 555, 1076, 141, + /* 260 */ 142, 93, 593, 1254, 1254, 1085, 1088, 1075, 1075, 139, + /* 270 */ 139, 140, 140, 140, 140, 1317, 134, 131, 238, 424, + /* 280 */ 549, 1597, 1531, 333, 97, 83, 83, 140, 140, 140, + /* 290 */ 140, 138, 138, 138, 138, 137, 137, 136, 136, 136, + /* 300 */ 135, 132, 463, 141, 142, 93, 1657, 1254, 1254, 1085, + /* 310 */ 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, 138, + /* 320 */ 138, 138, 138, 137, 137, 136, 136, 136, 135, 132, + /* 330 */ 463, 591, 1230, 958, 958, 138, 138, 138, 138, 137, + /* 340 */ 137, 136, 136, 136, 135, 132, 463, 44, 398, 547, + /* 350 */ 1306, 136, 136, 136, 135, 132, 463, 386, 593, 442, + /* 360 */ 595, 145, 595, 138, 138, 138, 138, 137, 137, 136, + /* 370 */ 136, 136, 135, 132, 463, 500, 1230, 112, 550, 460, + /* 380 */ 459, 51, 51, 424, 296, 296, 479, 334, 1259, 1230, + /* 390 */ 1231, 1230, 1599, 1261, 388, 312, 444, 590, 246, 579, + /* 400 */ 546, 1260, 271, 235, 329, 584, 551, 141, 142, 93, + /* 410 */ 429, 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, 140, + /* 420 */ 140, 140, 140, 22, 22, 1230, 1262, 424, 1262, 216, + /* 430 */ 296, 296, 98, 1230, 1231, 1230, 264, 884, 45, 528, + /* 440 */ 525, 524, 1041, 590, 1269, 579, 421, 420, 393, 523, + /* 450 */ 44, 141, 142, 93, 498, 1254, 1254, 1085, 1088, 1075, + /* 460 */ 1075, 139, 139, 140, 140, 140, 140, 138, 138, 138, + /* 470 */ 138, 137, 137, 136, 136, 136, 135, 132, 463, 593, + /* 480 */ 1611, 561, 1230, 1231, 1230, 23, 264, 515, 200, 528, + /* 490 */ 525, 524, 127, 585, 509, 4, 355, 487, 506, 523, + /* 500 */ 593, 498, 84, 84, 134, 131, 238, 329, 584, 588, + /* 510 */ 1627, 138, 138, 138, 138, 137, 137, 136, 136, 136, + /* 520 */ 135, 132, 463, 19, 19, 435, 1230, 1460, 297, 297, + /* 530 */ 311, 424, 1565, 464, 1631, 599, 2, 1284, 437, 574, + /* 540 */ 1107, 590, 330, 579, 158, 582, 489, 357, 573, 593, + /* 550 */ 592, 1366, 409, 1274, 1230, 141, 142, 93, 1364, 1254, + /* 560 */ 1254, 1085, 1088, 1075, 1075, 139, 139, 140, 140, 140, + /* 570 */ 140, 389, 84, 84, 1062, 567, 1230, 313, 1523, 593, + /* 580 */ 125, 125, 970, 1230, 1231, 1230, 296, 296, 126, 46, + /* 590 */ 464, 594, 464, 296, 296, 1050, 1230, 218, 439, 590, + /* 600 */ 1604, 579, 84, 84, 7, 403, 590, 515, 579, 325, + /* 610 */ 417, 1230, 1231, 1230, 250, 138, 138, 138, 138, 137, + /* 620 */ 137, 136, 136, 136, 135, 132, 463, 1050, 1050, 1052, + /* 630 */ 1053, 35, 1275, 1230, 1231, 1230, 424, 1370, 993, 574, + /* 640 */ 371, 414, 274, 412, 1597, 467, 1302, 552, 451, 590, + /* 650 */ 543, 579, 1530, 1230, 1231, 1230, 1214, 201, 409, 1174, + /* 660 */ 141, 142, 93, 223, 1254, 1254, 1085, 1088, 1075, 1075, + /* 670 */ 139, 139, 140, 140, 140, 140, 296, 296, 1250, 593, + /* 680 */ 424, 296, 296, 236, 529, 296, 296, 515, 100, 590, + /* 690 */ 1600, 579, 48, 1605, 590, 1230, 579, 7, 590, 577, + /* 700 */ 579, 904, 84, 84, 141, 142, 93, 496, 1254, 1254, + /* 710 */ 1085, 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, + /* 720 */ 138, 138, 138, 138, 137, 137, 136, 136, 136, 135, + /* 730 */ 132, 463, 1365, 1230, 296, 296, 1250, 115, 1275, 326, + /* 740 */ 233, 539, 1062, 40, 282, 127, 585, 590, 4, 579, + /* 750 */ 329, 584, 1230, 1231, 1230, 1598, 593, 388, 904, 1051, + /* 760 */ 1356, 1356, 588, 1050, 138, 138, 138, 138, 137, 137, + /* 770 */ 136, 136, 136, 135, 132, 463, 185, 593, 1230, 19, + /* 780 */ 19, 1230, 971, 1597, 424, 1651, 464, 129, 908, 1195, + /* 790 */ 1230, 1231, 1230, 1325, 443, 1050, 1050, 1052, 582, 1603, + /* 800 */ 149, 149, 1195, 7, 5, 1195, 1687, 410, 141, 142, + /* 810 */ 93, 1536, 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, + /* 820 */ 140, 140, 140, 140, 1214, 397, 593, 1062, 424, 1536, + /* 830 */ 1538, 50, 901, 125, 125, 1230, 1231, 1230, 1230, 1231, + /* 840 */ 1230, 126, 1230, 464, 594, 464, 515, 1230, 1050, 84, + /* 850 */ 84, 3, 141, 142, 93, 924, 1254, 1254, 1085, 1088, + /* 860 */ 1075, 1075, 139, 139, 140, 140, 140, 140, 138, 138, + /* 870 */ 138, 138, 137, 137, 136, 136, 136, 135, 132, 463, + /* 880 */ 1050, 1050, 1052, 1053, 35, 442, 457, 532, 433, 1230, + /* 890 */ 1062, 1361, 540, 540, 1598, 925, 388, 7, 1129, 1230, + /* 900 */ 1231, 1230, 1129, 1536, 1230, 1231, 1230, 1051, 570, 1214, + /* 910 */ 593, 1050, 138, 138, 138, 138, 137, 137, 136, 136, + /* 920 */ 136, 135, 132, 463, 6, 185, 1195, 1230, 231, 593, + /* 930 */ 382, 992, 424, 151, 151, 510, 1213, 557, 482, 1195, + /* 940 */ 381, 160, 1195, 1050, 1050, 1052, 1230, 1231, 1230, 422, + /* 950 */ 593, 447, 84, 84, 593, 217, 141, 142, 93, 593, + /* 960 */ 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, 140, 140, + /* 970 */ 140, 140, 1214, 19, 19, 593, 424, 19, 19, 442, + /* 980 */ 1063, 442, 19, 19, 1230, 1231, 1230, 515, 445, 458, + /* 990 */ 1597, 386, 315, 1175, 1685, 556, 1685, 450, 84, 84, + /* 1000 */ 141, 142, 93, 505, 1254, 1254, 1085, 1088, 1075, 1075, + /* 1010 */ 139, 139, 140, 140, 140, 140, 138, 138, 138, 138, + /* 1020 */ 137, 137, 136, 136, 136, 135, 132, 463, 442, 1147, + /* 1030 */ 454, 1597, 362, 1041, 593, 462, 1460, 1233, 47, 1393, + /* 1040 */ 324, 565, 565, 115, 1148, 449, 7, 460, 459, 307, + /* 1050 */ 375, 354, 593, 113, 593, 329, 584, 19, 19, 1149, + /* 1060 */ 138, 138, 138, 138, 137, 137, 136, 136, 136, 135, + /* 1070 */ 132, 463, 209, 1173, 563, 19, 19, 19, 19, 49, + /* 1080 */ 424, 944, 1175, 1686, 1046, 1686, 218, 355, 484, 343, + /* 1090 */ 210, 945, 569, 562, 1262, 1233, 1262, 490, 314, 423, + /* 1100 */ 424, 1598, 1206, 388, 141, 142, 93, 440, 1254, 1254, + /* 1110 */ 1085, 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, + /* 1120 */ 352, 316, 531, 316, 141, 142, 93, 549, 1254, 1254, + /* 1130 */ 1085, 1088, 1075, 1075, 139, 139, 140, 140, 140, 140, + /* 1140 */ 446, 10, 1598, 274, 388, 915, 281, 299, 383, 534, + /* 1150 */ 378, 533, 269, 593, 1206, 587, 587, 587, 374, 293, + /* 1160 */ 1579, 991, 1173, 302, 138, 138, 138, 138, 137, 137, + /* 1170 */ 136, 136, 136, 135, 132, 463, 53, 53, 520, 1250, + /* 1180 */ 593, 1147, 1576, 431, 138, 138, 138, 138, 137, 137, + /* 1190 */ 136, 136, 136, 135, 132, 463, 1148, 301, 593, 1577, + /* 1200 */ 593, 1307, 431, 54, 54, 593, 268, 593, 461, 461, + /* 1210 */ 461, 1149, 347, 492, 424, 135, 132, 463, 1146, 1195, + /* 1220 */ 474, 68, 68, 69, 69, 550, 332, 287, 21, 21, + /* 1230 */ 55, 55, 1195, 581, 424, 1195, 309, 1250, 141, 142, + /* 1240 */ 93, 119, 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, + /* 1250 */ 140, 140, 140, 140, 593, 237, 480, 1476, 141, 142, + /* 1260 */ 93, 593, 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, + /* 1270 */ 140, 140, 140, 140, 344, 430, 346, 70, 70, 494, + /* 1280 */ 991, 1132, 1132, 512, 56, 56, 1269, 593, 268, 593, + /* 1290 */ 369, 374, 593, 481, 215, 384, 1624, 481, 138, 138, + /* 1300 */ 138, 138, 137, 137, 136, 136, 136, 135, 132, 463, + /* 1310 */ 71, 71, 72, 72, 225, 73, 73, 593, 138, 138, + /* 1320 */ 138, 138, 137, 137, 136, 136, 136, 135, 132, 463, + /* 1330 */ 586, 431, 593, 872, 873, 874, 593, 911, 593, 1602, + /* 1340 */ 74, 74, 593, 7, 1460, 242, 593, 306, 424, 1578, + /* 1350 */ 472, 306, 364, 219, 367, 75, 75, 430, 345, 57, + /* 1360 */ 57, 58, 58, 432, 187, 59, 59, 593, 424, 61, + /* 1370 */ 61, 1475, 141, 142, 93, 123, 1254, 1254, 1085, 1088, + /* 1380 */ 1075, 1075, 139, 139, 140, 140, 140, 140, 424, 570, + /* 1390 */ 62, 62, 141, 142, 93, 911, 1254, 1254, 1085, 1088, + /* 1400 */ 1075, 1075, 139, 139, 140, 140, 140, 140, 161, 384, + /* 1410 */ 1624, 1474, 141, 130, 93, 441, 1254, 1254, 1085, 1088, + /* 1420 */ 1075, 1075, 139, 139, 140, 140, 140, 140, 267, 266, + /* 1430 */ 265, 1460, 138, 138, 138, 138, 137, 137, 136, 136, + /* 1440 */ 136, 135, 132, 463, 593, 1336, 593, 1269, 1460, 384, + /* 1450 */ 1624, 231, 138, 138, 138, 138, 137, 137, 136, 136, + /* 1460 */ 136, 135, 132, 463, 593, 163, 593, 76, 76, 77, + /* 1470 */ 77, 593, 138, 138, 138, 138, 137, 137, 136, 136, + /* 1480 */ 136, 135, 132, 463, 475, 593, 483, 78, 78, 20, + /* 1490 */ 20, 1249, 424, 491, 79, 79, 495, 422, 295, 235, + /* 1500 */ 1574, 38, 511, 896, 422, 335, 240, 422, 147, 147, + /* 1510 */ 112, 593, 424, 593, 101, 222, 991, 142, 93, 455, + /* 1520 */ 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, 140, 140, + /* 1530 */ 140, 140, 593, 39, 148, 148, 80, 80, 93, 551, + /* 1540 */ 1254, 1254, 1085, 1088, 1075, 1075, 139, 139, 140, 140, + /* 1550 */ 140, 140, 328, 923, 922, 64, 64, 502, 1656, 1005, + /* 1560 */ 933, 896, 124, 422, 121, 254, 593, 1006, 593, 226, + /* 1570 */ 593, 127, 585, 164, 4, 16, 138, 138, 138, 138, + /* 1580 */ 137, 137, 136, 136, 136, 135, 132, 463, 588, 81, + /* 1590 */ 81, 65, 65, 82, 82, 593, 138, 138, 138, 138, + /* 1600 */ 137, 137, 136, 136, 136, 135, 132, 463, 593, 226, + /* 1610 */ 237, 966, 464, 593, 298, 593, 965, 593, 66, 66, + /* 1620 */ 593, 1170, 593, 411, 582, 353, 469, 115, 593, 471, + /* 1630 */ 169, 173, 173, 593, 44, 991, 174, 174, 89, 89, + /* 1640 */ 67, 67, 593, 85, 85, 150, 150, 1114, 1043, 593, + /* 1650 */ 273, 86, 86, 1062, 593, 503, 171, 171, 593, 125, + /* 1660 */ 125, 497, 593, 273, 336, 152, 152, 126, 1335, 464, + /* 1670 */ 594, 464, 146, 146, 1050, 593, 545, 172, 172, 593, + /* 1680 */ 1054, 165, 165, 256, 339, 156, 156, 127, 585, 1586, + /* 1690 */ 4, 329, 584, 499, 358, 273, 115, 348, 155, 155, + /* 1700 */ 930, 931, 153, 153, 588, 1114, 1050, 1050, 1052, 1053, + /* 1710 */ 35, 1554, 521, 593, 270, 1008, 1009, 9, 593, 372, + /* 1720 */ 593, 115, 593, 168, 593, 115, 593, 1110, 464, 270, + /* 1730 */ 996, 964, 273, 129, 1645, 1214, 154, 154, 1054, 1404, + /* 1740 */ 582, 88, 88, 90, 90, 87, 87, 52, 52, 60, + /* 1750 */ 60, 1405, 504, 537, 559, 1179, 961, 507, 129, 558, + /* 1760 */ 127, 585, 1126, 4, 1126, 1125, 894, 1125, 162, 1062, + /* 1770 */ 963, 359, 129, 1401, 363, 125, 125, 588, 366, 368, + /* 1780 */ 370, 1349, 1334, 126, 1333, 464, 594, 464, 377, 387, + /* 1790 */ 1050, 1391, 1414, 1618, 1459, 1387, 1399, 208, 580, 1464, + /* 1800 */ 1314, 464, 243, 516, 1305, 1293, 1384, 1292, 1294, 1638, + /* 1810 */ 288, 170, 228, 582, 12, 408, 321, 322, 241, 323, + /* 1820 */ 245, 1446, 1050, 1050, 1052, 1053, 35, 559, 304, 350, + /* 1830 */ 351, 501, 560, 127, 585, 1441, 4, 1451, 1434, 310, + /* 1840 */ 1450, 526, 1062, 1332, 415, 380, 232, 1527, 125, 125, + /* 1850 */ 588, 1214, 1396, 356, 1526, 583, 126, 1397, 464, 594, + /* 1860 */ 464, 1641, 535, 1050, 1581, 1395, 1269, 1583, 1582, 213, + /* 1870 */ 402, 277, 214, 227, 464, 1573, 239, 1571, 1266, 1394, + /* 1880 */ 434, 198, 100, 224, 96, 183, 582, 191, 485, 193, + /* 1890 */ 486, 194, 195, 196, 519, 1050, 1050, 1052, 1053, 35, + /* 1900 */ 559, 113, 252, 413, 1447, 558, 493, 13, 1455, 416, + /* 1910 */ 1453, 1452, 14, 202, 1521, 1062, 1532, 508, 258, 106, + /* 1920 */ 514, 125, 125, 99, 1214, 1543, 289, 260, 206, 126, + /* 1930 */ 365, 464, 594, 464, 361, 517, 1050, 261, 448, 1295, + /* 1940 */ 262, 418, 1352, 1351, 108, 1350, 1655, 1654, 1343, 915, + /* 1950 */ 419, 1322, 233, 452, 319, 379, 1321, 453, 1623, 320, + /* 1960 */ 1320, 275, 1653, 544, 276, 1609, 1608, 1342, 1050, 1050, + /* 1970 */ 1052, 1053, 35, 1630, 1218, 466, 385, 456, 300, 1419, + /* 1980 */ 144, 1418, 570, 407, 407, 406, 284, 404, 11, 1508, + /* 1990 */ 881, 396, 120, 127, 585, 394, 4, 1214, 327, 114, + /* 2000 */ 1375, 1374, 220, 247, 400, 338, 401, 554, 42, 1224, + /* 2010 */ 588, 596, 283, 337, 285, 286, 188, 597, 1290, 1285, + /* 2020 */ 175, 1558, 176, 1559, 1557, 1556, 159, 317, 229, 177, + /* 2030 */ 868, 230, 91, 465, 464, 221, 331, 468, 1165, 470, + /* 2040 */ 473, 94, 244, 95, 249, 189, 582, 1124, 1122, 341, + /* 2050 */ 427, 190, 178, 1249, 179, 43, 192, 947, 349, 428, + /* 2060 */ 1138, 197, 251, 180, 181, 436, 102, 182, 438, 103, + /* 2070 */ 104, 199, 248, 1140, 253, 1062, 105, 255, 1137, 166, + /* 2080 */ 24, 125, 125, 257, 1264, 273, 360, 513, 259, 126, + /* 2090 */ 15, 464, 594, 464, 204, 883, 1050, 518, 263, 373, + /* 2100 */ 381, 92, 585, 1130, 4, 203, 205, 426, 107, 522, + /* 2110 */ 25, 26, 329, 584, 913, 572, 527, 376, 588, 926, + /* 2120 */ 530, 109, 184, 318, 167, 110, 27, 538, 1050, 1050, + /* 2130 */ 1052, 1053, 35, 1211, 1091, 17, 476, 111, 1181, 234, + /* 2140 */ 292, 1180, 464, 294, 207, 994, 129, 1201, 272, 1000, + /* 2150 */ 28, 1197, 29, 30, 582, 1199, 1205, 1214, 31, 1204, + /* 2160 */ 32, 1186, 41, 566, 33, 1105, 211, 8, 115, 1092, + /* 2170 */ 1090, 1094, 34, 278, 578, 1095, 117, 122, 118, 1145, + /* 2180 */ 36, 18, 128, 1062, 1055, 895, 957, 37, 589, 125, + /* 2190 */ 125, 279, 186, 280, 1646, 157, 405, 126, 1220, 464, + /* 2200 */ 594, 464, 1218, 466, 1050, 1219, 300, 1281, 1281, 1281, + /* 2210 */ 1281, 407, 407, 406, 284, 404, 1281, 1281, 881, 1281, + /* 2220 */ 300, 1281, 1281, 571, 1281, 407, 407, 406, 284, 404, + /* 2230 */ 1281, 247, 881, 338, 1281, 1281, 1050, 1050, 1052, 1053, + /* 2240 */ 35, 337, 1281, 1281, 1281, 247, 1281, 338, 1281, 1281, + /* 2250 */ 1281, 1281, 1281, 1281, 1281, 337, 1281, 1281, 1281, 1281, + /* 2260 */ 1281, 1281, 1281, 1281, 1281, 1214, 1281, 1281, 1281, 1281, + /* 2270 */ 1281, 1281, 249, 1281, 1281, 1281, 1281, 1281, 1281, 1281, + /* 2280 */ 178, 1281, 1281, 43, 1281, 1281, 249, 1281, 1281, 1281, + /* 2290 */ 1281, 1281, 1281, 1281, 178, 1281, 1281, 43, 1281, 1281, + /* 2300 */ 248, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, + /* 2310 */ 1281, 1281, 1281, 1281, 248, 1281, 1281, 1281, 1281, 1281, + /* 2320 */ 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, + /* 2330 */ 1281, 1281, 1281, 1281, 1281, 426, 1281, 1281, 1281, 1281, + /* 2340 */ 329, 584, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 426, + /* 2350 */ 1281, 1281, 1281, 1281, 329, 584, 1281, 1281, 1281, 1281, + /* 2360 */ 1281, 1281, 1281, 1281, 476, 1281, 1281, 1281, 1281, 1281, + /* 2370 */ 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 476, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 241, - /* 10 */ 242, 195, 217, 221, 195, 235, 254, 195, 256, 19, - /* 20 */ 225, 298, 254, 195, 256, 206, 213, 214, 206, 218, - /* 30 */ 219, 31, 206, 195, 218, 219, 195, 218, 219, 39, - /* 40 */ 218, 219, 313, 43, 44, 45, 317, 47, 48, 49, + /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 312, + /* 10 */ 195, 218, 195, 316, 195, 235, 254, 195, 256, 19, + /* 20 */ 297, 277, 278, 279, 218, 206, 213, 214, 206, 218, + /* 30 */ 219, 31, 206, 218, 219, 218, 219, 218, 219, 39, + /* 40 */ 218, 219, 195, 43, 44, 45, 195, 47, 48, 49, /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19, - /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 277, - /* 70 */ 278, 279, 234, 254, 255, 256, 254, 255, 256, 218, - /* 80 */ 254, 240, 256, 43, 44, 45, 264, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 271, - /* 100 */ 287, 22, 23, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 113, 114, 114, 47, 48, 49, 50, + /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 195, + /* 70 */ 255, 237, 238, 254, 255, 256, 254, 255, 256, 264, + /* 80 */ 254, 207, 256, 43, 44, 45, 264, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 251, + /* 100 */ 287, 253, 215, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 114, 82, 265, 195, 271, 11, /* 120 */ 187, 188, 189, 190, 191, 192, 190, 87, 192, 89, - /* 130 */ 197, 19, 199, 197, 318, 199, 320, 25, 195, 206, - /* 140 */ 299, 271, 206, 103, 104, 105, 106, 107, 108, 109, + /* 130 */ 197, 19, 199, 197, 317, 199, 319, 25, 271, 206, + /* 140 */ 218, 219, 206, 103, 104, 105, 106, 107, 108, 109, /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 195, 47, /* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 170 */ 58, 60, 21, 195, 241, 242, 215, 241, 242, 312, - /* 180 */ 313, 102, 70, 205, 317, 207, 242, 254, 77, 256, - /* 190 */ 254, 122, 256, 55, 56, 57, 58, 59, 254, 88, - /* 200 */ 256, 90, 269, 240, 93, 269, 107, 108, 109, 110, - /* 210 */ 111, 112, 113, 114, 271, 103, 104, 105, 106, 107, - /* 220 */ 108, 109, 110, 111, 112, 113, 114, 313, 117, 118, - /* 230 */ 119, 317, 81, 195, 301, 19, 195, 301, 277, 278, - /* 240 */ 279, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 195, 43, - /* 260 */ 44, 45, 74, 47, 48, 49, 50, 51, 52, 53, - /* 270 */ 54, 55, 56, 57, 58, 124, 195, 60, 109, 110, - /* 280 */ 111, 112, 113, 114, 68, 195, 103, 104, 105, 106, - /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 208, 218, - /* 300 */ 219, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 310 */ 112, 113, 114, 162, 233, 24, 128, 129, 130, 103, + /* 170 */ 58, 60, 139, 140, 241, 242, 289, 241, 242, 309, + /* 180 */ 310, 294, 70, 47, 48, 49, 50, 254, 77, 256, + /* 190 */ 254, 195, 256, 55, 56, 57, 58, 59, 221, 88, + /* 200 */ 109, 90, 269, 240, 93, 269, 107, 108, 109, 110, + /* 210 */ 111, 112, 113, 114, 215, 103, 104, 105, 106, 107, + /* 220 */ 108, 109, 110, 111, 112, 113, 114, 136, 117, 118, + /* 230 */ 119, 298, 141, 300, 298, 19, 300, 129, 130, 317, + /* 240 */ 318, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 250 */ 112, 113, 114, 114, 277, 278, 279, 146, 122, 43, + /* 260 */ 44, 45, 195, 47, 48, 49, 50, 51, 52, 53, + /* 270 */ 54, 55, 56, 57, 58, 218, 277, 278, 279, 19, + /* 280 */ 19, 195, 286, 23, 68, 218, 219, 55, 56, 57, + /* 290 */ 58, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 300 */ 112, 113, 114, 43, 44, 45, 232, 47, 48, 49, + /* 310 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 103, /* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 330 */ 114, 195, 195, 215, 117, 118, 119, 120, 195, 19, - /* 340 */ 123, 124, 125, 207, 24, 74, 246, 60, 310, 311, - /* 350 */ 133, 60, 311, 82, 22, 218, 219, 257, 195, 19, - /* 360 */ 73, 218, 219, 43, 44, 45, 206, 47, 48, 49, - /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 22, - /* 380 */ 23, 218, 219, 43, 44, 45, 54, 47, 48, 49, - /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 128, - /* 400 */ 82, 241, 242, 195, 117, 118, 119, 289, 60, 118, - /* 410 */ 139, 140, 294, 195, 254, 195, 256, 195, 255, 259, - /* 420 */ 260, 73, 22, 103, 104, 105, 106, 107, 108, 109, - /* 430 */ 110, 111, 112, 113, 114, 206, 218, 219, 218, 219, - /* 440 */ 218, 219, 234, 103, 104, 105, 106, 107, 108, 109, - /* 450 */ 110, 111, 112, 113, 114, 318, 319, 139, 140, 102, - /* 460 */ 60, 318, 319, 221, 19, 117, 118, 119, 23, 195, - /* 470 */ 241, 242, 313, 255, 206, 255, 317, 255, 206, 129, - /* 480 */ 130, 206, 264, 254, 264, 256, 264, 195, 43, 44, - /* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54, - /* 500 */ 55, 56, 57, 58, 246, 213, 214, 19, 19, 241, - /* 510 */ 242, 195, 23, 241, 242, 257, 241, 242, 118, 277, - /* 520 */ 278, 279, 254, 29, 256, 60, 254, 33, 256, 254, - /* 530 */ 206, 256, 43, 44, 45, 218, 47, 48, 49, 50, - /* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, - /* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - /* 560 */ 66, 19, 218, 60, 120, 241, 242, 123, 124, 125, - /* 570 */ 60, 232, 77, 19, 20, 26, 22, 133, 254, 287, - /* 580 */ 256, 265, 117, 118, 119, 90, 312, 313, 93, 47, - /* 590 */ 36, 317, 103, 104, 105, 106, 107, 108, 109, 110, - /* 600 */ 111, 112, 113, 114, 116, 117, 277, 278, 279, 60, - /* 610 */ 107, 108, 19, 276, 60, 31, 23, 152, 195, 116, - /* 620 */ 117, 118, 119, 39, 121, 276, 72, 117, 118, 119, - /* 630 */ 166, 167, 129, 145, 237, 238, 43, 44, 45, 276, - /* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 650 */ 57, 58, 315, 316, 144, 101, 19, 154, 116, 156, - /* 660 */ 23, 107, 108, 109, 315, 316, 117, 118, 119, 115, - /* 670 */ 60, 117, 118, 119, 132, 200, 122, 60, 315, 316, - /* 680 */ 43, 44, 45, 272, 47, 48, 49, 50, 51, 52, - /* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, - /* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155, - /* 710 */ 156, 157, 158, 212, 213, 214, 22, 195, 101, 22, - /* 720 */ 60, 19, 20, 60, 22, 139, 140, 117, 118, 119, - /* 730 */ 22, 251, 195, 253, 117, 118, 195, 183, 36, 122, - /* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 750 */ 113, 114, 195, 195, 60, 218, 219, 60, 195, 284, - /* 760 */ 19, 25, 60, 288, 23, 237, 238, 22, 60, 109, - /* 770 */ 233, 154, 155, 156, 72, 218, 219, 117, 118, 119, - /* 780 */ 117, 118, 119, 116, 43, 44, 45, 265, 47, 48, - /* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 800 */ 183, 243, 25, 101, 19, 60, 265, 144, 23, 107, - /* 810 */ 108, 117, 118, 119, 117, 118, 119, 115, 151, 117, - /* 820 */ 118, 119, 82, 195, 122, 117, 118, 119, 43, 44, - /* 830 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, - /* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108, - /* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157, - /* 860 */ 158, 121, 117, 118, 119, 307, 101, 309, 195, 22, - /* 870 */ 23, 195, 25, 19, 35, 139, 140, 195, 24, 139, - /* 880 */ 140, 208, 195, 118, 109, 183, 22, 122, 103, 104, - /* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, - /* 900 */ 304, 305, 77, 230, 127, 232, 67, 195, 19, 195, - /* 910 */ 195, 136, 23, 88, 75, 90, 141, 203, 93, 154, - /* 920 */ 155, 156, 208, 295, 60, 243, 22, 23, 19, 25, - /* 930 */ 218, 219, 43, 44, 45, 100, 47, 48, 49, 50, - /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 183, 102, - /* 950 */ 96, 195, 43, 44, 45, 240, 47, 48, 49, 50, - /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 134, - /* 970 */ 131, 146, 25, 286, 120, 121, 122, 123, 124, 125, - /* 980 */ 126, 117, 118, 119, 313, 195, 132, 195, 317, 307, - /* 990 */ 195, 309, 103, 104, 105, 106, 107, 108, 109, 110, - /* 1000 */ 111, 112, 113, 114, 195, 195, 102, 195, 195, 195, - /* 1010 */ 218, 219, 103, 104, 105, 106, 107, 108, 109, 110, - /* 1020 */ 111, 112, 113, 114, 77, 233, 195, 60, 218, 219, - /* 1030 */ 218, 219, 218, 219, 23, 195, 25, 90, 243, 159, - /* 1040 */ 93, 161, 19, 233, 195, 233, 23, 233, 16, 218, - /* 1050 */ 219, 195, 243, 212, 213, 214, 262, 263, 218, 219, - /* 1060 */ 195, 271, 19, 307, 233, 309, 43, 44, 45, 160, - /* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1080 */ 57, 58, 195, 218, 219, 118, 43, 44, 45, 240, - /* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1100 */ 57, 58, 307, 195, 309, 218, 219, 263, 12, 195, - /* 1110 */ 78, 267, 80, 112, 113, 114, 307, 22, 309, 24, - /* 1120 */ 255, 281, 266, 27, 107, 108, 103, 104, 105, 106, - /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 195, - /* 1140 */ 11, 22, 255, 24, 195, 195, 103, 104, 105, 106, - /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 19, 195, - /* 1160 */ 64, 195, 218, 219, 195, 313, 195, 218, 219, 317, - /* 1170 */ 74, 154, 195, 156, 195, 195, 19, 233, 23, 60, - /* 1180 */ 25, 24, 218, 219, 218, 219, 195, 218, 219, 218, - /* 1190 */ 219, 128, 129, 130, 162, 263, 19, 218, 219, 267, - /* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52, - /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 240, 228, 255, - /* 1220 */ 43, 44, 45, 25, 47, 48, 49, 50, 51, 52, - /* 1230 */ 53, 54, 55, 56, 57, 58, 135, 118, 137, 138, - /* 1240 */ 43, 44, 45, 22, 47, 48, 49, 50, 51, 52, - /* 1250 */ 53, 54, 55, 56, 57, 58, 117, 266, 129, 130, - /* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1270 */ 113, 114, 195, 195, 119, 295, 195, 206, 195, 195, - /* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1290 */ 113, 114, 195, 195, 195, 218, 219, 195, 195, 144, - /* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 1310 */ 113, 114, 241, 242, 67, 218, 219, 218, 219, 146, - /* 1320 */ 19, 218, 219, 240, 215, 254, 136, 256, 107, 108, - /* 1330 */ 195, 141, 255, 86, 128, 129, 130, 195, 165, 195, - /* 1340 */ 19, 143, 95, 272, 25, 44, 45, 266, 47, 48, - /* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 1360 */ 218, 219, 218, 219, 195, 12, 45, 195, 47, 48, - /* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - /* 1380 */ 27, 23, 7, 8, 9, 210, 211, 218, 219, 116, - /* 1390 */ 218, 219, 228, 16, 147, 42, 195, 295, 195, 19, - /* 1400 */ 20, 266, 22, 294, 103, 104, 105, 106, 107, 108, - /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 218, - /* 1420 */ 219, 218, 219, 195, 103, 104, 105, 106, 107, 108, - /* 1430 */ 109, 110, 111, 112, 113, 114, 195, 154, 119, 156, - /* 1440 */ 60, 189, 190, 191, 192, 195, 218, 219, 195, 197, - /* 1450 */ 195, 199, 72, 195, 19, 78, 195, 80, 206, 218, - /* 1460 */ 219, 195, 82, 144, 210, 211, 195, 15, 218, 219, - /* 1470 */ 47, 218, 219, 218, 219, 259, 260, 195, 261, 218, - /* 1480 */ 219, 101, 302, 303, 218, 219, 195, 107, 108, 218, - /* 1490 */ 219, 150, 151, 241, 242, 115, 25, 117, 118, 119, - /* 1500 */ 218, 219, 122, 195, 146, 195, 254, 195, 256, 218, - /* 1510 */ 219, 246, 25, 61, 246, 19, 20, 195, 22, 139, - /* 1520 */ 140, 269, 257, 195, 266, 257, 218, 219, 218, 219, - /* 1530 */ 218, 219, 36, 246, 154, 155, 156, 157, 158, 116, - /* 1540 */ 218, 219, 195, 22, 257, 49, 218, 219, 23, 195, - /* 1550 */ 25, 195, 117, 301, 195, 25, 60, 195, 195, 23, - /* 1560 */ 195, 25, 195, 183, 24, 218, 219, 130, 72, 195, - /* 1570 */ 22, 195, 218, 219, 218, 219, 195, 218, 219, 195, - /* 1580 */ 218, 219, 86, 218, 219, 218, 219, 91, 19, 20, - /* 1590 */ 153, 22, 218, 219, 218, 219, 195, 101, 195, 218, - /* 1600 */ 219, 195, 195, 107, 108, 36, 23, 195, 25, 195, - /* 1610 */ 62, 115, 195, 117, 118, 119, 195, 146, 122, 218, - /* 1620 */ 219, 218, 219, 195, 218, 219, 19, 60, 122, 60, - /* 1630 */ 218, 219, 218, 219, 195, 218, 219, 150, 132, 218, - /* 1640 */ 219, 72, 195, 23, 195, 25, 218, 219, 195, 60, - /* 1650 */ 154, 155, 156, 157, 158, 86, 23, 195, 25, 195, - /* 1660 */ 91, 19, 20, 142, 22, 218, 219, 218, 219, 130, - /* 1670 */ 101, 218, 219, 143, 121, 122, 107, 108, 36, 183, - /* 1680 */ 218, 219, 142, 60, 115, 118, 117, 118, 119, 7, - /* 1690 */ 8, 122, 153, 23, 23, 25, 25, 23, 23, 25, - /* 1700 */ 25, 23, 60, 25, 23, 98, 25, 118, 84, 85, - /* 1710 */ 23, 23, 25, 25, 72, 154, 23, 156, 25, 23, - /* 1720 */ 228, 25, 195, 154, 155, 156, 157, 158, 86, 195, - /* 1730 */ 195, 258, 195, 91, 291, 322, 195, 195, 195, 195, - /* 1740 */ 195, 118, 195, 101, 195, 195, 195, 195, 238, 107, - /* 1750 */ 108, 195, 183, 195, 195, 195, 290, 115, 195, 117, - /* 1760 */ 118, 119, 244, 195, 122, 195, 195, 195, 195, 195, - /* 1770 */ 195, 258, 258, 258, 258, 193, 245, 300, 216, 274, - /* 1780 */ 247, 270, 270, 274, 296, 296, 248, 222, 262, 198, - /* 1790 */ 262, 274, 61, 274, 248, 231, 154, 155, 156, 157, - /* 1800 */ 158, 0, 1, 2, 247, 227, 5, 221, 221, 221, - /* 1810 */ 142, 10, 11, 12, 13, 14, 262, 262, 17, 202, - /* 1820 */ 300, 19, 20, 300, 22, 183, 247, 251, 251, 245, - /* 1830 */ 202, 30, 38, 32, 202, 152, 151, 22, 36, 43, - /* 1840 */ 236, 40, 18, 202, 239, 239, 18, 239, 239, 283, - /* 1850 */ 201, 150, 236, 202, 236, 201, 159, 202, 248, 248, - /* 1860 */ 248, 248, 60, 63, 201, 275, 273, 275, 273, 275, - /* 1870 */ 22, 286, 71, 223, 72, 202, 223, 297, 297, 202, - /* 1880 */ 79, 201, 116, 82, 220, 201, 220, 220, 65, 293, - /* 1890 */ 292, 229, 22, 166, 127, 226, 24, 114, 226, 223, - /* 1900 */ 99, 222, 202, 101, 285, 92, 220, 308, 83, 107, - /* 1910 */ 108, 220, 220, 316, 220, 285, 268, 115, 229, 117, - /* 1920 */ 118, 119, 223, 321, 122, 268, 149, 146, 22, 19, - /* 1930 */ 20, 202, 22, 159, 282, 134, 321, 148, 280, 147, - /* 1940 */ 139, 140, 252, 141, 25, 204, 36, 252, 13, 251, - /* 1950 */ 196, 248, 250, 249, 196, 6, 154, 155, 156, 157, - /* 1960 */ 158, 209, 194, 194, 163, 194, 306, 306, 303, 224, - /* 1970 */ 60, 215, 215, 209, 215, 215, 215, 224, 216, 216, - /* 1980 */ 4, 209, 72, 3, 22, 183, 164, 15, 23, 16, - /* 1990 */ 23, 140, 152, 131, 25, 24, 143, 20, 16, 145, - /* 2000 */ 1, 143, 131, 62, 131, 37, 54, 152, 54, 54, - /* 2010 */ 54, 101, 131, 117, 1, 34, 142, 107, 108, 5, - /* 2020 */ 22, 116, 162, 76, 41, 115, 69, 117, 118, 119, - /* 2030 */ 1, 2, 122, 25, 5, 69, 142, 116, 20, 10, - /* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126, - /* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30, - /* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40, - /* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68, - /* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23, - /* 2090 */ 22, 98, 142, 183, 23, 23, 34, 22, 25, 89, - /* 2100 */ 71, 34, 117, 144, 34, 22, 76, 76, 79, 87, - /* 2110 */ 34, 82, 34, 44, 71, 94, 34, 23, 25, 24, - /* 2120 */ 34, 25, 79, 23, 23, 82, 23, 23, 99, 143, - /* 2130 */ 143, 22, 25, 25, 23, 22, 11, 22, 22, 25, - /* 2140 */ 23, 23, 99, 22, 22, 136, 142, 142, 142, 25, - /* 2150 */ 23, 15, 1, 1, 323, 323, 323, 323, 323, 323, - /* 2160 */ 323, 323, 323, 134, 323, 323, 323, 323, 139, 140, - /* 2170 */ 323, 323, 323, 323, 323, 323, 323, 134, 323, 323, - /* 2180 */ 323, 323, 139, 140, 323, 323, 323, 323, 323, 323, - /* 2190 */ 323, 323, 163, 323, 323, 323, 323, 323, 323, 323, - /* 2200 */ 323, 323, 323, 323, 323, 323, 163, 323, 323, 323, - /* 2210 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2220 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2230 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2240 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2250 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2260 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2270 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2280 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2290 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2300 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2310 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2320 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2330 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - /* 2340 */ 323, 187, 187, 187, 187, 187, 187, 187, 187, 187, - /* 2350 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, - /* 2360 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, - /* 2370 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, - /* 2380 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, - /* 2390 */ 187, 187, 187, 187, + /* 330 */ 114, 135, 60, 137, 138, 103, 104, 105, 106, 107, + /* 340 */ 108, 109, 110, 111, 112, 113, 114, 82, 281, 206, + /* 350 */ 195, 109, 110, 111, 112, 113, 114, 195, 195, 195, + /* 360 */ 205, 22, 207, 103, 104, 105, 106, 107, 108, 109, + /* 370 */ 110, 111, 112, 113, 114, 195, 60, 116, 117, 107, + /* 380 */ 108, 218, 219, 19, 241, 242, 121, 23, 116, 117, + /* 390 */ 118, 119, 306, 121, 308, 206, 234, 254, 15, 256, + /* 400 */ 195, 129, 259, 260, 139, 140, 145, 43, 44, 45, + /* 410 */ 200, 47, 48, 49, 50, 51, 52, 53, 54, 55, + /* 420 */ 56, 57, 58, 218, 219, 60, 154, 19, 156, 265, + /* 430 */ 241, 242, 24, 117, 118, 119, 120, 21, 73, 123, + /* 440 */ 124, 125, 74, 254, 61, 256, 107, 108, 221, 133, + /* 450 */ 82, 43, 44, 45, 195, 47, 48, 49, 50, 51, + /* 460 */ 52, 53, 54, 55, 56, 57, 58, 103, 104, 105, + /* 470 */ 106, 107, 108, 109, 110, 111, 112, 113, 114, 195, + /* 480 */ 317, 318, 117, 118, 119, 22, 120, 195, 22, 123, + /* 490 */ 124, 125, 19, 20, 284, 22, 128, 81, 288, 133, + /* 500 */ 195, 195, 218, 219, 277, 278, 279, 139, 140, 36, + /* 510 */ 195, 103, 104, 105, 106, 107, 108, 109, 110, 111, + /* 520 */ 112, 113, 114, 218, 219, 62, 60, 195, 241, 242, + /* 530 */ 271, 19, 240, 60, 189, 190, 191, 192, 233, 255, + /* 540 */ 124, 254, 197, 256, 199, 72, 129, 130, 264, 195, + /* 550 */ 195, 206, 22, 23, 60, 43, 44, 45, 206, 47, + /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* 570 */ 58, 195, 218, 219, 101, 195, 60, 271, 162, 195, + /* 580 */ 107, 108, 109, 117, 118, 119, 241, 242, 115, 73, + /* 590 */ 117, 118, 119, 241, 242, 122, 60, 195, 266, 254, + /* 600 */ 312, 256, 218, 219, 316, 203, 254, 195, 256, 255, + /* 610 */ 208, 117, 118, 119, 269, 103, 104, 105, 106, 107, + /* 620 */ 108, 109, 110, 111, 112, 113, 114, 154, 155, 156, + /* 630 */ 157, 158, 102, 117, 118, 119, 19, 242, 144, 255, + /* 640 */ 23, 206, 24, 298, 195, 300, 206, 195, 264, 254, + /* 650 */ 206, 256, 240, 117, 118, 119, 183, 22, 22, 23, + /* 660 */ 43, 44, 45, 151, 47, 48, 49, 50, 51, 52, + /* 670 */ 53, 54, 55, 56, 57, 58, 241, 242, 60, 195, + /* 680 */ 19, 241, 242, 195, 23, 241, 242, 195, 152, 254, + /* 690 */ 310, 256, 243, 312, 254, 60, 256, 316, 254, 206, + /* 700 */ 256, 60, 218, 219, 43, 44, 45, 272, 47, 48, + /* 710 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 720 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 730 */ 113, 114, 240, 60, 241, 242, 118, 25, 102, 255, + /* 740 */ 166, 167, 101, 22, 26, 19, 20, 254, 22, 256, + /* 750 */ 139, 140, 117, 118, 119, 306, 195, 308, 117, 118, + /* 760 */ 237, 238, 36, 122, 103, 104, 105, 106, 107, 108, + /* 770 */ 109, 110, 111, 112, 113, 114, 195, 195, 60, 218, + /* 780 */ 219, 60, 109, 195, 19, 217, 60, 25, 23, 77, + /* 790 */ 117, 118, 119, 225, 233, 154, 155, 156, 72, 312, + /* 800 */ 218, 219, 90, 316, 22, 93, 303, 304, 43, 44, + /* 810 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, + /* 820 */ 55, 56, 57, 58, 183, 195, 195, 101, 19, 213, + /* 830 */ 214, 243, 23, 107, 108, 117, 118, 119, 117, 118, + /* 840 */ 119, 115, 60, 117, 118, 119, 195, 60, 122, 218, + /* 850 */ 219, 22, 43, 44, 45, 35, 47, 48, 49, 50, + /* 860 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104, + /* 870 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + /* 880 */ 154, 155, 156, 157, 158, 195, 255, 67, 195, 60, + /* 890 */ 101, 240, 311, 312, 306, 75, 308, 316, 29, 117, + /* 900 */ 118, 119, 33, 287, 117, 118, 119, 118, 146, 183, + /* 910 */ 195, 122, 103, 104, 105, 106, 107, 108, 109, 110, + /* 920 */ 111, 112, 113, 114, 215, 195, 77, 60, 25, 195, + /* 930 */ 122, 144, 19, 218, 219, 66, 23, 88, 246, 90, + /* 940 */ 132, 25, 93, 154, 155, 156, 117, 118, 119, 257, + /* 950 */ 195, 131, 218, 219, 195, 265, 43, 44, 45, 195, + /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 970 */ 57, 58, 183, 218, 219, 195, 19, 218, 219, 195, + /* 980 */ 23, 195, 218, 219, 117, 118, 119, 195, 233, 255, + /* 990 */ 195, 195, 233, 22, 23, 146, 25, 233, 218, 219, + /* 1000 */ 43, 44, 45, 294, 47, 48, 49, 50, 51, 52, + /* 1010 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 114, 195, 12, + /* 1030 */ 234, 195, 240, 74, 195, 255, 195, 60, 243, 262, + /* 1040 */ 263, 311, 312, 25, 27, 19, 316, 107, 108, 265, + /* 1050 */ 24, 265, 195, 150, 195, 139, 140, 218, 219, 42, + /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + /* 1070 */ 113, 114, 233, 102, 67, 218, 219, 218, 219, 243, + /* 1080 */ 19, 64, 22, 23, 23, 25, 195, 128, 129, 130, + /* 1090 */ 233, 74, 233, 86, 154, 118, 156, 130, 265, 208, + /* 1100 */ 19, 306, 95, 308, 43, 44, 45, 266, 47, 48, + /* 1110 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 1120 */ 153, 230, 96, 232, 43, 44, 45, 19, 47, 48, + /* 1130 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* 1140 */ 114, 22, 306, 24, 308, 127, 120, 121, 122, 123, + /* 1150 */ 124, 125, 126, 195, 147, 212, 213, 214, 132, 23, + /* 1160 */ 195, 25, 102, 100, 103, 104, 105, 106, 107, 108, + /* 1170 */ 109, 110, 111, 112, 113, 114, 218, 219, 19, 60, + /* 1180 */ 195, 12, 210, 211, 103, 104, 105, 106, 107, 108, + /* 1190 */ 109, 110, 111, 112, 113, 114, 27, 134, 195, 195, + /* 1200 */ 195, 210, 211, 218, 219, 195, 47, 195, 212, 213, + /* 1210 */ 214, 42, 16, 130, 19, 112, 113, 114, 23, 77, + /* 1220 */ 195, 218, 219, 218, 219, 117, 163, 164, 218, 219, + /* 1230 */ 218, 219, 90, 64, 19, 93, 153, 118, 43, 44, + /* 1240 */ 45, 160, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1250 */ 55, 56, 57, 58, 195, 119, 272, 276, 43, 44, + /* 1260 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54, + /* 1270 */ 55, 56, 57, 58, 78, 116, 80, 218, 219, 116, + /* 1280 */ 144, 128, 129, 130, 218, 219, 61, 195, 47, 195, + /* 1290 */ 16, 132, 195, 263, 195, 314, 315, 267, 103, 104, + /* 1300 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + /* 1310 */ 218, 219, 218, 219, 151, 218, 219, 195, 103, 104, + /* 1320 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + /* 1330 */ 210, 211, 195, 7, 8, 9, 195, 60, 195, 312, + /* 1340 */ 218, 219, 195, 316, 195, 120, 195, 263, 19, 195, + /* 1350 */ 125, 267, 78, 24, 80, 218, 219, 116, 162, 218, + /* 1360 */ 219, 218, 219, 301, 302, 218, 219, 195, 19, 218, + /* 1370 */ 219, 276, 43, 44, 45, 160, 47, 48, 49, 50, + /* 1380 */ 51, 52, 53, 54, 55, 56, 57, 58, 19, 146, + /* 1390 */ 218, 219, 43, 44, 45, 118, 47, 48, 49, 50, + /* 1400 */ 51, 52, 53, 54, 55, 56, 57, 58, 165, 314, + /* 1410 */ 315, 276, 43, 44, 45, 266, 47, 48, 49, 50, + /* 1420 */ 51, 52, 53, 54, 55, 56, 57, 58, 128, 129, + /* 1430 */ 130, 195, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1440 */ 111, 112, 113, 114, 195, 228, 195, 61, 195, 314, + /* 1450 */ 315, 25, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1460 */ 111, 112, 113, 114, 195, 22, 195, 218, 219, 218, + /* 1470 */ 219, 195, 103, 104, 105, 106, 107, 108, 109, 110, + /* 1480 */ 111, 112, 113, 114, 195, 195, 246, 218, 219, 218, + /* 1490 */ 219, 25, 19, 246, 218, 219, 246, 257, 259, 260, + /* 1500 */ 195, 22, 266, 60, 257, 195, 120, 257, 218, 219, + /* 1510 */ 116, 195, 19, 195, 150, 151, 25, 44, 45, 266, + /* 1520 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1530 */ 57, 58, 195, 54, 218, 219, 218, 219, 45, 145, + /* 1540 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* 1550 */ 57, 58, 246, 121, 122, 218, 219, 19, 23, 31, + /* 1560 */ 25, 118, 159, 257, 161, 24, 195, 39, 195, 143, + /* 1570 */ 195, 19, 20, 22, 22, 24, 103, 104, 105, 106, + /* 1580 */ 107, 108, 109, 110, 111, 112, 113, 114, 36, 218, + /* 1590 */ 219, 218, 219, 218, 219, 195, 103, 104, 105, 106, + /* 1600 */ 107, 108, 109, 110, 111, 112, 113, 114, 195, 143, + /* 1610 */ 119, 136, 60, 195, 22, 195, 141, 195, 218, 219, + /* 1620 */ 195, 23, 195, 25, 72, 23, 131, 25, 195, 134, + /* 1630 */ 23, 218, 219, 195, 82, 144, 218, 219, 218, 219, + /* 1640 */ 218, 219, 195, 218, 219, 218, 219, 60, 23, 195, + /* 1650 */ 25, 218, 219, 101, 195, 117, 218, 219, 195, 107, + /* 1660 */ 108, 23, 195, 25, 195, 218, 219, 115, 228, 117, + /* 1670 */ 118, 119, 218, 219, 122, 195, 19, 218, 219, 195, + /* 1680 */ 60, 218, 219, 142, 195, 218, 219, 19, 20, 195, + /* 1690 */ 22, 139, 140, 23, 23, 25, 25, 195, 218, 219, + /* 1700 */ 7, 8, 218, 219, 36, 118, 154, 155, 156, 157, + /* 1710 */ 158, 195, 23, 195, 25, 84, 85, 49, 195, 23, + /* 1720 */ 195, 25, 195, 23, 195, 25, 195, 23, 60, 25, + /* 1730 */ 23, 23, 25, 25, 142, 183, 218, 219, 118, 195, + /* 1740 */ 72, 218, 219, 218, 219, 218, 219, 218, 219, 218, + /* 1750 */ 219, 195, 195, 146, 86, 98, 23, 195, 25, 91, + /* 1760 */ 19, 20, 154, 22, 156, 154, 23, 156, 25, 101, + /* 1770 */ 23, 195, 25, 195, 195, 107, 108, 36, 195, 195, + /* 1780 */ 195, 195, 228, 115, 195, 117, 118, 119, 195, 195, + /* 1790 */ 122, 261, 195, 321, 195, 195, 195, 258, 238, 195, + /* 1800 */ 195, 60, 299, 291, 195, 195, 258, 195, 195, 195, + /* 1810 */ 290, 244, 216, 72, 245, 193, 258, 258, 299, 258, + /* 1820 */ 299, 274, 154, 155, 156, 157, 158, 86, 247, 295, + /* 1830 */ 248, 295, 91, 19, 20, 270, 22, 274, 270, 248, + /* 1840 */ 274, 222, 101, 227, 274, 221, 231, 221, 107, 108, + /* 1850 */ 36, 183, 262, 247, 221, 283, 115, 262, 117, 118, + /* 1860 */ 119, 198, 116, 122, 220, 262, 61, 220, 220, 251, + /* 1870 */ 247, 142, 251, 245, 60, 202, 299, 202, 38, 262, + /* 1880 */ 202, 22, 152, 151, 296, 43, 72, 236, 18, 239, + /* 1890 */ 202, 239, 239, 239, 18, 154, 155, 156, 157, 158, + /* 1900 */ 86, 150, 201, 248, 275, 91, 248, 273, 236, 248, + /* 1910 */ 275, 275, 273, 236, 248, 101, 286, 202, 201, 159, + /* 1920 */ 63, 107, 108, 296, 183, 293, 202, 201, 22, 115, + /* 1930 */ 202, 117, 118, 119, 292, 223, 122, 201, 65, 202, + /* 1940 */ 201, 223, 220, 220, 22, 220, 226, 226, 229, 127, + /* 1950 */ 223, 220, 166, 24, 285, 220, 222, 114, 315, 285, + /* 1960 */ 220, 202, 220, 307, 92, 320, 320, 229, 154, 155, + /* 1970 */ 156, 157, 158, 0, 1, 2, 223, 83, 5, 268, + /* 1980 */ 149, 268, 146, 10, 11, 12, 13, 14, 22, 280, + /* 1990 */ 17, 202, 159, 19, 20, 251, 22, 183, 282, 148, + /* 2000 */ 252, 252, 250, 30, 249, 32, 248, 147, 25, 13, + /* 2010 */ 36, 204, 196, 40, 196, 6, 302, 194, 194, 194, + /* 2020 */ 209, 215, 209, 215, 215, 215, 224, 224, 216, 209, + /* 2030 */ 4, 216, 215, 3, 60, 22, 122, 19, 122, 19, + /* 2040 */ 125, 22, 15, 22, 71, 16, 72, 23, 23, 140, + /* 2050 */ 305, 152, 79, 25, 131, 82, 143, 20, 16, 305, + /* 2060 */ 1, 143, 145, 131, 131, 62, 54, 131, 37, 54, + /* 2070 */ 54, 152, 99, 117, 34, 101, 54, 24, 1, 5, + /* 2080 */ 22, 107, 108, 116, 76, 25, 162, 41, 142, 115, + /* 2090 */ 24, 117, 118, 119, 116, 20, 122, 19, 126, 23, + /* 2100 */ 132, 19, 20, 69, 22, 69, 22, 134, 22, 68, + /* 2110 */ 22, 22, 139, 140, 60, 141, 68, 24, 36, 28, + /* 2120 */ 97, 22, 37, 68, 23, 150, 34, 22, 154, 155, + /* 2130 */ 156, 157, 158, 23, 23, 22, 163, 25, 23, 142, + /* 2140 */ 23, 98, 60, 23, 22, 144, 25, 76, 34, 117, + /* 2150 */ 34, 89, 34, 34, 72, 87, 76, 183, 34, 94, + /* 2160 */ 34, 23, 22, 24, 34, 23, 25, 44, 25, 23, + /* 2170 */ 23, 23, 22, 22, 25, 11, 143, 25, 143, 23, + /* 2180 */ 22, 22, 22, 101, 23, 23, 136, 22, 25, 107, + /* 2190 */ 108, 142, 25, 142, 142, 23, 15, 115, 1, 117, + /* 2200 */ 118, 119, 1, 2, 122, 1, 5, 322, 322, 322, + /* 2210 */ 322, 10, 11, 12, 13, 14, 322, 322, 17, 322, + /* 2220 */ 5, 322, 322, 141, 322, 10, 11, 12, 13, 14, + /* 2230 */ 322, 30, 17, 32, 322, 322, 154, 155, 156, 157, + /* 2240 */ 158, 40, 322, 322, 322, 30, 322, 32, 322, 322, + /* 2250 */ 322, 322, 322, 322, 322, 40, 322, 322, 322, 322, + /* 2260 */ 322, 322, 322, 322, 322, 183, 322, 322, 322, 322, + /* 2270 */ 322, 322, 71, 322, 322, 322, 322, 322, 322, 322, + /* 2280 */ 79, 322, 322, 82, 322, 322, 71, 322, 322, 322, + /* 2290 */ 322, 322, 322, 322, 79, 322, 322, 82, 322, 322, + /* 2300 */ 99, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2310 */ 322, 322, 322, 322, 99, 322, 322, 322, 322, 322, + /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2330 */ 322, 322, 322, 322, 322, 134, 322, 322, 322, 322, + /* 2340 */ 139, 140, 322, 322, 322, 322, 322, 322, 322, 134, + /* 2350 */ 322, 322, 322, 322, 139, 140, 322, 322, 322, 322, + /* 2360 */ 322, 322, 322, 322, 163, 322, 322, 322, 322, 322, + /* 2370 */ 322, 322, 322, 322, 322, 322, 322, 322, 163, 322, + /* 2380 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2390 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2400 */ 322, 322, 322, 322, 322, 322, 322, 322, 187, 187, + /* 2410 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2420 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2430 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2440 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2450 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2460 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2470 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2480 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2490 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2500 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2510 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2520 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2530 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2540 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2550 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, + /* 2560 */ 187, 187, 187, 187, 187, 187, }; -#define YY_SHIFT_COUNT (582) +#define YY_SHIFT_COUNT (599) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2152) +#define YY_SHIFT_MAX (2215) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 2029, 1801, 2043, 1380, 1380, 318, 271, 1496, 1569, 1642, - /* 10 */ 702, 702, 702, 740, 318, 318, 318, 318, 318, 0, - /* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702, - /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 503, 503, - /* 40 */ 111, 111, 217, 287, 348, 610, 610, 736, 736, 736, - /* 50 */ 736, 40, 112, 320, 340, 445, 489, 593, 637, 741, - /* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177, - /* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, - /* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554, - /* 90 */ 1802, 1910, 702, 702, 702, 702, 702, 702, 702, 702, - /* 100 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - /* 110 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - /* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - /* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - /* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198, - /* 150 */ 183, 99, 169, 549, 610, 151, 542, 610, 610, 1017, - /* 160 */ 1017, 610, 1001, 350, 464, 464, 464, 586, 1, 1, - /* 170 */ 2207, 2207, 854, 854, 854, 465, 694, 694, 694, 694, - /* 180 */ 1096, 1096, 825, 549, 847, 904, 610, 610, 610, 610, - /* 190 */ 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, - /* 200 */ 610, 610, 610, 610, 610, 488, 947, 947, 610, 1129, - /* 210 */ 495, 495, 1139, 1139, 967, 967, 1173, 2207, 2207, 2207, - /* 220 */ 2207, 2207, 2207, 2207, 617, 765, 765, 697, 444, 708, - /* 230 */ 660, 745, 510, 663, 864, 610, 610, 610, 610, 610, - /* 240 */ 610, 610, 610, 610, 610, 188, 610, 610, 610, 610, - /* 250 */ 610, 610, 610, 610, 610, 610, 610, 610, 839, 839, - /* 260 */ 839, 610, 610, 610, 1155, 610, 610, 610, 1119, 1247, - /* 270 */ 610, 1353, 610, 610, 610, 610, 610, 610, 610, 610, - /* 280 */ 1063, 494, 1101, 291, 291, 291, 291, 1319, 1101, 1101, - /* 290 */ 775, 1221, 1375, 1452, 667, 1341, 1198, 1341, 1435, 1487, - /* 300 */ 667, 667, 1487, 667, 1198, 1435, 777, 1011, 1423, 584, - /* 310 */ 584, 584, 1273, 1273, 1273, 1273, 1471, 1471, 880, 1530, - /* 320 */ 1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683, - /* 330 */ 1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701, - /* 340 */ 1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828, - /* 350 */ 1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848, - /* 360 */ 1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766, - /* 370 */ 1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813, - /* 380 */ 1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789, - /* 390 */ 1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207, - /* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, - /* 410 */ 2207, 2207, 2207, 69, 1032, 79, 357, 1377, 1206, 400, - /* 420 */ 1525, 835, 332, 1540, 1437, 1539, 1536, 1548, 1583, 1620, - /* 430 */ 1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358, - /* 440 */ 1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693, - /* 450 */ 1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965, - /* 460 */ 1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854, - /* 470 */ 1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855, - /* 480 */ 1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014, - /* 490 */ 1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921, - /* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047, - /* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057, - /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950, - /* 530 */ 2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076, - /* 540 */ 2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083, - /* 550 */ 2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103, - /* 560 */ 2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117, - /* 570 */ 2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127, - /* 580 */ 2136, 2151, 2152, + /* 0 */ 2201, 1973, 2215, 1552, 1552, 33, 368, 1668, 1741, 1814, + /* 10 */ 726, 726, 726, 265, 33, 33, 33, 33, 33, 0, + /* 20 */ 0, 216, 1349, 726, 726, 726, 726, 726, 726, 726, + /* 30 */ 726, 726, 726, 726, 726, 726, 726, 726, 272, 272, + /* 40 */ 111, 111, 316, 365, 516, 867, 867, 916, 916, 916, + /* 50 */ 916, 40, 112, 260, 364, 408, 512, 617, 661, 765, + /* 60 */ 809, 913, 957, 1061, 1081, 1195, 1215, 1329, 1349, 1349, + /* 70 */ 1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349, 1349, + /* 80 */ 1349, 1349, 1349, 1349, 1349, 1349, 1369, 1349, 1473, 1493, + /* 90 */ 1493, 473, 1974, 2082, 726, 726, 726, 726, 726, 726, + /* 100 */ 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + /* 110 */ 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + /* 120 */ 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + /* 130 */ 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + /* 140 */ 726, 726, 726, 726, 726, 726, 138, 232, 232, 232, + /* 150 */ 232, 232, 232, 232, 188, 99, 242, 718, 416, 1159, + /* 160 */ 867, 867, 940, 940, 867, 1103, 417, 574, 574, 574, + /* 170 */ 611, 139, 139, 2379, 2379, 1026, 1026, 1026, 536, 466, + /* 180 */ 466, 466, 466, 1017, 1017, 849, 718, 971, 1060, 867, + /* 190 */ 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, + /* 200 */ 867, 867, 867, 867, 867, 867, 867, 867, 261, 712, + /* 210 */ 712, 867, 108, 1142, 1142, 977, 1108, 1108, 977, 977, + /* 220 */ 1243, 2379, 2379, 2379, 2379, 2379, 2379, 2379, 641, 789, + /* 230 */ 789, 635, 366, 721, 673, 782, 494, 787, 829, 867, + /* 240 */ 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, + /* 250 */ 959, 867, 867, 867, 867, 867, 867, 867, 867, 867, + /* 260 */ 867, 867, 867, 867, 867, 820, 820, 820, 867, 867, + /* 270 */ 867, 1136, 867, 867, 867, 1119, 1007, 867, 1169, 867, + /* 280 */ 867, 867, 867, 867, 867, 867, 867, 1225, 1153, 869, + /* 290 */ 196, 618, 618, 618, 618, 1491, 196, 196, 91, 339, + /* 300 */ 1326, 1386, 383, 1163, 1364, 1426, 1364, 1538, 903, 1163, + /* 310 */ 1163, 903, 1163, 1426, 1538, 1018, 1535, 1241, 1528, 1528, + /* 320 */ 1528, 1394, 1394, 1394, 1394, 762, 762, 1403, 1466, 1475, + /* 330 */ 1551, 1746, 1805, 1746, 1746, 1729, 1729, 1840, 1840, 1729, + /* 340 */ 1730, 1732, 1859, 1842, 1870, 1870, 1870, 1870, 1729, 1876, + /* 350 */ 1751, 1732, 1732, 1751, 1859, 1842, 1751, 1842, 1751, 1729, + /* 360 */ 1876, 1760, 1857, 1729, 1876, 1906, 1729, 1876, 1729, 1876, + /* 370 */ 1906, 1746, 1746, 1746, 1873, 1922, 1922, 1906, 1746, 1822, + /* 380 */ 1746, 1873, 1746, 1746, 1786, 1929, 1843, 1843, 1906, 1729, + /* 390 */ 1872, 1872, 1894, 1894, 1831, 1836, 1966, 1729, 1833, 1831, + /* 400 */ 1851, 1860, 1751, 1983, 1996, 1996, 2009, 2009, 2009, 2379, + /* 410 */ 2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379, 2379, + /* 420 */ 2379, 2379, 2379, 2379, 136, 1063, 1196, 530, 636, 1274, + /* 430 */ 1300, 1443, 1598, 1495, 1479, 967, 1083, 1602, 463, 1625, + /* 440 */ 1638, 1670, 1541, 1671, 1689, 1696, 1277, 1432, 1693, 808, + /* 450 */ 1700, 1607, 1657, 1587, 1704, 1707, 1631, 1708, 1733, 1608, + /* 460 */ 1611, 1743, 1747, 1620, 1592, 2026, 2030, 2013, 1914, 2018, + /* 470 */ 1916, 2020, 2019, 2021, 1915, 2027, 2029, 2024, 2025, 1909, + /* 480 */ 1899, 1923, 2028, 2028, 1913, 2037, 1917, 2042, 2059, 1918, + /* 490 */ 1932, 2028, 1933, 2003, 2031, 2028, 1919, 2012, 2015, 2016, + /* 500 */ 2022, 1936, 1956, 2040, 2053, 2077, 2074, 2058, 1967, 1924, + /* 510 */ 2034, 2060, 2036, 2008, 2046, 1946, 1978, 2066, 2075, 2078, + /* 520 */ 1968, 1972, 2084, 2041, 2086, 2088, 2076, 2089, 2048, 2054, + /* 530 */ 2093, 2023, 2091, 2099, 2055, 2085, 2101, 2092, 1975, 2105, + /* 540 */ 2110, 2111, 2112, 2115, 2113, 2043, 1997, 2117, 2120, 2032, + /* 550 */ 2114, 2122, 2001, 2121, 2116, 2118, 2119, 2124, 2062, 2071, + /* 560 */ 2068, 2123, 2080, 2065, 2126, 2138, 2140, 2139, 2141, 2143, + /* 570 */ 2130, 2033, 2035, 2142, 2121, 2146, 2147, 2148, 2150, 2149, + /* 580 */ 2152, 2156, 2151, 2164, 2158, 2159, 2161, 2162, 2160, 2165, + /* 590 */ 2163, 2050, 2049, 2051, 2052, 2167, 2172, 2181, 2197, 2204, }; -#define YY_REDUCE_COUNT (412) -#define YY_REDUCE_MIN (-277) -#define YY_REDUCE_MAX (1772) +#define YY_REDUCE_COUNT (423) +#define YY_REDUCE_MIN (-303) +#define YY_REDUCE_MAX (1825) static const short yy_reduce_ofst[] = { - /* 0 */ -67, 1252, -64, -178, -181, 160, 1071, 143, -184, 137, - /* 10 */ 218, 220, 222, -174, 229, 268, 272, 275, 324, -208, - /* 20 */ 242, -277, -39, 81, 537, 792, 810, 812, -189, 814, - /* 30 */ 831, 163, 865, 944, 887, 840, 964, 1077, -187, 292, - /* 40 */ -133, 274, 673, 558, 682, 795, 809, -238, -232, -238, - /* 50 */ -232, 329, 329, 329, 329, 329, 329, 329, 329, 329, - /* 60 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - /* 70 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, - /* 80 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 557, - /* 90 */ 712, 949, 966, 969, 971, 979, 1097, 1099, 1103, 1142, - /* 100 */ 1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255, - /* 110 */ 1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328, - /* 120 */ 1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381, - /* 130 */ 1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449, - /* 140 */ 1453, 1462, 329, 329, 329, 329, 329, 329, 329, 329, - /* 150 */ 329, 329, 329, -22, -159, 475, -220, 756, 38, 501, - /* 160 */ 841, 714, 329, 118, 337, 349, 363, -56, 329, 329, - /* 170 */ 329, 329, -205, -205, -205, 687, -172, -130, -57, 790, - /* 180 */ 397, 528, -271, 136, 596, 596, 90, 316, 522, 541, - /* 190 */ -37, 715, 849, 977, 628, 856, 980, 991, 1081, 1102, - /* 200 */ 1135, 1083, -162, 208, 1258, 794, -86, 159, 41, 1109, - /* 210 */ 671, 852, 844, 932, 1175, 1254, 480, 1180, 100, 258, - /* 220 */ 1265, 1268, 1216, 1287, -139, 317, 344, 63, 339, 423, - /* 230 */ 563, 636, 676, 813, 908, 914, 950, 1078, 1084, 1098, - /* 240 */ 1363, 1384, 1407, 1439, 1464, 411, 1527, 1534, 1535, 1537, - /* 250 */ 1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550, 990, 1164, - /* 260 */ 1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413, - /* 270 */ 1563, 1510, 1568, 563, 1570, 1571, 1572, 1573, 1574, 1575, - /* 280 */ 1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518, - /* 290 */ 1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538, - /* 300 */ 1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586, - /* 310 */ 1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579, - /* 320 */ 1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585, - /* 330 */ 1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610, - /* 340 */ 1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654, - /* 350 */ 1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653, - /* 360 */ 1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691, - /* 370 */ 1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602, - /* 380 */ 1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702, - /* 390 */ 1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661, - /* 400 */ 1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762, - /* 410 */ 1763, 1761, 1772, + /* 0 */ -67, 345, -64, -178, -181, 143, 435, -78, -183, 163, + /* 10 */ -185, 284, 384, -174, 189, 352, 440, 444, 493, -23, + /* 20 */ 227, -277, -1, 305, 561, 755, 759, 764, -189, 839, + /* 30 */ 857, 354, 484, 859, 631, 67, 734, 780, -187, 616, + /* 40 */ 581, 730, 891, 449, 588, 795, 836, -238, 287, -238, + /* 50 */ 287, -256, -256, -256, -256, -256, -256, -256, -256, -256, + /* 60 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + /* 70 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + /* 80 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256, + /* 90 */ -256, 205, 582, 715, 958, 985, 1003, 1005, 1010, 1012, + /* 100 */ 1059, 1066, 1092, 1094, 1097, 1122, 1137, 1141, 1143, 1147, + /* 110 */ 1151, 1172, 1249, 1251, 1269, 1271, 1276, 1290, 1316, 1318, + /* 120 */ 1337, 1371, 1373, 1375, 1400, 1413, 1418, 1420, 1422, 1425, + /* 130 */ 1427, 1433, 1438, 1447, 1454, 1459, 1463, 1467, 1480, 1484, + /* 140 */ 1518, 1523, 1525, 1527, 1529, 1531, -256, -256, -256, -256, + /* 150 */ -256, -256, -256, -256, -256, -256, -256, 155, 210, -220, + /* 160 */ 86, -130, 943, 996, 402, -256, -113, 981, 1095, 1135, + /* 170 */ 395, -256, -256, -256, -256, 568, 568, 568, -4, -153, + /* 180 */ -133, 259, 306, -166, 523, -303, -126, 503, 503, -37, + /* 190 */ -149, 164, 690, 292, 412, 492, 651, 784, 332, 786, + /* 200 */ 841, 1149, 833, 1236, 792, 162, 796, 1253, 777, 288, + /* 210 */ 381, 380, 709, 487, 1027, 972, 1030, 1084, 991, 1120, + /* 220 */ -152, 1062, 692, 1240, 1247, 1250, 1239, 1306, -207, -194, + /* 230 */ 57, 180, 74, 315, 355, 376, 452, 488, 630, 693, + /* 240 */ 965, 1004, 1025, 1099, 1154, 1289, 1305, 1310, 1469, 1489, + /* 250 */ 984, 1494, 1502, 1516, 1544, 1556, 1557, 1562, 1576, 1578, + /* 260 */ 1579, 1583, 1584, 1585, 1586, 1217, 1440, 1554, 1589, 1593, + /* 270 */ 1594, 1530, 1597, 1599, 1600, 1539, 1472, 1601, 1560, 1604, + /* 280 */ 355, 1605, 1609, 1610, 1612, 1613, 1614, 1503, 1512, 1520, + /* 290 */ 1567, 1548, 1558, 1559, 1561, 1530, 1567, 1567, 1569, 1596, + /* 300 */ 1622, 1519, 1521, 1547, 1565, 1581, 1568, 1534, 1582, 1563, + /* 310 */ 1566, 1591, 1570, 1606, 1536, 1619, 1615, 1616, 1624, 1626, + /* 320 */ 1633, 1590, 1595, 1603, 1617, 1618, 1621, 1572, 1623, 1628, + /* 330 */ 1663, 1644, 1577, 1647, 1648, 1673, 1675, 1588, 1627, 1678, + /* 340 */ 1630, 1629, 1634, 1651, 1650, 1652, 1653, 1654, 1688, 1701, + /* 350 */ 1655, 1635, 1636, 1658, 1639, 1672, 1661, 1677, 1666, 1715, + /* 360 */ 1717, 1632, 1642, 1724, 1726, 1712, 1728, 1736, 1737, 1739, + /* 370 */ 1718, 1722, 1723, 1725, 1719, 1720, 1721, 1727, 1731, 1734, + /* 380 */ 1735, 1738, 1740, 1742, 1643, 1656, 1669, 1674, 1753, 1759, + /* 390 */ 1645, 1646, 1711, 1713, 1748, 1744, 1709, 1789, 1716, 1749, + /* 400 */ 1752, 1755, 1758, 1807, 1816, 1818, 1823, 1824, 1825, 1745, + /* 410 */ 1754, 1714, 1811, 1806, 1808, 1809, 1810, 1813, 1802, 1803, + /* 420 */ 1812, 1815, 1817, 1820, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, - /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397, - /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, - /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, - /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, - /* 60 */ 1492, 1493, 1254, 1254, 1254, 1254, 1543, 1545, 1508, 1420, - /* 70 */ 1419, 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, - /* 80 */ 1486, 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, - /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 140 */ 1254, 1254, 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, - /* 150 */ 1456, 1458, 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, - /* 160 */ 1254, 1254, 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, - /* 170 */ 1473, 1472, 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, - /* 180 */ 1254, 1254, 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 200 */ 1254, 1254, 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, - /* 210 */ 1578, 1578, 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, - /* 220 */ 1358, 1358, 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, - /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, - /* 240 */ 1546, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, - /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, - /* 280 */ 1254, 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, - /* 290 */ 1357, 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, - /* 300 */ 1423, 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, - /* 310 */ 1397, 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, - /* 320 */ 1357, 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, - /* 330 */ 1638, 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, - /* 340 */ 1638, 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, - /* 350 */ 1525, 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, - /* 360 */ 1330, 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, - /* 370 */ 1319, 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, - /* 380 */ 1588, 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, - /* 390 */ 1401, 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, - /* 400 */ 1558, 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, - /* 410 */ 1288, 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, - /* 420 */ 1254, 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, - /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 440 */ 1564, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 450 */ 1254, 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, - /* 460 */ 1254, 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, - /* 470 */ 1254, 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, - /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, - /* 490 */ 1254, 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, - /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 510 */ 1254, 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 530 */ 1254, 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, - /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 550 */ 1254, 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, - /* 560 */ 1254, 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, - /* 580 */ 1266, 1254, 1254, + /* 0 */ 1691, 1691, 1691, 1516, 1279, 1392, 1279, 1279, 1279, 1279, + /* 10 */ 1516, 1516, 1516, 1279, 1279, 1279, 1279, 1279, 1279, 1422, + /* 20 */ 1422, 1568, 1312, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 30 */ 1279, 1279, 1279, 1279, 1279, 1515, 1279, 1279, 1279, 1279, + /* 40 */ 1607, 1607, 1279, 1279, 1279, 1279, 1279, 1592, 1591, 1279, + /* 50 */ 1279, 1279, 1431, 1279, 1279, 1279, 1438, 1279, 1279, 1279, + /* 60 */ 1279, 1279, 1517, 1518, 1279, 1279, 1279, 1279, 1567, 1569, + /* 70 */ 1533, 1445, 1444, 1443, 1442, 1551, 1410, 1436, 1429, 1433, + /* 80 */ 1512, 1513, 1511, 1670, 1518, 1517, 1279, 1432, 1480, 1496, + /* 90 */ 1479, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 100 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 110 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 120 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 130 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 140 */ 1279, 1279, 1279, 1279, 1279, 1279, 1488, 1495, 1494, 1493, + /* 150 */ 1502, 1492, 1489, 1482, 1481, 1483, 1484, 1303, 1300, 1354, + /* 160 */ 1279, 1279, 1279, 1279, 1279, 1485, 1312, 1473, 1472, 1471, + /* 170 */ 1279, 1499, 1486, 1498, 1497, 1575, 1644, 1643, 1534, 1279, + /* 180 */ 1279, 1279, 1279, 1279, 1279, 1607, 1279, 1279, 1279, 1279, + /* 190 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 200 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1412, 1607, + /* 210 */ 1607, 1279, 1312, 1607, 1607, 1308, 1413, 1413, 1308, 1308, + /* 220 */ 1416, 1587, 1383, 1383, 1383, 1383, 1392, 1383, 1279, 1279, + /* 230 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 240 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1572, 1570, 1279, + /* 250 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 260 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 270 */ 1279, 1279, 1279, 1279, 1279, 1388, 1279, 1279, 1279, 1279, + /* 280 */ 1279, 1279, 1279, 1279, 1279, 1279, 1637, 1683, 1279, 1546, + /* 290 */ 1368, 1388, 1388, 1388, 1388, 1390, 1369, 1367, 1382, 1313, + /* 300 */ 1286, 1683, 1683, 1448, 1437, 1389, 1437, 1680, 1435, 1448, + /* 310 */ 1448, 1435, 1448, 1389, 1680, 1329, 1659, 1324, 1422, 1422, + /* 320 */ 1422, 1412, 1412, 1412, 1412, 1416, 1416, 1514, 1389, 1382, + /* 330 */ 1279, 1355, 1683, 1355, 1355, 1398, 1398, 1682, 1682, 1398, + /* 340 */ 1534, 1667, 1457, 1357, 1363, 1363, 1363, 1363, 1398, 1297, + /* 350 */ 1435, 1667, 1667, 1435, 1457, 1357, 1435, 1357, 1435, 1398, + /* 360 */ 1297, 1550, 1678, 1398, 1297, 1524, 1398, 1297, 1398, 1297, + /* 370 */ 1524, 1355, 1355, 1355, 1344, 1279, 1279, 1524, 1355, 1329, + /* 380 */ 1355, 1344, 1355, 1355, 1625, 1279, 1528, 1528, 1524, 1398, + /* 390 */ 1617, 1617, 1425, 1425, 1430, 1416, 1519, 1398, 1279, 1430, + /* 400 */ 1428, 1426, 1435, 1347, 1640, 1640, 1636, 1636, 1636, 1688, + /* 410 */ 1688, 1587, 1652, 1312, 1312, 1312, 1312, 1652, 1331, 1331, + /* 420 */ 1313, 1313, 1312, 1652, 1279, 1279, 1279, 1279, 1279, 1279, + /* 430 */ 1279, 1647, 1279, 1279, 1535, 1279, 1279, 1279, 1279, 1279, + /* 440 */ 1279, 1279, 1402, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 450 */ 1279, 1279, 1593, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 460 */ 1279, 1279, 1279, 1279, 1462, 1279, 1282, 1584, 1279, 1279, + /* 470 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 480 */ 1279, 1279, 1439, 1440, 1279, 1279, 1279, 1279, 1279, 1279, + /* 490 */ 1279, 1454, 1279, 1279, 1279, 1449, 1279, 1279, 1279, 1279, + /* 500 */ 1279, 1279, 1279, 1279, 1403, 1279, 1279, 1279, 1279, 1279, + /* 510 */ 1279, 1549, 1548, 1279, 1279, 1400, 1279, 1279, 1279, 1279, + /* 520 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1327, + /* 530 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 540 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 550 */ 1279, 1279, 1279, 1427, 1279, 1279, 1279, 1279, 1279, 1279, + /* 560 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1622, 1417, + /* 570 */ 1279, 1279, 1279, 1279, 1671, 1279, 1279, 1279, 1279, 1377, + /* 580 */ 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, 1279, + /* 590 */ 1663, 1371, 1463, 1279, 1466, 1301, 1279, 1291, 1279, 1279, }; /********** End of lemon-generated parsing tables *****************************/ @@ -179402,34 +181811,33 @@ static const char *const yyTokenName[] = { /* 292 */ "foreach_clause", /* 293 */ "when_clause", /* 294 */ "trigger_cmd", - /* 295 */ "trnm", - /* 296 */ "tridxby", - /* 297 */ "database_kw_opt", - /* 298 */ "key_opt", - /* 299 */ "add_column_fullname", - /* 300 */ "kwcolumn_opt", - /* 301 */ "create_vtab", - /* 302 */ "vtabarglist", - /* 303 */ "vtabarg", - /* 304 */ "vtabargtoken", - /* 305 */ "lp", - /* 306 */ "anylist", - /* 307 */ "wqitem", - /* 308 */ "wqas", - /* 309 */ "withnm", - /* 310 */ "windowdefn_list", - /* 311 */ "windowdefn", - /* 312 */ "window", - /* 313 */ "frame_opt", - /* 314 */ "part_opt", - /* 315 */ "filter_clause", - /* 316 */ "over_clause", - /* 317 */ "range_or_rows", - /* 318 */ "frame_bound", - /* 319 */ "frame_bound_s", - /* 320 */ "frame_bound_e", - /* 321 */ "frame_exclude_opt", - /* 322 */ "frame_exclude", + /* 295 */ "tridxby", + /* 296 */ "database_kw_opt", + /* 297 */ "key_opt", + /* 298 */ "alter_add", + /* 299 */ "kwcolumn_opt", + /* 300 */ "create_vtab", + /* 301 */ "vtabarglist", + /* 302 */ "vtabarg", + /* 303 */ "vtabargtoken", + /* 304 */ "lp", + /* 305 */ "anylist", + /* 306 */ "wqitem", + /* 307 */ "wqas", + /* 308 */ "withnm", + /* 309 */ "windowdefn_list", + /* 310 */ "windowdefn", + /* 311 */ "window", + /* 312 */ "frame_opt", + /* 313 */ "part_opt", + /* 314 */ "filter_clause", + /* 315 */ "over_clause", + /* 316 */ "range_or_rows", + /* 317 */ "frame_bound", + /* 318 */ "frame_bound_s", + /* 319 */ "frame_bound_e", + /* 320 */ "frame_exclude_opt", + /* 321 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -179559,8 +181967,8 @@ static const char *const yyRuleName[] = { /* 119 */ "fullname ::= nm DOT nm", /* 120 */ "xfullname ::= nm", /* 121 */ "xfullname ::= nm DOT nm", - /* 122 */ "xfullname ::= nm DOT nm AS nm", - /* 123 */ "xfullname ::= nm AS nm", + /* 122 */ "xfullname ::= nm AS nm", + /* 123 */ "xfullname ::= nm DOT nm AS nm", /* 124 */ "joinop ::= COMMA|JOIN", /* 125 */ "joinop ::= JOIN_KW JOIN", /* 126 */ "joinop ::= JOIN_KW nm JOIN", @@ -179709,143 +182117,146 @@ static const char *const yyRuleName[] = { /* 269 */ "when_clause ::= WHEN expr", /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 272 */ "trnm ::= nm DOT nm", - /* 273 */ "tridxby ::= INDEXED BY nm", - /* 274 */ "tridxby ::= NOT INDEXED", - /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", - /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 278 */ "trigger_cmd ::= scanpt select scanpt", - /* 279 */ "expr ::= RAISE LP IGNORE RP", - /* 280 */ "expr ::= RAISE LP raisetype COMMA expr RP", - /* 281 */ "raisetype ::= ROLLBACK", - /* 282 */ "raisetype ::= ABORT", - /* 283 */ "raisetype ::= FAIL", - /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 286 */ "cmd ::= DETACH database_kw_opt expr", - /* 287 */ "key_opt ::=", - /* 288 */ "key_opt ::= KEY expr", - /* 289 */ "cmd ::= REINDEX", - /* 290 */ "cmd ::= REINDEX nm dbnm", - /* 291 */ "cmd ::= ANALYZE", - /* 292 */ "cmd ::= ANALYZE nm dbnm", - /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 272 */ "tridxby ::= INDEXED BY nm", + /* 273 */ "tridxby ::= NOT INDEXED", + /* 274 */ "trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt", + /* 275 */ "trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt", + /* 276 */ "trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt", + /* 277 */ "trigger_cmd ::= scanpt select scanpt", + /* 278 */ "expr ::= RAISE LP IGNORE RP", + /* 279 */ "expr ::= RAISE LP raisetype COMMA expr RP", + /* 280 */ "raisetype ::= ROLLBACK", + /* 281 */ "raisetype ::= ABORT", + /* 282 */ "raisetype ::= FAIL", + /* 283 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 284 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 285 */ "cmd ::= DETACH database_kw_opt expr", + /* 286 */ "key_opt ::=", + /* 287 */ "key_opt ::= KEY expr", + /* 288 */ "cmd ::= REINDEX", + /* 289 */ "cmd ::= REINDEX nm dbnm", + /* 290 */ "cmd ::= ANALYZE", + /* 291 */ "cmd ::= ANALYZE nm dbnm", + /* 292 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 293 */ "cmd ::= alter_add carglist", + /* 294 */ "alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken", /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", - /* 296 */ "add_column_fullname ::= fullname", - /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 298 */ "cmd ::= create_vtab", - /* 299 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 301 */ "vtabarg ::=", - /* 302 */ "vtabargtoken ::= ANY", - /* 303 */ "vtabargtoken ::= lp anylist RP", - /* 304 */ "lp ::= LP", - /* 305 */ "with ::= WITH wqlist", - /* 306 */ "with ::= WITH RECURSIVE wqlist", - /* 307 */ "wqas ::= AS", - /* 308 */ "wqas ::= AS MATERIALIZED", - /* 309 */ "wqas ::= AS NOT MATERIALIZED", - /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", - /* 311 */ "withnm ::= nm", - /* 312 */ "wqlist ::= wqitem", - /* 313 */ "wqlist ::= wqlist COMMA wqitem", - /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 315 */ "windowdefn ::= nm AS LP window RP", - /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 318 */ "window ::= ORDER BY sortlist frame_opt", - /* 319 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 320 */ "window ::= nm frame_opt", - /* 321 */ "frame_opt ::=", - /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 325 */ "frame_bound_s ::= frame_bound", - /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 327 */ "frame_bound_e ::= frame_bound", - /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 330 */ "frame_bound ::= CURRENT ROW", - /* 331 */ "frame_exclude_opt ::=", - /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 333 */ "frame_exclude ::= NO OTHERS", - /* 334 */ "frame_exclude ::= CURRENT ROW", - /* 335 */ "frame_exclude ::= GROUP|TIES", - /* 336 */ "window_clause ::= WINDOW windowdefn_list", - /* 337 */ "filter_over ::= filter_clause over_clause", - /* 338 */ "filter_over ::= over_clause", - /* 339 */ "filter_over ::= filter_clause", - /* 340 */ "over_clause ::= OVER LP window RP", - /* 341 */ "over_clause ::= OVER nm", - /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 343 */ "term ::= QNUMBER", - /* 344 */ "input ::= cmdlist", - /* 345 */ "cmdlist ::= cmdlist ecmd", - /* 346 */ "cmdlist ::= ecmd", - /* 347 */ "ecmd ::= SEMI", - /* 348 */ "ecmd ::= cmdx SEMI", - /* 349 */ "ecmd ::= explain cmdx SEMI", - /* 350 */ "trans_opt ::=", - /* 351 */ "trans_opt ::= TRANSACTION", - /* 352 */ "trans_opt ::= TRANSACTION nm", - /* 353 */ "savepoint_opt ::= SAVEPOINT", - /* 354 */ "savepoint_opt ::=", - /* 355 */ "cmd ::= create_table create_table_args", - /* 356 */ "table_option_set ::= table_option", - /* 357 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 358 */ "columnlist ::= columnname carglist", - /* 359 */ "nm ::= ID|INDEXED|JOIN_KW", - /* 360 */ "nm ::= STRING", - /* 361 */ "typetoken ::= typename", - /* 362 */ "typename ::= ID|STRING", - /* 363 */ "signed ::= plus_num", - /* 364 */ "signed ::= minus_num", - /* 365 */ "carglist ::= carglist ccons", - /* 366 */ "carglist ::=", - /* 367 */ "ccons ::= NULL onconf", - /* 368 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 369 */ "ccons ::= AS generated", - /* 370 */ "conslist_opt ::= COMMA conslist", - /* 371 */ "conslist ::= conslist tconscomma tcons", - /* 372 */ "conslist ::= tcons", - /* 373 */ "tconscomma ::=", - /* 374 */ "defer_subclause_opt ::= defer_subclause", - /* 375 */ "resolvetype ::= raisetype", - /* 376 */ "selectnowith ::= oneselect", - /* 377 */ "oneselect ::= values", - /* 378 */ "sclp ::= selcollist COMMA", - /* 379 */ "as ::= ID|STRING", - /* 380 */ "indexed_opt ::= indexed_by", - /* 381 */ "returning ::=", - /* 382 */ "expr ::= term", - /* 383 */ "likeop ::= LIKE_KW|MATCH", - /* 384 */ "case_operand ::= expr", - /* 385 */ "exprlist ::= nexprlist", - /* 386 */ "nmnum ::= plus_num", - /* 387 */ "nmnum ::= nm", - /* 388 */ "nmnum ::= ON", - /* 389 */ "nmnum ::= DELETE", - /* 390 */ "nmnum ::= DEFAULT", - /* 391 */ "plus_num ::= INTEGER|FLOAT", - /* 392 */ "foreach_clause ::=", - /* 393 */ "foreach_clause ::= FOR EACH ROW", - /* 394 */ "trnm ::= nm", - /* 395 */ "tridxby ::=", - /* 396 */ "database_kw_opt ::= DATABASE", - /* 397 */ "database_kw_opt ::=", - /* 398 */ "kwcolumn_opt ::=", - /* 399 */ "kwcolumn_opt ::= COLUMNKW", - /* 400 */ "vtabarglist ::= vtabarg", - /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 402 */ "vtabarg ::= vtabarg vtabargtoken", - /* 403 */ "anylist ::=", - /* 404 */ "anylist ::= anylist LP anylist RP", - /* 405 */ "anylist ::= anylist ANY", - /* 406 */ "with ::=", - /* 407 */ "windowdefn_list ::= windowdefn", - /* 408 */ "window ::= frame_opt", + /* 296 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 297 */ "cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm", + /* 298 */ "cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL", + /* 299 */ "cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf", + /* 300 */ "cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf", + /* 301 */ "cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf", + /* 302 */ "cmd ::= create_vtab", + /* 303 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 304 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 305 */ "vtabarg ::=", + /* 306 */ "vtabargtoken ::= ANY", + /* 307 */ "vtabargtoken ::= lp anylist RP", + /* 308 */ "lp ::= LP", + /* 309 */ "with ::= WITH wqlist", + /* 310 */ "with ::= WITH RECURSIVE wqlist", + /* 311 */ "wqas ::= AS", + /* 312 */ "wqas ::= AS MATERIALIZED", + /* 313 */ "wqas ::= AS NOT MATERIALIZED", + /* 314 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", + /* 315 */ "withnm ::= nm", + /* 316 */ "wqlist ::= wqitem", + /* 317 */ "wqlist ::= wqlist COMMA wqitem", + /* 318 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 319 */ "windowdefn ::= nm AS LP window RP", + /* 320 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 321 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 322 */ "window ::= ORDER BY sortlist frame_opt", + /* 323 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 324 */ "window ::= nm frame_opt", + /* 325 */ "frame_opt ::=", + /* 326 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 327 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 328 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 329 */ "frame_bound_s ::= frame_bound", + /* 330 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 331 */ "frame_bound_e ::= frame_bound", + /* 332 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 333 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 334 */ "frame_bound ::= CURRENT ROW", + /* 335 */ "frame_exclude_opt ::=", + /* 336 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 337 */ "frame_exclude ::= NO OTHERS", + /* 338 */ "frame_exclude ::= CURRENT ROW", + /* 339 */ "frame_exclude ::= GROUP|TIES", + /* 340 */ "window_clause ::= WINDOW windowdefn_list", + /* 341 */ "filter_over ::= filter_clause over_clause", + /* 342 */ "filter_over ::= over_clause", + /* 343 */ "filter_over ::= filter_clause", + /* 344 */ "over_clause ::= OVER LP window RP", + /* 345 */ "over_clause ::= OVER nm", + /* 346 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 347 */ "term ::= QNUMBER", + /* 348 */ "input ::= cmdlist", + /* 349 */ "cmdlist ::= cmdlist ecmd", + /* 350 */ "cmdlist ::= ecmd", + /* 351 */ "ecmd ::= SEMI", + /* 352 */ "ecmd ::= cmdx SEMI", + /* 353 */ "ecmd ::= explain cmdx SEMI", + /* 354 */ "trans_opt ::=", + /* 355 */ "trans_opt ::= TRANSACTION", + /* 356 */ "trans_opt ::= TRANSACTION nm", + /* 357 */ "savepoint_opt ::= SAVEPOINT", + /* 358 */ "savepoint_opt ::=", + /* 359 */ "cmd ::= create_table create_table_args", + /* 360 */ "table_option_set ::= table_option", + /* 361 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 362 */ "columnlist ::= columnname carglist", + /* 363 */ "nm ::= ID|INDEXED|JOIN_KW", + /* 364 */ "nm ::= STRING", + /* 365 */ "typetoken ::= typename", + /* 366 */ "typename ::= ID|STRING", + /* 367 */ "signed ::= plus_num", + /* 368 */ "signed ::= minus_num", + /* 369 */ "carglist ::= carglist ccons", + /* 370 */ "carglist ::=", + /* 371 */ "ccons ::= NULL onconf", + /* 372 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 373 */ "ccons ::= AS generated", + /* 374 */ "conslist_opt ::= COMMA conslist", + /* 375 */ "conslist ::= conslist tconscomma tcons", + /* 376 */ "conslist ::= tcons", + /* 377 */ "tconscomma ::=", + /* 378 */ "defer_subclause_opt ::= defer_subclause", + /* 379 */ "resolvetype ::= raisetype", + /* 380 */ "selectnowith ::= oneselect", + /* 381 */ "oneselect ::= values", + /* 382 */ "sclp ::= selcollist COMMA", + /* 383 */ "as ::= ID|STRING", + /* 384 */ "indexed_opt ::= indexed_by", + /* 385 */ "returning ::=", + /* 386 */ "expr ::= term", + /* 387 */ "likeop ::= LIKE_KW|MATCH", + /* 388 */ "case_operand ::= expr", + /* 389 */ "exprlist ::= nexprlist", + /* 390 */ "nmnum ::= plus_num", + /* 391 */ "nmnum ::= nm", + /* 392 */ "nmnum ::= ON", + /* 393 */ "nmnum ::= DELETE", + /* 394 */ "nmnum ::= DEFAULT", + /* 395 */ "plus_num ::= INTEGER|FLOAT", + /* 396 */ "foreach_clause ::=", + /* 397 */ "foreach_clause ::= FOR EACH ROW", + /* 398 */ "tridxby ::=", + /* 399 */ "database_kw_opt ::= DATABASE", + /* 400 */ "database_kw_opt ::=", + /* 401 */ "kwcolumn_opt ::=", + /* 402 */ "kwcolumn_opt ::= COLUMNKW", + /* 403 */ "vtabarglist ::= vtabarg", + /* 404 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 405 */ "vtabarg ::= vtabarg vtabargtoken", + /* 406 */ "anylist ::=", + /* 407 */ "anylist ::= anylist LP anylist RP", + /* 408 */ "anylist ::= anylist ANY", + /* 409 */ "with ::=", + /* 410 */ "windowdefn_list ::= windowdefn", + /* 411 */ "window ::= frame_opt", }; #endif /* NDEBUG */ @@ -179860,15 +182271,24 @@ static int yyGrowStack(yyParser *p){ int newSize; int idx; yyStackEntry *pNew; +#ifdef YYSIZELIMIT + int nLimit = YYSIZELIMIT(sqlite3ParserCTX(p)); +#endif newSize = oldSize*2 + 100; +#ifdef YYSIZELIMIT + if( newSize>nLimit ){ + newSize = nLimit; + if( newSize<=oldSize ) return 1; + } +#endif idx = (int)(p->yytos - p->yystack); if( p->yystack==p->yystk0 ){ - pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); + pNew = YYREALLOC(0, newSize*sizeof(pNew[0]), sqlite3ParserCTX(p)); if( pNew==0 ) return 1; memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); + pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]), sqlite3ParserCTX(p)); if( pNew==0 ) return 1; } p->yystack = pNew; @@ -179975,7 +182395,7 @@ static void yy_destructor( case 254: /* values */ case 256: /* mvalues */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy637)); +sqlite3SelectDelete(pParse->db, (yypminor->yy555)); } break; case 218: /* term */ @@ -179987,10 +182407,10 @@ sqlite3SelectDelete(pParse->db, (yypminor->yy637)); case 283: /* case_else */ case 286: /* vinto */ case 293: /* when_clause */ - case 298: /* key_opt */ - case 315: /* filter_clause */ + case 297: /* key_opt */ + case 314: /* filter_clause */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy590)); +sqlite3ExprDelete(pParse->db, (yypminor->yy454)); } break; case 223: /* eidlist_opt */ @@ -180005,9 +182425,9 @@ sqlite3ExprDelete(pParse->db, (yypminor->yy590)); case 271: /* setlist */ case 280: /* paren_exprlist */ case 282: /* case_exprlist */ - case 314: /* part_opt */ + case 313: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy402)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); } break; case 240: /* fullname */ @@ -180016,51 +182436,51 @@ sqlite3ExprListDelete(pParse->db, (yypminor->yy402)); case 260: /* stl_prefix */ case 265: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy563)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy203)); } break; case 243: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy125)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; case 253: /* window_clause */ - case 310: /* windowdefn_list */ + case 309: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy483)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy211)); } break; case 266: /* idlist */ case 273: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy204)); +sqlite3IdListDelete(pParse->db, (yypminor->yy132)); } break; case 276: /* filter_over */ - case 311: /* windowdefn */ - case 312: /* window */ - case 313: /* frame_opt */ - case 316: /* over_clause */ + case 310: /* windowdefn */ + case 311: /* window */ + case 312: /* frame_opt */ + case 315: /* over_clause */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy483)); +sqlite3WindowDelete(pParse->db, (yypminor->yy211)); } break; case 289: /* trigger_cmd_list */ case 294: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427)); } break; case 291: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy28).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy286).b); } break; - case 318: /* frame_bound */ - case 319: /* frame_bound_s */ - case 320: /* frame_bound_e */ + case 317: /* frame_bound */ + case 318: /* frame_bound_s */ + case 319: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -180113,7 +182533,9 @@ SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){ } #if YYGROWABLESTACK - if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); + if( pParser->yystack!=pParser->yystk0 ){ + YYFREE(pParser->yystack, sqlite3ParserCTX(pParser)); + } #endif } @@ -180296,7 +182718,7 @@ static void yyStackOverflow(yyParser *yypParser){ ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ - sqlite3OomFault(pParse->db); + if( pParse->nErr==0 ) sqlite3ErrorMsg(pParse, "Recursion limit"); /******** End %stack_overflow code ********************************************/ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */ sqlite3ParserCTX_STORE @@ -180484,8 +182906,8 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 240, /* (119) fullname ::= nm DOT nm */ 265, /* (120) xfullname ::= nm */ 265, /* (121) xfullname ::= nm DOT nm */ - 265, /* (122) xfullname ::= nm DOT nm AS nm */ - 265, /* (123) xfullname ::= nm AS nm */ + 265, /* (122) xfullname ::= nm AS nm */ + 265, /* (123) xfullname ::= nm DOT nm AS nm */ 261, /* (124) joinop ::= COMMA|JOIN */ 261, /* (125) joinop ::= JOIN_KW JOIN */ 261, /* (126) joinop ::= JOIN_KW nm JOIN */ @@ -180634,143 +183056,146 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 293, /* (269) when_clause ::= WHEN expr */ 289, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ 289, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ - 295, /* (272) trnm ::= nm DOT nm */ - 296, /* (273) tridxby ::= INDEXED BY nm */ - 296, /* (274) tridxby ::= NOT INDEXED */ - 294, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 294, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 294, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 294, /* (278) trigger_cmd ::= scanpt select scanpt */ - 219, /* (279) expr ::= RAISE LP IGNORE RP */ - 219, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ - 238, /* (281) raisetype ::= ROLLBACK */ - 238, /* (282) raisetype ::= ABORT */ - 238, /* (283) raisetype ::= FAIL */ - 192, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ - 192, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 192, /* (286) cmd ::= DETACH database_kw_opt expr */ - 298, /* (287) key_opt ::= */ - 298, /* (288) key_opt ::= KEY expr */ - 192, /* (289) cmd ::= REINDEX */ - 192, /* (290) cmd ::= REINDEX nm dbnm */ - 192, /* (291) cmd ::= ANALYZE */ - 192, /* (292) cmd ::= ANALYZE nm dbnm */ - 192, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 192, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 295, /* (272) tridxby ::= INDEXED BY nm */ + 295, /* (273) tridxby ::= NOT INDEXED */ + 294, /* (274) trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */ + 294, /* (275) trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */ + 294, /* (276) trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */ + 294, /* (277) trigger_cmd ::= scanpt select scanpt */ + 219, /* (278) expr ::= RAISE LP IGNORE RP */ + 219, /* (279) expr ::= RAISE LP raisetype COMMA expr RP */ + 238, /* (280) raisetype ::= ROLLBACK */ + 238, /* (281) raisetype ::= ABORT */ + 238, /* (282) raisetype ::= FAIL */ + 192, /* (283) cmd ::= DROP TRIGGER ifexists fullname */ + 192, /* (284) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 192, /* (285) cmd ::= DETACH database_kw_opt expr */ + 297, /* (286) key_opt ::= */ + 297, /* (287) key_opt ::= KEY expr */ + 192, /* (288) cmd ::= REINDEX */ + 192, /* (289) cmd ::= REINDEX nm dbnm */ + 192, /* (290) cmd ::= ANALYZE */ + 192, /* (291) cmd ::= ANALYZE nm dbnm */ + 192, /* (292) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 192, /* (293) cmd ::= alter_add carglist */ + 298, /* (294) alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */ 192, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - 299, /* (296) add_column_fullname ::= fullname */ - 192, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 192, /* (298) cmd ::= create_vtab */ - 192, /* (299) cmd ::= create_vtab LP vtabarglist RP */ - 301, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 303, /* (301) vtabarg ::= */ - 304, /* (302) vtabargtoken ::= ANY */ - 304, /* (303) vtabargtoken ::= lp anylist RP */ - 305, /* (304) lp ::= LP */ - 269, /* (305) with ::= WITH wqlist */ - 269, /* (306) with ::= WITH RECURSIVE wqlist */ - 308, /* (307) wqas ::= AS */ - 308, /* (308) wqas ::= AS MATERIALIZED */ - 308, /* (309) wqas ::= AS NOT MATERIALIZED */ - 307, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ - 309, /* (311) withnm ::= nm */ - 243, /* (312) wqlist ::= wqitem */ - 243, /* (313) wqlist ::= wqlist COMMA wqitem */ - 310, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 311, /* (315) windowdefn ::= nm AS LP window RP */ - 312, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 312, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 312, /* (318) window ::= ORDER BY sortlist frame_opt */ - 312, /* (319) window ::= nm ORDER BY sortlist frame_opt */ - 312, /* (320) window ::= nm frame_opt */ - 313, /* (321) frame_opt ::= */ - 313, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 313, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 317, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ - 319, /* (325) frame_bound_s ::= frame_bound */ - 319, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ - 320, /* (327) frame_bound_e ::= frame_bound */ - 320, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 318, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ - 318, /* (330) frame_bound ::= CURRENT ROW */ - 321, /* (331) frame_exclude_opt ::= */ - 321, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 322, /* (333) frame_exclude ::= NO OTHERS */ - 322, /* (334) frame_exclude ::= CURRENT ROW */ - 322, /* (335) frame_exclude ::= GROUP|TIES */ - 253, /* (336) window_clause ::= WINDOW windowdefn_list */ - 276, /* (337) filter_over ::= filter_clause over_clause */ - 276, /* (338) filter_over ::= over_clause */ - 276, /* (339) filter_over ::= filter_clause */ - 316, /* (340) over_clause ::= OVER LP window RP */ - 316, /* (341) over_clause ::= OVER nm */ - 315, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ - 218, /* (343) term ::= QNUMBER */ - 187, /* (344) input ::= cmdlist */ - 188, /* (345) cmdlist ::= cmdlist ecmd */ - 188, /* (346) cmdlist ::= ecmd */ - 189, /* (347) ecmd ::= SEMI */ - 189, /* (348) ecmd ::= cmdx SEMI */ - 189, /* (349) ecmd ::= explain cmdx SEMI */ - 194, /* (350) trans_opt ::= */ - 194, /* (351) trans_opt ::= TRANSACTION */ - 194, /* (352) trans_opt ::= TRANSACTION nm */ - 196, /* (353) savepoint_opt ::= SAVEPOINT */ - 196, /* (354) savepoint_opt ::= */ - 192, /* (355) cmd ::= create_table create_table_args */ - 205, /* (356) table_option_set ::= table_option */ - 203, /* (357) columnlist ::= columnlist COMMA columnname carglist */ - 203, /* (358) columnlist ::= columnname carglist */ - 195, /* (359) nm ::= ID|INDEXED|JOIN_KW */ - 195, /* (360) nm ::= STRING */ - 210, /* (361) typetoken ::= typename */ - 211, /* (362) typename ::= ID|STRING */ - 212, /* (363) signed ::= plus_num */ - 212, /* (364) signed ::= minus_num */ - 209, /* (365) carglist ::= carglist ccons */ - 209, /* (366) carglist ::= */ - 217, /* (367) ccons ::= NULL onconf */ - 217, /* (368) ccons ::= GENERATED ALWAYS AS generated */ - 217, /* (369) ccons ::= AS generated */ - 204, /* (370) conslist_opt ::= COMMA conslist */ - 230, /* (371) conslist ::= conslist tconscomma tcons */ - 230, /* (372) conslist ::= tcons */ - 231, /* (373) tconscomma ::= */ - 235, /* (374) defer_subclause_opt ::= defer_subclause */ - 237, /* (375) resolvetype ::= raisetype */ - 241, /* (376) selectnowith ::= oneselect */ - 242, /* (377) oneselect ::= values */ - 257, /* (378) sclp ::= selcollist COMMA */ - 258, /* (379) as ::= ID|STRING */ - 267, /* (380) indexed_opt ::= indexed_by */ - 275, /* (381) returning ::= */ - 219, /* (382) expr ::= term */ - 277, /* (383) likeop ::= LIKE_KW|MATCH */ - 281, /* (384) case_operand ::= expr */ - 264, /* (385) exprlist ::= nexprlist */ - 287, /* (386) nmnum ::= plus_num */ - 287, /* (387) nmnum ::= nm */ - 287, /* (388) nmnum ::= ON */ - 287, /* (389) nmnum ::= DELETE */ - 287, /* (390) nmnum ::= DEFAULT */ - 213, /* (391) plus_num ::= INTEGER|FLOAT */ - 292, /* (392) foreach_clause ::= */ - 292, /* (393) foreach_clause ::= FOR EACH ROW */ - 295, /* (394) trnm ::= nm */ - 296, /* (395) tridxby ::= */ - 297, /* (396) database_kw_opt ::= DATABASE */ - 297, /* (397) database_kw_opt ::= */ - 300, /* (398) kwcolumn_opt ::= */ - 300, /* (399) kwcolumn_opt ::= COLUMNKW */ - 302, /* (400) vtabarglist ::= vtabarg */ - 302, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ - 303, /* (402) vtabarg ::= vtabarg vtabargtoken */ - 306, /* (403) anylist ::= */ - 306, /* (404) anylist ::= anylist LP anylist RP */ - 306, /* (405) anylist ::= anylist ANY */ - 269, /* (406) with ::= */ - 310, /* (407) windowdefn_list ::= windowdefn */ - 312, /* (408) window ::= frame_opt */ + 192, /* (296) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 192, /* (297) cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */ + 192, /* (298) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */ + 192, /* (299) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */ + 192, /* (300) cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */ + 192, /* (301) cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */ + 192, /* (302) cmd ::= create_vtab */ + 192, /* (303) cmd ::= create_vtab LP vtabarglist RP */ + 300, /* (304) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 302, /* (305) vtabarg ::= */ + 303, /* (306) vtabargtoken ::= ANY */ + 303, /* (307) vtabargtoken ::= lp anylist RP */ + 304, /* (308) lp ::= LP */ + 269, /* (309) with ::= WITH wqlist */ + 269, /* (310) with ::= WITH RECURSIVE wqlist */ + 307, /* (311) wqas ::= AS */ + 307, /* (312) wqas ::= AS MATERIALIZED */ + 307, /* (313) wqas ::= AS NOT MATERIALIZED */ + 306, /* (314) wqitem ::= withnm eidlist_opt wqas LP select RP */ + 308, /* (315) withnm ::= nm */ + 243, /* (316) wqlist ::= wqitem */ + 243, /* (317) wqlist ::= wqlist COMMA wqitem */ + 309, /* (318) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 310, /* (319) windowdefn ::= nm AS LP window RP */ + 311, /* (320) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (321) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (322) window ::= ORDER BY sortlist frame_opt */ + 311, /* (323) window ::= nm ORDER BY sortlist frame_opt */ + 311, /* (324) window ::= nm frame_opt */ + 312, /* (325) frame_opt ::= */ + 312, /* (326) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 312, /* (327) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 316, /* (328) range_or_rows ::= RANGE|ROWS|GROUPS */ + 318, /* (329) frame_bound_s ::= frame_bound */ + 318, /* (330) frame_bound_s ::= UNBOUNDED PRECEDING */ + 319, /* (331) frame_bound_e ::= frame_bound */ + 319, /* (332) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 317, /* (333) frame_bound ::= expr PRECEDING|FOLLOWING */ + 317, /* (334) frame_bound ::= CURRENT ROW */ + 320, /* (335) frame_exclude_opt ::= */ + 320, /* (336) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 321, /* (337) frame_exclude ::= NO OTHERS */ + 321, /* (338) frame_exclude ::= CURRENT ROW */ + 321, /* (339) frame_exclude ::= GROUP|TIES */ + 253, /* (340) window_clause ::= WINDOW windowdefn_list */ + 276, /* (341) filter_over ::= filter_clause over_clause */ + 276, /* (342) filter_over ::= over_clause */ + 276, /* (343) filter_over ::= filter_clause */ + 315, /* (344) over_clause ::= OVER LP window RP */ + 315, /* (345) over_clause ::= OVER nm */ + 314, /* (346) filter_clause ::= FILTER LP WHERE expr RP */ + 218, /* (347) term ::= QNUMBER */ + 187, /* (348) input ::= cmdlist */ + 188, /* (349) cmdlist ::= cmdlist ecmd */ + 188, /* (350) cmdlist ::= ecmd */ + 189, /* (351) ecmd ::= SEMI */ + 189, /* (352) ecmd ::= cmdx SEMI */ + 189, /* (353) ecmd ::= explain cmdx SEMI */ + 194, /* (354) trans_opt ::= */ + 194, /* (355) trans_opt ::= TRANSACTION */ + 194, /* (356) trans_opt ::= TRANSACTION nm */ + 196, /* (357) savepoint_opt ::= SAVEPOINT */ + 196, /* (358) savepoint_opt ::= */ + 192, /* (359) cmd ::= create_table create_table_args */ + 205, /* (360) table_option_set ::= table_option */ + 203, /* (361) columnlist ::= columnlist COMMA columnname carglist */ + 203, /* (362) columnlist ::= columnname carglist */ + 195, /* (363) nm ::= ID|INDEXED|JOIN_KW */ + 195, /* (364) nm ::= STRING */ + 210, /* (365) typetoken ::= typename */ + 211, /* (366) typename ::= ID|STRING */ + 212, /* (367) signed ::= plus_num */ + 212, /* (368) signed ::= minus_num */ + 209, /* (369) carglist ::= carglist ccons */ + 209, /* (370) carglist ::= */ + 217, /* (371) ccons ::= NULL onconf */ + 217, /* (372) ccons ::= GENERATED ALWAYS AS generated */ + 217, /* (373) ccons ::= AS generated */ + 204, /* (374) conslist_opt ::= COMMA conslist */ + 230, /* (375) conslist ::= conslist tconscomma tcons */ + 230, /* (376) conslist ::= tcons */ + 231, /* (377) tconscomma ::= */ + 235, /* (378) defer_subclause_opt ::= defer_subclause */ + 237, /* (379) resolvetype ::= raisetype */ + 241, /* (380) selectnowith ::= oneselect */ + 242, /* (381) oneselect ::= values */ + 257, /* (382) sclp ::= selcollist COMMA */ + 258, /* (383) as ::= ID|STRING */ + 267, /* (384) indexed_opt ::= indexed_by */ + 275, /* (385) returning ::= */ + 219, /* (386) expr ::= term */ + 277, /* (387) likeop ::= LIKE_KW|MATCH */ + 281, /* (388) case_operand ::= expr */ + 264, /* (389) exprlist ::= nexprlist */ + 287, /* (390) nmnum ::= plus_num */ + 287, /* (391) nmnum ::= nm */ + 287, /* (392) nmnum ::= ON */ + 287, /* (393) nmnum ::= DELETE */ + 287, /* (394) nmnum ::= DEFAULT */ + 213, /* (395) plus_num ::= INTEGER|FLOAT */ + 292, /* (396) foreach_clause ::= */ + 292, /* (397) foreach_clause ::= FOR EACH ROW */ + 295, /* (398) tridxby ::= */ + 296, /* (399) database_kw_opt ::= DATABASE */ + 296, /* (400) database_kw_opt ::= */ + 299, /* (401) kwcolumn_opt ::= */ + 299, /* (402) kwcolumn_opt ::= COLUMNKW */ + 301, /* (403) vtabarglist ::= vtabarg */ + 301, /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */ + 302, /* (405) vtabarg ::= vtabarg vtabargtoken */ + 305, /* (406) anylist ::= */ + 305, /* (407) anylist ::= anylist LP anylist RP */ + 305, /* (408) anylist ::= anylist ANY */ + 269, /* (409) with ::= */ + 309, /* (410) windowdefn_list ::= windowdefn */ + 311, /* (411) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -180898,8 +183323,8 @@ static const signed char yyRuleInfoNRhs[] = { -3, /* (119) fullname ::= nm DOT nm */ -1, /* (120) xfullname ::= nm */ -3, /* (121) xfullname ::= nm DOT nm */ - -5, /* (122) xfullname ::= nm DOT nm AS nm */ - -3, /* (123) xfullname ::= nm AS nm */ + -3, /* (122) xfullname ::= nm AS nm */ + -5, /* (123) xfullname ::= nm DOT nm AS nm */ -1, /* (124) joinop ::= COMMA|JOIN */ -2, /* (125) joinop ::= JOIN_KW JOIN */ -3, /* (126) joinop ::= JOIN_KW nm JOIN */ @@ -181048,143 +183473,146 @@ static const signed char yyRuleInfoNRhs[] = { -2, /* (269) when_clause ::= WHEN expr */ -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ - -3, /* (272) trnm ::= nm DOT nm */ - -3, /* (273) tridxby ::= INDEXED BY nm */ - -2, /* (274) tridxby ::= NOT INDEXED */ - -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - -3, /* (278) trigger_cmd ::= scanpt select scanpt */ - -4, /* (279) expr ::= RAISE LP IGNORE RP */ - -6, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */ - -1, /* (281) raisetype ::= ROLLBACK */ - -1, /* (282) raisetype ::= ABORT */ - -1, /* (283) raisetype ::= FAIL */ - -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ - -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - -3, /* (286) cmd ::= DETACH database_kw_opt expr */ - 0, /* (287) key_opt ::= */ - -2, /* (288) key_opt ::= KEY expr */ - -1, /* (289) cmd ::= REINDEX */ - -3, /* (290) cmd ::= REINDEX nm dbnm */ - -1, /* (291) cmd ::= ANALYZE */ - -3, /* (292) cmd ::= ANALYZE nm dbnm */ - -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ - -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -3, /* (272) tridxby ::= INDEXED BY nm */ + -2, /* (273) tridxby ::= NOT INDEXED */ + -9, /* (274) trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */ + -8, /* (275) trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */ + -6, /* (276) trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */ + -3, /* (277) trigger_cmd ::= scanpt select scanpt */ + -4, /* (278) expr ::= RAISE LP IGNORE RP */ + -6, /* (279) expr ::= RAISE LP raisetype COMMA expr RP */ + -1, /* (280) raisetype ::= ROLLBACK */ + -1, /* (281) raisetype ::= ABORT */ + -1, /* (282) raisetype ::= FAIL */ + -4, /* (283) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (284) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (285) cmd ::= DETACH database_kw_opt expr */ + 0, /* (286) key_opt ::= */ + -2, /* (287) key_opt ::= KEY expr */ + -1, /* (288) cmd ::= REINDEX */ + -3, /* (289) cmd ::= REINDEX nm dbnm */ + -1, /* (290) cmd ::= ANALYZE */ + -3, /* (291) cmd ::= ANALYZE nm dbnm */ + -6, /* (292) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -2, /* (293) cmd ::= alter_add carglist */ + -7, /* (294) alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */ -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - -1, /* (296) add_column_fullname ::= fullname */ - -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - -1, /* (298) cmd ::= create_vtab */ - -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */ - -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 0, /* (301) vtabarg ::= */ - -1, /* (302) vtabargtoken ::= ANY */ - -3, /* (303) vtabargtoken ::= lp anylist RP */ - -1, /* (304) lp ::= LP */ - -2, /* (305) with ::= WITH wqlist */ - -3, /* (306) with ::= WITH RECURSIVE wqlist */ - -1, /* (307) wqas ::= AS */ - -2, /* (308) wqas ::= AS MATERIALIZED */ - -3, /* (309) wqas ::= AS NOT MATERIALIZED */ - -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ - -1, /* (311) withnm ::= nm */ - -1, /* (312) wqlist ::= wqitem */ - -3, /* (313) wqlist ::= wqlist COMMA wqitem */ - -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (315) windowdefn ::= nm AS LP window RP */ - -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (318) window ::= ORDER BY sortlist frame_opt */ - -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */ - -2, /* (320) window ::= nm frame_opt */ - 0, /* (321) frame_opt ::= */ - -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (325) frame_bound_s ::= frame_bound */ - -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (327) frame_bound_e ::= frame_bound */ - -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (330) frame_bound ::= CURRENT ROW */ - 0, /* (331) frame_exclude_opt ::= */ - -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (333) frame_exclude ::= NO OTHERS */ - -2, /* (334) frame_exclude ::= CURRENT ROW */ - -1, /* (335) frame_exclude ::= GROUP|TIES */ - -2, /* (336) window_clause ::= WINDOW windowdefn_list */ - -2, /* (337) filter_over ::= filter_clause over_clause */ - -1, /* (338) filter_over ::= over_clause */ - -1, /* (339) filter_over ::= filter_clause */ - -4, /* (340) over_clause ::= OVER LP window RP */ - -2, /* (341) over_clause ::= OVER nm */ - -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (343) term ::= QNUMBER */ - -1, /* (344) input ::= cmdlist */ - -2, /* (345) cmdlist ::= cmdlist ecmd */ - -1, /* (346) cmdlist ::= ecmd */ - -1, /* (347) ecmd ::= SEMI */ - -2, /* (348) ecmd ::= cmdx SEMI */ - -3, /* (349) ecmd ::= explain cmdx SEMI */ - 0, /* (350) trans_opt ::= */ - -1, /* (351) trans_opt ::= TRANSACTION */ - -2, /* (352) trans_opt ::= TRANSACTION nm */ - -1, /* (353) savepoint_opt ::= SAVEPOINT */ - 0, /* (354) savepoint_opt ::= */ - -2, /* (355) cmd ::= create_table create_table_args */ - -1, /* (356) table_option_set ::= table_option */ - -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (358) columnlist ::= columnname carglist */ - -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */ - -1, /* (360) nm ::= STRING */ - -1, /* (361) typetoken ::= typename */ - -1, /* (362) typename ::= ID|STRING */ - -1, /* (363) signed ::= plus_num */ - -1, /* (364) signed ::= minus_num */ - -2, /* (365) carglist ::= carglist ccons */ - 0, /* (366) carglist ::= */ - -2, /* (367) ccons ::= NULL onconf */ - -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (369) ccons ::= AS generated */ - -2, /* (370) conslist_opt ::= COMMA conslist */ - -3, /* (371) conslist ::= conslist tconscomma tcons */ - -1, /* (372) conslist ::= tcons */ - 0, /* (373) tconscomma ::= */ - -1, /* (374) defer_subclause_opt ::= defer_subclause */ - -1, /* (375) resolvetype ::= raisetype */ - -1, /* (376) selectnowith ::= oneselect */ - -1, /* (377) oneselect ::= values */ - -2, /* (378) sclp ::= selcollist COMMA */ - -1, /* (379) as ::= ID|STRING */ - -1, /* (380) indexed_opt ::= indexed_by */ - 0, /* (381) returning ::= */ - -1, /* (382) expr ::= term */ - -1, /* (383) likeop ::= LIKE_KW|MATCH */ - -1, /* (384) case_operand ::= expr */ - -1, /* (385) exprlist ::= nexprlist */ - -1, /* (386) nmnum ::= plus_num */ - -1, /* (387) nmnum ::= nm */ - -1, /* (388) nmnum ::= ON */ - -1, /* (389) nmnum ::= DELETE */ - -1, /* (390) nmnum ::= DEFAULT */ - -1, /* (391) plus_num ::= INTEGER|FLOAT */ - 0, /* (392) foreach_clause ::= */ - -3, /* (393) foreach_clause ::= FOR EACH ROW */ - -1, /* (394) trnm ::= nm */ - 0, /* (395) tridxby ::= */ - -1, /* (396) database_kw_opt ::= DATABASE */ - 0, /* (397) database_kw_opt ::= */ - 0, /* (398) kwcolumn_opt ::= */ - -1, /* (399) kwcolumn_opt ::= COLUMNKW */ - -1, /* (400) vtabarglist ::= vtabarg */ - -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (402) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (403) anylist ::= */ - -4, /* (404) anylist ::= anylist LP anylist RP */ - -2, /* (405) anylist ::= anylist ANY */ - 0, /* (406) with ::= */ - -1, /* (407) windowdefn_list ::= windowdefn */ - -1, /* (408) window ::= frame_opt */ + -8, /* (296) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -6, /* (297) cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */ + -9, /* (298) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */ + -10, /* (299) cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */ + -11, /* (300) cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */ + -9, /* (301) cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */ + -1, /* (302) cmd ::= create_vtab */ + -4, /* (303) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (304) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (305) vtabarg ::= */ + -1, /* (306) vtabargtoken ::= ANY */ + -3, /* (307) vtabargtoken ::= lp anylist RP */ + -1, /* (308) lp ::= LP */ + -2, /* (309) with ::= WITH wqlist */ + -3, /* (310) with ::= WITH RECURSIVE wqlist */ + -1, /* (311) wqas ::= AS */ + -2, /* (312) wqas ::= AS MATERIALIZED */ + -3, /* (313) wqas ::= AS NOT MATERIALIZED */ + -6, /* (314) wqitem ::= withnm eidlist_opt wqas LP select RP */ + -1, /* (315) withnm ::= nm */ + -1, /* (316) wqlist ::= wqitem */ + -3, /* (317) wqlist ::= wqlist COMMA wqitem */ + -3, /* (318) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (319) windowdefn ::= nm AS LP window RP */ + -5, /* (320) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (321) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (322) window ::= ORDER BY sortlist frame_opt */ + -5, /* (323) window ::= nm ORDER BY sortlist frame_opt */ + -2, /* (324) window ::= nm frame_opt */ + 0, /* (325) frame_opt ::= */ + -3, /* (326) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (327) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (328) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (329) frame_bound_s ::= frame_bound */ + -2, /* (330) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (331) frame_bound_e ::= frame_bound */ + -2, /* (332) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (333) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (334) frame_bound ::= CURRENT ROW */ + 0, /* (335) frame_exclude_opt ::= */ + -2, /* (336) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (337) frame_exclude ::= NO OTHERS */ + -2, /* (338) frame_exclude ::= CURRENT ROW */ + -1, /* (339) frame_exclude ::= GROUP|TIES */ + -2, /* (340) window_clause ::= WINDOW windowdefn_list */ + -2, /* (341) filter_over ::= filter_clause over_clause */ + -1, /* (342) filter_over ::= over_clause */ + -1, /* (343) filter_over ::= filter_clause */ + -4, /* (344) over_clause ::= OVER LP window RP */ + -2, /* (345) over_clause ::= OVER nm */ + -5, /* (346) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (347) term ::= QNUMBER */ + -1, /* (348) input ::= cmdlist */ + -2, /* (349) cmdlist ::= cmdlist ecmd */ + -1, /* (350) cmdlist ::= ecmd */ + -1, /* (351) ecmd ::= SEMI */ + -2, /* (352) ecmd ::= cmdx SEMI */ + -3, /* (353) ecmd ::= explain cmdx SEMI */ + 0, /* (354) trans_opt ::= */ + -1, /* (355) trans_opt ::= TRANSACTION */ + -2, /* (356) trans_opt ::= TRANSACTION nm */ + -1, /* (357) savepoint_opt ::= SAVEPOINT */ + 0, /* (358) savepoint_opt ::= */ + -2, /* (359) cmd ::= create_table create_table_args */ + -1, /* (360) table_option_set ::= table_option */ + -4, /* (361) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (362) columnlist ::= columnname carglist */ + -1, /* (363) nm ::= ID|INDEXED|JOIN_KW */ + -1, /* (364) nm ::= STRING */ + -1, /* (365) typetoken ::= typename */ + -1, /* (366) typename ::= ID|STRING */ + -1, /* (367) signed ::= plus_num */ + -1, /* (368) signed ::= minus_num */ + -2, /* (369) carglist ::= carglist ccons */ + 0, /* (370) carglist ::= */ + -2, /* (371) ccons ::= NULL onconf */ + -4, /* (372) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (373) ccons ::= AS generated */ + -2, /* (374) conslist_opt ::= COMMA conslist */ + -3, /* (375) conslist ::= conslist tconscomma tcons */ + -1, /* (376) conslist ::= tcons */ + 0, /* (377) tconscomma ::= */ + -1, /* (378) defer_subclause_opt ::= defer_subclause */ + -1, /* (379) resolvetype ::= raisetype */ + -1, /* (380) selectnowith ::= oneselect */ + -1, /* (381) oneselect ::= values */ + -2, /* (382) sclp ::= selcollist COMMA */ + -1, /* (383) as ::= ID|STRING */ + -1, /* (384) indexed_opt ::= indexed_by */ + 0, /* (385) returning ::= */ + -1, /* (386) expr ::= term */ + -1, /* (387) likeop ::= LIKE_KW|MATCH */ + -1, /* (388) case_operand ::= expr */ + -1, /* (389) exprlist ::= nexprlist */ + -1, /* (390) nmnum ::= plus_num */ + -1, /* (391) nmnum ::= nm */ + -1, /* (392) nmnum ::= ON */ + -1, /* (393) nmnum ::= DELETE */ + -1, /* (394) nmnum ::= DEFAULT */ + -1, /* (395) plus_num ::= INTEGER|FLOAT */ + 0, /* (396) foreach_clause ::= */ + -3, /* (397) foreach_clause ::= FOR EACH ROW */ + 0, /* (398) tridxby ::= */ + -1, /* (399) database_kw_opt ::= DATABASE */ + 0, /* (400) database_kw_opt ::= */ + 0, /* (401) kwcolumn_opt ::= */ + -1, /* (402) kwcolumn_opt ::= COLUMNKW */ + -1, /* (403) vtabarglist ::= vtabarg */ + -3, /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (405) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (406) anylist ::= */ + -4, /* (407) anylist ::= anylist LP anylist RP */ + -2, /* (408) anylist ::= anylist ANY */ + 0, /* (409) with ::= */ + -1, /* (410) windowdefn_list ::= windowdefn */ + -1, /* (411) window ::= frame_opt */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -181236,16 +183664,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy502 = TK_DEFERRED;} +{yymsp[1].minor.yy144 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); -{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/} + case 328: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==328); +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -181268,7 +183696,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144); } break; case 14: /* createkw ::= CREATE */ @@ -181284,38 +183712,38 @@ static YYACTIONTYPE yy_reduce( case 81: /* ifexists ::= */ yytestcase(yyruleno==81); case 100: /* distinct ::= */ yytestcase(yyruleno==100); case 246: /* collate ::= */ yytestcase(yyruleno==246); -{yymsp[1].minor.yy502 = 0;} +{yymsp[1].minor.yy144 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy502 = 1;} +{yymsp[-2].minor.yy144 = 1;} break; case 17: /* temp ::= TEMP */ -{yymsp[0].minor.yy502 = pParse->db->init.busy==0;} +{yymsp[0].minor.yy144 = pParse->db->init.busy==0;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 21: /* table_option_set ::= */ -{yymsp[1].minor.yy9 = 0;} +{yymsp[1].minor.yy391 = 0;} break; case 22: /* table_option_set ::= table_option_set COMMA table_option */ -{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;} - yymsp[-2].minor.yy9 = yylhsminor.yy9; +{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;} + yymsp[-2].minor.yy391 = yylhsminor.yy391; break; case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy9 = 0; + yymsp[-1].minor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -181323,13 +183751,13 @@ static YYACTIONTYPE yy_reduce( case 24: /* table_option ::= nm */ { if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ - yylhsminor.yy9 = TF_Strict; + yylhsminor.yy391 = TF_Strict; }else{ - yylhsminor.yy9 = 0; + yylhsminor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } - yymsp[0].minor.yy9 = yylhsminor.yy9; + yymsp[0].minor.yy391 = yylhsminor.yy391; break; case 25: /* columnname ::= nm typetoken */ {sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} @@ -181355,7 +183783,7 @@ static YYACTIONTYPE yy_reduce( case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy342 = yyLookaheadToken.z; + yymsp[1].minor.yy168 = yyLookaheadToken.z; } break; case 31: /* scantok ::= */ @@ -181369,17 +183797,17 @@ static YYACTIONTYPE yy_reduce( {ASSERT_IS_CREATE; pParse->u1.cr.constraintName = yymsp[0].minor.yy0;} break; case 33: /* ccons ::= DEFAULT scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 34: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 35: /* ccons ::= DEFAULT PLUS scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0); sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; @@ -181394,133 +183822,133 @@ static YYACTIONTYPE yy_reduce( } break; case 38: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);} break; case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);} break; case 40: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 41: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);} break; case 43: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);} break; case 44: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 45: /* generated ::= LP expr RP */ -{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);} +{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);} break; case 46: /* generated ::= LP expr RP ID */ -{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);} +{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);} break; case 48: /* autoinc ::= AUTOINCR */ -{yymsp[0].minor.yy502 = 1;} +{yymsp[0].minor.yy144 = 1;} break; case 49: /* refargs ::= */ -{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 50: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; } +{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; } break; case 51: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy481.value = 0; yymsp[-1].minor.yy481.mask = 0x000000; } +{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; } break; case 52: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy481.value = 0; yymsp[-2].minor.yy481.mask = 0x000000; } +{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; } break; case 53: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502; yymsp[-2].minor.yy481.mask = 0x0000ff; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; } break; case 54: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8; yymsp[-2].minor.yy481.mask = 0x00ff00; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; } break; case 55: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy502 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */} break; case 56: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy502 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 57: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy502 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */} break; case 58: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */} break; case 59: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy502 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */} break; case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy502 = 0;} +{yymsp[-2].minor.yy144 = 0;} break; case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); -{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;} +{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;} break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); -{yymsp[-1].minor.yy502 = 1;} +{yymsp[-1].minor.yy144 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy502 = 0;} +{yymsp[-1].minor.yy144 = 0;} break; case 66: /* tconscomma ::= COMMA */ {ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;} break; case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);} break; case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 70: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144); } break; case 73: /* onconf ::= */ case 75: /* orconf ::= */ yytestcase(yyruleno==75); -{yymsp[1].minor.yy502 = OE_Default;} +{yymsp[1].minor.yy144 = OE_Default;} break; case 74: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;} +{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;} break; case 77: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy502 = OE_Ignore;} +{yymsp[0].minor.yy144 = OE_Ignore;} break; case 78: /* resolvetype ::= REPLACE */ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); -{yymsp[0].minor.yy502 = OE_Replace;} +{yymsp[0].minor.yy144 = OE_Replace;} break; case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144); } break; case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144); } break; case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144); } break; case 84: /* cmd ::= select */ @@ -181529,20 +183957,20 @@ static YYACTIONTYPE yy_reduce( if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0 || sqlite3ReadSchema(pParse)==SQLITE_OK ){ - sqlite3Select(pParse, yymsp[0].minor.yy637, &dest); + sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); } - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 85: /* select ::= WITH wqlist selectnowith */ -{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} +{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);} +{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy637; + Select *p = yymsp[0].minor.yy555; if( p ){ parserDoubleLinkSelect(pParse, p); } @@ -181550,8 +183978,8 @@ static YYACTIONTYPE yy_reduce( break; case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy637; - Select *pLhs = yymsp[-2].minor.yy637; + Select *pRhs = yymsp[0].minor.yy555; + Select *pLhs = yymsp[-2].minor.yy555; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -181561,60 +183989,60 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy502; + pRhs->op = (u8)yymsp[-1].minor.yy144; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue; pRhs->selFlags &= ~(u32)SF_MultiValue; - if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy637 = pRhs; + yymsp[-2].minor.yy555 = pRhs; } break; case 89: /* multiselect_op ::= UNION */ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); -{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/} break; case 90: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy502 = TK_ALL;} +{yymsp[-1].minor.yy144 = TK_ALL;} break; case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590); + yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454); } break; case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590); - if( yymsp[-9].minor.yy637 ){ - yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483; + yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454); + if( yymsp[-9].minor.yy555 ){ + yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211); } } break; case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0); } break; case 95: /* oneselect ::= mvalues */ { - sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637); + sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555); } break; case 96: /* mvalues ::= values COMMA LP nexprlist RP */ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); { - yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402); + yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14); } break; case 98: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy502 = SF_Distinct;} +{yymsp[0].minor.yy144 = SF_Distinct;} break; case 99: /* distinct ::= ALL */ -{yymsp[0].minor.yy502 = SF_All;} +{yymsp[0].minor.yy144 = SF_All;} break; case 101: /* sclp ::= */ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); @@ -181622,20 +184050,20 @@ static YYACTIONTYPE yy_reduce( case 234: /* exprlist ::= */ yytestcase(yyruleno==234); case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); -{yymsp[1].minor.yy402 = 0;} +{yymsp[1].minor.yy14 = 0;} break; case 102: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168); } break; case 103: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); - yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p); } break; case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ @@ -181645,7 +184073,7 @@ static YYACTIONTYPE yy_reduce( sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot); } break; case 105: /* as ::= AS nm */ @@ -181656,50 +184084,50 @@ static YYACTIONTYPE yy_reduce( break; case 107: /* from ::= */ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); -{yymsp[1].minor.yy563 = 0;} +{yymsp[1].minor.yy203 = 0;} break; case 108: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy563 = yymsp[0].minor.yy563; - sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563); + yymsp[-1].minor.yy203 = yymsp[0].minor.yy203; + sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203); } break; case 109: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502; + if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144; } break; case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); + yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); } break; case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421); - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0); } break; case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); - sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402); + yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14); } break; case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ { - yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269); } break; case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){ - yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563; - }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){ - yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421); - if( yymsp[-5].minor.yy563 ){ - SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1]; - SrcItem *pOld = yymsp[-3].minor.yy563->a; + 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 ); @@ -181724,12 +184152,12 @@ static YYACTIONTYPE yy_reduce( } pOld->zName = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563); + sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0); - yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421); + 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); } } break; @@ -181738,57 +184166,67 @@ static YYACTIONTYPE yy_reduce( {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; case 118: /* fullname ::= nm */ + case 120: /* xfullname ::= nm */ yytestcase(yyruleno==120); { - yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy563 = yylhsminor.yy563; + yymsp[0].minor.yy203 = yylhsminor.yy203; break; case 119: /* fullname ::= nm DOT nm */ + case 121: /* xfullname ::= nm DOT nm */ yytestcase(yyruleno==121); { - yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy563 = yylhsminor.yy563; - break; - case 120: /* xfullname ::= nm */ -{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} - break; - case 121: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + yymsp[-2].minor.yy203 = yylhsminor.yy203; break; - case 122: /* xfullname ::= nm DOT nm AS nm */ + case 122: /* xfullname ::= nm AS nm */ { - yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); + if( yylhsminor.yy203 ){ + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[-2].minor.yy0); + }else{ + yylhsminor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + } + } } + yymsp[-2].minor.yy203 = yylhsminor.yy203; break; - case 123: /* xfullname ::= nm AS nm */ + case 123: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); + if( yylhsminor.yy203 ){ + if( IN_RENAME_OBJECT ){ + sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[-2].minor.yy0); + }else{ + yylhsminor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + } + } } + yymsp[-4].minor.yy203 = yylhsminor.yy203; break; case 124: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy502 = JT_INNER; } +{ yymsp[0].minor.yy144 = JT_INNER; } break; case 125: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} +{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; case 126: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} +{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; case 127: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} +{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; case 128: /* on_using ::= ON expr */ -{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;} +{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;} break; case 129: /* on_using ::= USING LP idlist RP */ -{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;} +{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;} break; case 130: /* on_using ::= */ -{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;} +{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;} break; case 132: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} @@ -181798,35 +184236,35 @@ static YYACTIONTYPE yy_reduce( break; case 135: /* orderby_opt ::= ORDER BY sortlist */ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); -{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;} +{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;} break; case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590); - sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454); + sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; case 137: /* sortlist ::= expr sortorder nulls */ { - yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; case 138: /* sortorder ::= ASC */ -{yymsp[0].minor.yy502 = SQLITE_SO_ASC;} +{yymsp[0].minor.yy144 = SQLITE_SO_ASC;} break; case 139: /* sortorder ::= DESC */ -{yymsp[0].minor.yy502 = SQLITE_SO_DESC;} +{yymsp[0].minor.yy144 = SQLITE_SO_DESC;} break; case 140: /* sortorder ::= */ case 143: /* nulls ::= */ yytestcase(yyruleno==143); -{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;} +{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;} break; case 141: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;} +{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;} break; case 142: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;} +{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;} break; case 146: /* having_opt ::= */ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); @@ -181835,42 +184273,42 @@ static YYACTIONTYPE yy_reduce( case 232: /* case_else ::= */ yytestcase(yyruleno==232); case 233: /* case_operand ::= */ yytestcase(yyruleno==233); case 252: /* vinto ::= */ yytestcase(yyruleno==252); -{yymsp[1].minor.yy590 = 0;} +{yymsp[1].minor.yy454 = 0;} break; case 147: /* having_opt ::= HAVING expr */ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); -{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;} +{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;} break; case 149: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);} +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);} break; case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; case 151: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);} +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);} break; case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0); } break; case 157: /* where_opt_ret ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;} break; case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;} break; case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list"); - if( yymsp[-1].minor.yy563 ){ - SrcList *pFromClause = yymsp[-1].minor.yy563; + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list"); + if( yymsp[-1].minor.yy203 ){ + SrcList *pFromClause = yymsp[-1].minor.yy203; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; @@ -181879,90 +184317,90 @@ static YYACTIONTYPE yy_reduce( as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } - yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause); } - sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0); + sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0); } break; case 160: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1); } break; case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); + yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; case 162: /* setlist ::= nm EQ expr */ { - yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590); - sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy402 = yylhsminor.yy402; + yymsp[-2].minor.yy14 = yylhsminor.yy14; break; case 163: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590); + yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403); + sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122); } break; case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0); + sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0); } break; case 166: /* upsert ::= */ -{ yymsp[1].minor.yy403 = 0; } +{ yymsp[1].minor.yy122 = 0; } break; case 167: /* upsert ::= RETURNING selcollist */ -{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); } +{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); } break; case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);} +{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);} break; case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); } +{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); } break; case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ -{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } +{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } break; case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);} +{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);} break; case 172: /* returning ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);} +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);} break; case 175: /* idlist_opt ::= */ -{yymsp[1].minor.yy204 = 0;} +{yymsp[1].minor.yy132 = 0;} break; case 176: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;} +{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;} break; case 177: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);} +{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);} break; case 178: /* idlist ::= nm */ -{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} +{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; case 179: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;} +{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;} break; case 180: /* expr ::= ID|INDEXED|JOIN_KW */ -{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 181: /* expr ::= nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); - yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy590 = yylhsminor.yy590; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; case 182: /* expr ::= nm DOT nm DOT nm */ { @@ -181973,27 +184411,32 @@ static YYACTIONTYPE yy_reduce( if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, 0, temp1); } - yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy590 = yylhsminor.yy590; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; case 183: /* term ::= NULL|FLOAT|BLOB */ case 184: /* term ::= STRING */ yytestcase(yyruleno==184); -{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 185: /* term ::= INTEGER */ { - yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); - if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); + int iValue; + if( sqlite3GetInt32(yymsp[0].minor.yy0.z, &iValue)==0 ){ + yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 0); + }else{ + yylhsminor.yy454 = sqlite3ExprInt32(pParse->db, iValue); + } + if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy590 = yylhsminor.yy590; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; case 186: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n); + yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -182002,80 +184445,82 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ parserSyntaxError(pParse, &t); - yymsp[0].minor.yy590 = 0; + yymsp[0].minor.yy454 = 0; }else{ - yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable); + yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); } } } break; case 187: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1); } break; case 188: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0); + yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0); } break; case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144); } - yymsp[-4].minor.yy590 = yylhsminor.yy590; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14); } - yymsp[-7].minor.yy590 = yylhsminor.yy590; + yymsp[-7].minor.yy454 = yylhsminor.yy454; break; case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy590 = yylhsminor.yy590; + yymsp[-3].minor.yy454 = yylhsminor.yy454; break; case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502); - sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-5].minor.yy590 = yylhsminor.yy590; + yymsp[-5].minor.yy454 = yylhsminor.yy454; break; case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502); - sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); - sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14); } - yymsp[-8].minor.yy590 = yylhsminor.yy590; + yymsp[-8].minor.yy454 = yylhsminor.yy454; break; case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-4].minor.yy590 = yylhsminor.yy590; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; case 195: /* term ::= CTIME_KW */ { - yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy590 = yylhsminor.yy590; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; case 196: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590); - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy590 ){ - yymsp[-4].minor.yy590->x.pList = pList; - if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy454 ){ + int i; + yymsp[-4].minor.yy454->x.pList = pList; + for(i=0; inExpr; i++){ + assert( pList->a[i].pExpr!=0 ); + yymsp[-4].minor.yy454->flags |= pList->a[i].pExpr->flags & EP_Propagate; } }else{ sqlite3ExprListDelete(pParse->db, pList); @@ -182083,7 +184528,7 @@ static YYACTIONTYPE yy_reduce( } break; case 197: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} +{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; case 198: /* expr ::= expr OR expr */ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); @@ -182092,7 +184537,7 @@ static YYACTIONTYPE yy_reduce( case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); -{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);} +{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; case 205: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} @@ -182102,11 +184547,11 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590); - yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0); - if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454); + yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0); + if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc; } break; case 207: /* expr ::= expr likeop expr ESCAPE expr */ @@ -182114,91 +184559,88 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); - yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); - if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc; } break; case 208: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);} +{yymsp[-1].minor.yy454 = sqlite3PExprIsNull(pParse,yymsp[0].major,yymsp[-1].minor.yy454);} break; case 209: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);} +{yymsp[-2].minor.yy454 = sqlite3PExprIsNull(pParse,TK_NOTNULL,yymsp[-2].minor.yy454);} break; case 210: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL); + yymsp[-2].minor.yy454 = sqlite3PExprIs(pParse, TK_IS, yymsp[-2].minor.yy454, yymsp[0].minor.yy454); } break; case 211: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL); + yymsp[-3].minor.yy454 = sqlite3PExprIs(pParse, TK_ISNOT, yymsp[-3].minor.yy454, yymsp[0].minor.yy454); } break; case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */ { - yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL); + yymsp[-5].minor.yy454 = sqlite3PExprIs(pParse, TK_IS, yymsp[-5].minor.yy454, yymsp[0].minor.yy454); } break; case 213: /* expr ::= expr IS DISTINCT FROM expr */ { - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL); + yymsp[-4].minor.yy454 = sqlite3PExprIs(pParse, TK_ISNOT, yymsp[-4].minor.yy454, yymsp[0].minor.yy454); } break; case 214: /* expr ::= NOT expr */ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); -{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/} +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} break; case 216: /* expr ::= PLUS|MINUS expr */ { - Expr *p = yymsp[0].minor.yy590; + Expr *p = yymsp[0].minor.yy454; u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); assert( TK_UPLUS>TK_PLUS ); assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); if( p && p->op==TK_UPLUS ){ p->op = op; - yymsp[-1].minor.yy590 = p; + yymsp[-1].minor.yy454 = p; }else{ - yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0); + yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); /*A-overwrites-B*/ } } break; case 217: /* expr ::= expr PTR expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590); - pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590); - yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); } - yymsp[-2].minor.yy590 = yylhsminor.yy590; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; case 218: /* between_op ::= BETWEEN */ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); -{yymsp[0].minor.yy502 = 0;} +{yymsp[0].minor.yy144 = 0;} break; case 220: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590); - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0); - if( yymsp[-4].minor.yy590 ){ - yymsp[-4].minor.yy590->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = pList; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; case 223: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy402==0 ){ + if( yymsp[-1].minor.yy14==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -182211,145 +184653,145 @@ static YYACTIONTYPE yy_reduce( ** it is or not) and if it is an aggregate, that could change the meaning ** of the whole query. */ - Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false"); + Expr *pB = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false"); if( pB ) sqlite3ExprIdToTrueFalse(pB); - if( !ExprHasProperty(yymsp[-4].minor.yy590, EP_HasFunc) ){ - sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590); - yymsp[-4].minor.yy590 = pB; + if( !ExprHasProperty(yymsp[-4].minor.yy454, EP_HasFunc) ){ + sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454); + yymsp[-4].minor.yy454 = pB; }else{ - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, yymsp[-3].minor.yy502 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy590); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, yymsp[-3].minor.yy144 ? TK_OR : TK_AND, pB, yymsp[-4].minor.yy454); } }else{ - Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr; - if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){ - yymsp[-1].minor.yy402->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); + Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr; + if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){ + yymsp[-1].minor.yy14->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS); - }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){ - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS); + }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){ + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect); pRHS->x.pSelect = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); }else{ - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); - if( yymsp[-4].minor.yy590==0 ){ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402); - }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){ - int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr; - Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454==0 ){ + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); + }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14); if( pSelectRHS ){ parserDoubleLinkSelect(pParse, pSelectRHS); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS); } }else{ - yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); } } - if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } } break; case 224: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637); + yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555); } break; case 225: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637); - if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy402 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402); - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect); - if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0); + if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; case 227: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637); + p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555); } break; case 228: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0); - if( yymsp[-4].minor.yy590 ){ - yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } } break; case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590); - yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454); } break; case 230: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590); - yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454); } break; case 235: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);} +{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);} break; case 236: /* nexprlist ::= expr */ -{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/} +{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/} break; case 238: /* paren_exprlist ::= LP exprlist RP */ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); -{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;} +{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;} break; case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; case 240: /* uniqueflag ::= UNIQUE */ - case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); -{yymsp[0].minor.yy502 = OE_Abort;} + case 281: /* raisetype ::= ABORT */ yytestcase(yyruleno==281); +{yymsp[0].minor.yy144 = OE_Abort;} break; case 241: /* uniqueflag ::= */ -{yymsp[1].minor.yy502 = OE_None;} +{yymsp[1].minor.yy144 = OE_None;} break; case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); + yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); } break; case 245: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/ + yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/ } break; case 248: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);} +{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);} break; case 249: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);} +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);} break; case 250: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);} +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);} break; case 253: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} @@ -182371,12 +184813,12 @@ static YYACTIONTYPE yy_reduce( Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all); } break; case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ #ifdef SQLITE_DEBUG assert( pParse->isCreate ); /* Set by createkw reduce action */ @@ -182385,421 +184827,437 @@ static YYACTIONTYPE yy_reduce( } break; case 262: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ } +{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ } break; case 263: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy502 = TK_INSTEAD;} +{ yymsp[-1].minor.yy144 = TK_INSTEAD;} break; case 264: /* trigger_time ::= */ -{ yymsp[1].minor.yy502 = TK_BEFORE; } +{ yymsp[1].minor.yy144 = TK_BEFORE; } break; case 265: /* trigger_event ::= DELETE|INSERT */ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); -{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;} +{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;} break; case 267: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;} +{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;} break; case 268: /* when_clause ::= */ - case 287: /* key_opt ::= */ yytestcase(yyruleno==287); -{ yymsp[1].minor.yy590 = 0; } + case 286: /* key_opt ::= */ yytestcase(yyruleno==286); +{ yymsp[1].minor.yy454 = 0; } break; case 269: /* when_clause ::= WHEN expr */ - case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); -{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; } + case 287: /* key_opt ::= KEY expr */ yytestcase(yyruleno==287); +{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; } break; case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy319!=0 ); - yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319; - yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319; + yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427; + yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy319!=0 ); - yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319; -} - break; - case 272: /* trnm ::= nm DOT nm */ -{ - yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; - sqlite3ErrorMsg(pParse, - "qualified table names are not allowed on INSERT, UPDATE, and DELETE " - "statements within triggers"); + yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; - case 273: /* tridxby ::= INDEXED BY nm */ + case 272: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 274: /* tridxby ::= NOT INDEXED */ + case 273: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ -{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);} - yymsp[-8].minor.yy319 = yylhsminor.yy319; + case 274: /* trigger_cmd ::= UPDATE orconf xfullname tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, yymsp[-6].minor.yy203, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-8].minor.yy427 = yylhsminor.yy427; break; - case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 275: /* trigger_cmd ::= scanpt insert_cmd INTO xfullname idlist_opt select upsert scanpt */ { - yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/ + yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,yymsp[-4].minor.yy203,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/ } - yymsp[-7].minor.yy319 = yylhsminor.yy319; + yymsp[-7].minor.yy427 = yylhsminor.yy427; break; - case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);} - yymsp[-5].minor.yy319 = yylhsminor.yy319; + case 276: /* trigger_cmd ::= DELETE FROM xfullname tridxby where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-5].minor.yy427 = yylhsminor.yy427; break; - case 278: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/} - yymsp[-2].minor.yy319 = yylhsminor.yy319; + case 277: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/} + yymsp[-2].minor.yy427 = yylhsminor.yy427; break; - case 279: /* expr ::= RAISE LP IGNORE RP */ + case 278: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy590 ){ - yymsp[-3].minor.yy590->affExpr = OE_Ignore; + yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy454 ){ + yymsp[-3].minor.yy454->affExpr = OE_Ignore; } } break; - case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */ + case 279: /* expr ::= RAISE LP raisetype COMMA expr RP */ { - yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0); - if( yymsp[-5].minor.yy590 ) { - yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502; + yymsp[-5].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy454, 0); + if( yymsp[-5].minor.yy454 ) { + yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144; } } break; - case 281: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy502 = OE_Rollback;} + case 280: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy144 = OE_Rollback;} break; - case 283: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy502 = OE_Fail;} + case 282: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy144 = OE_Fail;} break; - case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 283: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144); } break; - case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 284: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590); + sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454); } break; - case 286: /* cmd ::= DETACH database_kw_opt expr */ + case 285: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy590); + sqlite3Detach(pParse, yymsp[0].minor.yy454); } break; - case 289: /* cmd ::= REINDEX */ + case 288: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 290: /* cmd ::= REINDEX nm dbnm */ + case 289: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 291: /* cmd ::= ANALYZE */ + case 290: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 292: /* cmd ::= ANALYZE nm dbnm */ + case 291: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 292: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0); } break; - case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 293: /* cmd ::= alter_add carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); +} + break; + case 294: /* alter_add ::= ALTER TABLE fullname ADD kwcolumn_opt nm typetoken */ +{ + disableLookaside(pParse); + sqlite3AlterBeginAddColumn(pParse, yymsp[-4].minor.yy203); + sqlite3AddColumn(pParse, yymsp[-1].minor.yy0, yymsp[0].minor.yy0); + yymsp[-6].minor.yy0 = yymsp[-1].minor.yy0; } break; case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { - sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0); + sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0); } break; - case 296: /* add_column_fullname ::= fullname */ + case 296: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); +} + break; + case 297: /* cmd ::= ALTER TABLE fullname DROP CONSTRAINT nm */ +{ + sqlite3AlterDropConstraint(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0, 0); +} + break; + case 298: /* cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm DROP NOT NULL */ +{ + sqlite3AlterDropConstraint(pParse, yymsp[-6].minor.yy203, 0, &yymsp[-3].minor.yy0); +} + break; + case 299: /* cmd ::= ALTER TABLE fullname ALTER kwcolumn_opt nm SET NOT NULL onconf */ +{ + sqlite3AlterSetNotNull(pParse, yymsp[-7].minor.yy203, &yymsp[-4].minor.yy0, &yymsp[-2].minor.yy0); +} + break; + case 300: /* cmd ::= ALTER TABLE fullname ADD CONSTRAINT nm CHECK LP expr RP onconf */ +{ + sqlite3AlterAddConstraint(pParse, yymsp[-8].minor.yy203, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy0.z+1, (yymsp[-1].minor.yy0.z-yymsp[-3].minor.yy0.z-1), yymsp[-2].minor.yy454); } break; - case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 301: /* cmd ::= ALTER TABLE fullname ADD CHECK LP expr RP onconf */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterAddConstraint(pParse, yymsp[-6].minor.yy203, &yymsp[-4].minor.yy0, 0, yymsp[-3].minor.yy0.z+1, (yymsp[-1].minor.yy0.z-yymsp[-3].minor.yy0.z-1), yymsp[-2].minor.yy454); } break; - case 298: /* cmd ::= create_vtab */ + case 302: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 299: /* cmd ::= create_vtab LP vtabarglist RP */ + case 303: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 304: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144); } break; - case 301: /* vtabarg ::= */ + case 305: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 302: /* vtabargtoken ::= ANY */ - case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303); - case 304: /* lp ::= LP */ yytestcase(yyruleno==304); + case 306: /* vtabargtoken ::= ANY */ + case 307: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==307); + case 308: /* lp ::= LP */ yytestcase(yyruleno==308); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 305: /* with ::= WITH wqlist */ - case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); } + case 309: /* with ::= WITH wqlist */ + case 310: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==310); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } break; - case 307: /* wqas ::= AS */ -{yymsp[0].minor.yy444 = M10d_Any;} + case 311: /* wqas ::= AS */ +{yymsp[0].minor.yy462 = M10d_Any;} break; - case 308: /* wqas ::= AS MATERIALIZED */ -{yymsp[-1].minor.yy444 = M10d_Yes;} + case 312: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy462 = M10d_Yes;} break; - case 309: /* wqas ::= AS NOT MATERIALIZED */ -{yymsp[-2].minor.yy444 = M10d_No;} + case 313: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy462 = M10d_No;} break; - case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ + case 314: /* wqitem ::= withnm eidlist_opt wqas LP select RP */ { - yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/ + yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/ } break; - case 311: /* withnm ::= nm */ + case 315: /* withnm ::= nm */ {pParse->bHasWith = 1;} break; - case 312: /* wqlist ::= wqitem */ + case 316: /* wqlist ::= wqitem */ { - yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/ + yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/ } break; - case 313: /* wqlist ::= wqlist COMMA wqitem */ + case 317: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361); + yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67); } break; - case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 318: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy483!=0 ); - sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483); - yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483; - yylhsminor.yy483 = yymsp[0].minor.yy483; + assert( yymsp[0].minor.yy211!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211); + yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-2].minor.yy483 = yylhsminor.yy483; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 315: /* windowdefn ::= nm AS LP window RP */ + case 319: /* windowdefn ::= nm AS LP window RP */ { - if( ALWAYS(yymsp[-1].minor.yy483) ){ - yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); + if( ALWAYS(yymsp[-1].minor.yy211) ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy483 = yymsp[-1].minor.yy483; + yylhsminor.yy211 = yymsp[-1].minor.yy211; } - yymsp[-4].minor.yy483 = yylhsminor.yy483; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 320: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { - yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0); + yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0); } break; - case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 321: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { - yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy483 = yylhsminor.yy483; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - case 318: /* window ::= ORDER BY sortlist frame_opt */ + case 322: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0); + yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0); } break; - case 319: /* window ::= nm ORDER BY sortlist frame_opt */ + case 323: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy483 = yylhsminor.yy483; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 320: /* window ::= nm frame_opt */ + case 324: /* window ::= nm frame_opt */ { - yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy483 = yylhsminor.yy483; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 321: /* frame_opt ::= */ + case 325: /* frame_opt ::= */ { - yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 326: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { - yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462); } - yymsp[-2].minor.yy483 = yylhsminor.yy483; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 327: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { - yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462); } - yymsp[-5].minor.yy483 = yylhsminor.yy483; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - case 325: /* frame_bound_s ::= frame_bound */ - case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); -{yylhsminor.yy205 = yymsp[0].minor.yy205;} - yymsp[0].minor.yy205 = yylhsminor.yy205; + case 329: /* frame_bound_s ::= frame_bound */ + case 331: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==331); +{yylhsminor.yy509 = yymsp[0].minor.yy509;} + yymsp[0].minor.yy509 = yylhsminor.yy509; break; - case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); - case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); -{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;} - yymsp[-1].minor.yy205 = yylhsminor.yy205; + case 330: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 332: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==332); + case 334: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==334); +{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ -{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;} - yymsp[-1].minor.yy205 = yylhsminor.yy205; + case 333: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - case 331: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy444 = 0;} + case 335: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy462 = 0;} break; - case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;} + case 336: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;} break; - case 333: /* frame_exclude ::= NO OTHERS */ - case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); -{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/} + case 337: /* frame_exclude ::= NO OTHERS */ + case 338: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==338); +{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 335: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/} + case 339: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/} break; - case 336: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; } + case 340: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; } break; - case 337: /* filter_over ::= filter_clause over_clause */ + case 341: /* filter_over ::= filter_clause over_clause */ { - if( yymsp[0].minor.yy483 ){ - yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590; + if( yymsp[0].minor.yy211 ){ + yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } - yylhsminor.yy483 = yymsp[0].minor.yy483; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-1].minor.yy483 = yylhsminor.yy483; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 338: /* filter_over ::= over_clause */ + case 342: /* filter_over ::= over_clause */ { - yylhsminor.yy483 = yymsp[0].minor.yy483; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[0].minor.yy483 = yylhsminor.yy483; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 339: /* filter_over ::= filter_clause */ + case 343: /* filter_over ::= filter_clause */ { - yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy483 ){ - yylhsminor.yy483->eFrmType = TK_FILTER; - yylhsminor.yy483->pFilter = yymsp[0].minor.yy590; + yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy211 ){ + yylhsminor.yy211->eFrmType = TK_FILTER; + yylhsminor.yy211->pFilter = yymsp[0].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590); + sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454); } } - yymsp[0].minor.yy483 = yylhsminor.yy483; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 340: /* over_clause ::= OVER LP window RP */ + case 344: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483; - assert( yymsp[-3].minor.yy483!=0 ); + yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211; + assert( yymsp[-3].minor.yy211!=0 ); } break; - case 341: /* over_clause ::= OVER nm */ + case 345: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy483 ){ - yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy211 ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; - case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; } + case 346: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; } break; - case 343: /* term ::= QNUMBER */ + case 347: /* term ::= QNUMBER */ { - yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); - sqlite3DequoteNumber(pParse, yylhsminor.yy590); + yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); + sqlite3DequoteNumber(pParse, yylhsminor.yy454); } - yymsp[0].minor.yy590 = yylhsminor.yy590; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; default: - /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); - /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345); - /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346); - /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347); - /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348); - /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349); - /* (350) trans_opt ::= */ yytestcase(yyruleno==350); - /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351); - /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352); - /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353); - /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354); - /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355); - /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356); - /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357); - /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358); - /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359); - /* (360) nm ::= STRING */ yytestcase(yyruleno==360); - /* (361) typetoken ::= typename */ yytestcase(yyruleno==361); - /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362); - /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); - /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); - /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365); - /* (366) carglist ::= */ yytestcase(yyruleno==366); - /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367); - /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368); - /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369); - /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370); - /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371); - /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372); - /* (373) tconscomma ::= */ yytestcase(yyruleno==373); - /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374); - /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375); - /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376); - /* (377) oneselect ::= values */ yytestcase(yyruleno==377); - /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378); - /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379); - /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380); - /* (381) returning ::= */ yytestcase(yyruleno==381); - /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382); - /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383); - /* (384) case_operand ::= expr */ yytestcase(yyruleno==384); - /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385); - /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386); - /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387); - /* (388) nmnum ::= ON */ yytestcase(yyruleno==388); - /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389); - /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390); - /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391); - /* (392) foreach_clause ::= */ yytestcase(yyruleno==392); - /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393); - /* (394) trnm ::= nm */ yytestcase(yyruleno==394); - /* (395) tridxby ::= */ yytestcase(yyruleno==395); - /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396); - /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397); - /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398); - /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399); - /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400); - /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401); - /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402); - /* (403) anylist ::= */ yytestcase(yyruleno==403); - /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404); - /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405); - /* (406) with ::= */ yytestcase(yyruleno==406); - /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407); - /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408); + /* (348) input ::= cmdlist */ yytestcase(yyruleno==348); + /* (349) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==349); + /* (350) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=350); + /* (351) ecmd ::= SEMI */ yytestcase(yyruleno==351); + /* (352) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==352); + /* (353) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=353); + /* (354) trans_opt ::= */ yytestcase(yyruleno==354); + /* (355) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==355); + /* (356) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==356); + /* (357) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==357); + /* (358) savepoint_opt ::= */ yytestcase(yyruleno==358); + /* (359) cmd ::= create_table create_table_args */ yytestcase(yyruleno==359); + /* (360) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=360); + /* (361) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==361); + /* (362) columnlist ::= columnname carglist */ yytestcase(yyruleno==362); + /* (363) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==363); + /* (364) nm ::= STRING */ yytestcase(yyruleno==364); + /* (365) typetoken ::= typename */ yytestcase(yyruleno==365); + /* (366) typename ::= ID|STRING */ yytestcase(yyruleno==366); + /* (367) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=367); + /* (368) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=368); + /* (369) carglist ::= carglist ccons */ yytestcase(yyruleno==369); + /* (370) carglist ::= */ yytestcase(yyruleno==370); + /* (371) ccons ::= NULL onconf */ yytestcase(yyruleno==371); + /* (372) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==372); + /* (373) ccons ::= AS generated */ yytestcase(yyruleno==373); + /* (374) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==374); + /* (375) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==375); + /* (376) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=376); + /* (377) tconscomma ::= */ yytestcase(yyruleno==377); + /* (378) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=378); + /* (379) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=379); + /* (380) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=380); + /* (381) oneselect ::= values */ yytestcase(yyruleno==381); + /* (382) sclp ::= selcollist COMMA */ yytestcase(yyruleno==382); + /* (383) as ::= ID|STRING */ yytestcase(yyruleno==383); + /* (384) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=384); + /* (385) returning ::= */ yytestcase(yyruleno==385); + /* (386) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=386); + /* (387) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==387); + /* (388) case_operand ::= expr */ yytestcase(yyruleno==388); + /* (389) exprlist ::= nexprlist */ yytestcase(yyruleno==389); + /* (390) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=390); + /* (391) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=391); + /* (392) nmnum ::= ON */ yytestcase(yyruleno==392); + /* (393) nmnum ::= DELETE */ yytestcase(yyruleno==393); + /* (394) nmnum ::= DEFAULT */ yytestcase(yyruleno==394); + /* (395) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==395); + /* (396) foreach_clause ::= */ yytestcase(yyruleno==396); + /* (397) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==397); + /* (398) tridxby ::= */ yytestcase(yyruleno==398); + /* (399) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==399); + /* (400) database_kw_opt ::= */ yytestcase(yyruleno==400); + /* (401) kwcolumn_opt ::= */ yytestcase(yyruleno==401); + /* (402) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==402); + /* (403) vtabarglist ::= vtabarg */ yytestcase(yyruleno==403); + /* (404) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==404); + /* (405) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==405); + /* (406) anylist ::= */ yytestcase(yyruleno==406); + /* (407) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==407); + /* (408) anylist ::= anylist ANY */ yytestcase(yyruleno==408); + /* (409) with ::= */ yytestcase(yyruleno==409); + /* (410) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=410); + /* (411) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=411); break; /********** End reduce actions ************************************************/ }; @@ -183578,8 +186036,8 @@ static const unsigned char aKWCode[148] = {0, /* Check to see if z[0..n-1] is a keyword. If it is, write the ** parser symbol code for that keyword into *pType. Always ** return the integer n (the length of the token). */ -static int keywordCode(const char *z, int n, int *pType){ - int i, j; +static i64 keywordCode(const char *z, i64 n, int *pType){ + i64 i, j; const char *zKW; assert( n>=2 ); i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127; @@ -184133,7 +186591,7 @@ SQLITE_PRIVATE i64 sqlite3GetToken(const unsigned char *z, int *tokenType){ } case CC_DOLLAR: case CC_VARALPHA: { - int n = 0; + i64 n = 0; testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' ); testcase( z[0]=='#' ); *tokenType = TK_VARIABLE; @@ -184229,7 +186687,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ int tokenType; /* type of the next token */ int lastTokenParsed = -1; /* type of the previous token */ sqlite3 *db = pParse->db; /* The database connection */ - int mxSqlLen; /* Max length of an SQL string */ + i64 mxSqlLen; /* Max length of an SQL string */ Parse *pParentParse = 0; /* Outer parse context, if any */ #ifdef sqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object */ @@ -184359,7 +186817,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ } if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){ if( pParse->zErrMsg==0 ){ - pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); + pParse->zErrMsg = sqlite3DbStrDup(db, sqlite3ErrStr(pParse->rc)); } if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail); @@ -184440,7 +186898,7 @@ SQLITE_PRIVATE char *sqlite3Normalize( sqlite3_str_append(pStr, " NULL", 5); break; } - /* Fall through */ + /* no break */ deliberate_fall_through } case TK_STRING: case TK_INTEGER: @@ -184504,7 +186962,7 @@ SQLITE_PRIVATE char *sqlite3Normalize( } case TK_SELECT: { iStartIN = 0; - /* fall through */ + /* no break */ deliberate_fall_through } default: { if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr); @@ -185885,6 +188343,14 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ rc = setupLookaside(db, pBuf, sz, cnt); break; } + case SQLITE_DBCONFIG_FP_DIGITS: { + int nIn = va_arg(ap, int); + int *pOut = va_arg(ap, int*); + if( nIn>3 && nIn<24 ) db->nFpDigit = (u8)nIn; + if( pOut ) *pOut = db->nFpDigit; + rc = SQLITE_OK; + break; + } default: { static const struct { int op; /* The opcode */ @@ -186017,13 +188483,17 @@ static int nocaseCollatingFunc( ** Return the ROWID of the most recent insert */ SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){ + i64 iRet; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - return db->lastRowid; + sqlite3_mutex_enter(db->mutex); + iRet = db->lastRowid; + sqlite3_mutex_leave(db->mutex); + return iRet; } /* @@ -186045,13 +188515,17 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3 *db, sqlite3_int64 iRowid) ** Return the number of changes in the most recent call to sqlite3_exec(). */ SQLITE_API sqlite3_int64 sqlite3_changes64(sqlite3 *db){ + i64 iRet; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - return db->nChange; + sqlite3_mutex_enter(db->mutex); + iRet = db->nChange; + sqlite3_mutex_leave(db->mutex); + return iRet; } SQLITE_API int sqlite3_changes(sqlite3 *db){ return (int)sqlite3_changes64(db); @@ -186061,13 +188535,17 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){ ** Return the number of changes since the database handle was opened. */ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3 *db){ + i64 iRet; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - return db->nTotalChange; + sqlite3_mutex_enter(db->mutex); + iRet = db->nTotalChange; + sqlite3_mutex_leave(db->mutex); + return iRet; } SQLITE_API int sqlite3_total_changes(sqlite3 *db){ return (int)sqlite3_total_changes64(db); @@ -186750,6 +189228,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; #endif + sqlite3_mutex_enter(db->mutex); if( ms>0 ){ sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, (void*)db); @@ -186760,6 +189239,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){ }else{ sqlite3_busy_handler(db, 0, 0); } + sqlite3_mutex_leave(db->mutex); return SQLITE_OK; } @@ -187440,6 +189920,9 @@ SQLITE_API void *sqlite3_wal_hook( sqlite3_mutex_leave(db->mutex); return pRet; #else + UNUSED_PARAMETER(db); + UNUSED_PARAMETER(xCallback); + UNUSED_PARAMETER(pArg); return 0; #endif } @@ -187455,6 +189938,11 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( int *pnCkpt /* OUT: Total number of frames checkpointed */ ){ #ifdef SQLITE_OMIT_WAL + UNUSED_PARAMETER(db); + UNUSED_PARAMETER(zDb); + UNUSED_PARAMETER(eMode); + UNUSED_PARAMETER(pnLog); + UNUSED_PARAMETER(pnCkpt); return SQLITE_OK; #else int rc; /* Return code */ @@ -187468,11 +189956,12 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( if( pnLog ) *pnLog = -1; if( pnCkpt ) *pnCkpt = -1; + assert( SQLITE_CHECKPOINT_NOOP==-1 ); assert( SQLITE_CHECKPOINT_PASSIVE==0 ); assert( SQLITE_CHECKPOINT_FULL==1 ); assert( SQLITE_CHECKPOINT_RESTART==2 ); assert( SQLITE_CHECKPOINT_TRUNCATE==3 ); - if( eModeSQLITE_CHECKPOINT_TRUNCATE ){ + if( eModeSQLITE_CHECKPOINT_TRUNCATE ){ /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint ** mode: */ return SQLITE_MISUSE_BKPT; @@ -187656,9 +190145,11 @@ SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zMsg){ */ SQLITE_API int sqlite3_error_offset(sqlite3 *db){ int iOffset = -1; - if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){ + if( db && sqlite3SafetyCheckSickOrOk(db) ){ sqlite3_mutex_enter(db->mutex); - iOffset = db->errByteOffset; + if( db->errCode ){ + iOffset = db->errByteOffset; + } sqlite3_mutex_leave(db->mutex); } return iOffset; @@ -187712,25 +190203,43 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){ ** passed to this function, we assume a malloc() failed during sqlite3_open(). */ SQLITE_API int sqlite3_errcode(sqlite3 *db){ - if( db && !sqlite3SafetyCheckSickOrOk(db) ){ + int iRet; + if( !db ) return SQLITE_NOMEM_BKPT; + if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } - if( !db || db->mallocFailed ){ - return SQLITE_NOMEM_BKPT; + sqlite3_mutex_enter(db->mutex); + if( db->mallocFailed ){ + iRet = SQLITE_NOMEM_BKPT; + }else{ + iRet = db->errCode & db->errMask; } - return db->errCode & db->errMask; + sqlite3_mutex_leave(db->mutex); + return iRet; } SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){ - if( db && !sqlite3SafetyCheckSickOrOk(db) ){ + int iRet; + if( !db ) return SQLITE_NOMEM_BKPT; + if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; } - if( !db || db->mallocFailed ){ - return SQLITE_NOMEM_BKPT; + sqlite3_mutex_enter(db->mutex); + if( db->mallocFailed ){ + iRet = SQLITE_NOMEM_BKPT; + }else{ + iRet = db->errCode; } - return db->errCode; + sqlite3_mutex_leave(db->mutex); + return iRet; } SQLITE_API int sqlite3_system_errno(sqlite3 *db){ - return db ? db->iSysErrno : 0; + int iRet = 0; + if( db ){ + sqlite3_mutex_enter(db->mutex); + iRet = db->iSysErrno; + sqlite3_mutex_leave(db->mutex); + } + return iRet; } /* @@ -187836,6 +190345,7 @@ static const int aHardLimit[] = { SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */ SQLITE_MAX_TRIGGER_DEPTH, SQLITE_MAX_WORKER_THREADS, + SQLITE_MAX_PARSER_DEPTH, }; /* @@ -187850,6 +190360,9 @@ static const int aHardLimit[] = { #if SQLITE_MAX_SQL_LENGTH>SQLITE_MAX_LENGTH # error SQLITE_MAX_SQL_LENGTH must not be greater than SQLITE_MAX_LENGTH #endif +#if SQLITE_MAX_SQL_LENGTH>2147482624 /* 1024 less than 2^31 */ +# error SQLITE_MAX_SQL_LENGTH must not be greater than 2147482624 +#endif #if SQLITE_MAX_COMPOUND_SELECT<2 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 #endif @@ -187905,6 +190418,7 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN ); assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH ); + assert( aHardLimit[SQLITE_LIMIT_PARSER_DEPTH]==SQLITE_MAX_PARSER_DEPTH ); assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT); assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP ); assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG ); @@ -187914,12 +190428,13 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER); assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH ); assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS ); - assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) ); + assert( SQLITE_LIMIT_PARSER_DEPTH==(SQLITE_N_LIMIT-1) ); if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ return -1; } + sqlite3_mutex_enter(db->mutex); oldLimit = db->aLimit[limitId]; if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ @@ -187929,6 +190444,7 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ } db->aLimit[limitId] = newLimit; } + sqlite3_mutex_leave(db->mutex); return oldLimit; /* IMP: R-53341-35419 */ } @@ -187971,7 +190487,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( const char *zVfs = zDefaultVfs; char *zFile; char c; - int nUri = sqlite3Strlen30(zUri); + i64 nUri = strlen(zUri); assert( *pzErrMsg==0 ); @@ -187981,8 +190497,8 @@ SQLITE_PRIVATE int sqlite3ParseUri( ){ char *zOpt; int eState; /* Parser state when parsing URI */ - int iIn; /* Input character index */ - int iOut = 0; /* Output character index */ + i64 iIn; /* Input character index */ + i64 iOut = 0; /* Output character index */ u64 nByte = nUri+8; /* Bytes of space to allocate */ /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen @@ -188016,7 +190532,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", - iIn-7, &zUri[7]); + (int)(iIn-7), &zUri[7]); rc = SQLITE_ERROR; goto parse_uri_out; } @@ -188091,11 +190607,11 @@ SQLITE_PRIVATE int sqlite3ParseUri( ** here. Options that are interpreted here include "vfs" and those that ** correspond to flags that may be passed to the sqlite3_open_v2() ** method. */ - zOpt = &zFile[sqlite3Strlen30(zFile)+1]; + zOpt = &zFile[strlen(zFile)+1]; while( zOpt[0] ){ - int nOpt = sqlite3Strlen30(zOpt); + i64 nOpt = strlen(zOpt); char *zVal = &zOpt[nOpt+1]; - int nVal = sqlite3Strlen30(zVal); + i64 nVal = strlen(zVal); if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ zVfs = zVal; @@ -188141,7 +190657,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( int mode = 0; for(i=0; aMode[i].z; i++){ const char *z = aMode[i].z; - if( nVal==sqlite3Strlen30(z) && 0==memcmp(zVal, z, nVal) ){ + if( nVal==(i64)strlen(z) && 0==memcmp(zVal, z, nVal) ){ mode = aMode[i].mode; break; } @@ -188278,7 +190794,7 @@ static int openDatabase( db = sqlite3MallocZero( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; if( isThreadsafe -#ifdef SQLITE_ENABLE_MULTITHREADED_CHECKS +#if defined(SQLITE_THREAD_MISUSE_WARNINGS) || sqlite3GlobalConfig.bCoreMutex #endif ){ @@ -188299,6 +190815,7 @@ static int openDatabase( db->aDb = db->aDbStatic; db->lookaside.bDisable = 1; db->lookaside.sz = 0; + db->nFpDigit = 17; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); @@ -188744,6 +191261,12 @@ SQLITE_API int sqlite3_collation_needed16( */ SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ DbClientData *p; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !zName || !sqlite3SafetyCheckOk(db) ){ + (void)SQLITE_MISUSE_BKPT; + return 0; + } +#endif sqlite3_mutex_enter(db->mutex); for(p=db->pDbData; p; p=p->pNext){ if( strcmp(p->zName, zName)==0 ){ @@ -188819,13 +191342,17 @@ SQLITE_API int sqlite3_global_recover(void){ ** by the next COMMIT or ROLLBACK. */ SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){ + int iRet; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - return db->autoCommit; + sqlite3_mutex_enter(db->mutex); + iRet = db->autoCommit; + sqlite3_mutex_leave(db->mutex); + return iRet; } /* @@ -189601,6 +192128,17 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } + /* sqlite3_test_control(SQLITE_TESTCTRL_ATOF, const char *z, double *p); + ** + ** Test access to the sqlite3AtoF() routine. + */ + case SQLITE_TESTCTRL_ATOF: { + const char *z = va_arg(ap,const char*); + double *pR = va_arg(ap,double*); + rc = sqlite3AtoF(z,pR); + break; + } + #if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) /* sqlite3_test_control(SQLITE_TESTCTRL_TUNE, id, *piValue) ** @@ -189817,6 +192355,7 @@ SQLITE_API const char *sqlite3_filename_journal(const char *zFilename){ } SQLITE_API const char *sqlite3_filename_wal(const char *zFilename){ #ifdef SQLITE_OMIT_WAL + UNUSED_PARAMETER(zFilename); return 0; #else zFilename = sqlite3_filename_journal(zFilename); @@ -189838,17 +192377,19 @@ SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ ** of range. */ SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N){ + const char *zRet = 0; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif - if( N<0 || N>=db->nDb ){ - return 0; - }else{ - return db->aDb[N].zDbSName; + sqlite3_mutex_enter(db->mutex); + if( N>=0 && NnDb ){ + zRet = db->aDb[N].zDbSName; } + sqlite3_mutex_leave(db->mutex); + return zRet; } /* @@ -191190,7 +193731,16 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */ #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) -#define deliberate_fall_through +#if !defined(deliberate_fall_through) +# if defined(__has_attribute) +# if __has_attribute(fallthrough) +# define deliberate_fall_through __attribute__((fallthrough)); +# endif +# endif +#endif +#if !defined(deliberate_fall_through) +# define deliberate_fall_through +#endif /* ** Macros needed to provide flexible arrays in a portable way @@ -191588,6 +194138,15 @@ SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int); (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \ ) +SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( + Fts3Table *p, /* Prepare for this connection */ + const char *zSql, /* SQL to prepare */ + int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ + int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ + sqlite3_stmt **pp /* OUT: Prepared statement */ +); + + /* fts3.c */ SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...); SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); @@ -191681,6 +194240,12 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); SQLITE_EXTENSION_INIT1 #endif + +/* +** Assume any b-tree layer with more levels than this is corrupt. +*/ +#define FTS3_MAX_BTREE_HEIGHT 48 + typedef struct Fts3HashWrapper Fts3HashWrapper; struct Fts3HashWrapper { Fts3Hash hash; /* Hash table */ @@ -193195,9 +195760,7 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){ zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); if( !zSql ) return SQLITE_NOMEM; p->bLock++; - rc = sqlite3_prepare_v3( - p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 - ); + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); p->bLock--; sqlite3_free(zSql); } @@ -193399,7 +195962,11 @@ static int fts3SelectLeaf( assert( piLeaf || piLeaf2 ); fts3GetVarint32(zNode, &iHeight); - rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); + if( iHeight>FTS3_MAX_BTREE_HEIGHT ){ + rc = FTS_CORRUPT_VTAB; + }else{ + rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); + } assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); if( rc==SQLITE_OK && iHeight>1 ){ @@ -193444,8 +196011,13 @@ static void fts3PutDeltaVarint( sqlite3_int64 iVal /* Write this value to the list */ ){ assert_fts3_nc( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) ); - *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev); - *piPrev = iVal; + if( iVal-(*piPrev)>=0 ){ + /* Refuse to write a negative delta integer. This only happens with a + ** corrupt db (see the assert above) and can cause buffer overwrites + ** in some cases. */ + *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev); + *piPrev = iVal; + } } /* @@ -194772,9 +197344,7 @@ static int fts3FilterMethod( } if( zSql ){ p->bLock++; - rc = sqlite3_prepare_v3( - p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0 - ); + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, 1, &pCsr->pStmt); p->bLock--; sqlite3_free(zSql); }else{ @@ -195397,6 +197967,7 @@ static int fts3IntegrityMethod( UNUSED_PARAMETER(isQuick); rc = sqlite3Fts3IntegrityCheck(p, &bOk); + assert( pVtab->zErrMsg==0 || rc!=SQLITE_OK ); assert( rc!=SQLITE_CORRUPT_VTAB ); if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" @@ -195800,6 +198371,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ char *p1; char *p2; char *aOut; + i64 nAlloc = (i64)nPoslist*2 + FTS3_BUFFER_PADDING; if( nMaxUndeferred>iPrev ){ p1 = aPoslist; @@ -195811,7 +198383,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ nDistance = iPrev - nMaxUndeferred; } - aOut = (char *)sqlite3Fts3MallocZero(((i64)nPoslist)+FTS3_BUFFER_PADDING); + aOut = (char *)sqlite3Fts3MallocZero(nAlloc); if( !aOut ){ sqlite3_free(aPoslist); return SQLITE_NOMEM; @@ -197913,7 +200485,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ pCsr->aStat[1].nDoc++; } eState = 2; - /* fall through */ + /* no break */ deliberate_fall_through case 2: if( v==0 ){ /* 0x00. Next integer will be a docid. */ @@ -197929,7 +200501,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ /* State 3. The integer just read is a column number. */ default: assert( eState==3 ); iCol = (int)v; - if( iCol<1 ){ + if( iCol<1 || iCol>(pFts3->nColumn+1) ){ rc = SQLITE_CORRUPT_VTAB; break; } @@ -198619,6 +201191,7 @@ static int getNextNode( assert( nKey==4 ); if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear); + if( nNear>=1000000000 ) nNear = 1000000000; } } @@ -201834,9 +204407,9 @@ typedef struct SegmentWriter SegmentWriter; ** incrementally. See function fts3PendingListAppend() for details. */ struct PendingList { - int nData; + sqlite3_int64 nData; char *aData; - int nSpace; + sqlite3_int64 nSpace; sqlite3_int64 iLastDocid; sqlite3_int64 iLastCol; sqlite3_int64 iLastPos; @@ -202009,6 +204582,24 @@ struct SegmentNode { #define SQL_UPDATE_LEVEL_IDX 38 #define SQL_UPDATE_LEVEL 39 +/* +** Wrapper around sqlite3_prepare_v3() to ensure that SQLITE_PREPARE_FROM_DDL +** is always set. +*/ +SQLITE_PRIVATE int sqlite3Fts3PrepareStmt( + Fts3Table *p, /* Prepare for this connection */ + const char *zSql, /* SQL to prepare */ + int bPersist, /* True to set SQLITE_PREPARE_PERSISTENT */ + int bAllowVtab, /* True to omit SQLITE_PREPARE_NO_VTAB */ + sqlite3_stmt **pp /* OUT: Prepared statement */ +){ + int f = SQLITE_PREPARE_FROM_DDL + |((bAllowVtab==0) ? SQLITE_PREPARE_NO_VTAB : 0) + |(bPersist ? SQLITE_PREPARE_PERSISTENT : 0); + + return sqlite3_prepare_v3(p->db, zSql, -1, f, pp, NULL); +} + /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, @@ -202134,12 +204725,12 @@ static int fts3SqlStmt( pStmt = p->aStmt[eStmt]; if( !pStmt ){ - int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; + int bAllowVtab = 0; char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ - f &= ~SQLITE_PREPARE_NO_VTAB; + bAllowVtab = 1; zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); @@ -202147,7 +204738,7 @@ static int fts3SqlStmt( if( !zSql ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL); + rc = sqlite3Fts3PrepareStmt(p, zSql, 1, bAllowVtab, &pStmt); sqlite3_free(zSql); assert( rc==SQLITE_OK || pStmt==0 ); p->aStmt[eStmt] = pStmt; @@ -202496,7 +205087,9 @@ static int fts3PendingTermsAddOne( pList = (PendingList *)fts3HashFind(pHash, zToken, nToken); if( pList ){ - p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem)); + assert( (i64)pList->nData+(i64)nToken+(i64)sizeof(Fts3HashElem) + <= (i64)p->nPendingData ); + p->nPendingData -= (int)(pList->nData + nToken + sizeof(Fts3HashElem)); } if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){ if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){ @@ -202509,7 +205102,9 @@ static int fts3PendingTermsAddOne( } } if( rc==SQLITE_OK ){ - p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem)); + assert( (i64)p->nPendingData + pList->nData + nToken + + sizeof(Fts3HashElem) <= 0x3fffffff ); + p->nPendingData += (int)(pList->nData + nToken + sizeof(Fts3HashElem)); } return rc; } @@ -204843,6 +207438,10 @@ static void fts3ReadEndBlockField( for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){ iVal = iVal*10 + (zText[i] - '0'); } + + /* This if() clause is just to avoid an integer overflow. The record is + ** corrupt in this case. */ + if( (i64)iVal==SMALLEST_INT64 ) iMul = 1; *pnByte = ((i64)iVal * (i64)iMul); } } @@ -205310,7 +207909,7 @@ static int fts3DoRebuild(Fts3Table *p){ if( !zSql ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); sqlite3_free(zSql); } @@ -206069,7 +208668,7 @@ static int fts3IncrmergeLoad( return FTS_CORRUPT_VTAB; } - pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT; + pWriter->nLeafEst = (int)(((iEnd - iStart)+1)/FTS_MAX_APPENDABLE_HEIGHT); pWriter->iStart = iStart; pWriter->iEnd = iEnd; pWriter->iAbsLevel = iAbsLevel; @@ -207063,7 +209662,7 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ if( !zSql ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + rc = sqlite3Fts3PrepareStmt(p, zSql, 0, 1, &pStmt); sqlite3_free(zSql); } @@ -207193,7 +209792,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ v = atoi(&zVal[9]); if( v>=24 && v<=p->nPgsz-35 ) p->nNodeSize = v; rc = SQLITE_OK; - }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ + }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 11) ){ v = atoi(&zVal[11]); if( v>=64 && v<=FTS3_MAX_PENDING_DATA ) p->nMaxPendingData = v; rc = SQLITE_OK; @@ -208187,8 +210786,8 @@ static int fts3StringAppend( ** to grow the buffer until so that it is big enough to accommodate the ** appended data. */ - if( pStr->n+nAppend+1>=pStr->nAlloc ){ - sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; + if( (i64)pStr->n+(i64)nAppend+1>=(i64)pStr->nAlloc ){ + i64 nAlloc = pStr->nAlloc+(i64)nAppend+100; char *zNew = sqlite3_realloc64(pStr->z, nAlloc); if( !zNew ){ return SQLITE_NOMEM; @@ -208460,7 +211059,7 @@ static int fts3ExprLHits( if( p->flag==FTS3_MATCHINFO_LHITS ){ p->aMatchinfo[iStart + iCol] = (u32)nHit; }else if( nHit ){ - p->aMatchinfo[iStart + (iCol+1)/32] |= (1 << (iCol&0x1F)); + p->aMatchinfo[iStart + iCol/32] |= (1U << (iCol&0x1F)); } } assert( *pIter==0x00 || *pIter==0x01 ); @@ -210453,7 +213052,8 @@ struct JsonString { /* Allowed values for JsonString.eErr */ #define JSTRING_OOM 0x01 /* Out of memory */ #define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ -#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ +#define JSTRING_TOODEEP 0x04 /* JSON nested too deep */ +#define JSTRING_ERR 0x08 /* Error already sent to sqlite3_result */ /* The "subtype" set for text JSON values passed through using ** sqlite3_result_subtype() and sqlite3_value_subtype(). @@ -210468,7 +213068,10 @@ struct JsonString { #define JSON_SQL 0x02 /* Result is always SQL */ #define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ #define JSON_ISSET 0x04 /* json_set(), not json_insert() */ -#define JSON_BLOB 0x08 /* Use the BLOB output format */ +#define JSON_AINS 0x08 /* json_array_insert(), not json_insert() */ +#define JSON_BLOB 0x10 /* Use the BLOB output format */ + +#define JSON_INSERT_TYPE(X) (((X)&0xC)>>2) /* A parsed JSON value. Lifecycle: @@ -210514,6 +213117,7 @@ struct JsonParse { #define JEDIT_REPL 2 /* Overwrite if exists */ #define JEDIT_INS 3 /* Insert if not exists */ #define JEDIT_SET 4 /* Insert or overwrite */ +#define JEDIT_AINS 5 /* array_insert() */ /* ** Maximum nesting depth of JSON for this implementation. @@ -210539,7 +213143,7 @@ struct JsonParse { **************************************************************************/ static void jsonReturnStringAsBlob(JsonString*); static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p); -static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); +static u32 jsonTranslateBlobToText(JsonParse*,u32,JsonString*); static void jsonReturnParse(sqlite3_context*,JsonParse*); static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); static void jsonParseFree(JsonParse*); @@ -210697,6 +213301,15 @@ static void jsonStringOom(JsonString *p){ jsonStringReset(p); } +/* Report JSON nested too deep +*/ +static void jsonStringTooDeep(JsonString *p){ + p->eErr |= JSTRING_TOODEEP; + assert( p->pCtx!=0 ); + sqlite3_result_error(p->pCtx, "JSON nested too deep", -1); + jsonStringReset(p); +} + /* Enlarge pJson->zBuf so that it can hold at least N more bytes. ** Return zero on success. Return non-zero on an OOM error */ @@ -210936,7 +213549,7 @@ static void jsonAppendSqlValue( break; } case SQLITE_FLOAT: { - jsonPrintf(100, p, "%!0.15g", sqlite3_value_double(pValue)); + jsonPrintf(100, p, "%!0.17g", sqlite3_value_double(pValue)); break; } case SQLITE_INTEGER: { @@ -210986,6 +213599,7 @@ static void jsonReturnString( ){ assert( (pParse!=0)==(ctx!=0) ); assert( ctx==0 || ctx==p->pCtx ); + jsonStringTerminate(p); if( p->eErr==0 ){ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); if( flags & JSON_BLOB ){ @@ -210993,7 +213607,7 @@ static void jsonReturnString( }else if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); - }else if( jsonStringTerminate(p) ){ + }else{ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ int rc; pParse->zJson = sqlite3RCStrRef(p->zBuf); @@ -211009,11 +213623,11 @@ static void jsonReturnString( sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, sqlite3RCStrUnref, SQLITE_UTF8); - }else{ - sqlite3_result_error_nomem(p->pCtx); } }else if( p->eErr & JSTRING_OOM ){ sqlite3_result_error_nomem(p->pCtx); + }else if( p->eErr & JSTRING_TOODEEP ){ + /* error already in p->pCtx */ }else if( p->eErr & JSTRING_MALFORMED ){ sqlite3_result_error(p->pCtx, "malformed JSON", -1); } @@ -211344,11 +213958,11 @@ static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ /* Slow version of jsonBlobAppendNode() that first resizes the ** pParse->aBlob structure. */ -static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); +static void jsonBlobAppendNode(JsonParse*,u8,u64,const void*); static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( JsonParse *pParse, u8 eType, - u32 szPayload, + u64 szPayload, const void *aPayload ){ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; @@ -211368,7 +213982,7 @@ static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( static void jsonBlobAppendNode( JsonParse *pParse, /* The JsonParse object under construction */ u8 eType, /* Node type. One of JSONB_* */ - u32 szPayload, /* Number of bytes of payload */ + u64 szPayload, /* Number of bytes of payload */ const void *aPayload /* The payload. Might be NULL */ ){ u8 *a; @@ -212224,12 +214838,8 @@ static int jsonConvertTextToBlob( */ static void jsonReturnStringAsBlob(JsonString *pStr){ JsonParse px; + assert( pStr->eErr==0 ); memset(&px, 0, sizeof(px)); - jsonStringTerminate(pStr); - if( pStr->eErr ){ - sqlite3_result_error_nomem(pStr->pCtx); - return; - } px.zJson = pStr->zBuf; px.nJson = pStr->nUsed; px.db = sqlite3_context_db_handle(pStr->pCtx); @@ -212253,9 +214863,10 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ u8 x; u32 sz; u32 n; - assert( i<=pParse->nBlob ); - x = pParse->aBlob[i]>>4; - if( x<=11 ){ + if( i>=pParse->nBlob ){ + *pSz = 0; + return 0; + }else if( (x = pParse->aBlob[i]>>4)<=11 ){ sz = x; n = 1; }else if( x==12 ){ @@ -212319,7 +214930,7 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ ** The pOut->eErr JSTRING_OOM flag is set on a OOM. */ static u32 jsonTranslateBlobToText( - const JsonParse *pParse, /* the complete parse of the JSON */ + JsonParse *pParse, /* the complete parse of the JSON */ u32 i, /* Start rendering at this index */ JsonString *pOut /* Write JSON here */ ){ @@ -212380,7 +214991,8 @@ static u32 jsonTranslateBlobToText( if( sz==0 ) goto malformed_jsonb; if( zIn[0]=='-' ){ jsonAppendChar(pOut, '-'); - k++; + if( sz<=1 ) goto malformed_jsonb; + k = 1; } if( zIn[k]=='.' ){ jsonAppendChar(pOut, '0'); @@ -212501,10 +215113,14 @@ static u32 jsonTranslateBlobToText( jsonAppendChar(pOut, '['); j = i+n; iEnd = j+sz; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + jsonStringTooDeep(pOut); + } while( jeErr==0 ){ j = jsonTranslateBlobToText(pParse, j, pOut); jsonAppendChar(pOut, ','); } + pParse->iDepth--; if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, ']'); @@ -212515,10 +215131,14 @@ static u32 jsonTranslateBlobToText( jsonAppendChar(pOut, '{'); j = i+n; iEnd = j+sz; + if( ++pParse->iDepth > JSON_MAX_DEPTH ){ + jsonStringTooDeep(pOut); + } while( jeErr==0 ){ j = jsonTranslateBlobToText(pParse, j, pOut); jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); } + pParse->iDepth--; if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; if( sz>0 ) jsonStringTrimOneChar(pOut); jsonAppendChar(pOut, '}'); @@ -212575,7 +215195,7 @@ static u32 jsonTranslateBlobToPrettyText( u32 i /* Start rendering at this index */ ){ u32 sz, n, j, iEnd; - const JsonParse *pParse = pPretty->pParse; + JsonParse *pParse = pPretty->pParse; JsonString *pOut = pPretty->pOut; n = jsonbPayloadSize(pParse, i, &sz); if( n==0 ){ @@ -212590,6 +215210,9 @@ static u32 jsonTranslateBlobToPrettyText( if( jnIndent++; + if( pPretty->nIndent >= JSON_MAX_DEPTH ){ + jsonStringTooDeep(pOut); + } while( pOut->eErr==0 ){ jsonPrettyIndent(pPretty); j = jsonTranslateBlobToPrettyText(pPretty, j); @@ -212611,6 +215234,10 @@ static u32 jsonTranslateBlobToPrettyText( if( jnIndent++; + if( pPretty->nIndent >= JSON_MAX_DEPTH ){ + jsonStringTooDeep(pOut); + } + pParse->iDepth = pPretty->nIndent; while( pOut->eErr==0 ){ jsonPrettyIndent(pPretty); j = jsonTranslateBlobToText(pParse, j, pOut); @@ -212768,6 +215395,7 @@ static void jsonBlobEdit( u32 nIns /* Bytes of content to insert */ ){ i64 d = (i64)nIns - (i64)nDel; + assert( pParse->nBlob >= (u64)iDel + (u64)nDel ); if( d<0 && d>=(-8) && aIns!=0 && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d) ){ @@ -213010,7 +215638,9 @@ static int jsonLabelCompare( */ #define JSON_LOOKUP_ERROR 0xffffffff #define JSON_LOOKUP_NOTFOUND 0xfffffffe -#define JSON_LOOKUP_PATHERROR 0xfffffffd +#define JSON_LOOKUP_NOTARRAY 0xfffffffd +#define JSON_LOOKUP_TOODEEP 0xfffffffc +#define JSON_LOOKUP_PATHERROR 0xfffffffb #define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) /* Forward declaration */ @@ -213039,7 +215669,7 @@ static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); static u32 jsonCreateEditSubstructure( JsonParse *pParse, /* The original JSONB that is being edited */ JsonParse *pIns, /* Populate this with the blob data to insert */ - const char *zTail /* Tail of the path that determins substructure */ + const char *zTail /* Tail of the path that determines substructure */ ){ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; int rc; @@ -213057,7 +215687,12 @@ static u32 jsonCreateEditSubstructure( pIns->eEdit = pParse->eEdit; pIns->nIns = pParse->nIns; pIns->aIns = pParse->aIns; + pIns->iDepth = pParse->iDepth+1; + if( pIns->iDepth >= JSON_MAX_DEPTH ){ + return JSON_LOOKUP_TOODEEP; + } rc = jsonLookupStep(pIns, 0, zTail, 0); + pParse->iDepth--; pParse->oom |= pIns->oom; } return rc; /* Error code only */ @@ -213074,9 +215709,9 @@ static u32 jsonCreateEditSubstructure( ** Return one of the JSON_LOOKUP error codes if problems are seen. ** ** This routine will also modify the blob. If pParse->eEdit is one of -** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be -** made to the selected value. If an edit is performed, then the return -** value does not necessarily point to the select element. If an edit +** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, JEDIT_SET, or JEDIT_AINS, then changes +** might be made to the selected value. If an edit is performed, then the +** return value does not necessarily point to the select element. If an edit ** is performed, the return value is only useful for detecting error ** conditions. */ @@ -213102,6 +215737,13 @@ static u32 jsonLookupStep( jsonBlobEdit(pParse, iRoot, sz, 0, 0); }else if( pParse->eEdit==JEDIT_INS ){ /* Already exists, so json_insert() is a no-op */ + }else if( pParse->eEdit==JEDIT_AINS ){ + /* json_array_insert() */ + if( zPath[-1]!=']' ){ + return JSON_LOOKUP_NOTARRAY; + }else{ + jsonBlobEdit(pParse, iRoot, 0, pParse->aIns, pParse->nIns); + } }else{ /* json_set() or json_replace() */ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); @@ -213156,7 +215798,11 @@ static u32 jsonLookupStep( n = jsonbPayloadSize(pParse, v, &sz); if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; assert( j>0 ); + if( ++pParse->iDepth >= JSON_MAX_DEPTH ){ + return JSON_LOOKUP_TOODEEP; + } rc = jsonLookupStep(pParse, v, &zPath[i], j); + pParse->iDepth--; if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } @@ -213173,6 +215819,10 @@ static u32 jsonLookupStep( JsonParse ix; /* Header of the label to be inserted */ testcase( pParse->eEdit==JEDIT_INS ); testcase( pParse->eEdit==JEDIT_SET ); + testcase( pParse->eEdit==JEDIT_AINS ); + if( pParse->eEdit==JEDIT_AINS && sqlite3_strglob("*]",&zPath[i])!=0 ){ + return JSON_LOOKUP_NOTARRAY; + } memset(&ix, 0, sizeof(ix)); ix.db = pParse->db; jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); @@ -213200,28 +215850,32 @@ static u32 jsonLookupStep( return rc; } }else if( zPath[0]=='[' ){ + u64 kk = 0; x = pParse->aBlob[iRoot] & 0x0f; if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; n = jsonbPayloadSize(pParse, iRoot, &sz); - k = 0; i = 1; while( sqlite3Isdigit(zPath[i]) ){ - k = k*10 + zPath[i] - '0'; + if( kk<0xffffffff ) kk = kk*10 + zPath[i] - '0'; + /* ^^^^^^^^^^--- Allow kk to be bigger than any JSON array so that + ** we get NOTFOUND instead of PATHERROR, without overflowing kk. */ i++; } if( i<2 || zPath[i]!=']' ){ if( zPath[1]=='#' ){ - k = jsonbArrayCount(pParse, iRoot); + kk = jsonbArrayCount(pParse, iRoot); i = 2; if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ - unsigned int nn = 0; + u64 nn = 0; i = 3; do{ - nn = nn*10 + zPath[i] - '0'; + if( nn<0xffffffff ) nn = nn*10 + zPath[i] - '0'; + /* ^^^^^^^^^^--- Allow nn to be bigger than any JSON array to + ** get NOTFOUND instead of PATHERROR, without overflowing nn. */ i++; }while( sqlite3Isdigit(zPath[i]) ); - if( nn>k ) return JSON_LOOKUP_NOTFOUND; - k -= nn; + if( nn>kk ) return JSON_LOOKUP_NOTFOUND; + kk -= nn; } if( zPath[i]!=']' ){ return JSON_LOOKUP_PATHERROR; @@ -213233,21 +215887,26 @@ static u32 jsonLookupStep( j = iRoot+n; iEnd = j+sz; while( jiDepth >= JSON_MAX_DEPTH ){ + return JSON_LOOKUP_TOODEEP; + } rc = jsonLookupStep(pParse, j, &zPath[i+1], 0); + pParse->iDepth--; if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); return rc; } - k--; + kk--; n = jsonbPayloadSize(pParse, j, &sz); if( n==0 ) return JSON_LOOKUP_ERROR; j += n+sz; } if( j>iEnd ) return JSON_LOOKUP_ERROR; - if( k>0 ) return JSON_LOOKUP_NOTFOUND; + if( kk>0 ) return JSON_LOOKUP_NOTFOUND; if( pParse->eEdit>=JEDIT_INS ){ JsonParse v; testcase( pParse->eEdit==JEDIT_INS ); + testcase( pParse->eEdit==JEDIT_AINS ); testcase( pParse->eEdit==JEDIT_SET ); rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); if( !JSON_LOOKUP_ISERROR(rc) @@ -213385,7 +216044,7 @@ static void jsonReturnFromBlob( to_double: z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); if( z==0 ) goto returnfromblob_oom; - rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); + rc = sqlite3AtoF(z, &r); sqlite3DbFree(db, z); if( rc<=0 ) goto returnfromblob_malformed; sqlite3_result_double(pCtx, r); @@ -213565,16 +216224,35 @@ static int jsonFunctionArgToBlob( } /* -** Generate a bad path error. +** Generate a path error. +** +** The specifics of the error are determined by the rc argument. +** +** rc error +** ----------------- ---------------------- +** JSON_LOOKUP_ARRAY "not an array" +** JSON_LOOKUP_TOODEEP "JSON nested too deep" +** JSON_LOOKUP_ERROR "malformed JSON" +** otherwise... "bad JSON path" ** ** If ctx is not NULL then push the error message into ctx and return NULL. ** If ctx is NULL, then return the text of the error message. */ static char *jsonBadPathError( sqlite3_context *ctx, /* The function call containing the error */ - const char *zPath /* The path with the problem */ + const char *zPath, /* The path with the problem */ + int rc /* Maybe JSON_LOOKUP_NOTARRAY */ ){ - char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + char *zMsg; + if( rc==(int)JSON_LOOKUP_NOTARRAY ){ + zMsg = sqlite3_mprintf("not an array element: %Q", zPath); + }else if( rc==(int)JSON_LOOKUP_ERROR ){ + zMsg = sqlite3_mprintf("malformed JSON"); + }else if( rc==(int)JSON_LOOKUP_TOODEEP ){ + zMsg = sqlite3_mprintf("JSON path too deep"); + }else{ + zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + } if( ctx==0 ) return zMsg; if( zMsg ){ sqlite3_result_error(ctx, zMsg, -1); @@ -213591,13 +216269,13 @@ static char *jsonBadPathError( ** and return the result. ** ** The specific operation is determined by eEdit, which can be one -** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. +** of JEDIT_INS, JEDIT_REPL, JEDIT_SET, or JEDIT_AINS. */ static void jsonInsertIntoBlob( sqlite3_context *ctx, int argc, sqlite3_value **argv, - int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ + int eEdit /* JEDIT_INS, JEDIT_REPL, JEDIT_SET, JEDIT_AINS */ ){ int i; u32 rc = 0; @@ -213634,6 +216312,7 @@ static void jsonInsertIntoBlob( p->nIns = ax.nBlob; p->aIns = ax.aBlob; p->delta = 0; + p->iDepth = 0; rc = jsonLookupStep(p, 0, zPath+1, 0); } jsonParseReset(&ax); @@ -213646,11 +216325,7 @@ static void jsonInsertIntoBlob( jsonInsertIntoBlob_patherror: jsonParseFree(p); - if( rc==JSON_LOOKUP_ERROR ){ - sqlite3_result_error(ctx, "malformed JSON", -1); - }else{ - jsonBadPathError(ctx, zPath); - } + jsonBadPathError(ctx, zPath, rc); return; } @@ -214090,10 +216765,8 @@ static void jsonArrayLengthFunc( if( JSON_LOOKUP_ISERROR(i) ){ if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ - }else if( i==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); }else{ - sqlite3_result_error(ctx, "malformed JSON", -1); + jsonBadPathError(ctx, zPath, i); } eErr = 1; i = 0; @@ -214196,7 +216869,7 @@ static void jsonExtractFunc( j = jsonLookupStep(p, 0, jx.zBuf, 0); jsonStringReset(&jx); }else{ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); goto json_extract_error; } if( jnBlob ){ @@ -214227,11 +216900,8 @@ static void jsonExtractFunc( jsonAppendSeparator(&jx); jsonAppendRawNZ(&jx, "null", 4); } - }else if( j==JSON_LOOKUP_ERROR ){ - sqlite3_result_error(ctx, "malformed JSON", -1); - goto json_extract_error; }else{ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, j); goto json_extract_error; } } @@ -214255,6 +216925,7 @@ static void jsonExtractFunc( #define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ #define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ #define JSON_MERGE_OOM 3 /* Out-of-memory condition */ +#define JSON_MERGE_TOODEEP 4 /* Nested too deep */ /* ** RFC-7396 MergePatch for two JSONB blobs. @@ -214306,7 +216977,8 @@ static int jsonMergePatch( JsonParse *pTarget, /* The JSON parser that contains the TARGET */ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ const JsonParse *pPatch, /* The PATCH */ - u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ + u32 iPatch, /* Index of PATCH in pPatch->aBlob[] */ + u32 iDepth /* Nesting depth */ ){ u8 x; /* Type of a single node */ u32 n, sz=0; /* Return values from jsonbPayloadSize() */ @@ -214415,7 +217087,8 @@ static int jsonMergePatch( /* Algorithm line 12 */ int rc, savedDelta = pTarget->delta; pTarget->delta = 0; - rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); + if( iDepth>=JSON_MAX_DEPTH ) return JSON_MERGE_TOODEEP; + rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue, iDepth+1); if( rc ) return rc; pTarget->delta += savedDelta; } @@ -214436,7 +217109,8 @@ static int jsonMergePatch( pTarget->aBlob[iTEnd+szNew] = 0x00; savedDelta = pTarget->delta; pTarget->delta = 0; - rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); + if( iDepth>=JSON_MAX_DEPTH ) return JSON_MERGE_TOODEEP; + rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue,iDepth+1); if( rc ) return rc; pTarget->delta += savedDelta; } @@ -214467,11 +217141,13 @@ static void jsonPatchFunc( if( pTarget==0 ) return; pPatch = jsonParseFuncArg(ctx, argv[1], 0); if( pPatch ){ - rc = jsonMergePatch(pTarget, 0, pPatch, 0); + rc = jsonMergePatch(pTarget, 0, pPatch, 0, 0); if( rc==JSON_MERGE_OK ){ jsonReturnParse(ctx, pTarget); }else if( rc==JSON_MERGE_OOM ){ sqlite3_result_error_nomem(ctx); + }else if( rc==JSON_MERGE_TOODEEP ){ + sqlite3_result_error(ctx, "JSON nested too deep", -1); }else{ sqlite3_result_error(ctx, "malformed JSON", -1); } @@ -214559,10 +217235,8 @@ static void jsonRemoveFunc( if( JSON_LOOKUP_ISERROR(rc) ){ if( rc==JSON_LOOKUP_NOTFOUND ){ continue; /* No-op */ - }else if( rc==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); }else{ - sqlite3_result_error(ctx, "malformed JSON", -1); + jsonBadPathError(ctx, zPath, rc); } goto json_remove_done; } @@ -214572,7 +217246,7 @@ static void jsonRemoveFunc( return; json_remove_patherror: - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); json_remove_done: jsonParseFree(p); @@ -214616,16 +217290,18 @@ static void jsonSetFunc( int argc, sqlite3_value **argv ){ - int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); - int bIsSet = (flags&JSON_ISSET)!=0; + int eInsType = JSON_INSERT_TYPE(flags); + static const char *azInsType[] = { "insert", "set", "array_insert" }; + static const u8 aEditType[] = { JEDIT_INS, JEDIT_SET, JEDIT_AINS }; if( argc<1 ) return; + assert( eInsType>=0 && eInsType<=2 ); if( (argc&1)==0 ) { - jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); + jsonWrongNumArgs(ctx, azInsType[eInsType]); return; } - jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); + jsonInsertIntoBlob(ctx, argc, argv, aEditType[eInsType]); } /* @@ -214650,17 +217326,15 @@ static void jsonTypeFunc( zPath = (const char*)sqlite3_value_text(argv[1]); if( zPath==0 ) goto json_type_done; if( zPath[0]!='$' ){ - jsonBadPathError(ctx, zPath); + jsonBadPathError(ctx, zPath, 0); goto json_type_done; } i = jsonLookupStep(p, 0, zPath+1, 0); if( JSON_LOOKUP_ISERROR(i) ){ if( i==JSON_LOOKUP_NOTFOUND ){ /* no-op */ - }else if( i==JSON_LOOKUP_PATHERROR ){ - jsonBadPathError(ctx, zPath); }else{ - sqlite3_result_error(ctx, "malformed JSON", -1); + jsonBadPathError(ctx, zPath, i); } goto json_type_done; } @@ -214914,12 +217588,12 @@ static void jsonArrayStep( } static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ - int flags; pStr->pCtx = ctx; - jsonAppendChar(pStr, ']'); - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + jsonAppendRawNZ(pStr, "]", 2); + jsonStringTrimOneChar(pStr); if( pStr->eErr ){ jsonReturnString(pStr, 0, 0); return; @@ -214940,6 +217614,9 @@ static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); jsonStringTrimOneChar(pStr); } + }else if( flags & JSON_BLOB ){ + static const u8 emptyArray = 0x0b; + sqlite3_result_blob(ctx, &emptyArray, 1, SQLITE_STATIC); }else{ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); } @@ -214973,11 +217650,9 @@ static void jsonGroupInverse( UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); -#ifdef NEVER /* pStr is always non-NULL since jsonArrayStep() or jsonObjectStep() will ** always have been called to initialize it */ if( NEVER(!pStr) ) return; -#endif z = pStr->zBuf; for(i=1; inUsed && ((c = z[i])!=',' || inStr || nNest); i++){ if( c=='"' ){ @@ -215006,6 +217681,13 @@ static void jsonGroupInverse( ** json_group_obj(NAME,VALUE) ** ** Return a JSON object composed of all names and values in the aggregate. +** +** Rows for which NAME is NULL do not result in a new entry. However, we +** do initially insert a "@" entry into the growing string for each null entry +** and change the first character of the string to "@" to signal that the +** string contains null entries. The "@" markers are needed in order to +** correctly process xInverse() requests. The initial "@" is converted +** back into "{" and the "@" null values are removed by jsonObjectCompute(). */ static void jsonObjectStep( sqlite3_context *ctx, @@ -215023,7 +217705,7 @@ static void jsonObjectStep( if( pStr->zBuf==0 ){ jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '{'); - }else if( pStr->nUsed>1 && z!=0 ){ + }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; @@ -215031,28 +217713,75 @@ static void jsonObjectStep( jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); jsonAppendSqlValue(pStr, argv[1]); + }else{ + pStr->zBuf[0] = '@'; + jsonAppendRawNZ(pStr, "@", 1); } } } static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ - int flags; - jsonAppendChar(pStr, '}'); + JsonString *pOgStr = pStr; + JsonString tmpStr; + jsonAppendRawNZ(pOgStr, "}", 2); /* Ensure it is zero-terminated */ + jsonStringTrimOneChar(pOgStr); /* Remove the zero terminator */ pStr->pCtx = ctx; - flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); if( pStr->eErr ){ jsonReturnString(pStr, 0, 0); return; - }else if( flags & JSON_BLOB ){ + } + if( pStr->zBuf[0]!='{' ){ + /* The string contains null entries that need to be removed */ + u64 i, j; + int inStr = 0; + if( !isFinal ){ + /* Work with a temporary copy of the string if this is not the + ** final result */ + jsonStringInit(&tmpStr, ctx); + jsonAppendRawNZ(&tmpStr, pStr->zBuf, pStr->nUsed+1); + pStr = &tmpStr; + if( pStr->eErr ){ + jsonReturnString(pStr, 0, 0); + return; + } + jsonStringTrimOneChar(pStr); /* Remove zero terminator */ + } + /* Fix up the string by changing the initial "@" flag back to + ** to "{" and removing all subsequence "@" entries, with their + ** associated comma delimeters. */ + pStr->zBuf[0] = '{'; + for(i=j=1; inUsed; i++){ + char c = pStr->zBuf[i]; + if( c=='"' ){ + inStr = !inStr; + pStr->zBuf[j++] = '"'; + }else if( c=='\\' ){ + pStr->zBuf[j++] = '\\'; + pStr->zBuf[j++] = pStr->zBuf[++i]; + }else if( c=='@' && !inStr ){ + assert( i+1nUsed ); + if( pStr->zBuf[i+1]==',' ){ + i++; + }else if( pStr->zBuf[j-1]==',' ){ + j--; + } + }else{ + pStr->zBuf[j++] = c; + } + } + pStr->zBuf[j] = 0; /* Restore zero terminator */ + pStr->nUsed = j; /* Truncate the string */ + } + if( flags & JSON_BLOB ){ jsonReturnStringAsBlob(pStr); if( isFinal ){ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); }else{ - jsonStringTrimOneChar(pStr); + jsonStringTrimOneChar(pOgStr); } - return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : @@ -215060,8 +217789,12 @@ static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); - jsonStringTrimOneChar(pStr); + jsonStringTrimOneChar(pOgStr); } + if( pStr!=pOgStr ) jsonStringReset(pStr); + }else if( flags & JSON_BLOB ){ + static const unsigned char emptyObject = 0x0c; + sqlite3_result_blob(ctx, &emptyObject, 1, SQLITE_STATIC); }else{ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); } @@ -215223,7 +217956,9 @@ static int jsonSkipLabel(JsonEachCursor *p){ if( p->eType==JSONB_OBJECT ){ u32 sz = 0; u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); - return p->i + n + sz; + sz += p->i + n; + if( sz >= p->sParse.nBlob ) sz = p->i; + return sz; }else{ return p->i; } @@ -215562,7 +218297,7 @@ static int jsonEachFilter( if( zRoot==0 ) return SQLITE_OK; if( zRoot[0]!='$' ){ sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } @@ -215580,7 +218315,7 @@ static int jsonEachFilter( return SQLITE_OK; } sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot, 0); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } @@ -215670,6 +218405,8 @@ SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), + JFUNCTION(json_array_insert, -1,1,1, 1,0,JSON_AINS, jsonSetFunc), + JFUNCTION(jsonb_array_insert,-1,1,0, 1,1,JSON_AINS, jsonSetFunc), JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), @@ -215908,7 +218645,7 @@ struct Rtree { u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */ u8 nBytesPerCell; /* Bytes consumed per cell */ u8 inWrTrans; /* True if inside write transaction */ - u8 nAux; /* # of auxiliary columns in %_rowid */ + u16 nAux; /* # of auxiliary columns in %_rowid */ #ifdef SQLITE_ENABLE_GEOPOLY u8 nAuxNotNull; /* Number of initial not-null aux columns */ #endif @@ -216048,7 +218785,7 @@ struct RtreeCursor { sqlite3_stmt *pReadAux; /* Statement to read aux-data */ RtreeSearchPoint sPoint; /* Cached next search point */ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */ - u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */ + u32 anQueue[RTREE_MAX_DEPTH+2]; /* Number of queued entries by iLevel */ }; /* Return the Rtree of a RtreeCursor */ @@ -216503,6 +219240,9 @@ static int nodeAcquire( rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } + }else if( iNode<=0 ){ + RTREE_IS_CORRUPT(pRtree); + rc = SQLITE_CORRUPT_VTAB; }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){ pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ @@ -216528,7 +219268,7 @@ static int nodeAcquire( */ if( rc==SQLITE_OK && pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); - if( pRtree->iDepth>RTREE_MAX_DEPTH ){ + if( pRtree->iDepth>=RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; RTREE_IS_CORRUPT(pRtree); } @@ -216779,7 +219519,17 @@ static void rtreeRelease(Rtree *pRtree){ pRtree->inWrTrans = 0; assert( pRtree->nCursor==0 ); nodeBlobReset(pRtree); - assert( pRtree->nNodeRef==0 || pRtree->bCorrupt ); + if( pRtree->nNodeRef ){ + int i; + assert( pRtree->bCorrupt ); + for(i=0; iaHash[i] ){ + RtreeNode *pNext = pRtree->aHash[i]->pNext; + sqlite3_free(pRtree->aHash[i]); + pRtree->aHash[i] = pNext; + } + } + } sqlite3_finalize(pRtree->pWriteNode); sqlite3_finalize(pRtree->pDeleteNode); sqlite3_finalize(pRtree->pReadRowid); @@ -217134,7 +219884,7 @@ static int nodeRowidIndex( ){ int ii; int nCell = NCELL(pNode); - assert( nCell<200 ); + assert( nCell<65536 && nCell>=0 ); for(ii=0; iiRTREE_MAXCELLS ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell); while( p->iCell100) ){ + if( cnt>100 ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -218429,15 +221182,6 @@ static int SplitNode( rc = updateMapping(pRtree, pCell->iRowid, pLeft, iHeight); } - if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pRight); - pRight = 0; - } - if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pLeft); - pLeft = 0; - } - splitnode_out: nodeRelease(pRtree, pRight); nodeRelease(pRtree, pLeft); @@ -218622,7 +221366,7 @@ static int rtreeInsertCell( rc = SplitNode(pRtree, pNode, pCell, iHeight); }else{ rc = AdjustTree(pRtree, pNode, pCell); - if( ALWAYS(rc==SQLITE_OK) ){ + if( rc==SQLITE_OK ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ @@ -219382,7 +222126,7 @@ static int rtreeInit( "Auxiliary rtree columns must be last" /* 4 */ }; - assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */ + assert( RTREE_MAX_AUX_COLUMN<256 ); if( argc<6 || argc>RTREE_MAX_AUX_COLUMN+3 ){ *pzErr = sqlite3_mprintf("%s", aErrMsg[2 + (argc>=6)]); return SQLITE_ERROR; @@ -220250,7 +222994,7 @@ static int geopolyParseNumber(GeoParse *p, GeoCoord *pVal){ /* The sqlite3AtoF() routine is much much faster than atof(), if it ** is available */ double r; - (void)sqlite3AtoF((const char*)p->z, &r, j, SQLITE_UTF8); + (void)sqlite3AtoF((const char*)p->z, &r); *pVal = r; #else *pVal = (GeoCoord)atof((const char*)p->z); @@ -221301,6 +224045,11 @@ static int geopolyInit( int ii; (void)pAux; + if( argc>=RTREE_MAX_AUX_COLUMN+4 ){ + *pzErr = sqlite3_mprintf("Too many columns for a geopoly table"); + return SQLITE_ERROR; + } + sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); @@ -222435,7 +225184,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ const UChar *zInput; /* Pointer to input string */ UChar *zOutput = 0; /* Pointer to output buffer */ int nInput; /* Size of utf-16 input string in bytes */ - int nOut; /* Size of output buffer in bytes */ + sqlite3_int64 nOut; /* Size of output buffer in bytes */ int cnt; int bToUpper; /* True for toupper(), false for tolower() */ UErrorCode status; @@ -222458,7 +225207,7 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ } for(cnt=0; cnt<2; cnt++){ - UChar *zNew = sqlite3_realloc(zOutput, nOut); + UChar *zNew = sqlite3_realloc64(zOutput, nOut); if( zNew==0 ){ sqlite3_free(zOutput); sqlite3_result_error_nomem(p); @@ -222467,9 +225216,9 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){ zOutput = zNew; status = U_ZERO_ERROR; if( bToUpper ){ - nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); + nOut = 2LL*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); }else{ - nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); + nOut = 2LL*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status); } if( U_SUCCESS(status) ){ @@ -224082,16 +226831,26 @@ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36, -1, 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, -1, -1, -1, 63, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; unsigned int v = 0; int c; unsigned char *z = (unsigned char*)*pz; - unsigned char *zStart = z; - while( (c = zValue[0x7f&*(z++)])>=0 ){ - v = (v<<6) + c; + unsigned char *zEnd = z + (*pLen); + while( z=0 ){ + v = (v<<6) + c; + z++; } - z--; - *pLen -= (int)(z - zStart); + + *pLen -= (int)(z - (unsigned char*)*pz); *pz = (char*)z; return v; } @@ -224160,21 +226919,22 @@ static int rbuDeltaApply( int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ - unsigned int limit; - unsigned int total = 0; + sqlite3_uint64 limit; + sqlite3_uint64 total = 0; #if RBU_ENABLE_DELTA_CKSUM char *zOrigOut = zOut; #endif limit = rbuDeltaGetInt(&zDelta, &lenDelta); - if( *zDelta!='\n' ){ + if( lenDelta<=0 || *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } - zDelta++; lenDelta--; - while( *zDelta && lenDelta>0 ){ + zDelta++; lenDelta--; /* Skip the \n */ + while( lenDelta>0 && zDelta[0] ){ unsigned int cnt, ofst; cnt = rbuDeltaGetInt(&zDelta, &lenDelta); + if( lenDelta<=0 ) return -1; switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; @@ -224204,7 +226964,7 @@ static int rbuDeltaApply( /* ERROR: insert command gives an output larger than predicted */ return -1; } - if( (int)cnt>lenDelta ){ + if( cnt>lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } @@ -224242,7 +227002,7 @@ static int rbuDeltaApply( static int rbuDeltaOutputSize(const char *zDelta, int lenDelta){ int size; size = rbuDeltaGetInt(&zDelta, &lenDelta); - if( *zDelta!='\n' ){ + if( lenDelta<=0 || *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } @@ -224290,7 +227050,7 @@ static void rbuFossilDeltaFunc( return; } - aOut = sqlite3_malloc(nOut+1); + aOut = sqlite3_malloc64((i64)nOut+1); if( aOut==0 ){ sqlite3_result_error_nomem(context); }else{ @@ -225835,8 +228595,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){ /* If necessary, grow the pIter->aIdxCol[] array */ if( iIdxCol==nIdxAlloc ){ - RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc( - pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan) + RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc64( + pIter->aIdxCol, nIdxAlloc*sizeof(RbuSpan) + 16*sizeof(RbuSpan) ); if( aIdxCol==0 ){ rc = SQLITE_NOMEM; @@ -226217,13 +228977,13 @@ static int rbuGetUpdateStmt( char *zUpdate = 0; pUp->zMask = (char*)&pUp[1]; - memcpy(pUp->zMask, zMask, pIter->nTblCol); pUp->pNext = pIter->pRbuUpdate; pIter->pRbuUpdate = pUp; if( zSet ){ const char *zPrefix = ""; - + assert( p->rc==SQLITE_OK ); + memcpy(pUp->zMask, zMask, pIter->nTblCol); if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_"; zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s", zPrefix, pIter->zTbl, zSet, zWhere @@ -226313,6 +229073,9 @@ static RbuState *rbuLoadState(sqlite3rbu *p){ case RBU_STATE_ROW: pRet->nRow = sqlite3_column_int(pStmt, 1); + if( pRet->nRow<0 ){ + rc = SQLITE_CORRUPT; + } break; case RBU_STATE_PROGRESS: @@ -230184,12 +232947,13 @@ static int dbpageFilter( pCsr->szPage = sqlite3BtreeGetPageSize(pBt); pCsr->mxPgno = sqlite3BtreeLastPage(pBt); if( idxNum & 1 ){ + i64 iPg = sqlite3_value_int64(argv[idxNum>>1]); assert( argc>(idxNum>>1) ); - pCsr->pgno = sqlite3_value_int(argv[idxNum>>1]); - if( pCsr->pgno<1 || pCsr->pgno>pCsr->mxPgno ){ + if( iPg<1 || iPg>pCsr->mxPgno ){ pCsr->pgno = 1; pCsr->mxPgno = 0; }else{ + pCsr->pgno = (Pgno)iPg; pCsr->mxPgno = pCsr->pgno; } }else{ @@ -230509,6 +233273,7 @@ struct carray_bind { int nData; /* Number of elements */ int mFlags; /* Control flags */ void (*xDel)(void*); /* Destructor for aData */ + void *pDel; /* Alternative argument to xDel() */ }; @@ -230841,7 +233606,7 @@ static sqlite3_module carrayModule = { static void carrayBindDel(void *pPtr){ carray_bind *p = (carray_bind*)pPtr; if( p->xDel!=SQLITE_STATIC ){ - p->xDel(p->aData); + p->xDel(p->pDel); } sqlite3_free(p); } @@ -230849,14 +233614,26 @@ static void carrayBindDel(void *pPtr){ /* ** Invoke this interface in order to bind to the single-argument ** version of CARRAY(). +** +** pStmt The prepared statement to which to bind +** idx The index of the parameter of pStmt to which to bind +** aData The data to be bound +** nData The number of elements in aData +** mFlags One of SQLITE_CARRAY_xxxx indicating datatype of aData +** xDestroy Destructor for pDestroy or aData if pDestroy==NULL. +** pDestroy Invoke xDestroy on this pointer if not NULL +** +** The destructor is called pDestroy if pDestroy!=NULL, or against +** aData if pDestroy==NULL. */ -SQLITE_API int sqlite3_carray_bind( +SQLITE_API int sqlite3_carray_bind_v2( sqlite3_stmt *pStmt, int idx, void *aData, int nData, int mFlags, - void (*xDestroy)(void*) + void (*xDestroy)(void*), + void *pDestroy ){ carray_bind *pNew = 0; int i; @@ -230933,20 +233710,38 @@ SQLITE_API int sqlite3_carray_bind( memcpy(pNew->aData, aData, sz); } pNew->xDel = sqlite3_free; + pNew->pDel = pNew->aData; }else{ pNew->aData = aData; pNew->xDel = xDestroy; + pNew->pDel = pDestroy; } return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); carray_bind_error: if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ - xDestroy(aData); + xDestroy(pDestroy); } sqlite3_free(pNew); return rc; } +/* +** Invoke this interface in order to bind to the single-argument +** version of CARRAY(). Same as sqlite3_carray_bind_v2() with the +** pDestroy parameter set to NULL. +*/ +SQLITE_API int sqlite3_carray_bind( + sqlite3_stmt *pStmt, + int idx, + void *aData, + int nData, + int mFlags, + void (*xDestroy)(void*) +){ + return sqlite3_carray_bind_v2(pStmt,idx,aData,nData,mFlags,xDestroy,aData); +} + /* ** Invoke this routine to register the carray() function. */ @@ -231309,6 +234104,21 @@ static int sessionVarintGet(const u8 *aBuf, int *piVal){ return getVarint32(aBuf, *piVal); } +/* +** Read a varint value from buffer aBuf[], size nBuf bytes, into *piVal. +** Return the number of bytes read. +*/ +static int sessionVarintGetSafe(const u8 *aBuf, int nBuf, int *piVal){ + u8 aCopy[9]; + const u8 *aRead = aBuf; + memset(aCopy, 0, sizeof(aCopy)); + if( nBuf> 0) & 0xFF; } +/* +** Write a double value to the buffer aBuf[]. +*/ +static void sessionPutDouble(u8 *aBuf, double r){ + /* TODO: SQLite does something special to deal with mixed-endian + ** floating point values (e.g. ARM7). This code probably should + ** too. */ + u64 i; + assert( sizeof(double)==8 && sizeof(u64)==8 ); + memcpy(&i, &r, 8); + sessionPutI64(aBuf, i); +} + /* ** This function is used to serialize the contents of value pValue (see ** comment titled "RECORD FORMAT" above). @@ -231374,16 +234197,13 @@ static int sessionSerializeValue( /* TODO: SQLite does something special to deal with mixed-endian ** floating point values (e.g. ARM7). This code probably should ** too. */ - u64 i; if( eType==SQLITE_INTEGER ){ - i = (u64)sqlite3_value_int64(pValue); + u64 i = (u64)sqlite3_value_int64(pValue); + sessionPutI64(&aBuf[1], i); }else{ - double r; - assert( sizeof(double)==8 && sizeof(u64)==8 ); - r = sqlite3_value_double(pValue); - memcpy(&i, &r, 8); + double r = sqlite3_value_double(pValue); + sessionPutDouble(&aBuf[1], r); } - sessionPutI64(&aBuf[1], i); } nByte = 9; break; @@ -231573,10 +234393,11 @@ static int sessionSerialLen(const u8 *a){ int n; assert( a!=0 ); e = *a; - if( e==0 || e==0xFF ) return 1; - if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; - return sessionVarintGet(&a[1], &n) + 1 + n; + if( e==SQLITE_TEXT || e==SQLITE_BLOB ){ + return sessionVarintGet(&a[1], &n) + 1 + n; + } + return 1; } /* @@ -231599,31 +234420,31 @@ static unsigned int sessionChangeHash( u8 *a = aRecord; /* Used to iterate through change record */ for(i=0; inCol; i++){ - int eType = *a; int isPK = pTab->abPK[i]; if( bPkOnly && isPK==0 ) continue; - /* It is not possible for eType to be SQLITE_NULL here. The session - ** module does not record changes for rows with NULL values stored in - ** primary key columns. */ - assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT - || eType==SQLITE_TEXT || eType==SQLITE_BLOB - || eType==SQLITE_NULL || eType==0 - ); - assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) ); - if( isPK ){ - a++; + int eType = *a++; + + assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT + || eType==SQLITE_TEXT || eType==SQLITE_BLOB + || eType==SQLITE_NULL || eType==0 + ); + h = sessionHashAppendType(h, eType); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ h = sessionHashAppendI64(h, sessionGetI64(a)); a += 8; - }else{ + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ int n; a += sessionVarintGet(a, &n); h = sessionHashAppendBlob(h, n, a); a += n; } + /* It should not be possible for eType to be SQLITE_NULL or 0x00 here, + ** as the session module does not record changes for rows with NULL + ** values stored in primary key columns. But a corrupt changesets + ** may contain such a value. */ }else{ a += sessionSerialLen(a); } @@ -232315,9 +235136,7 @@ static void sessionUpdateOneChange( case SQLITE_FLOAT: { double rVal = sqlite3_column_double(pDflt, iField); - i64 iVal = 0; - memcpy(&iVal, &rVal, sizeof(rVal)); - sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); + sessionPutDouble(&pNew->aRecord[pNew->nRecord], rVal); pNew->nRecord += 8; break; } @@ -232413,7 +235232,7 @@ static void sessionAppendStr( int *pRc ){ int nStr = sqlite3Strlen30(zStr); - if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ + if( 0==sessionBufferGrow(p, (i64)nStr+1, pRc) ){ memcpy(&p->aBuf[p->nBuf], zStr, nStr); p->nBuf += nStr; p->aBuf[p->nBuf] = 0x00; @@ -232481,6 +235300,16 @@ static int sessionPrepareDfltStmt( return rc; } +/* +** Finalize statement pStmt. If (*pRc) is SQLITE_OK when this function is +** called, set it to the results of the sqlite3_finalize() call. Or, if +** it is already set to an error code, leave it as is. +*/ +static void sessionFinalizeStmt(sqlite3_stmt *pStmt, int *pRc){ + int rc = sqlite3_finalize(pStmt); + if( *pRc==SQLITE_OK ) *pRc = rc; +} + /* ** Table pTab has one or more existing change-records with old.* records ** with fewer than pTab->nCol columns. This function updates all such @@ -232503,9 +235332,8 @@ static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){ } } + sessionFinalizeStmt(pStmt, &rc); pSession->rc = rc; - rc = sqlite3_finalize(pStmt); - if( pSession->rc==SQLITE_OK ) pSession->rc = rc; return pSession->rc; } @@ -233073,7 +235901,7 @@ static int sessionDiffFindNew( rc = SQLITE_NOMEM; }else{ sqlite3_stmt *pStmt; - rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(pSession->db, zStmt, -1, &pStmt, 0); if( rc==SQLITE_OK ){ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; pDiffCtx->pStmt = pStmt; @@ -233136,7 +235964,7 @@ static int sessionDiffFindModified( rc = SQLITE_NOMEM; }else{ sqlite3_stmt *pStmt; - rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(pSession->db, zStmt, -1, &pStmt, 0); if( rc==SQLITE_OK ){ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx; @@ -233574,15 +236402,14 @@ static void sessionAppendCol( int eType = sqlite3_column_type(pStmt, iCol); sessionAppendByte(p, (u8)eType, pRc); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ - sqlite3_int64 i; u8 aBuf[8]; if( eType==SQLITE_INTEGER ){ - i = sqlite3_column_int64(pStmt, iCol); + sqlite3_int64 i = sqlite3_column_int64(pStmt, iCol); + sessionPutI64(aBuf, i); }else{ double r = sqlite3_column_double(pStmt, iCol); - memcpy(&i, &r, 8); + sessionPutDouble(aBuf, r); } - sessionPutI64(aBuf, i); sessionAppendBlob(p, aBuf, 8, pRc); } if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){ @@ -233832,11 +236659,11 @@ static int sessionSelectStmt( ); sessionAppendStr(&cols, "tbl, ?2, stat", &rc); }else{ - #if 0 +#if 0 if( bRowid ){ sessionAppendStr(&cols, SESSIONS_ROWID, &rc); } - #endif +#endif for(i=0; irc ) return pSession->rc; - rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); - if( rc!=SQLITE_OK ) return rc; sqlite3_mutex_enter(sqlite3_db_mutex(db)); + rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0); + if( rc!=SQLITE_OK ){ + sqlite3_mutex_leave(sqlite3_db_mutex(db)); + return rc; + } for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ if( pTab->nEntry ){ @@ -234518,7 +237348,8 @@ static int sessionReadRecord( u8 *aVal = &pIn->aData[pIn->iNext]; if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ int nByte; - pIn->iNext += sessionVarintGet(aVal, &nByte); + int nRem = pIn->nData - pIn->iNext; + pIn->iNext += sessionVarintGetSafe(aVal, nRem, &nByte); rc = sessionInputBuffer(pIn, nByte); if( rc==SQLITE_OK ){ if( nByte<0 || nByte>pIn->nData-pIn->iNext ){ @@ -234571,7 +237402,8 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){ rc = sessionInputBuffer(pIn, 9); if( rc==SQLITE_OK ){ - nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol); + int nBuf = pIn->nData - pIn->iNext; + nRead += sessionVarintGetSafe(&pIn->aData[pIn->iNext], nBuf, &nCol); /* The hard upper limit for the number of columns in an SQLite ** database table is, according to sqliteLimit.h, 32676. So ** consider any table-header that purports to have more than 65536 @@ -234591,8 +237423,15 @@ static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){ while( (pIn->iNext + nRead)nData && pIn->aData[pIn->iNext + nRead] ){ nRead++; } + + /* Break out of the loop if if the nul-terminator byte has been found. + ** Otherwise, read some more input data and keep seeking. If there is + ** no more input data, consider the changeset corrupt. */ if( (pIn->iNext + nRead)nData ) break; rc = sessionInputBuffer(pIn, nRead + 100); + if( rc==SQLITE_OK && (pIn->iNext + nRead)>=pIn->nData ){ + rc = SQLITE_CORRUPT_BKPT; + } } *pnByte = nRead+1; return rc; @@ -234613,7 +237452,7 @@ static int sessionChangesetBufferRecord( int *pnByte /* OUT: Size of record in bytes */ ){ int rc = SQLITE_OK; - int nByte = 0; + i64 nByte = 0; int i; for(i=0; rc==SQLITE_OK && iaData[pIn->iNext + nByte++]; if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ int n; - nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n); + int nRem = pIn->nData - (pIn->iNext + nByte); + nByte += sessionVarintGetSafe(&pIn->aData[pIn->iNext+nByte], nRem, &n); nByte += n; rc = sessionInputBuffer(pIn, nByte); }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ nByte += 8; + }else if( eType!=0 && eType!=SQLITE_NULL ){ + rc = SQLITE_CORRUPT_BKPT; } } + if( rc==SQLITE_OK && (pIn->iNext+nByte)>pIn->nData ){ + rc = SQLITE_CORRUPT_BKPT; + } } *pnByte = nByte; return rc; @@ -234724,10 +237569,10 @@ static int sessionChangesetNextOne( memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2); } - /* Make sure the buffer contains at least 10 bytes of input data, or all - ** remaining data if there are less than 10 bytes available. This is - ** sufficient either for the 'T' or 'P' byte and the varint that follows - ** it, or for the two single byte values otherwise. */ + /* Make sure the buffer contains at least 2 bytes of input data, or all + ** remaining data if there are less than 2 bytes available. This is + ** sufficient either for the 'T' or 'P' byte that begins a new table, + ** or for the "op" and "bIndirect" single bytes otherwise. */ p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; @@ -234757,11 +237602,13 @@ static int sessionChangesetNextOne( return (p->rc = SQLITE_CORRUPT_BKPT); } - p->op = op; - p->bIndirect = p->in.aData[p->in.iNext++]; - if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){ + if( (op!=SQLITE_UPDATE && op!=SQLITE_DELETE && op!=SQLITE_INSERT) + || (p->in.iNext>=p->in.nData) + ){ return (p->rc = SQLITE_CORRUPT_BKPT); } + p->op = op; + p->bIndirect = p->in.aData[p->in.iNext++]; if( paRec ){ int nVal; /* Number of values to buffer */ @@ -235070,7 +237917,13 @@ static int sessionChangesetInvert( /* Test for EOF. */ if( (rc = sessionInputBuffer(pInput, 2)) ) goto finished_invert; - if( pInput->iNext>=pInput->nData ) break; + if( pInput->iNext+1>=pInput->nData ){ + if( pInput->iNext!=pInput->nData ){ + rc = SQLITE_CORRUPT_BKPT; + goto finished_invert; + } + break; + } eType = pInput->aData[pInput->iNext]; switch( eType ){ @@ -235266,6 +238119,7 @@ struct SessionApplyCtx { u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ + u8 bNoUpdateLoop; /* No update-loop processing */ int bRowid; char *zErr; /* Error message, if any */ }; @@ -235839,7 +238693,7 @@ static int sessionConflictHandler( u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent]; int nBlob = pIter->in.iNext - pIter->in.iCurrent; sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc); - return SQLITE_OK; + return rc; }else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE || eType==SQLITE_CHANGESET_CONFLICT ){ @@ -235961,7 +238815,7 @@ static int sessionApplyOneOp( for(i=0; rc==SQLITE_OK && iabPK[i] || (bPatchset==0 && pOld) ){ + if( pOld && (p->abPK[i] || bPatchset==0) ){ rc = sessionBindValue(pUp, i*2+2, pOld); } if( rc==SQLITE_OK && pNew ){ @@ -236087,7 +238941,264 @@ static int sessionApplyOneWithRetry( } /* -** Retry the changes accumulated in the pApply->constraints buffer. +** Create an iterator to iterate through the retry buffer pRetry. +*/ +static int sessionRetryIterInit( + SessionBuffer *pRetry, /* Buffer to iterate through */ + int bPatchset, /* True for patchset, false for changeset */ + const char *zTab, /* Table name */ + SessionApplyCtx *pApply, /* Session apply context */ + sqlite3_changeset_iter **ppIter /* OUT: New iterator */ +){ + sqlite3_changeset_iter *pRet = 0; + int rc = SQLITE_OK; + + rc = sessionChangesetStart( + &pRet, 0, 0, pRetry->nBuf, pRetry->aBuf, pApply->bInvertConstraints, 1 + ); + if( rc==SQLITE_OK ){ + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); + pRet->bPatchset = bPatchset; + pRet->zTab = (char*)zTab; + pRet->nCol = pApply->nCol; + pRet->abPK = pApply->abPK; + sessionBufferGrow(&pRet->tblhdr, nByte, &rc); + pRet->apValue = (sqlite3_value**)pRet->tblhdr.aBuf; + if( rc==SQLITE_OK ){ + memset(pRet->apValue, 0, nByte); + }else{ + sqlite3changeset_finalize(pRet); + pRet = 0; + } + } + + *ppIter = pRet; + return rc; +} + +/* +** Attempt to apply all the changes in retry buffer pRetry to the database. +** Except, if parameter iSkip is greater than or equal to 0, skip change +** iSkip. +*/ +static int sessionApplyRetryBuffer( + SessionBuffer *pRetry, /* Buffer to apply changes from */ + int iSkip, /* If >=0, index of change to omit */ + sqlite3 *db, /* Database handle */ + int bPatchset, /* True for patchset, false for changeset */ + const char *zTab, /* Name of table to write to */ + SessionApplyCtx *pApply, /* Apply context */ + int(*xConflict)(void*, int, sqlite3_changeset_iter*), + void *pCtx /* First argument passed to xConflict */ +){ + int rc = SQLITE_OK; + int rc2 = SQLITE_OK; + int ii = 0; + sqlite3_changeset_iter *pIter = 0; + + assert( pApply->constraints.nBuf==0 ); + + rc = sessionRetryIterInit(pRetry, bPatchset, zTab, pApply, &pIter); + + for(ii=0; rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter); ii++){ + if( ii!=iSkip ){ + rc = sessionApplyOneWithRetry(db, pIter, pApply, xConflict, pCtx); + } + } + + rc2 = sqlite3changeset_finalize(pIter); + if( rc==SQLITE_OK ) rc = rc2; + assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 ); + + return rc; +} + +/* +** Check if table zTab in the "main" database of db is a WITHOUT ROWID +** table. +** +** If no error occurs, return SQLITE_OK and set output variable (*pbWR) to +** true if zTab is a WITHOUT ROWID table, or false otherwise. Or, if an +** error does occur, return an SQLite error code. The final value of (*pbWR) +** is undefined in this case. +*/ +static int sessionTableIsWithoutRowid(sqlite3 *db, const char *zTab, int *pbWR){ + sqlite3_stmt *pList = 0; + char *zSql = 0; + int rc = SQLITE_OK; + + zSql = sqlite3_mprintf("PRAGMA table_list = %Q", zTab); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(db, zSql, -1, &pList, 0); + sqlite3_free(zSql); + } + + if( rc==SQLITE_OK ){ + sqlite3_step(pList); + *pbWR = sqlite3_column_int(pList, 4); + rc = sqlite3_finalize(pList); + } + + return rc; +} + +/* +** Iterator pUp points to an UPDATE change. This function deletes the +** affected row from the database and creates an INSERT statement that +** may be used to reinsert the row as it is after the UPDATE change +** has been applied. +** +** If successful, SQLITE_OK is returned and output variable (*ppInsert) +** is left pointing to a prepared INSERT statement. It is the responsibility +** of the caller to eventually free this statement using sqlite3_finalize(). +** Or, if an error occurs, an SQLite error code is returned and (*ppInsert) +** set to NULL. pApply->zErr may be set to an error message in this case. +*/ +static int sessionUpdateToDeleteInsert( + sqlite3 *db, /* Database to write to */ + const char *zTab, /* Table name */ + SessionApplyCtx *pApply, /* Apply context */ + sqlite3_changeset_iter *pUp, /* Iterator pointing to UPDATE change */ + sqlite3_stmt **ppInsert /* OUT: INSERT statement */ +){ + sqlite3_stmt *pRet = 0; /* The INSERT statement */ + sqlite3_stmt *pSelect = 0; /* SELECT to read current values of row */ + int rc = SQLITE_OK; + int bWR = 0; + + rc = sessionTableIsWithoutRowid(db, zTab, &bWR); + if( rc==SQLITE_OK ){ + char *zSelect = 0; + char *zInsert = 0; + SessionBuffer cols = {0, 0, 0}; + SessionBuffer insbind = {0, 0, 0}; + SessionBuffer pkcols = {0, 0, 0}; + SessionBuffer selbind = {0, 0, 0}; + + const char *zComma = ""; + const char *zComma2 = ""; + int ii; + for(ii=0; iinCol; ii++){ + sessionAppendStr(&cols, zComma, &rc); + sessionAppendIdent(&cols, pApply->azCol[ii], &rc); + sessionAppendStr(&insbind, zComma, &rc); + sessionAppendStr(&insbind, "?", &rc); + zComma = ", "; + + if( pApply->abPK[ii] ){ + sessionAppendStr(&pkcols, zComma2, &rc); + sessionAppendIdent(&pkcols, pApply->azCol[ii], &rc); + sessionAppendStr(&selbind, zComma2, &rc); + sessionAppendPrintf(&selbind, &rc, "?%d", ii+1); + zComma2 = ", "; + } + } + if( bWR==0 ){ + sessionAppendStr(&cols, zComma, &rc); + sessionAppendStr(&cols, SESSIONS_ROWID, &rc); + sessionAppendStr(&insbind, zComma, &rc); + sessionAppendStr(&insbind, "?", &rc); + } + + if( rc==SQLITE_OK ){ + zSelect = sqlite3_mprintf("SELECT %s FROM %Q WHERE (%s) IS (%s)", + cols.aBuf, zTab, pkcols.aBuf, selbind.aBuf + ); + if( zSelect==0 ) rc = SQLITE_NOMEM; + } + if( rc==SQLITE_OK ){ + zInsert = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)", + zTab, cols.aBuf, insbind.aBuf + ); + if( zInsert==0 ) rc = SQLITE_NOMEM; + } + + if( rc==SQLITE_OK ){ + rc = sessionPrepare(db, &pSelect, &pApply->zErr, zSelect); + } + if( rc==SQLITE_OK ){ + rc = sessionPrepare(db, &pRet, &pApply->zErr, zInsert); + } + + sqlite3_free(zSelect); + sqlite3_free(zInsert); + sqlite3_free(cols.aBuf); + sqlite3_free(insbind.aBuf); + sqlite3_free(pkcols.aBuf); + sqlite3_free(selbind.aBuf); + } + + if( rc==SQLITE_OK ){ + rc = sessionBindRow( + pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pSelect + ); + } + + if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){ + int iCol; + for(iCol=0; iColnCol; iCol++){ + sqlite3_value *pVal = pUp->apValue[iCol+pApply->nCol]; + if( pVal==0 ){ + pVal = sqlite3_column_value(pSelect, iCol); + } + rc = sqlite3_bind_value(pRet, iCol+1, pVal); + } + if( bWR==0 ){ + sqlite3_bind_int64(pRet, iCol+1, sqlite3_column_int64(pSelect, iCol)); + } + } + sessionFinalizeStmt(pSelect, &rc); + + /* Delete the row from the database. */ + if( rc==SQLITE_OK ){ + rc = sessionBindRow( + pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pApply->pDelete + ); + sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1); + } + if( rc==SQLITE_OK ){ + sqlite3_step(pApply->pDelete); + rc = sqlite3_reset(pApply->pDelete); + } + + if( rc!=SQLITE_OK ){ + sqlite3_finalize(pRet); + pRet = 0; + } + + *ppInsert = pRet; + return rc; +} + +/* +** Retry the changes accumulated in the pApply->constraints buffer. The +** pApply->constraints buffer contains all changes to table zTab that +** could not be applied due to SQLITE_CONSTRAINT errors. This function +** attempts to apply them as follows: +** +** 1) It runs through the buffer and attempts to retry each change, +** removing any that are successfully applied from the buffer. This +** is repeated until no further progress can be made. +** +** 2) For each UPDATE change in the buffer, try the following in a +** savepoint transaction: +** +** a) DELETE the affected row, +** b) Attempt step (1) with remaining changes, +** c) Attempt to INSERT a row equivalent to the one that would be +** created by applying this UPDATE change. +** +** If the INSERT in (c) succeeds, the savepoint is committed and all +** successfully applied changes are removed from the buffer. Step (2) +** is then repeated. +** +** 3) Once step (2) has been attempted for each UPDATE in the change, +** a final attempt is made to apply each remaining change. This time, +** if an SQLITE_CONSTRAINT error is encountered, the conflict handler +** is invoked and the user has to decide whether to omit the change +** or rollback the entire _apply() operation. */ static int sessionRetryConstraints( sqlite3 *db, @@ -236098,41 +239209,101 @@ static int sessionRetryConstraints( void *pCtx /* First argument passed to xConflict */ ){ int rc = SQLITE_OK; + int iUpdate = 0; + /* Step (1) */ while( pApply->constraints.nBuf ){ - sqlite3_changeset_iter *pIter2 = 0; SessionBuffer cons = pApply->constraints; memset(&pApply->constraints, 0, sizeof(SessionBuffer)); - rc = sessionChangesetStart( - &pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1 + rc = sessionApplyRetryBuffer( + &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx + ); + + sqlite3_free(cons.aBuf); + if( rc!=SQLITE_OK ) break; + + /* If no progress has been made this round, break out of the loop. */ + if( pApply->constraints.nBuf>=cons.nBuf ) break; + } + + /* Step (2) */ + while( rc==SQLITE_OK && pApply->constraints.nBuf && !pApply->bNoUpdateLoop ){ + SessionBuffer cons = {0, 0, 0}; + sqlite3_changeset_iter *pUp = 0; + sqlite3_stmt *pInsert = 0; + int iSkip = 0; + + rc = sessionRetryIterInit( + &pApply->constraints, bPatchset, zTab, pApply, &pUp ); if( rc==SQLITE_OK ){ - size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); - int rc2; - pIter2->bPatchset = bPatchset; - pIter2->zTab = (char*)zTab; - pIter2->nCol = pApply->nCol; - pIter2->abPK = pApply->abPK; - sessionBufferGrow(&pIter2->tblhdr, nByte, &rc); - pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf; - if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte); + int iThis = -1; + while( SQLITE_ROW==sqlite3changeset_next(pUp) ){ + if( pUp->op==SQLITE_UPDATE ) iThis++; + if( iThis==iUpdate ) break; + iSkip++; + } + if( iThis==iUpdate ){ + rc = sqlite3_exec(db, "SAVEPOINT update_op", 0, 0, 0); + if( rc==SQLITE_OK ){ + rc = sessionUpdateToDeleteInsert(db, zTab, pApply, pUp, &pInsert); + } + } + sqlite3changeset_finalize(pUp); + if( iThis!=iUpdate ) break; + } - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){ - rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx); + if( rc==SQLITE_OK ){ + cons = pApply->constraints; + + while( rc==SQLITE_OK && pApply->constraints.nBuf>0 ){ + SessionBuffer app = pApply->constraints; + memset(&pApply->constraints, 0, sizeof(SessionBuffer)); + rc = sessionApplyRetryBuffer( + &app, iSkip, db, bPatchset, zTab, pApply, xConflict, pCtx + ); + if( app.aBuf!=cons.aBuf ){ + sqlite3_free(app.aBuf); + } + if( pApply->constraints.nBuf>=app.nBuf ){ + break; + } + iSkip = -1; } + } - rc2 = sqlite3changeset_finalize(pIter2); - if( rc==SQLITE_OK ) rc = rc2; + iUpdate++; + if( rc==SQLITE_OK ){ + sqlite3_step(pInsert); + rc = sqlite3_finalize(pInsert); + if( rc==SQLITE_CONSTRAINT ){ + rc = sqlite3_exec(db, "ROLLBACK TO update_op", 0, 0, 0); + sqlite3_free(pApply->constraints.aBuf); + pApply->constraints = cons; + memset(&cons, 0, sizeof(cons)); + }else if( rc==SQLITE_OK ){ + iUpdate = 0; + } + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(db, "RELEASE update_op", 0, 0, 0); + } + }else{ + sqlite3_finalize(pInsert); } - assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 ); sqlite3_free(cons.aBuf); - if( rc!=SQLITE_OK ) break; - if( pApply->constraints.nBuf>=cons.nBuf ){ - /* No progress was made on the last round. */ - pApply->bDeferConstraints = 0; - } + } + + /* Step (3) */ + if( rc==SQLITE_OK && pApply->constraints.nBuf ){ + SessionBuffer cons = pApply->constraints; + memset(&pApply->constraints, 0, sizeof(SessionBuffer)); + pApply->bDeferConstraints = 0; + rc = sessionApplyRetryBuffer( + &cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx + ); + sqlite3_free(cons.aBuf); } return rc; @@ -236186,6 +239357,7 @@ static int sessionChangesetApply( sApply.bRebase = (ppRebase && pnRebase); sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT); sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP); + sApply.bNoUpdateLoop = !!(flags & SQLITE_CHANGESETAPPLY_NOUPDATELOOP); if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0); } @@ -236609,6 +239781,21 @@ SQLITE_API int sqlite3changeset_apply_strm( ); } +/* +** The parts of the sqlite3_changegroup structure used by the +** sqlite3changegroup_change_xxx() APIs. +*/ +typedef struct ChangeData ChangeData; +struct ChangeData { + SessionTable *pTab; + int bIndirect; + int eOp; + + int nBufAlloc; + SessionBuffer *aBuf; + SessionBuffer record; +}; + /* ** sqlite3_changegroup handle. */ @@ -236620,12 +239807,17 @@ struct sqlite3_changegroup { sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ + ChangeData cd; /* Used by changegroup_change_xxx() APIs. */ }; /* ** This function is called to merge two changes to the same row together as ** part of an sqlite3changeset_concat() operation. A new change object is ** allocated and a pointer to it stored in *ppNew. +** +** Because they have been vetted by sqlite3changegroup_add() or similar, +** both the aRec[] change and the pExist change are safe to use without +** checking for buffer overflows. */ static int sessionChangeMerge( SessionTable *pTab, /* Table structure */ @@ -236766,7 +239958,7 @@ static int sessionChangeMerge( memcpy(aCsr, aRec, nRec); aCsr += nRec; }else{ - if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){ + if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist,0,aRec,0) ){ sqlite3_free(pNew); pNew = 0; } @@ -236859,15 +240051,14 @@ static int sessionChangesetExtendRecord( switch( eType ){ case SQLITE_FLOAT: case SQLITE_INTEGER: { - i64 iVal; - if( eType==SQLITE_INTEGER ){ - iVal = sqlite3_column_int64(pTab->pDfltStmt, ii); - }else{ - double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); - memcpy(&iVal, &rVal, sizeof(i64)); - } if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ - sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); + if( eType==SQLITE_INTEGER ){ + sqlite3_int64 iVal = sqlite3_column_int64(pTab->pDfltStmt, ii); + sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); + }else{ + double rVal = sqlite3_column_double(pTab->pDfltStmt, ii); + sessionPutDouble(&pOut->aBuf[pOut->nBuf], rVal); + } pOut->nBuf += 8; } break; @@ -236938,13 +240129,19 @@ static int sessionChangesetFindTable( int nCol = 0; *ppTab = 0; - sqlite3changeset_pk(pIter, &abPK, &nCol); /* Search the list for an existing table */ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; } + + if( pIter ){ + sqlite3changeset_pk(pIter, &abPK, &nCol); + }else if( !pTab && !pGrp->db ){ + return SQLITE_OK; + } + /* If one was not found above, create a new table now */ if( !pTab ){ SessionTable **ppNew; @@ -236956,15 +240153,17 @@ static int sessionChangesetFindTable( memset(pTab, 0, sizeof(SessionTable)); pTab->nCol = nCol; pTab->abPK = (u8*)&pTab[1]; - memcpy(pTab->abPK, abPK, nCol); + if( nCol>0 ){ + memcpy(pTab->abPK, abPK, nCol); + } pTab->zName = (char*)&pTab->abPK[nCol]; memcpy(pTab->zName, zTab, nTab+1); if( pGrp->db ){ pTab->nCol = 0; rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); - if( rc ){ - assert( pTab->azCol==0 ); + if( rc || pTab->nCol==0 ){ + sqlite3_free(pTab->azCol); sqlite3_free(pTab); return rc; } @@ -236979,7 +240178,7 @@ static int sessionChangesetFindTable( } /* Check that the table is compatible. */ - if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ + if( pIter && !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ rc = SQLITE_SCHEMA; } @@ -236988,44 +240187,27 @@ static int sessionChangesetFindTable( } /* -** Add the change currently indicated by iterator pIter to the hash table -** belonging to changegroup pGrp. +** Add a single change to the changegroup pGrp. */ static int sessionOneChangeToHash( - sqlite3_changegroup *pGrp, - sqlite3_changeset_iter *pIter, - int bRebase + sqlite3_changegroup *pGrp, /* Changegroup to update */ + SessionTable *pTab, /* Table change pertains to */ + int op, /* One of SQLITE_INSERT, UPDATE, DELETE */ + int bIndirect, /* True to flag change as "indirect" */ + int nCol, /* Number of columns in record(s) */ + u8 *aRec, /* Serialized change record(s) */ + int nRec, /* Size of aRec[] in bytes */ + int bRebase /* True if this is a rebase blob */ ){ int rc = SQLITE_OK; - int nCol = 0; - int op = 0; int iHash = 0; - int bIndirect = 0; SessionChange *pChange = 0; SessionChange *pExist = 0; SessionChange **pp = 0; - SessionTable *pTab = 0; - u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; - int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; assert( nRec>0 ); - /* Ensure that only changesets, or only patchsets, but not a mixture - ** of both, are being combined. It is an error to try to combine a - ** changeset and a patchset. */ - if( pGrp->pList==0 ){ - pGrp->bPatch = pIter->bPatchset; - }else if( pIter->bPatchset!=pGrp->bPatch ){ - rc = SQLITE_ERROR; - } - - if( rc==SQLITE_OK ){ - const char *zTab = 0; - sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); - rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); - } - - if( rc==SQLITE_OK && nColnCol ){ + if( nColnCol ){ SessionBuffer *pBuf = &pGrp->rec; rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); aRec = pBuf->aBuf; @@ -237033,7 +240215,7 @@ static int sessionOneChangeToHash( assert( pGrp->db ); } - if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ + if( rc==SQLITE_OK && sessionGrowHash(0, pGrp->bPatch, pTab) ){ rc = SQLITE_NOMEM; } @@ -237041,12 +240223,12 @@ static int sessionOneChangeToHash( /* Search for existing entry. If found, remove it from the hash table. ** Code below may link it back in. */ iHash = sessionChangeHash( - pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange + pTab, (pGrp->bPatch && op==SQLITE_DELETE), aRec, pTab->nChange ); for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; - if( pIter->bPatchset ){ + if( pGrp->bPatch ){ bPkOnly1 = (*pp)->op==SQLITE_DELETE; bPkOnly2 = op==SQLITE_DELETE; } @@ -237061,7 +240243,7 @@ static int sessionOneChangeToHash( if( rc==SQLITE_OK ){ rc = sessionChangeMerge(pTab, bRebase, - pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange + pGrp->bPatch, pExist, op, bIndirect, aRec, nRec, &pChange ); } if( rc==SQLITE_OK && pChange ){ @@ -237070,6 +240252,47 @@ static int sessionOneChangeToHash( pTab->nEntry++; } + return rc; +} + +/* +** Add the change currently indicated by iterator pIter to the hash table +** belonging to changegroup pGrp. +*/ +static int sessionOneChangeIterToHash( + sqlite3_changegroup *pGrp, + sqlite3_changeset_iter *pIter, + int bRebase +){ + u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; + int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; + const char *zTab = 0; + int nCol = 0; + int op = 0; + int bIndirect = 0; + int rc = SQLITE_OK; + SessionTable *pTab = 0; + + /* Ensure that only changesets, or only patchsets, but not a mixture + ** of both, are being combined. It is an error to try to combine a + ** changeset and a patchset. */ + if( pGrp->pList==0 ){ + pGrp->bPatch = pIter->bPatchset; + }else if( pIter->bPatchset!=pGrp->bPatch ){ + rc = SQLITE_ERROR; + } + + if( rc==SQLITE_OK ){ + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); + } + + if( rc==SQLITE_OK ){ + rc = sessionOneChangeToHash( + pGrp, pTab, op, bIndirect, nCol, aRec, nRec, bRebase + ); + } + if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } @@ -237089,7 +240312,12 @@ static int sessionChangesetToHash( pIter->in.bNoDiscard = 1; while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ - rc = sessionOneChangeToHash(pGrp, pIter, bRebase); + if( bRebase && pIter->bPatchset ){ + /* A patchset may not be used as a rebase */ + rc = SQLITE_ERROR; + }else{ + rc = sessionOneChangeIterToHash(pGrp, pIter, bRebase); + } if( rc!=SQLITE_OK ) break; } @@ -237179,6 +240407,33 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){ return rc; } +/* +** Configure a changegroup object. +*/ +SQLITE_API int sqlite3changegroup_config( + sqlite3_changegroup *pGrp, + int op, + void *pArg +){ + int rc = SQLITE_OK; + + switch( op ){ + case SQLITE_CHANGEGROUP_CONFIG_PATCHSET: { + int arg = *(int*)pArg; + if( pGrp->pList==0 && arg>=0 ){ + pGrp->bPatch = (arg>0); + } + *(int*)pArg = pGrp->bPatch; + break; + } + default: + rc = SQLITE_MISUSE; + break; + } + + return rc; +} + /* ** Provide a database schema to the changegroup object. */ @@ -237237,7 +240492,7 @@ SQLITE_API int sqlite3changegroup_add_change( rc = SQLITE_ERROR; }else{ pIter->in.bNoDiscard = 1; - rc = sessionOneChangeToHash(pGrp, pIter, 0); + rc = sessionOneChangeIterToHash(pGrp, pIter, 0); } return rc; } @@ -237289,6 +240544,12 @@ SQLITE_API int sqlite3changegroup_output_strm( */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ + int ii; + for(ii=0; iicd.nBufAlloc; ii++){ + sqlite3_free(pGrp->cd.aBuf[ii].aBuf); + } + sqlite3_free(pGrp->cd.record.aBuf); + sqlite3_free(pGrp->cd.aBuf); sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp->rec.aBuf); @@ -237374,14 +240635,17 @@ static void sessionAppendRecordMerge( u8 *a2, int n2, /* Record 2 */ int *pRc /* IN/OUT: error code */ ){ - sessionBufferGrow(pBuf, n1+n2, pRc); + u8 *a1Eof = &a1[n1]; + u8 *a2Eof = &a2[n2]; + + sessionBufferGrow(pBuf, (i64)n1+n2, pRc); if( *pRc==SQLITE_OK ){ int i; u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; for(i=0; i0 && (*a1==0 || *a1==0xFF)) ){ memcpy(pOut, a2, nn2); pOut += nn2; }else{ @@ -237423,20 +240687,21 @@ static void sessionAppendPartialUpdate( u8 *aChange, int nChange, /* Record to rebase against */ int *pRc /* IN/OUT: Return Code */ ){ - sessionBufferGrow(pBuf, 2+nRec+nChange, pRc); + sessionBufferGrow(pBuf, (i64)2+nRec+nChange, pRc); if( *pRc==SQLITE_OK ){ int bData = 0; u8 *pOut = &pBuf->aBuf[pBuf->nBuf]; int i; u8 *a1 = aRec; u8 *a2 = aChange; + u8 *a2Eof = &a2[nChange]; *pOut++ = SQLITE_UPDATE; *pOut++ = pIter->bIndirect; for(i=0; inCol; i++){ int n1 = sessionSerialLen(a1); - int n2 = sessionSerialLen(a2); - if( pIter->abPK[i] || a2[0]==0 ){ + int n2 = (a2>=a2Eof) ? 0 : sessionSerialLen(a2); + if( n2<=0 || pIter->abPK[i] || a2[0]==0 ){ if( !pIter->abPK[i] && a1[0] ) bData = 1; memcpy(pOut, a1, n1); pOut += n1; @@ -237637,8 +240902,8 @@ SQLITE_API int sqlite3rebaser_configure( sqlite3_rebaser *p, int nRebase, const void *pRebase ){ - sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ int rc; /* Return code */ + sqlite3_changeset_iter *pIter = 0; /* Iterator opened on pData/nData */ rc = sqlite3changeset_start(&pIter, nRebase, (void*)pRebase); if( rc==SQLITE_OK ){ rc = sessionChangesetToHash(pIter, &p->grp, 1); @@ -237719,6 +240984,328 @@ SQLITE_API int sqlite3session_config(int op, void *pArg){ return rc; } +/* +** Begin adding a change to a changegroup object. +*/ +SQLITE_API int sqlite3changegroup_change_begin( + sqlite3_changegroup *pGrp, + int eOp, + const char *zTab, + int bIndirect, + char **pzErr +){ + SessionTable *pTab = 0; + int rc = SQLITE_OK; + + if( pGrp->cd.pTab ){ + rc = SQLITE_MISUSE; + }else if( eOp!=SQLITE_INSERT && eOp!=SQLITE_UPDATE && eOp!=SQLITE_DELETE ){ + rc = SQLITE_ERROR; + }else{ + rc = sessionChangesetFindTable(pGrp, zTab, 0, &pTab); + } + if( rc==SQLITE_OK ){ + if( pTab==0 ){ + if( pzErr ){ + *pzErr = sqlite3_mprintf("no such table: %s", zTab); + } + rc = SQLITE_ERROR; + }else{ + int nReq = pTab->nCol * (eOp==SQLITE_UPDATE ? 2 : 1); + pGrp->cd.pTab = pTab; + pGrp->cd.eOp = eOp; + pGrp->cd.bIndirect = bIndirect; + + if( pGrp->cd.nBufAlloccd.aBuf, nReq * sizeof(SessionBuffer) + ); + if( aBuf==0 ){ + rc = SQLITE_NOMEM; + }else{ + memset(&aBuf[pGrp->cd.nBufAlloc], 0, + sizeof(SessionBuffer) * (nReq - pGrp->cd.nBufAlloc) + ); + pGrp->cd.aBuf = aBuf; + pGrp->cd.nBufAlloc = nReq; + } + } + +#ifdef SQLITE_DEBUG + { + /* Assert that all column values are currently undefined */ + int ii; + for(ii=0; iicd.nBufAlloc; ii++){ + assert( pGrp->cd.aBuf[ii].nBuf==0 ); + } + } +#endif + } + } + + return rc; +} + +/* +** This function does processing common to the _change_int64(), _change_text() +** and other similar APIs. +*/ +static int checkChangeParams( + sqlite3_changegroup *pGrp, + int bNew, + int iCol, + sqlite3_int64 nReq, + SessionBuffer **ppBuf +){ + int rc = SQLITE_OK; + if( pGrp->cd.pTab==0 ){ + rc = SQLITE_MISUSE; + }else if( iCol<0 || iCol>=pGrp->cd.pTab->nCol ){ + rc = SQLITE_RANGE; + }else if( + (bNew && pGrp->cd.eOp==SQLITE_DELETE) + || (!bNew && pGrp->cd.eOp==SQLITE_INSERT) + ){ + rc = SQLITE_ERROR; + }else{ + SessionBuffer *pBuf = &pGrp->cd.aBuf[iCol]; + if( pGrp->cd.eOp==SQLITE_UPDATE && bNew ){ + pBuf += pGrp->cd.pTab->nCol; + } + pBuf->nBuf = 0; + sessionBufferGrow(pBuf, nReq, &rc); + pBuf->nBuf = nReq; + *ppBuf = pBuf; + } + return rc; +} + +/* +** Configure the change currently under construction with an integer value. +*/ +SQLITE_API int sqlite3changegroup_change_int64( + sqlite3_changegroup *pGrp, + int bNew, + int iCol, + sqlite3_int64 iVal +){ + int rc = SQLITE_OK; + SessionBuffer *pBuf = 0; + + if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){ + return rc; + } + + pBuf->aBuf[0] = SQLITE_INTEGER; + sessionPutI64(&pBuf->aBuf[1], iVal); + return SQLITE_OK; +} + +/* +** Configure the change currently under construction with a null value. +*/ +SQLITE_API int sqlite3changegroup_change_null( + sqlite3_changegroup *pGrp, + int bNew, + int iCol +){ + int rc = SQLITE_OK; + SessionBuffer *pBuf = 0; + + if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 1, &pBuf)) ){ + return rc; + } + + pBuf->aBuf[0] = SQLITE_NULL; + return SQLITE_OK; +} + +/* +** Configure the change currently under construction with a real value. +*/ +SQLITE_API int sqlite3changegroup_change_double( + sqlite3_changegroup *pGrp, + int bNew, + int iCol, + double fVal +){ + int rc = SQLITE_OK; + SessionBuffer *pBuf = 0; + + if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, 9, &pBuf)) ){ + return rc; + } + + pBuf->aBuf[0] = SQLITE_FLOAT; + sessionPutDouble(&pBuf->aBuf[1], fVal); + return SQLITE_OK; +} + +/* +** Configure the change currently under construction with a text value. +*/ +SQLITE_API int sqlite3changegroup_change_text( + sqlite3_changegroup *pGrp, + int bNew, + int iCol, + const char *pVal, + int nVal +){ + int nText = nVal>=0 ? nVal : strlen(pVal); + sqlite3_int64 nByte = 1 + sessionVarintLen(nText) + nText; + int rc = SQLITE_OK; + SessionBuffer *pBuf = 0; + + if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){ + return rc; + } + + pBuf->aBuf[0] = SQLITE_TEXT; + pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nText)); + memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nText); + pBuf->nBuf += nText; + + return SQLITE_OK; +} + +/* +** Configure the change currently under construction with a blob value. +*/ +SQLITE_API int sqlite3changegroup_change_blob( + sqlite3_changegroup *pGrp, + int bNew, + int iCol, + const void *pVal, + int nVal +){ + sqlite3_int64 nByte = 1 + sessionVarintLen(nVal) + (i64)nVal; + int rc = SQLITE_OK; + SessionBuffer *pBuf = 0; + + if( SQLITE_OK!=(rc = checkChangeParams(pGrp, bNew, iCol, nByte, &pBuf)) ){ + return rc; + } + + pBuf->aBuf[0] = SQLITE_BLOB; + pBuf->nBuf = (1 + sessionVarintPut(&pBuf->aBuf[1], nVal)); + memcpy(&pBuf->aBuf[pBuf->nBuf], pVal, nVal); + pBuf->nBuf += nVal; + + return SQLITE_OK; +} + +/* +** Finish any change currently being constructed by the changegroup object. +*/ +SQLITE_API int sqlite3changegroup_change_finish( + sqlite3_changegroup *pGrp, + int bDiscard, + char **pzErr +){ + int rc = SQLITE_OK; + if( pGrp->cd.pTab ){ + SessionBuffer *aBuf = pGrp->cd.aBuf; + int ii; + + if( bDiscard==0 ){ + int nBuf = pGrp->cd.pTab->nCol; + u8 eUndef = SQLITE_NULL; + if( pGrp->cd.eOp==SQLITE_UPDATE ){ + for(ii=0; iicd.pTab->abPK[ii] ){ + if( aBuf[ii].nBuf<=1 ){ + *pzErr = sqlite3_mprintf( + "invalid change: %s value in PK of old.* record", + aBuf[ii].nBuf==1 ? "null" : "undefined" + ); + rc = SQLITE_ERROR; + break; + }else if( aBuf[ii + nBuf].nBuf>0 ){ + *pzErr = sqlite3_mprintf( + "invalid change: defined value in PK of new.* record" + ); + rc = SQLITE_ERROR; + break; + } + }else + if( pGrp->bPatch==0 && (aBuf[ii].nBuf>0)!=(aBuf[ii+nBuf].nBuf>0) ){ + *pzErr = sqlite3_mprintf( + "invalid change: column %d " + "- old.* value is %sdefined but new.* is %sdefined", + ii, aBuf[ii].nBuf ? "" : "un", aBuf[ii+nBuf].nBuf ? "" : "un" + ); + rc = SQLITE_ERROR; + break; + } + } + eUndef = 0x00; + if( pGrp->bPatch==0 ) nBuf = nBuf * 2; + }else{ + for(ii=0; iicd.pTab->abPK[ii]; + if( (pGrp->cd.eOp==SQLITE_INSERT || pGrp->bPatch==0 || isPK) + && aBuf[ii].nBuf==0 + ){ + *pzErr = sqlite3_mprintf( + "invalid change: column %d is undefined", ii + ); + rc = SQLITE_ERROR; + break; + } + if( aBuf[ii].nBuf==1 && isPK ){ + *pzErr = sqlite3_mprintf( + "invalid change: null value in PK" + ); + rc = SQLITE_ERROR; + break; + } + } + } + + pGrp->cd.record.nBuf = 0; + for(ii=0; iicd.aBuf[ii]; + if( pGrp->bPatch ){ + if( pGrp->cd.pTab->abPK[ii]==0 ){ + if( pGrp->cd.eOp==SQLITE_UPDATE ){ + p += pGrp->cd.pTab->nCol; + }else if( pGrp->cd.eOp==SQLITE_DELETE ){ + continue; + } + } + } + if( 0==sessionBufferGrow(&pGrp->cd.record, p->nBuf?p->nBuf:1, &rc) ){ + if( p->nBuf ){ + memcpy(&pGrp->cd.record.aBuf[pGrp->cd.record.nBuf],p->aBuf,p->nBuf); + pGrp->cd.record.nBuf += p->nBuf; + }else{ + pGrp->cd.record.aBuf[pGrp->cd.record.nBuf++] = eUndef; + } + } + } + if( rc==SQLITE_OK ){ + rc = sessionOneChangeToHash( + pGrp, pGrp->cd.pTab, + pGrp->cd.eOp, pGrp->cd.bIndirect, pGrp->cd.pTab->nCol, + pGrp->cd.record.aBuf, pGrp->cd.record.nBuf, 0 + ); + } + } + + /* Reset all aBuf[] entries to "undefined". */ + { + int nZero = pGrp->cd.pTab->nCol; + if( pGrp->cd.eOp==SQLITE_UPDATE ) nZero += nZero; + for(ii=0; iicd.aBuf[ii].nBuf = 0; + } + } + pGrp->cd.pTab = 0; + } + + return rc; +} + #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ @@ -239649,14 +243236,22 @@ typedef union { #define sqlite3Fts5ParserARG_PARAM ,pParse #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse; #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse; +#undef fts5YYREALLOC #define fts5YYREALLOC realloc +#undef fts5YYFREE #define fts5YYFREE free +#undef fts5YYDYNSTACK #define fts5YYDYNSTACK 0 +#undef fts5YYSIZELIMIT +#define sqlite3Fts5ParserCTX(P) 0 #define sqlite3Fts5ParserCTX_SDECL #define sqlite3Fts5ParserCTX_PDECL #define sqlite3Fts5ParserCTX_PARAM #define sqlite3Fts5ParserCTX_FETCH #define sqlite3Fts5ParserCTX_STORE +#undef fts5YYERRORSYMBOL +#undef fts5YYERRSYMDT +#undef fts5YYFALLBACK #define fts5YYNSTATE 35 #define fts5YYNRULE 28 #define fts5YYNRULE_WITH_ACTION 28 @@ -239981,15 +243576,24 @@ static int fts5yyGrowStack(fts5yyParser *p){ int newSize; int idx; fts5yyStackEntry *pNew; +#ifdef fts5YYSIZELIMIT + int nLimit = fts5YYSIZELIMIT(sqlite3Fts5ParserCTX(p)); +#endif newSize = oldSize*2 + 100; +#ifdef fts5YYSIZELIMIT + if( newSize>nLimit ){ + newSize = nLimit; + if( newSize<=oldSize ) return 1; + } +#endif idx = (int)(p->fts5yytos - p->fts5yystack); if( p->fts5yystack==p->fts5yystk0 ){ - pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0])); + pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p)); if( pNew==0 ) return 1; memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0])); + pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0]), sqlite3Fts5ParserCTX(p)); if( pNew==0 ) return 1; } p->fts5yystack = pNew; @@ -240169,7 +243773,9 @@ static void sqlite3Fts5ParserFinalize(void *p){ } #if fts5YYGROWABLESTACK - if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack); + if( pParser->fts5yystack!=pParser->fts5yystk0 ){ + fts5YYFREE(pParser->fts5yystack, sqlite3Fts5ParserCTX(pParser)); + } #endif } @@ -241422,7 +245028,7 @@ static void fts5SnippetFunction( int rc = SQLITE_OK; /* Return code */ int iCol; /* 1st argument to snippet() */ const char *zEllips; /* 4th argument to snippet() */ - int nToken; /* 5th argument to snippet() */ + i64 nToken; /* 5th argument to snippet() */ int nInst = 0; /* Number of instance matches this row */ int i; /* Used to iterate through instances */ int nPhrase; /* Number of phrases in query */ @@ -241447,11 +245053,11 @@ static void fts5SnippetFunction( ctx.zClose = fts5ValueToText(apVal[2]); ctx.iRangeEnd = -1; zEllips = fts5ValueToText(apVal[3]); - nToken = sqlite3_value_int(apVal[4]); + nToken = (int)(MIN( MAX(sqlite3_value_int64(apVal[4]), 0), 64)); iBestCol = (iCol>=0 ? iCol : 0); nPhrase = pApi->xPhraseCount(pFts); - aSeen = sqlite3_malloc(nPhrase); + aSeen = sqlite3_malloc64(nPhrase); if( aSeen==0 ){ rc = SQLITE_NOMEM; } @@ -242106,7 +245712,7 @@ static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){ if( nIn<0 ){ nIn = (int)strlen(pIn); } - zRet = (char*)sqlite3_malloc(nIn+1); + zRet = (char*)sqlite3_malloc64((i64)nIn+1); if( zRet ){ memcpy(zRet, pIn, nIn); zRet[nIn] = '\0'; @@ -242806,7 +246412,7 @@ static int sqlite3Fts5ConfigParse( sqlite3_int64 nByte; int bUnindexed = 0; /* True if there are one or more UNINDEXED */ - *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); + *ppOut = pRet = (Fts5Config*)sqlite3_malloc64(sizeof(Fts5Config)); if( pRet==0 ) return SQLITE_NOMEM; memset(pRet, 0, sizeof(Fts5Config)); pRet->pGlobal = pGlobal; @@ -243354,8 +246960,6 @@ static void sqlite3Fts5ConfigErrmsg(Fts5Config *pConfig, const char *zFmt, ...){ va_end(ap); } - - /* ** 2014 May 31 ** @@ -243672,7 +247276,7 @@ static int sqlite3Fts5ExprNew( assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 ); if( sParse.rc==SQLITE_OK ){ - *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr)); + *ppNew = pNew = sqlite3_malloc64(sizeof(Fts5Expr)); if( pNew==0 ){ sParse.rc = SQLITE_NOMEM; sqlite3Fts5ParseNodeFree(sParse.pExpr); @@ -243824,7 +247428,7 @@ static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){ p2->pRoot = 0; if( sParse.rc==SQLITE_OK ){ - Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc( + Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc64( p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*) ); if( ap==0 ){ @@ -244165,7 +247769,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){ i64 iPos = a[i].reader.iPos; Fts5PoslistWriter *pWriter = &a[i].writer; if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){ - sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos); + sqlite3Fts5PoslistSafeAppend(a[i].pOut, &pWriter->iPrev, iPos); } } @@ -245116,10 +248720,10 @@ static int fts5ParseTokenize( memset(pSyn, 0, (size_t)nByte); pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); pSyn->nFullTerm = pSyn->nQueryTerm = nToken; + memcpy(pSyn->pTerm, pToken, nToken); if( pCtx->pConfig->bTokendata ){ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); } - memcpy(pSyn->pTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; } @@ -246736,7 +250340,7 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte int rc = SQLITE_OK; Fts5Hash *pNew; - *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash)); + *ppNew = pNew = (Fts5Hash*)sqlite3_malloc64(sizeof(Fts5Hash)); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -248119,6 +251723,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); if( pRet ){ pRet->nn = nByte; + pRet->szLeaf = 0; aOut = pRet->p = (u8*)pRet + szData; }else{ rc = SQLITE_NOMEM; @@ -248131,10 +251736,8 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ sqlite3_free(pRet); pRet = 0; }else{ - /* TODO1: Fix this */ pRet->p[nByte] = 0x00; pRet->p[nByte+1] = 0x00; - pRet->szLeaf = fts5GetU16(&pRet->p[2]); } } p->rc = rc; @@ -248155,10 +251758,18 @@ static void fts5DataRelease(Fts5Data *pData){ sqlite3_free(pData); } +/* +** Read a leaf-page record. This is similar to fts5DataRead(), except that +** it fills in the Fts5Data.szLeaf value before returning. +*/ static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){ Fts5Data *pRet = fts5DataRead(p, iRowid); if( pRet ){ - if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){ + assert( pRet->szLeaf==0 ); + if( pRet->nn>=4 ){ + pRet->szLeaf = fts5GetU16(&pRet->p[2]); + } + if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){ FTS5_CORRUPT_ROWID(p, iRowid); fts5DataRelease(pRet); pRet = 0; @@ -248402,7 +252013,7 @@ static int fts5StructureDecode( i += fts5GetVarint32(&pData[i], nTotal); if( nTotalnMerge ) rc = FTS5_CORRUPT; pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, - nTotal * sizeof(Fts5StructureSegment) + (i64)nTotal * sizeof(Fts5StructureSegment) ); nSegment -= nTotal; } @@ -249329,7 +252940,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ /* If necessary, grow the pIter->aRowidOffset[] array. */ if( iRowidOffset>=pIter->nRowidOffset ){ - int nNew = pIter->nRowidOffset + 8; + i64 nNew = pIter->nRowidOffset + 8; int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int)); if( aNew==0 ){ p->rc = SQLITE_NOMEM; @@ -249358,7 +252969,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){ Fts5Data *pNew; pIter->iLeafPgno--; - pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID( + pNew = fts5LeafRead(p, FTS5_SEGMENT_ROWID( pIter->pSeg->iSegid, pIter->iLeafPgno )); if( pNew ){ @@ -249812,6 +253423,10 @@ static void fts5LeafSeek( if( nKeepn ){ + FTS5_CORRUPT_ITER(p, pIter); + return; + } assert( nKeep>=nMatch ); if( nKeep==nMatch ){ @@ -250063,6 +253678,10 @@ static void fts5SegIterNextInit( pIter->iPgidxOff = pIter->pLeaf->szLeaf; pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); + if( iTermOff > pIter->pLeaf->szLeaf ){ + p->rc = FTS5_CORRUPT; + return; + } pIter->iLeafOffset = iTermOff; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); @@ -250788,8 +254407,7 @@ static void fts5PoslistFilterCallback( do { while( ieState ){ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart); @@ -250938,7 +254556,7 @@ static void fts5IndexExtractColset( /* Advance pointer p until it points to pEnd or an 0x01 byte that is ** not part of a varint */ while( paiCol[i]==iCurrent ){ @@ -251035,8 +254653,11 @@ static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS ); assert( pIter->pColset ); + assert( pIter->poslist.nSpace>=pIter->pIndex->pConfig->nCol ); - if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){ + if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf + || pSeg->nPos>pIter->pIndex->pConfig->nCol + ){ fts5IterSetOutputs_Col(pIter, pSeg); }else{ u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset]; @@ -252387,7 +256008,7 @@ static void fts5SecureDeleteOverflow( int iNext = 0; u8 *aPg = 0; - pLeaf = fts5DataRead(p, iRowid); + pLeaf = fts5LeafRead(p, iRowid); if( pLeaf==0 ) break; aPg = pLeaf->p; @@ -252395,7 +256016,7 @@ static void fts5SecureDeleteOverflow( if( iNext!=0 ){ *pbLastInDoclist = 0; } - if( iNext==0 && pLeaf->szLeaf!=pLeaf->nn ){ + if( iNext==0 && pLeaf->szLeafnn ){ fts5GetVarint32(&aPg[pLeaf->szLeaf], iNext); } @@ -252530,6 +256151,11 @@ static void fts5DoSecureDelete( }else{ iStart = fts5GetU16(&aPg[0]); } + if( iStart>nPg ){ + FTS5_CORRUPT_IDX(p); + sqlite3_free(aIdx); + return; + } iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta); assert_nc( iSOP<=pSeg->iLeafOffset ); @@ -252635,31 +256261,31 @@ static void fts5DoSecureDelete( ** is another term following it on this page. So the subsequent term ** needs to be moved to replace the term associated with the entry ** being removed. */ - int nPrefix = 0; - int nSuffix = 0; - int nPrefix2 = 0; - int nSuffix2 = 0; + u64 nPrefix = 0; + u64 nSuffix = 0; + u64 nPrefix2 = 0; + u64 nSuffix2 = 0; iDelKeyOff = iNextOff; - iNextOff += fts5GetVarint32(&aPg[iNextOff], nPrefix2); - iNextOff += fts5GetVarint32(&aPg[iNextOff], nSuffix2); + iNextOff += fts5GetVarint(&aPg[iNextOff], &nPrefix2); + iNextOff += fts5GetVarint(&aPg[iNextOff], &nSuffix2); if( iKey!=1 ){ - iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nPrefix); + iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nPrefix); } - iKeyOff += fts5GetVarint32(&aPg[iKeyOff], nSuffix); + iKeyOff += fts5GetVarint(&aPg[iKeyOff], &nSuffix); nPrefix = MIN(nPrefix, nPrefix2); nSuffix = (nPrefix2 + nSuffix2) - nPrefix; - if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){ + if( (iKeyOff+nSuffix)>(u64)iPgIdx || (iNextOff+nSuffix2)>(u64)iPgIdx ){ FTS5_CORRUPT_IDX(p); }else{ if( iKey!=1 ){ iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix); } iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix); - if( nPrefix2>pSeg->term.n ){ + if( nPrefix2>(u64)pSeg->term.n ){ FTS5_CORRUPT_IDX(p); }else if( nPrefix2>nPrefix ){ memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix); @@ -252677,7 +256303,7 @@ static void fts5DoSecureDelete( /* The entry being removed may be the only position list in ** its doclist. */ for(iPgno=pSeg->iLeafPgno-1; iPgno>pSeg->iTermLeafPgno; iPgno-- ){ - Fts5Data *pPg = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); + Fts5Data *pPg = fts5LeafRead(p, FTS5_SEGMENT_ROWID(iSegid, iPgno)); int bEmpty = (pPg && pPg->nn==4); fts5DataRelease(pPg); if( bEmpty==0 ) break; @@ -252685,12 +256311,12 @@ static void fts5DoSecureDelete( if( iPgno==pSeg->iTermLeafPgno ){ i64 iId = FTS5_SEGMENT_ROWID(iSegid, pSeg->iTermLeafPgno); - Fts5Data *pTerm = fts5DataRead(p, iId); + Fts5Data *pTerm = fts5LeafRead(p, iId); if( pTerm && pTerm->szLeaf==pSeg->iTermLeafOffset ){ u8 *aTermIdx = &pTerm->p[pTerm->szLeaf]; int nTermIdx = pTerm->nn - pTerm->szLeaf; int iTermIdx = 0; - int iTermOff = 0; + i64 iTermOff = 0; while( 1 ){ u32 iVal = 0; @@ -252701,12 +256327,15 @@ static void fts5DoSecureDelete( } nTermIdx = iTermIdx; - memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); - fts5PutU16(&pTerm->p[2], iTermOff); - - fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); - if( nTermIdx==0 ){ - fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); + if( iTermOff>pTerm->szLeaf ){ + FTS5_CORRUPT_IDX(p); + }else{ + memmove(&pTerm->p[iTermOff], &pTerm->p[pTerm->szLeaf], nTermIdx); + fts5PutU16(&pTerm->p[2], iTermOff); + fts5DataWrite(p, iId, pTerm->p, iTermOff+nTermIdx); + if( nTermIdx==0 ){ + fts5SecureDeleteIdxEntry(p, iSegid, pSeg->iTermLeafPgno); + } } } fts5DataRelease(pTerm); @@ -252729,7 +256358,9 @@ static void fts5DoSecureDelete( int iPrevKeyOut = 0; int iKeyIn = 0; - memmove(&aPg[iOff], &aPg[iNextOff], nMove); + if( nMove>0 ){ + memmove(&aPg[iOff], &aPg[iNextOff], nMove); + } iPgIdx -= nShift; nPg = iPgIdx; fts5PutU16(&aPg[2], iPgIdx); @@ -253649,16 +257280,16 @@ struct Fts5TokenDataMap { ** aMap[] variables. */ struct Fts5TokenDataIter { - int nMapAlloc; /* Allocated size of aMap[] in entries */ - int nMap; /* Number of valid entries in aMap[] */ + i64 nMapAlloc; /* Allocated size of aMap[] in entries */ + i64 nMap; /* Number of valid entries in aMap[] */ Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */ /* The following are used for prefix-queries only. */ Fts5Buffer terms; /* The following are used for other full-token tokendata queries only. */ - int nIter; - int nIterAlloc; + i64 nIter; + i64 nIterAlloc; Fts5PoslistReader *aPoslistReader; int *aPoslistToIter; Fts5Iter *apIter[FLEXARRAY]; @@ -253714,11 +257345,11 @@ static void fts5TokendataIterAppendMap( ){ if( p->rc==SQLITE_OK ){ if( pT->nMap==pT->nMapAlloc ){ - int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; - int nAlloc = nNew * sizeof(Fts5TokenDataMap); + i64 nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; + i64 nAlloc = nNew * sizeof(Fts5TokenDataMap); Fts5TokenDataMap *aNew; - aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc); + aNew = (Fts5TokenDataMap*)sqlite3_realloc64(pT->aMap, nAlloc); if( aNew==0 ){ p->rc = SQLITE_NOMEM; return; @@ -253744,7 +257375,7 @@ static void fts5TokendataIterAppendMap( */ static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ Fts5TokenDataMap *aTmp = 0; - int nByte = pT->nMap * sizeof(Fts5TokenDataMap); + i64 nByte = pT->nMap * sizeof(Fts5TokenDataMap); aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte); if( aTmp ){ @@ -254278,9 +257909,10 @@ static Fts5TokenDataIter *fts5AppendTokendataIter( if( p->rc==SQLITE_OK ){ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ - int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; - int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); - Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); + i64 nAlloc = pIn ? pIn->nIterAlloc*2 : 16; + i64 nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); + Fts5TokenDataIter *pNew; + pNew = (Fts5TokenDataIter*)sqlite3_realloc64(pIn, nByte); if( pNew==0 ){ p->rc = SQLITE_NOMEM; @@ -254377,8 +258009,8 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ /* Ensure the token-mapping is large enough */ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ - int nNew = (pT->nMapAlloc + nByte) * 2; - Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( + i64 nNew = (pT->nMapAlloc + nByte) * 2; + Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc64( pT->aMap, nNew*sizeof(Fts5TokenDataMap) ); if( aNew==0 ){ @@ -255209,8 +258841,8 @@ static void fts5IndexTombstoneRebuild( ){ const int MINSLOT = 32; int nSlotPerPage = MAX(MINSLOT, (p->pConfig->pgsz - 8) / szKey); - int nSlot = 0; /* Number of slots in each output page */ - int nOut = 0; + i64 nSlot = 0; /* Number of slots in each output page */ + i64 nOut = 0; /* Figure out how many output pages (nOut) and how many slots per ** page (nSlot). There are three possibilities: @@ -255235,23 +258867,26 @@ static void fts5IndexTombstoneRebuild( nSlot = MINSLOT; }else if( pSeg->nPgTombstone==1 ){ /* Case 2. */ - int nElem = (int)fts5GetU32(&pData1->p[4]); + u32 nElem = fts5GetU32(&pData1->p[4]); assert( pData1 && iPg1==0 ); - nOut = 1; - nSlot = MAX(nElem*4, MINSLOT); - if( nSlot>nSlotPerPage ) nOut = 0; + if( nElem>((u32)nSlotPerPage/4) ){ + nOut = 0; + }else{ + nOut = 1; + nSlot = MAX((i64)nElem*4, MINSLOT); + } } if( nOut==0 ){ /* Case 3. */ - nOut = (pSeg->nPgTombstone * 2 + 1); + nOut = ((i64)pSeg->nPgTombstone * 2 + 1); nSlot = nSlotPerPage; } /* Allocate the required array and output pages */ while( 1 ){ int res = 0; - int ii = 0; - int szPage = 0; + i64 ii = 0; + i64 szPage = 0; Fts5Data **apOut = 0; /* Allocate space for the new hash table */ @@ -255628,7 +259263,7 @@ static void fts5IndexIntegrityCheckEmpty( /* Now check that the iter.nEmpty leaves following the current leaf ** (a) exist and (b) contain no terms. */ for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){ - Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i)); + Fts5Data *pLeaf = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i)); if( pLeaf ){ if( !fts5LeafIsTermless(pLeaf) || (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf)) @@ -255756,9 +259391,13 @@ static void fts5IndexIntegrityCheckSegment( FTS5_CORRUPT_ROWID(p, iRow); }else{ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm); - res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); - if( res==0 ) res = nTerm - nIdxTerm; - if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow); + if( (i64)iOff+(i64)nTerm>(i64)pLeaf->szLeaf ){ + FTS5_CORRUPT_ROWID(p, iRow); + }else{ + res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); + if( res==0 ) res = nTerm - nIdxTerm; + if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow); + } } fts5IntegrityCheckPgidx(p, iRow, pLeaf); @@ -255789,7 +259428,7 @@ static void fts5IndexIntegrityCheckSegment( /* Check any rowid-less pages that occur before the current leaf. */ for(iPg=iPrevLeaf+1; iPg=3008002 ) #endif { - pIdxInfo->estimatedRows = nRow; + pIdxInfo->estimatedRows = MAX(1, nRow); } #endif } @@ -257362,19 +261001,30 @@ static int fts5UsePatternMatch( ** a) If a MATCH operator is present, the cost depends on the other ** constraints also present. As follows: ** -** * No other constraints: cost=1000.0 -** * One rowid range constraint: cost=750.0 -** * Both rowid range constraints: cost=500.0 -** * An == rowid constraint: cost=100.0 +** * No other constraints: cost=50000.0 +** * One rowid range constraint: cost=37500.0 +** * Both rowid range constraints: cost=30000.0 +** * An == rowid constraint: cost=25000.0 ** ** b) Otherwise, if there is no MATCH: ** -** * No other constraints: cost=1000000.0 -** * One rowid range constraint: cost=750000.0 -** * Both rowid range constraints: cost=250000.0 -** * An == rowid constraint: cost=10.0 +** * No other constraints: cost=3000000.0 +** * One rowid range constraints: cost=2250000.0 +** * Both rowid range constraint: cost=750000.0 +** * An == rowid constraint: cost=25.0 ** ** Costs are not modified by the ORDER BY clause. +** +** The ratios used in case (a) are based on informal results obtained from +** the tool/fts5cost.tcl script. The "MATCH and ==" combination has the +** cost set quite high because the query may be a prefix query. Unless +** there is a prefix index, prefix queries with rowid constraints are much +** more expensive than non-prefix queries with rowid constraints. +** +** The estimated rows returned is set to the cost/40. For simple queries, +** experimental results show that cost/4 might be about right. But for +** more complex queries that use multiple terms the number of rows might +** be far fewer than this. So we compromise and use cost/40. */ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ Fts5Table *pTab = (Fts5Table*)pVTab; @@ -257407,7 +261057,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ return SQLITE_ERROR; } - idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1); + idxStr = (char*)sqlite3_malloc64((i64)pInfo->nConstraint * 8 + 1); if( idxStr==0 ) return SQLITE_NOMEM; pInfo->idxStr = idxStr; pInfo->needToFreeIdxStr = 1; @@ -257500,21 +261150,35 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ /* Calculate the estimated cost based on the flags set in idxFlags. */ if( bSeenEq ){ - pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0; - fts5SetUniqueFlag(pInfo); + pInfo->estimatedCost = nSeenMatch ? 25000.0 : 25.0; fts5SetEstimatedRows(pInfo, 1); + fts5SetUniqueFlag(pInfo); }else{ - if( bSeenLt && bSeenGt ){ - pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0; - }else if( bSeenLt || bSeenGt ){ - pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0; + i64 nEstRows; + if( nSeenMatch ){ + if( bSeenLt && bSeenGt ){ + pInfo->estimatedCost = 50000.0; + }else if( bSeenLt || bSeenGt ){ + pInfo->estimatedCost = 37500.0; + }else{ + pInfo->estimatedCost = 50000.0; + } + nEstRows = (i64)(pInfo->estimatedCost / 40.0); + for(i=1; iestimatedCost *= 2.5; + nEstRows = nEstRows / 2; + } }else{ - pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0; - } - for(i=1; iestimatedCost *= 0.4; + if( bSeenLt && bSeenGt ){ + pInfo->estimatedCost = 750000.0; + }else if( bSeenLt || bSeenGt ){ + pInfo->estimatedCost = 2250000.0; + }else{ + pInfo->estimatedCost = 3000000.0; + } + nEstRows = (i64)(pInfo->estimatedCost / 4.0); } - fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0)); + fts5SetEstimatedRows(pInfo, nEstRows); } pInfo->idxNum = idxFlags; @@ -258857,6 +262521,7 @@ static int fts5UpdateMethod( } update_out: + sqlite3Fts5IndexCloseReader(pTab->p.pIndex); pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -259454,19 +263119,23 @@ static int fts5ApiPhraseFirstColumn( if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ Fts5Sorter *pSorter = pCsr->pSorter; - int n; - if( pSorter ){ - int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); - n = pSorter->aIdx[iPhrase] - i1; - pIter->a = &pSorter->aPoslist[i1]; + if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ + rc = SQLITE_RANGE; }else{ - rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); - } - if( rc==SQLITE_OK ){ - assert( pIter->a || n==0 ); - pIter->b = (pIter->a ? &pIter->a[n] : 0); - *piCol = 0; - fts5ApiPhraseNextColumn(pCtx, pIter, piCol); + int n; + if( pSorter ){ + int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); + n = pSorter->aIdx[iPhrase] - i1; + pIter->a = &pSorter->aPoslist[i1]; + }else{ + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); + } + if( rc==SQLITE_OK ){ + assert( pIter->a || n==0 ); + pIter->b = (pIter->a ? &pIter->a[n] : 0); + *piCol = 0; + fts5ApiPhraseNextColumn(pCtx, pIter, piCol); + } } }else{ int n; @@ -260374,7 +264043,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2026-03-13 10:38:09 737ae4a34738ffa0c3ff7f9bb18df914dd1cad163f28fd6b6e114a344fe6d618", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc", -1, SQLITE_TRANSIENT); } /* @@ -260538,7 +264207,7 @@ static int fts5Init(sqlite3 *db){ int rc; Fts5Global *pGlobal = 0; - pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); + pGlobal = (Fts5Global*)sqlite3_malloc64(sizeof(Fts5Global)); if( pGlobal==0 ){ rc = SQLITE_NOMEM; }else{ @@ -261016,34 +264685,31 @@ static int sqlite3Fts5StorageOpen( if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){ - int nDefn = 32 + pConfig->nCol*10; - char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 20); - if( zDefn==0 ){ - rc = SQLITE_NOMEM; - }else{ - int i; - int iOff; - sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY"); - iOff = (int)strlen(zDefn); - for(i=0; inCol; i++){ - if( pConfig->eContent==FTS5_CONTENT_NORMAL - || pConfig->abUnindexed[i] - ){ - sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i); - iOff += (int)strlen(&zDefn[iOff]); - } + int i = 0; + char *zDefn = 0; + sqlite3_str *pDefn = sqlite3_str_new(pConfig->db); + + sqlite3_str_appendf(pDefn, "id INTEGER PRIMARY KEY"); + for(i=0; inCol; i++){ + if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->abUnindexed[i] ){ + sqlite3_str_appendf(pDefn, ", c%d", i); } - if( pConfig->bLocale ){ - for(i=0; inCol; i++){ - if( pConfig->abUnindexed[i]==0 ){ - sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", l%d", i); - iOff += (int)strlen(&zDefn[iOff]); - } + } + if( pConfig->bLocale ){ + for(i=0; inCol; i++){ + if( pConfig->abUnindexed[i]==0 ){ + sqlite3_str_appendf(pDefn, ", l%d", i); } } + } + zDefn = sqlite3_str_finish(pDefn); + + if( zDefn ){ rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); + sqlite3_free(zDefn); + }else{ + rc = SQLITE_NOMEM; } - sqlite3_free(zDefn); } if( rc==SQLITE_OK && pConfig->bColumnsize ){ @@ -262254,7 +265920,7 @@ static int fts5AsciiCreate( if( nArg%2 ){ rc = SQLITE_ERROR; }else{ - p = sqlite3_malloc(sizeof(AsciiTokenizer)); + p = sqlite3_malloc64(sizeof(AsciiTokenizer)); if( p==0 ){ rc = SQLITE_NOMEM; }else{ @@ -262549,7 +266215,7 @@ static int fts5UnicodeCreate( if( nArg%2 ){ rc = SQLITE_ERROR; }else{ - p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer)); + p = (Unicode61Tokenizer*)sqlite3_malloc64(sizeof(Unicode61Tokenizer)); if( p ){ const char *zCat = "L* N* Co"; int i; @@ -262768,11 +266434,17 @@ static int fts5PorterCreate( const char *zBase = "unicode61"; fts5_tokenizer_v2 *pV2 = 0; - if( nArg>0 ){ - zBase = azArg[0]; + while( nArg>0 ){ + if( sqlite3_stricmp(azArg[0],"porter")==0 ){ + nArg--; + azArg++; + }else{ + zBase = azArg[0]; + break; + } } - pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); + pRet = (PorterTokenizer*)sqlite3_malloc64(sizeof(PorterTokenizer)); if( pRet ){ memset(pRet, 0, sizeof(PorterTokenizer)); rc = pApi->xFindTokenizer_v2(pApi, zBase, &pUserdata, &pV2); @@ -263479,7 +267151,7 @@ static int fts5TriCreate( rc = SQLITE_ERROR; }else{ int i; - pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew)); + pNew = (TrigramTokenizer*)sqlite3_malloc64(sizeof(*pNew)); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -265465,7 +269137,7 @@ static int fts5VocabFilterMethod( const char *zCopy = (const char *)sqlite3_value_text(pLe); if( zCopy==0 ) zCopy = ""; pCsr->nLeTerm = sqlite3_value_bytes(pLe); - pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1); + pCsr->zLeTerm = sqlite3_malloc64((i64)pCsr->nLeTerm+1); if( pCsr->zLeTerm==0 ){ rc = SQLITE_NOMEM; }else{ diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h b/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h index 6871f8d6d8..aecd0f8385 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h @@ -147,12 +147,12 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.51.3" -#define SQLITE_VERSION_NUMBER 3051003 -#define SQLITE_SOURCE_ID "2026-03-13 10:38:09 737ae4a34738ffa0c3ff7f9bb18df914dd1cad163f28fd6b6e114a344fe6d618" -#define SQLITE_SCM_BRANCH "branch-3.51" -#define SQLITE_SCM_TAGS "release version-3.51.3" -#define SQLITE_SCM_DATETIME "2026-03-13T10:38:09.694Z" +#define SQLITE_VERSION "3.53.4" +#define SQLITE_VERSION_NUMBER 3053004 +#define SQLITE_SOURCE_ID "2026-07-24 19:02:57 bf7c7f30031888f4e796e429ab3978879485813aaca6f641c7b33e4e09459bcc" +#define SQLITE_SCM_BRANCH "branch-3.53" +#define SQLITE_SCM_TAGS "release version-3.53.4" +#define SQLITE_SCM_DATETIME "2026-07-24T19:02:57.525Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -579,7 +579,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) -#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal only */ /* ** CAPI3REF: Flags For File Open Operations @@ -1291,6 +1291,12 @@ struct sqlite3_io_methods { #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO +/* reserved file-control numbers: +** 101 +** 102 +** 103 +*/ + /* ** CAPI3REF: Mutex Handle @@ -1491,7 +1497,7 @@ typedef const char *sqlite3_filename; ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. ** -** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** ^The xSetSystemCall(), xGetSystemCall(), and xNextSystemCall() interfaces ** are not used by the SQLite core. These optional interfaces are provided ** by some VFSes to facilitate testing of the VFS code. By overriding ** system calls with functions under its control, a test program can @@ -1712,7 +1718,8 @@ SQLITE_API int sqlite3_os_end(void); ** are called "anytime configuration options". ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ** [sqlite3_shutdown()] with a first argument that is not an anytime -** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. +** configuration option, then the sqlite3_config() call will +** return SQLITE_MISUSE. ** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** @@ -2278,9 +2285,10 @@ struct sqlite3_mem_methods { ** is less than 8. The "sz" argument should be a multiple of 8 less than ** 65536. If "sz" does not meet this constraint, it is reduced in size until ** it does. -**
  • The third argument ("cnt") is the number of slots. Lookaside is disabled -** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so -** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" +**

  • The third argument ("cnt") is the number of slots. +** Lookaside is disabled if "cnt"is less than 1. +* The "cnt" value will be reduced, if necessary, so +** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt" ** parameter is usually chosen so that the product of "sz" and "cnt" is less ** than 1,000,000. ** @@ -2568,12 +2576,15 @@ struct sqlite3_mem_methods { ** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] **

    SQLITE_DBCONFIG_STMT_SCANSTATUS
    **
    The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in -** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears -** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() -** statistics. For statistics to be collected, the flag must be set on -** the database handle both when the SQL statement is prepared and when it -** is stepped. The flag is set (collection of statistics is enabled) -** by default.

    This option takes two arguments: an integer and a pointer to +** [SQLITE_ENABLE_STMT_SCANSTATUS] builds. In this case, it sets or clears +** a flag that enables collection of run-time performance statistics +** used by [sqlite3_stmt_scanstatus_v2()] and the [nexec and ncycle] +** columns of the [bytecode virtual table]. +** For statistics to be collected, the flag must be set on +** the database handle both when the SQL statement is +** [sqlite3_prepare|prepared] and when it is [sqlite3_step|stepped]. +** The flag is set (collection of statistics is enabled) by default. +**

    This option takes two arguments: an integer and a pointer to ** an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after @@ -2646,16 +2657,34 @@ struct sqlite3_mem_methods { ** comments are allowed in SQL text after processing the first argument. **

    ** +** [[SQLITE_DBCONFIG_FP_DIGITS]] +**
    SQLITE_DBCONFIG_FP_DIGITS
    +**
    The SQLITE_DBCONFIG_FP_DIGITS setting is a small integer that determines +** the number of significant digits that SQLite will attempt to preserve when +** converting floating point numbers (IEEE 754 "doubles") into text. The +** default value 17, as of SQLite version 3.52.0. The value was 15 in all +** prior versions.

    +** This option takes two arguments which are an integer and a pointer +** to an integer. The first argument is a small integer, between 3 and 23, or +** zero. The FP_DIGITS setting is changed to that small integer, or left +** unaltered if the first argument is zero or out of range. The second argument +** is a pointer to an integer. If the pointer is not NULL, then the value of +** the FP_DIGITS setting, after possibly being modified by the first +** arguments, is written into the integer to which the second argument points. +**

    +** ** ** ** [[DBCONFIG arguments]]

    Arguments To SQLITE_DBCONFIG Options

    ** **

    Most of the SQLITE_DBCONFIG options take two arguments, so that the ** overall call to [sqlite3_db_config()] has a total of four parameters. -** The first argument (the third parameter to sqlite3_db_config()) is an integer. -** The second argument is a pointer to an integer. If the first argument is 1, -** then the option becomes enabled. If the first integer argument is 0, then the -** option is disabled. If the first argument is -1, then the option setting +** The first argument (the third parameter to sqlite3_db_config()) is +** an integer. +** The second argument is a pointer to an integer. If the first argument is 1, +** then the option becomes enabled. If the first integer argument is 0, +** then the option is disabled. +** If the first argument is -1, then the option setting ** is unchanged. The second argument, the pointer to an integer, may be NULL. ** If the second argument is not NULL, then a value of 0 or 1 is written into ** the integer to which the second argument points, depending on whether the @@ -2663,9 +2692,10 @@ struct sqlite3_mem_methods { ** the first argument. ** **

    While most SQLITE_DBCONFIG options use the argument format -** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] -** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the -** documentation of those exceptional options for details. +** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME], +** [SQLITE_DBCONFIG_LOOKASIDE], and [SQLITE_DBCONFIG_FP_DIGITS] options +** are different. See the documentation of those exceptional options for +** details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ @@ -2690,7 +2720,8 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_FP_DIGITS 1023 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1023 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -4172,6 +4203,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename); **

  • sqlite3_errmsg() **
  • sqlite3_errmsg16() **
  • sqlite3_error_offset() +**
  • sqlite3_db_handle() ** ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language @@ -4218,7 +4250,7 @@ SQLITE_API const char *sqlite3_errstr(int); SQLITE_API int sqlite3_error_offset(sqlite3 *db); /* -** CAPI3REF: Set Error Codes And Message +** CAPI3REF: Set Error Code And Message ** METHOD: sqlite3 ** ** Set the error code of the database handle passed as the first argument @@ -4335,7 +4367,12 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** or in an ORDER BY or GROUP BY clause.
  • )^ ** ** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
    SQLITE_LIMIT_EXPR_DEPTH
    -**
    The maximum depth of the parse tree on any expression.
    )^ +**
    The maximum depth of the parse tree on any expression and +** the maximum nesting depth for subqueries and VIEWs
    )^ +** +** [[SQLITE_LIMIT_PARSER_DEPTH]] ^(
    SQLITE_LIMIT_PARSER_DEPTH
    +**
    The maximum depth of the LALR(1) parser stack used to analyze +** input SQL statements.
    )^ ** ** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    **
    The maximum number of terms in a compound SELECT statement.
    )^ @@ -4362,7 +4399,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); **
    The maximum index number of any [parameter] in an SQL statement.)^ ** ** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    -**
    The maximum depth of recursion for triggers.
    )^ +**
    The maximum depth of recursion for triggers, and the maximum +** nesting depth for separate triggers.
    )^ ** ** [[SQLITE_LIMIT_WORKER_THREADS]] ^(
    SQLITE_LIMIT_WORKER_THREADS
    **
    The maximum number of auxiliary worker threads that a single @@ -4381,6 +4419,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 +#define SQLITE_LIMIT_PARSER_DEPTH 12 /* ** CAPI3REF: Prepare Flags @@ -4425,12 +4464,29 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** fails, the sqlite3_prepare_v3() call returns the same error indications ** with or without this flag; it just omits the call to [sqlite3_log()] that ** logs the error. +** +** [[SQLITE_PREPARE_FROM_DDL]]
    SQLITE_PREPARE_FROM_DDL
    +**
    The SQLITE_PREPARE_FROM_DDL flag causes the SQL compiler to enforce +** security constraints that would otherwise only be enforced when parsing +** the database schema. In other words, the SQLITE_PREPARE_FROM_DDL flag +** causes the SQL compiler to treat the SQL statement being prepared as if +** it had come from an attacker. When SQLITE_PREPARE_FROM_DDL is used and +** [SQLITE_DBCONFIG_TRUSTED_SCHEMA] is off, SQL functions may only be called +** if they are tagged with [SQLITE_INNOCUOUS] and virtual tables may only +** be used if they are tagged with [SQLITE_VTAB_INNOCUOUS]. Best practice +** is to use the SQLITE_PREPARE_FROM_DDL option when preparing any SQL that +** is derived from parts of the database schema. In particular, virtual +** table implementations that run SQL statements that are derived from +** arguments to their CREATE VIRTUAL TABLE statement should always use +** [sqlite3_prepare_v3()] and set the SQLITE_PREPARE_FROM_DDL flag to +** prevent bypass of the [SQLITE_DBCONFIG_TRUSTED_SCHEMA] security checks. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 #define SQLITE_PREPARE_NO_VTAB 0x04 #define SQLITE_PREPARE_DONT_LOG 0x10 +#define SQLITE_PREPARE_FROM_DDL 0x20 /* ** CAPI3REF: Compiling An SQL Statement @@ -4444,8 +4500,9 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** ** The preferred routine to use is [sqlite3_prepare_v2()]. The ** [sqlite3_prepare()] interface is legacy and should be avoided. -** [sqlite3_prepare_v3()] has an extra "prepFlags" option that is used -** for special purposes. +** [sqlite3_prepare_v3()] has an extra +** [SQLITE_PREPARE_FROM_DDL|"prepFlags" option] that is sometimes +** needed for special purpose or to pass along security restrictions. ** ** The use of the UTF-8 interfaces is preferred, as SQLite currently ** does all parsing using UTF-8. The UTF-16 interfaces are provided @@ -4850,8 +4907,8 @@ typedef struct sqlite3_context sqlite3_context; ** it should be a pointer to well-formed UTF16 text. ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then ** it should be a pointer to a well-formed unicode string that is -** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 -** otherwise. +** either UTF8 if the sixth parameter is SQLITE_UTF8 or SQLITE_UTF8_ZT, +** or UTF16 otherwise. ** ** [[byte-order determination rules]] ^The byte-order of ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) @@ -4897,10 +4954,15 @@ typedef struct sqlite3_context sqlite3_context; ** object and pointer to it must remain valid until then. ^SQLite will then ** manage the lifetime of its private copy. ** -** ^The sixth argument to sqlite3_bind_text64() must be one of -** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] -** to specify the encoding of the text in the third parameter. If -** the sixth argument to sqlite3_bind_text64() is not one of the +** ^The sixth argument (the E argument) +** to sqlite3_bind_text64(S,K,Z,N,D,E) must be one of +** [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE] to specify the encoding of the text in the +** third parameter, Z. The special value [SQLITE_UTF8_ZT] means that the +** string argument is both UTF-8 encoded and is zero-terminated. In other +** words, SQLITE_UTF8_ZT means that the Z array is allocated to hold at +** least N+1 bytes and that the Z[N] byte is zero. If +** the E argument to sqlite3_bind_text64(S,K,Z,N,D,E) is not one of the ** allowed values shown above, or if the text encoding is different ** from the encoding specified by the sixth parameter, then the behavior ** is undefined. @@ -5767,6 +5829,52 @@ SQLITE_API int sqlite3_create_window_function( ** ** These constants define integer codes that represent the various ** text encodings supported by SQLite. +** +**
    +** [[SQLITE_UTF8]]
    SQLITE_UTF8
    Text is encoding as UTF-8
    +** +** [[SQLITE_UTF16LE]]
    SQLITE_UTF16LE
    Text is encoding as UTF-16 +** with each code point being expressed "little endian" - the least significant +** byte first. This is the usual encoding, for example on Windows.
    +** +** [[SQLITE_UTF16BE]]
    SQLITE_UTF16BE
    Text is encoding as UTF-16 +** with each code point being expressed "big endian" - the most significant +** byte first. This encoding is less common, but is still sometimes seen, +** specially on older systems. +** +** [[SQLITE_UTF16]]
    SQLITE_UTF16
    Text is encoding as UTF-16 +** with each code point being expressed either little endian or as big +** endian, according to the native endianness of the host computer. +** +** [[SQLITE_ANY]]
    SQLITE_ANY
    This encoding value may only be used +** to declare the preferred text for [application-defined SQL functions] +** created using [sqlite3_create_function()] and similar. If the preferred +** encoding (the 4th parameter to sqlite3_create_function() - the eTextRep +** parameter) is SQLITE_ANY, that indicates that the function does not have +** a preference regarding the text encoding of its parameters and can take +** any text encoding that the SQLite core find convenient to supply. This +** option is deprecated. Please do not use it in new applications. +** +** [[SQLITE_UTF16_ALIGNED]]
    SQLITE_UTF16_ALIGNED
    This encoding +** value may be used as the 3rd parameter (the eTextRep parameter) to +** [sqlite3_create_collation()] and similar. This encoding value means +** that the application-defined collating sequence created expects its +** input strings to be in UTF16 in native byte order, and that the start +** of the strings must be aligned to a 2-byte boundary. +** +** [[SQLITE_UTF8_ZT]]
    SQLITE_UTF8_ZT
    This option can only be +** used to specify the text encoding to strings input to +** [sqlite3_result_text64()] and [sqlite3_bind_text64()]. +** The SQLITE_UTF8_ZT encoding means that the input string (call it "z") +** is UTF-8 encoded and that it is zero-terminated. If the length parameter +** (call it "n") is non-negative, this encoding option means that the caller +** guarantees that z array contains at least n+1 bytes and that the z[n] +** byte has a value of zero. +** This option gives the same output as SQLITE_UTF8, but can be more efficient +** by avoiding the need to make a copy of the input string, in some cases. +** However, if z is allocated to hold fewer than n+1 bytes or if the +** z[n] byte is not zero, undefined behavior may result. +**
    */ #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ @@ -5774,6 +5882,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_UTF16 4 /* Use native byte order */ #define SQLITE_ANY 5 /* Deprecated */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ +#define SQLITE_UTF8_ZT 16 /* Zero-terminated UTF8 */ /* ** CAPI3REF: Function Flags @@ -6008,26 +6117,22 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** the SQL function that supplied the [sqlite3_value*] parameters. ** ** As long as the input parameter is correct, these routines can only -** fail if an out-of-memory error occurs during a format conversion. -** Only the following subset of interfaces are subject to out-of-memory -** errors: -** -**
      -**
    • sqlite3_value_blob() -**
    • sqlite3_value_text() -**
    • sqlite3_value_text16() -**
    • sqlite3_value_text16le() -**
    • sqlite3_value_text16be() -**
    • sqlite3_value_bytes() -**
    • sqlite3_value_bytes16() -**
    -** +** fail if an out-of-memory error occurs while trying to do a +** UTF8→UTF16 or UTF16→UTF8 conversion. ** If an out-of-memory error occurs, then the return value from these ** routines is the same as if the column had contained an SQL NULL value. -** Valid SQL NULL returns can be distinguished from out-of-memory errors -** by invoking the [sqlite3_errcode()] immediately after the suspect +** If the input sqlite3_value was not obtained from [sqlite3_value_dup()], +** then valid SQL NULL returns can also be distinguished from +** out-of-memory errors after extracting the value +** by invoking the [sqlite3_errcode()] immediately after the suspicious ** return value is obtained and before any ** other SQLite interface is called on the same [database connection]. +** If the input sqlite3_value was obtained from sqlite3_value_dup() then +** it is disconnected from the database connection and so sqlite3_errcode() +** will not work. +** In that case, the only way to distinguish an out-of-memory +** condition from a true SQL NULL is to invoke sqlite3_value_type() on the +** input to see if it is NULL prior to trying to extract the value. */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API double sqlite3_value_double(sqlite3_value*); @@ -6054,7 +6159,8 @@ SQLITE_API int sqlite3_value_frombind(sqlite3_value*); ** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) ** returns something other than SQLITE_TEXT, then the return value from ** sqlite3_value_encoding(X) is meaningless. ^Calls to -** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], +** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], +** [sqlite3_value_text16be(X)], ** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or ** [sqlite3_value_bytes16(X)] might change the encoding of the value X and ** thus change the return from subsequent calls to sqlite3_value_encoding(X). @@ -6185,17 +6291,17 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** query execution, under some circumstances the associated auxiliary data ** might be preserved. An example of where this might be useful is in a ** regular-expression matching function. The compiled version of the regular -** expression can be stored as auxiliary data associated with the pattern string. -** Then as long as the pattern string remains the same, +** expression can be stored as auxiliary data associated with the pattern +** string. Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** -** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data -** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument -** value to the application-defined function. ^N is zero for the left-most -** function argument. ^If there is no auxiliary data -** associated with the function argument, the sqlite3_get_auxdata(C,N) interface -** returns a NULL pointer. +** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary +** data associated by the sqlite3_set_auxdata(C,N,P,X) function with the +** Nth argument value to the application-defined function. ^N is zero +** for the left-most function argument. ^If there is no auxiliary data +** associated with the function argument, the sqlite3_get_auxdata(C,N) +** interface returns a NULL pointer. ** ** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the ** N-th argument of the application-defined function. ^Subsequent @@ -6279,10 +6385,14 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi ** ** There is no limit (other than available memory) on the number of different ** client data pointers (with different names) that can be attached to a -** single database connection. However, the implementation is optimized -** for the case of having only one or two different client data names. -** Applications and wrapper libraries are discouraged from using more than -** one client data name each. +** single database connection. However, the current implementation stores +** the content on a linked list. Insert and retrieval performance will +** be proportional to the number of entries. The design use case, and +** the use case for which the implementation is optimized, is +** that an application will store only small number of client data names, +** typically just one or two. This interface is not intended to be a +** generalized key/value store for thousands or millions of keys. It +** will work for that, but performance might be disappointing. ** ** There is no way to enumerate the client data pointers ** associated with a database connection. The N parameter can be thought @@ -6390,10 +6500,14 @@ typedef void (*sqlite3_destructor_type)(void*); ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. -** ^The sqlite3_result_text64() interface sets the return value of an +** ^The sqlite3_result_text64(C,Z,N,D,E) interface sets the return value of an ** application-defined function to be a text string in an encoding -** specified by the fifth (and last) parameter, which must be one -** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]. +** specified the E parameter, which must be one +** of [SQLITE_UTF8], [SQLITE_UTF8_ZT], [SQLITE_UTF16], [SQLITE_UTF16BE], +** or [SQLITE_UTF16LE]. ^The special value [SQLITE_UTF8_ZT] means that +** the result text is both UTF-8 and zero-terminated. In other words, +** SQLITE_UTF8_ZT means that the Z array holds at least N+1 bytes and that +** the Z[N] is zero. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. ** ^If the 3rd parameter to any of the sqlite3_result_text* interfaces @@ -6480,7 +6594,7 @@ SQLITE_API void sqlite3_result_int(sqlite3_context*, int); SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void sqlite3_result_null(sqlite3_context*); SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64, +SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char *z, sqlite3_uint64 n, void(*)(void*), unsigned char encoding); SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); @@ -7419,7 +7533,7 @@ SQLITE_API int sqlite3_table_column_metadata( ** ^The sqlite3_load_extension() interface attempts to load an ** [SQLite extension] library contained in the file zFile. If ** the file cannot be loaded directly, attempts are made to load -** with various operating-system specific extensions added. +** with various operating-system specific filename extensions added. ** So for example, if "samplelib" cannot be loaded, then names like ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might ** be tried also. @@ -7427,10 +7541,10 @@ SQLITE_API int sqlite3_table_column_metadata( ** ^The entry point is zProc. ** ^(zProc may be 0, in which case SQLite will try to come up with an ** entry point name on its own. It first tries "sqlite3_extension_init". -** If that does not work, it constructs a name "sqlite3_X_init" where -** X consists of the lower-case equivalent of all ASCII alphabetic -** characters in the filename from the last "/" to the first following -** "." and omitting any initial "lib".)^ +** If that does not work, it tries names of the form "sqlite3_X_init" +** where X consists of the lower-case equivalent of all ASCII alphabetic +** characters or all ASCII alphanumeric characters in the filename from +** the last "/" to the first following "." and omitting any initial "lib".)^ ** ^The sqlite3_load_extension() interface returns ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. ** ^If an error occurs and pzErrMsg is not 0, then the @@ -7504,7 +7618,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); **
     **    int xEntryPoint(
     **      sqlite3 *db,
    -**      const char **pzErrMsg,
    +**      char **pzErrMsg,
     **      const struct sqlite3_api_routines *pThunk
     **    );
     ** 
    )^ @@ -8254,13 +8368,6 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix ** and Windows. ** -** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor -** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex -** implementation is included with the library. In this case the -** application must supply a custom mutex implementation using the -** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function -** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize(). ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() @@ -8615,6 +8722,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ +#define SQLITE_TESTCTRL_ATOF 34 #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* @@ -8723,17 +8831,22 @@ SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*); ** pass the returned value to [sqlite3_free()] to avoid a memory leak. ** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any ** errors were encountered during construction of the string. ^The -** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the +** [sqlite3_str_finish(X)] interface might also return a NULL pointer if the ** string in [sqlite3_str] object X is zero bytes long. +** +** ^The [sqlite3_str_free(X)] interface destroys both the sqlite3_str object +** X and the string content it contains. Calling sqlite3_str_free(X) is +** the equivalent of calling [sqlite3_free](sqlite3_str_finish(X)). */ SQLITE_API char *sqlite3_str_finish(sqlite3_str*); +SQLITE_API void sqlite3_str_free(sqlite3_str*); /* ** CAPI3REF: Add Content To A Dynamic String ** METHOD: sqlite3_str ** -** These interfaces add content to an sqlite3_str object previously obtained -** from [sqlite3_str_new()]. +** These interfaces add or remove content to an sqlite3_str object +** previously obtained from [sqlite3_str_new()]. ** ** ^The [sqlite3_str_appendf(X,F,...)] and ** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf] @@ -8756,6 +8869,10 @@ SQLITE_API char *sqlite3_str_finish(sqlite3_str*); ** ^The [sqlite3_str_reset(X)] method resets the string under construction ** inside [sqlite3_str] object X back to zero bytes in length. ** +** ^The [sqlite3_str_truncate(X,N)] method changes the length of the string +** under construction to be N bytes or less. This routine is a no-op if +** N is negative or if the string is already N bytes or smaller in size. +** ** These methods do not return a result code. ^If an error occurs, that fact ** is recorded in the [sqlite3_str] object and can be recovered by a ** subsequent call to [sqlite3_str_errcode(X)]. @@ -8766,6 +8883,7 @@ SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N); SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn); SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C); SQLITE_API void sqlite3_str_reset(sqlite3_str*); +SQLITE_API void sqlite3_str_truncate(sqlite3_str*,int N); /* ** CAPI3REF: Status Of A Dynamic String @@ -10296,7 +10414,8 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** ** sqlite3_vtab_distinct() return value ** Rows are returned in aOrderBy order -** Rows with the same value in all aOrderBy columns are adjacent +** Rows with the same value in all aOrderBy columns are +** adjacent ** Duplicates over all colUsed columns may be omitted ** 0yesyesno ** 1noyesno @@ -10305,8 +10424,8 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); ** ** ** ^For the purposes of comparing virtual table output values to see if the -** values are the same value for sorting purposes, two NULL values are considered -** to be the same. In other words, the comparison operator is "IS" +** values are the same value for sorting purposes, two NULL values are +** considered to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements @@ -10599,9 +10718,9 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** a variable pointed to by the "pOut" parameter. ** ** The "flags" parameter must be passed a mask of flags. At present only -** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** one flag is defined - [SQLITE_SCANSTAT_COMPLEX]. If SQLITE_SCANSTAT_COMPLEX ** is specified, then status information is available for all elements -** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** of a query plan that are reported by "[EXPLAIN QUERY PLAN]" output. If ** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ** the EXPLAIN QUERY PLAN output) are available. Invoking API @@ -10615,7 +10734,8 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** elements used to implement the statement - a non-zero value is returned and ** the variable that pOut points to is unchanged. ** -** See also: [sqlite3_stmt_scanstatus_reset()] +** See also: [sqlite3_stmt_scanstatus_reset()] and the +** [nexec and ncycle] columns of the [bytecode virtual table]. */ SQLITE_API int sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ @@ -11157,19 +11277,42 @@ SQLITE_API int sqlite3_deserialize( /* ** CAPI3REF: Bind array values to the CARRAY table-valued function ** -** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to -** one of the first argument of the [carray() table-valued function]. The -** S parameter is a pointer to the [prepared statement] that uses the carray() -** functions. I is the parameter index to be bound. P is a pointer to the -** array to be bound, and N is the number of eements in the array. The -** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], -** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to -** indicate the datatype of the array being bound. The X argument is not a -** NULL pointer, then SQLite will invoke the function X on the P parameter -** after it has finished using P, even if the call to -** sqlite3_carray_bind() fails. The special-case finalizer -** SQLITE_TRANSIENT has no effect here. -*/ +** The sqlite3_carray_bind_v2(S,I,P,N,F,X,D) interface binds an array value to +** parameter that is the first argument of the [carray() table-valued function]. +** The S parameter is a pointer to the [prepared statement] that uses the +** carray() functions. I is the parameter index to be bound. I must be the +** index of the parameter that is the first argument to the carray() +** table-valued function. P is a pointer to the array to be bound, and N +** is the number of elements in the array. The F argument is one of +** constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], +** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], +** or [SQLITE_CARRAY_BLOB] to indicate the datatype of the array P. +** +** If the X argument is not a NULL pointer or one of the special +** values [SQLITE_STATIC] or [SQLITE_TRANSIENT], then SQLite will invoke +** the function X with argument D when it is finished using the data in P. +** The call to X(D) is a destructor for the array P. The destructor X(D) +** is invoked even if the call to sqlite3_carray_bind_v2() fails. If the X +** parameter is the special-case value [SQLITE_STATIC], then SQLite assumes +** that the data static and the destructor is never invoked. If the X +** parameter is the special-case value [SQLITE_TRANSIENT], then +** sqlite3_carray_bind_v2() makes its own private copy of the data prior +** to returning and never invokes the destructor X. +** +** The sqlite3_carray_bind() function works the same as sqlite3_carray_bind_v2() +** with a D parameter set to P. In other words, +** sqlite3_carray_bind(S,I,P,N,F,X) is same as +** sqlite3_carray_bind_v2(S,I,P,N,F,X,P). +*/ +SQLITE_API int sqlite3_carray_bind_v2( + sqlite3_stmt *pStmt, /* Statement to be bound */ + int i, /* Parameter index */ + void *aData, /* Pointer to array data */ + int nData, /* Number of data elements */ + int mFlags, /* CARRAY flags */ + void (*xDel)(void*), /* Destructor for aData */ + void *pDel /* Optional argument to xDel() */ +); SQLITE_API int sqlite3_carray_bind( sqlite3_stmt *pStmt, /* Statement to be bound */ int i, /* Parameter index */ @@ -12713,11 +12856,23 @@ SQLITE_API int sqlite3changeset_apply_v3( ** database behave as if they were declared with "ON UPDATE NO ACTION ON ** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL ** or SET DEFAULT. +** +**
    SQLITE_CHANGESETAPPLY_NOUPDATELOOP
    +** Sometimes, a changeset contains two or more update statements such that +** although after applying all updates the database will contain no +** constraint violations, no single update can be applied before the others. +** The simplest example of this is a pair of UPDATEs that have "swapped" +** two column values with a UNIQUE constraint. +**

    +** Usually, sqlite3changeset_apply() and similar functions work hard to try +** to find a way to apply such a changeset. However, if this flag is set, +** then all such updates are considered CONSTRAINT conflicts. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 #define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 +#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP 0x0010 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -13200,6 +13355,232 @@ SQLITE_API int sqlite3session_config(int op, void *pArg); */ #define SQLITE_SESSION_CONFIG_STRMSIZE 1 +/* +** CAPI3REF: Configure a changegroup object +** +** Configure the changegroup object passed as the first argument. +** At present the only valid value for the second parameter is +** [SQLITE_CHANGEGROUP_CONFIG_PATCHSET]. +*/ +SQLITE_API int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg); + +/* +** CAPI3REF: Options for sqlite3changegroup_config(). +** +** The following values may be passed as the 2nd parameter to +** sqlite3changegroup_config(). +** +**

    SQLITE_CHANGEGROUP_CONFIG_PATCHSET
    +** A changegroup object generates either a changeset or patchset. Usually, +** this is determined by whether the first call to sqlite3changegroup_add() +** is passed a changeset or a patchset. Or, if the first changes are added +** to the changegroup object using the sqlite3changegroup_change_xxx() +** APIs, then this option may be used to configure whether the changegroup +** object generates a changeset or patchset. +** +** When this option is invoked, parameter pArg must point to a value of +** type int. If the changegroup currently contains zero changes, and the +** value of the int variable is zero or greater than zero, then the +** changegroup is configured to generate a changeset or patchset, +** respectively. It is a no-op, not an error, if the changegroup is not +** configured because it has already started accumulating changes. +** +** Before returning, the int variable is set to 0 if the changegroup is +** configured to generate a changeset, or 1 if it is configured to generate +** a patchset. +*/ +#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1 + + +/* +** CAPI3REF: Begin adding a change to a changegroup +** +** This API is used, in concert with other sqlite3changegroup_change_xxx() +** APIs, to add changes to a changegroup object one at a time. To add a +** single change, the caller must: +** +** 1. Invoke sqlite3changegroup_change_begin() to indicate the type of +** change (INSERT, UPDATE or DELETE), the affected table and whether +** or not the change should be marked as indirect. +** +** 2. Invoke sqlite3changegroup_change_int64() or one of the other four +** value functions - _null(), _double(), _text() or _blob() - one or +** more times to specify old.* and new.* values for the change being +** constructed. +** +** 3. Invoke sqlite3changegroup_change_finish() to either finish adding +** the change to the group, or to discard the change altogether. +** +** The first argument to this function must be a pointer to the existing +** changegroup object that the change will be added to. The second argument +** must be SQLITE_INSERT, SQLITE_UPDATE or SQLITE_DELETE. The third is the +** name of the table that the change affects, and the fourth is a boolean +** flag specifying whether the change should be marked as "indirect" (if +** bIndirect is non-zero) or not indirect (if bIndirect is zero). +** +** Following a successful call to this function, this function may not be +** called again on the same changegroup object until after +** sqlite3changegroup_change_finish() has been called. Doing so is an +** SQLITE_MISUSE error. +** +** The changegroup object passed as the first argument must be already +** configured with schema data for the specified table. It may be configured +** either by calling sqlite3changegroup_schema() with a database that contains +** the table, or sqlite3changegroup_add() with a changeset that contains the +** table. If the changegroup object has not been configured with a schema for +** the specified table when this function is called, SQLITE_ERROR is returned. +** +** If successful, SQLITE_OK is returned. Otherwise, if an error occurs, an +** SQLite error code is returned. In this case, if argument pzErr is non-NULL, +** then (*pzErr) may be set to point to a buffer containing a utf-8 formated, +** nul-terminated, English language error message. It is the responsibility +** of the caller to eventually free this buffer using sqlite3_free(). +*/ +SQLITE_API int sqlite3changegroup_change_begin( + sqlite3_changegroup*, + int eOp, + const char *zTab, + int bIndirect, + char **pzErr +); + +/* +** CAPI3REF: Add a 64-bit integer to a changegroup +** +** This function may only be called between a successful call to +** sqlite3changegroup_change_begin() and its matching +** sqlite3changegroup_change_finish() call. If it is called at any +** other time, it is an SQLITE_MISUSE error. Calling this function +** specifies a 64-bit integer value to be used in the change currently being +** added to the changegroup object passed as the first argument. +** +** The second parameter, bNew, specifies whether the value is to be part of +** the new.* (if bNew is non-zero) or old.* (if bNew is zero) record of +** the change under construction. If this does not match the type of change +** specified by the preceding call to sqlite3changegroup_change_begin() (i.e. +** an old.* value for an SQLITE_INSERT change, or a new.* value for an +** SQLITE_DELETE), then SQLITE_ERROR is returned. +** +** The third parameter specifies the column of the old.* or new.* record that +** the value will be a part of. If the specified table has an explicit primary +** key, then this is the index of the table column, numbered from 0 in the order +** specified within the CREATE TABLE statement. Or, if the table uses an +** implicit rowid key, then the column 0 is the rowid and the explicit columns +** are numbered starting from 1. If the iCol parameter is less than 0 or greater +** than the index of the last column in the table, SQLITE_RANGE is returned. +** +** The fourth parameter is the integer value to use as part of the old.* or +** new.* record. +** +** If this call is successful, SQLITE_OK is returned. Otherwise, if an +** error occurs, an SQLite error code is returned. +*/ +SQLITE_API int sqlite3changegroup_change_int64( + sqlite3_changegroup*, + int bNew, + int iCol, + sqlite3_int64 iVal +); + +/* +** CAPI3REF: Add a NULL to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). Except that +** it configures the change currently under construction with a NULL value +** instead of a 64-bit integer. +*/ +SQLITE_API int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int); + +/* +** CAPI3REF: Add an double to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). Except that +** it configures the change currently being constructed with a real value +** instead of a 64-bit integer. +*/ +SQLITE_API int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double); + +/* +** CAPI3REF: Add a text value to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). It configures +** the currently accumulated change with a text value instead of a 64-bit +** integer. Parameter pVal points to a buffer containing the text encoded using +** utf-8. Parameter nVal may either be the size of the text value in bytes, or +** else a negative value, in which case the buffer pVal points to is assumed to +** be nul-terminated. +*/ +SQLITE_API int sqlite3changegroup_change_text( + sqlite3_changegroup*, int, int, const char *pVal, int nVal +); + +/* +** CAPI3REF: Add a blob to a changegroup +** +** This function is similar to sqlite3changegroup_change_int64(). It configures +** the currently accumulated change with a blob value instead of a 64-bit +** integer. Parameter pVal points to a buffer containing the blob. Parameter +** nVal is the size of the blob in bytes. +*/ +SQLITE_API int sqlite3changegroup_change_blob( + sqlite3_changegroup*, int, int, const void *pVal, int nVal +); + +/* +** CAPI3REF: Finish adding one-at-at-time changes to a changegroup +** +** This function may only be called following a successful call to +** sqlite3changegroup_change_begin(). Otherwise, it is an SQLITE_MISUSE error. +** +** If parameter bDiscard is non-zero, then the current change is simply +** discarded. In this case this function is always successful and SQLITE_OK +** returned. +** +** If parameter bDiscard is zero, then an attempt is made to add the current +** change to the changegroup. Assuming the changegroup is configured to +** produce a changeset (not a patchset), this requires that: +** +** * If the change is an INSERT or DELETE, then a value must be specified +** for all columns of the new.* or old.* record, respectively. +** +** * If the change is an UPDATE record, then values must be provided for +** the PRIMARY KEY columns of the old.* record, but must not be provided +** for PRIMARY KEY columns of the new.* record. +** +** * If the change is an UPDATE record, then for each non-PRIMARY KEY +** column in the old.* record for which a value has been provided, a +** value must also be provided for the same column in the new.* record. +** Similarly, for each non-PK column in the old.* record for which +** a value is not provided, a value must not be provided for the same +** column in the new.* record. +** +** * All values specified for PRIMARY KEY columns must be non-NULL. +** +** Otherwise, it is an error. +** +** If the changegroup already contains a change for the same row (identified +** by PRIMARY KEY columns), then the current change is combined with the +** existing change in the same way as for sqlite3changegroup_add(). +** +** For a patchset, all of the above rules apply except that it doesn't matter +** whether or not values are provided for the non-PK old.* record columns +** for an UPDATE or DELETE change. This means that code used to produce +** a changeset using the sqlite3changegroup_change_xxx() APIs may also +** be used to produce patchsets. +** +** If the call is successful, SQLITE_OK is returned. Otherwise, if an error +** occurs, an SQLite error code is returned. If an error is returned and +** parameter pzErr is not NULL, then (*pzErr) may be set to point to a buffer +** containing a nul-terminated, utf-8 encoded, English language error message. +** It is the responsibility of the caller to eventually free any such error +** message buffer using sqlite3_free(). +*/ +SQLITE_API int sqlite3changegroup_change_finish( + sqlite3_changegroup*, + int bDiscard, + char **pzErr +); + /* ** Make sure we can call this stuff from C++. */ diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3.go b/vendor/github.com/mattn/go-sqlite3/sqlite3.go index 1a5433c7ed..c30bb8f1e7 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3.go @@ -69,13 +69,13 @@ _sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zV } static int -_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) { - return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT); +_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, sqlite3_uint64 np) { + return sqlite3_bind_text64(stmt, n, p, np, SQLITE_TRANSIENT, SQLITE_UTF8); } static int -_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) { - return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT); +_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, sqlite3_uint64 np) { + return sqlite3_bind_blob64(stmt, n, p, np, SQLITE_TRANSIENT); } typedef struct { @@ -220,8 +220,8 @@ _sqlite3_prepare_v2_internal(sqlite3 *db, const char *zSql, int nBytes, sqlite3_ } #endif -void _sqlite3_result_text(sqlite3_context* ctx, const char* s) { - sqlite3_result_text(ctx, s, -1, &free); +void _sqlite3_result_text(sqlite3_context* ctx, const char* s, int n) { + sqlite3_result_text(ctx, s, n, &free); } void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l) { @@ -445,15 +445,20 @@ type SQLiteDriver struct { // SQLiteConn implements driver.Conn. type SQLiteConn struct { - mu sync.Mutex - db *C.sqlite3 - loc *time.Location - txlock string - funcs []*functionInfo - aggregators []*aggInfo - stmtCache map[string][]*SQLiteStmt - stmtCacheSize int - stmtCacheCount int + mu sync.Mutex + db *C.sqlite3 + loc *time.Location + txlock string + funcs []*functionInfo + aggregators []*aggInfo + // Prepared-statement cache. The slice is allocated at Open with a + // fixed capacity equal to the configured cache size; cap bounds the + // cache, len is the live count, and entries are ordered LRU-first + // (index 0 is the oldest, the tail is most recently put). Access + // requires mu; stmtCacheEnabled is immutable after Open and is the + // only field safe to read without the lock. + stmtCache []*SQLiteStmt + stmtCacheEnabled bool } // SQLiteTx implements driver.Tx. @@ -471,6 +476,12 @@ type SQLiteStmt struct { cls bool // True if the statement was created by SQLiteConn.Query namedParams map[string][3]int cacheKey string + metadata *sqliteStmtMetadata +} + +type sqliteStmtMetadata struct { + cols []string + decltype []string } // SQLiteResult implements sql.Result. @@ -1017,25 +1028,37 @@ func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.Name if err != nil { return nil, err } - s.(*SQLiteStmt).cls = true + ss := s.(*SQLiteStmt) + ss.cls = true + // sqlite3_prepare_v2 returns SQLITE_OK with a NULL statement handle + // when the input is empty or contains only whitespace/comments. + if ss.s == nil { + tail := ss.t + ss.Close() + if tail == "" { + return &SQLiteRows{cls: true, ctx: ctx}, nil + } + query = tail + continue + } na := s.NumInput() if len(args)-start < na { - s.Close() + ss.Close() return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)-start) } stmtArgs := stmtArgs(args, start, na) - rows, err := s.(*SQLiteStmt).query(ctx, stmtArgs) + rows, err := ss.query(ctx, stmtArgs) if err != nil && err != driver.ErrSkip { - s.Close() + ss.Close() return rows, err } start += na - tail := s.(*SQLiteStmt).t + tail := ss.t if tail == "" { return rows, nil } rows.Close() - s.Close() + ss.Close() query = tail } } @@ -1572,6 +1595,20 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, errors.New("sqlite succeeded without returning a database") } + // Create connection to SQLite + conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} + if stmtCacheSize > 0 { + conn.stmtCache = make([]*SQLiteStmt, 0, stmtCacheSize) + conn.stmtCacheEnabled = true + } + + // fail closes the connection so no error path leaks the database + // handle or any callback handles registered on it. + fail := func(err error) (driver.Conn, error) { + conn.Close() + return nil, err + } + exec := func(s string) error { cs := C.CString(s) rv := C.sqlite3_exec(db, cs, nil, nil, nil) @@ -1584,8 +1621,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // Busy timeout if err := exec(fmt.Sprintf("PRAGMA busy_timeout = %d;", busyTimeout)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } // USER AUTHENTICATION @@ -1610,65 +1646,59 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // NO => Continue // - // Create connection to SQLite - conn := &SQLiteConn{db: db, loc: loc, txlock: txlock, stmtCacheSize: stmtCacheSize} - if stmtCacheSize > 0 { - conn.stmtCache = make(map[string][]*SQLiteStmt) - } - // Password Cipher has to be registered before authentication if len(authCrypt) > 0 { switch strings.ToUpper(authCrypt) { case "SHA1": if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA1, true); err != nil { - return nil, fmt.Errorf("CryptEncoderSHA1: %s", err) + return fail(fmt.Errorf("CryptEncoderSHA1: %s", err)) } case "SSHA1": if len(authSalt) == 0 { - return nil, fmt.Errorf("_auth_crypt=ssha1, requires _auth_salt") + return fail(fmt.Errorf("_auth_crypt=ssha1, requires _auth_salt")) } if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA1(authSalt), true); err != nil { - return nil, fmt.Errorf("CryptEncoderSSHA1: %s", err) + return fail(fmt.Errorf("CryptEncoderSSHA1: %s", err)) } case "SHA256": if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA256, true); err != nil { - return nil, fmt.Errorf("CryptEncoderSHA256: %s", err) + return fail(fmt.Errorf("CryptEncoderSHA256: %s", err)) } case "SSHA256": if len(authSalt) == 0 { - return nil, fmt.Errorf("_auth_crypt=ssha256, requires _auth_salt") + return fail(fmt.Errorf("_auth_crypt=ssha256, requires _auth_salt")) } if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA256(authSalt), true); err != nil { - return nil, fmt.Errorf("CryptEncoderSSHA256: %s", err) + return fail(fmt.Errorf("CryptEncoderSSHA256: %s", err)) } case "SHA384": if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA384, true); err != nil { - return nil, fmt.Errorf("CryptEncoderSHA384: %s", err) + return fail(fmt.Errorf("CryptEncoderSHA384: %s", err)) } case "SSHA384": if len(authSalt) == 0 { - return nil, fmt.Errorf("_auth_crypt=ssha384, requires _auth_salt") + return fail(fmt.Errorf("_auth_crypt=ssha384, requires _auth_salt")) } if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA384(authSalt), true); err != nil { - return nil, fmt.Errorf("CryptEncoderSSHA384: %s", err) + return fail(fmt.Errorf("CryptEncoderSSHA384: %s", err)) } case "SHA512": if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSHA512, true); err != nil { - return nil, fmt.Errorf("CryptEncoderSHA512: %s", err) + return fail(fmt.Errorf("CryptEncoderSHA512: %s", err)) } case "SSHA512": if len(authSalt) == 0 { - return nil, fmt.Errorf("_auth_crypt=ssha512, requires _auth_salt") + return fail(fmt.Errorf("_auth_crypt=ssha512, requires _auth_salt")) } if err := conn.RegisterFunc("sqlite_crypt", CryptEncoderSSHA512(authSalt), true); err != nil { - return nil, fmt.Errorf("CryptEncoderSSHA512: %s", err) + return fail(fmt.Errorf("CryptEncoderSSHA512: %s", err)) } } } // Preform Authentication if err := conn.Authenticate(authUser, authPass); err != nil { - return nil, err + return fail(err) } // Register: authenticate @@ -1686,7 +1716,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // If the SQLITE_USER table is not present in the database file, then // this interface is a harmless no-op returnning SQLITE_OK. if err := conn.RegisterFunc("authenticate", conn.authenticate, true); err != nil { - return nil, err + return fail(err) } // // Register: auth_user_add @@ -1699,7 +1729,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // for any ATTACH-ed databases. Any call to AuthUserAdd by a // non-admin user results in an error. if err := conn.RegisterFunc("auth_user_add", conn.authUserAdd, true); err != nil { - return nil, err + return fail(err) } // // Register: auth_user_change @@ -1709,7 +1739,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // credentials or admin privilege setting. No user may change their own // admin privilege setting. if err := conn.RegisterFunc("auth_user_change", conn.authUserChange, true); err != nil { - return nil, err + return fail(err) } // // Register: auth_user_delete @@ -1719,13 +1749,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // the database cannot be converted into a no-authentication-required // database. if err := conn.RegisterFunc("auth_user_delete", conn.authUserDelete, true); err != nil { - return nil, err + return fail(err) } // Register: auth_enabled // auth_enabled can be used to check if user authentication is enabled if err := conn.RegisterFunc("auth_enabled", conn.authEnabled, true); err != nil { - return nil, err + return fail(err) } // Auto Vacuum @@ -1736,8 +1766,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // and activating user authentication creates the internal table `sqlite_user`. if autoVacuum > -1 { if err := exec(fmt.Sprintf("PRAGMA auto_vacuum = %d;", autoVacuum)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } @@ -1747,17 +1776,17 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // has provided an username and password within the DSN. // We are not allowed to continue. if len(authUser) == 0 { - return nil, fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'") + return fail(fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'")) } if len(authPass) == 0 { - return nil, fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'") + return fail(fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'")) } // Check if User Authentication is Enabled authExists := conn.AuthEnabled() if !authExists { if err := conn.AuthUserAdd(authUser, authPass, true); err != nil { - return nil, err + return fail(err) } } } @@ -1765,40 +1794,35 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // Case Sensitive LIKE if caseSensitiveLike > -1 { if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Defer Foreign Keys if deferForeignKeys > -1 { if err := exec(fmt.Sprintf("PRAGMA defer_foreign_keys = %d;", deferForeignKeys)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Foreign Keys if foreignKeys > -1 { if err := exec(fmt.Sprintf("PRAGMA foreign_keys = %d;", foreignKeys)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Ignore CHECK Constraints if ignoreCheckConstraints > -1 { if err := exec(fmt.Sprintf("PRAGMA ignore_check_constraints = %d;", ignoreCheckConstraints)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Journal Mode if journalMode != "" { if err := exec(fmt.Sprintf("PRAGMA journal_mode = %s;", journalMode)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } @@ -1806,23 +1830,20 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // Because the default is NORMAL and this is not changed in this package // by using the compile time SQLITE_DEFAULT_LOCKING_MODE this PRAGMA can always be executed if err := exec(fmt.Sprintf("PRAGMA locking_mode = %s;", lockingMode)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } // Query Only if queryOnly > -1 { if err := exec(fmt.Sprintf("PRAGMA query_only = %d;", queryOnly)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Recursive Triggers if recursiveTriggers > -1 { if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } @@ -1833,8 +1854,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // you can compile with secure_delete 'ON' and disable it for a specific database connection. if secureDelete != "DEFAULT" { if err := exec(fmt.Sprintf("PRAGMA secure_delete = %s;", secureDelete)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } @@ -1842,37 +1862,32 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { // // Because default is NORMAL this statement is always executed if err := exec(fmt.Sprintf("PRAGMA synchronous = %s;", synchronousMode)); err != nil { - conn.Close() - return nil, err + return fail(err) } // Writable Schema if writableSchema > -1 { if err := exec(fmt.Sprintf("PRAGMA writable_schema = %d;", writableSchema)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } // Cache Size if cacheSize != nil { if err := exec(fmt.Sprintf("PRAGMA cache_size = %d;", *cacheSize)); err != nil { - C.sqlite3_close_v2(db) - return nil, err + return fail(err) } } if len(d.Extensions) > 0 { if err := conn.loadExtensions(d.Extensions); err != nil { - conn.Close() - return nil, err + return fail(err) } } if d.ConnectHook != nil { if err := d.ConnectHook(conn); err != nil { - conn.Close() - return nil, err + return fail(err) } } runtime.SetFinalizer(conn, (*SQLiteConn).Close) @@ -1907,7 +1922,7 @@ func (c *SQLiteConn) dbConnOpen() bool { } func (c *SQLiteConn) takeCachedStmt(query string) *SQLiteStmt { - if c == nil || query == "" || c.stmtCacheSize <= 0 { + if c == nil || query == "" || !c.stmtCacheEnabled { return nil } @@ -1917,21 +1932,25 @@ func (c *SQLiteConn) takeCachedStmt(query string) *SQLiteStmt { if c.db == nil { return nil } - stmts := c.stmtCache[query] - if len(stmts) == 0 { - return nil - } - s := stmts[len(stmts)-1] - if len(stmts) == 1 { - delete(c.stmtCache, query) - } else { - c.stmtCache[query] = stmts[:len(stmts)-1] + // Scan from the MRU end (tail) so that a stmt put just before is + // found immediately. + for i := len(c.stmtCache) - 1; i >= 0; i-- { + s := c.stmtCache[i] + if s.cacheKey != query { + continue + } + n := len(c.stmtCache) + copy(c.stmtCache[i:n-1], c.stmtCache[i+1:n]) + c.stmtCache[n-1] = nil + c.stmtCache = c.stmtCache[:n-1] + // The stmt was marked closed by Close before being cached, and + // cls may have been set if Query opened it; reset both so the + // caller gets a stmt equivalent to a fresh Prepare. + s.closed = false + s.cls = false + return s } - c.stmtCacheCount-- - s.closed = false - s.cls = false - s.t = "" - return s + return nil } func (c *SQLiteConn) putCachedStmt(s *SQLiteStmt) bool { @@ -1942,32 +1961,50 @@ func (c *SQLiteConn) putCachedStmt(s *SQLiteStmt) bool { c.mu.Lock() defer c.mu.Unlock() - if c.db == nil || c.stmtCacheCount >= c.stmtCacheSize { + return c.putCachedStmtLocked(s) +} + +func (c *SQLiteConn) putCachedStmtLocked(s *SQLiteStmt) bool { + if c.db == nil { return false } rv := C._sqlite3_reset_clear(s.s) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { return false } - c.stmtCache[s.cacheKey] = append(c.stmtCache[s.cacheKey], s) - c.stmtCacheCount++ + // If full, finalize the LRU entry at index 0 and shift left; the + // freed tail slot is immediately reused by the append below. + if len(c.stmtCache) == cap(c.stmtCache) { + finalizeCachedStmt(c.stmtCache[0]) + copy(c.stmtCache, c.stmtCache[1:]) + c.stmtCache = c.stmtCache[:len(c.stmtCache)-1] + } + c.stmtCache = append(c.stmtCache, s) return true } func (c *SQLiteConn) closeCachedStmtsLocked() { - for key, stmts := range c.stmtCache { - for _, s := range stmts { - if s == nil || s.s == nil { - continue - } - runtime.SetFinalizer(s, nil) - C.sqlite3_finalize(s.s) - s.s = nil - s.c = nil - } - delete(c.stmtCache, key) + for i, s := range c.stmtCache { + c.stmtCache[i] = nil + finalizeCachedStmt(s) } - c.stmtCacheCount = 0 + c.stmtCache = c.stmtCache[:0] +} + +// finalizeCachedStmt tears down a stmt that was sitting in the connection's +// stmt cache. The caller must hold c.mu. It is safe to pass a nil stmt or a +// stmt whose handle has already been released. +func finalizeCachedStmt(s *SQLiteStmt) { + if s == nil { + return + } + runtime.SetFinalizer(s, nil) + if s.s != nil { + C.sqlite3_finalize(s.s) + s.s = nil + } + s.c = nil + s.closed = true } // Prepare the query string. Return a new statement. @@ -2002,7 +2039,7 @@ func (c *SQLiteConn) prepareWithCache(ctx context.Context, query string) (driver return nil, err } ss := stmt.(*SQLiteStmt) - if ss.t == "" { + if ss.t == "" && c.stmtCacheEnabled { ss.cacheKey = query } return ss, nil @@ -2033,7 +2070,9 @@ func (c *SQLiteConn) GetFilename(schemaName string) string { if schemaName == "" { schemaName = "main" } - return C.GoString(C.sqlite3_db_filename(c.db, C.CString(schemaName))) + cSchema := C.CString(schemaName) + defer C.free(unsafe.Pointer(cSchema)) + return C.GoString(C.sqlite3_db_filename(c.db, cSchema)) } // GetLimit returns the current value of a run-time limit. @@ -2120,12 +2159,20 @@ func (s *SQLiteStmt) Close() error { s.c = nil return nil } - if !conn.dbConnOpen() { + if s.cacheKey != "" { + conn.mu.Lock() + if conn.db == nil { + conn.mu.Unlock() + return errors.New("sqlite statement with already closed database connection") + } + if conn.putCachedStmtLocked(s) { + conn.mu.Unlock() + return nil + } + conn.mu.Unlock() + } else if !conn.dbConnOpen() { return errors.New("sqlite statement with already closed database connection") } - if s.cacheKey != "" && conn.putCachedStmt(s) { - return nil - } s.s = nil s.c = nil rv := C.sqlite3_finalize(stmt) @@ -2144,9 +2191,9 @@ var placeHolder = []byte{0} func bindText(s *C.sqlite3_stmt, n C.int, v string) C.int { if len(v) == 0 { - return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0)) + return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.sqlite3_uint64(0)) } - return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(unsafe.StringData(v))), C.int(len(v))) + return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(unsafe.StringData(v))), C.sqlite3_uint64(len(v))) } func bindValue(s *C.sqlite3_stmt, n C.int, value driver.Value) C.int { @@ -2172,14 +2219,14 @@ func bindValue(s *C.sqlite3_stmt, n C.int, value driver.Value) C.int { if ln == 0 { v = placeHolder } - return C._sqlite3_bind_blob(s, n, unsafe.Pointer(&v[0]), C.int(ln)) + return C._sqlite3_bind_blob(s, n, unsafe.Pointer(&v[0]), C.sqlite3_uint64(ln)) case time.Time: var buf [64]byte b := v.AppendFormat(buf[:0], SQLiteTimestampFormats[0]) if len(b) == 0 { - return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0)) + return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.sqlite3_uint64(0)) } - return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) + return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&b[0])), C.sqlite3_uint64(len(b))) default: return C.SQLITE_MISUSE } @@ -2238,6 +2285,17 @@ func stmtArgs(args []driver.NamedValue, start, na int) []driver.NamedValue { return stmtArgs } +// bindError converts a non-OK return code from bindValue into an error. +// The synthetic SQLITE_MISUSE returned for unsupported Go types is never +// recorded in the database handle, so lastError may report no error; fall +// back to an explicit message instead of silently ignoring the failure. +func (s *SQLiteStmt) bindError(v driver.Value) error { + if err := s.c.lastError(); err != nil { + return err + } + return fmt.Errorf("sqlite3: unsupported bind type %T", v) +} + func (s *SQLiteStmt) bind(args []driver.NamedValue) error { rv := C._sqlite3_reset_clear(s.s) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { @@ -2257,7 +2315,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error { n := C.int(arg.Ordinal) rv = bindValue(s.s, n, arg.Value) if rv != C.SQLITE_OK { - return s.c.lastError() + return s.bindError(arg.Value) } } return nil @@ -2267,7 +2325,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error { if arg.Name == "" { rv = bindValue(s.s, C.int(arg.Ordinal), arg.Value) if rv != C.SQLITE_OK { - return s.c.lastError() + return s.bindError(arg.Value) } continue } @@ -2278,7 +2336,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error { } rv = bindValue(s.s, C.int(idx), arg.Value) if rv != C.SQLITE_OK { - return s.c.lastError() + return s.bindError(arg.Value) } } } @@ -2439,14 +2497,51 @@ func (rc *SQLiteRows) Close() error { return nil } +func (s *SQLiteStmt) cacheMetadata() bool { + return !s.cls || s.cacheKey != "" +} + +func (s *SQLiteStmt) columnNamesLocked(n int) []string { + if s.metadata == nil { + s.metadata = &sqliteStmtMetadata{} + } + if len(s.metadata.cols) != n { + s.metadata.cols = make([]string, n) + for i := range s.metadata.cols { + s.metadata.cols[i] = C.GoString(C.sqlite3_column_name(s.s, C.int(i))) + } + } + return s.metadata.cols +} + +func (s *SQLiteStmt) declTypesLocked(n int) []string { + if s.metadata == nil { + s.metadata = &sqliteStmtMetadata{} + } + if len(s.metadata.decltype) != n { + s.metadata.decltype = make([]string, n) + for i := range s.metadata.decltype { + s.metadata.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.s, C.int(i)))) + } + } + return s.metadata.decltype +} + // Columns return column names. func (rc *SQLiteRows) Columns() []string { + if rc.s == nil { + return rc.cols + } rc.s.mu.Lock() defer rc.s.mu.Unlock() if rc.s.s != nil && int(rc.nc) != len(rc.cols) { - rc.cols = make([]string, rc.nc) - for i := range rc.cols { - rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) + if rc.s.cacheMetadata() { + rc.cols = rc.s.columnNamesLocked(int(rc.nc)) + } else { + rc.cols = make([]string, rc.nc) + for i := range rc.cols { + rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) + } } } return rc.cols @@ -2454,9 +2549,13 @@ func (rc *SQLiteRows) Columns() []string { func (rc *SQLiteRows) declTypes() []string { if rc.s.s != nil && rc.decltype == nil { - rc.decltype = make([]string, rc.nc) - for i := range rc.decltype { - rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) + if rc.s.cacheMetadata() { + rc.decltype = rc.s.declTypesLocked(int(rc.nc)) + } else { + rc.decltype = make([]string, rc.nc) + for i := range rc.decltype { + rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) + } } } return rc.decltype @@ -2464,6 +2563,9 @@ func (rc *SQLiteRows) declTypes() []string { // DeclTypes return column types. func (rc *SQLiteRows) DeclTypes() []string { + if rc.s == nil { + return rc.decltype + } rc.s.mu.Lock() defer rc.s.mu.Unlock() return rc.declTypes() @@ -2471,6 +2573,9 @@ func (rc *SQLiteRows) DeclTypes() []string { // Next move cursor to next. Attempts to honor context timeout from QueryContext call. func (rc *SQLiteRows) Next(dest []driver.Value) error { + if rc.s == nil { + return io.EOF + } rc.s.mu.Lock() defer rc.s.mu.Unlock() diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_context.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_context.go index 7c7431dcce..4436279e9c 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_context.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_context.go @@ -28,7 +28,6 @@ import "C" import ( "math" - "reflect" "unsafe" ) @@ -91,9 +90,15 @@ func (c *SQLiteContext) ResultNull() { // ResultText sets the result of an SQL function. // See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html func (c *SQLiteContext) ResultText(s string) { - h := (*reflect.StringHeader)(unsafe.Pointer(&s)) - cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) - C.my_result_text((*C.sqlite3_context)(c), cs, l) + if i64 && len(s) > math.MaxInt32 { + C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) + return + } + if len(s) == 0 { + C.my_result_text((*C.sqlite3_context)(c), (*C.char)(unsafe.Pointer(&placeHolder[0])), 0) + return + } + C.my_result_text((*C.sqlite3_context)(c), (*C.char)(unsafe.Pointer(unsafe.StringData(s))), C.int(len(s))) } // ResultZeroblob sets the result of an SQL function. diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go index 03cbc8b686..fbbb649394 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go @@ -74,11 +74,11 @@ func (c *SQLiteConn) loadExtension(lib string, entry *string) error { } var errMsg *C.char - defer C.sqlite3_free(unsafe.Pointer(errMsg)) - rv := C.sqlite3_load_extension(c.db, clib, centry, &errMsg) if rv != C.SQLITE_OK { - return errors.New(C.GoString(errMsg)) + err := errors.New(C.GoString(errMsg)) + C.sqlite3_free(unsafe.Pointer(errMsg)) + return err } return nil diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_dbstat.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_dbstat.go new file mode 100644 index 0000000000..d033846144 --- /dev/null +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_dbstat.go @@ -0,0 +1,15 @@ +// Copyright (C) 2025 Yasuhiro Matsumoto . +// Copyright (C) 2025 Jakob Borg . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +//go:build sqlite_dbstat +// +build sqlite_dbstat + +package sqlite3 + +/* +#cgo CFLAGS: -DSQLITE_ENABLE_DBSTAT_VTAB +*/ +import "C" diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go index 8cce278fd4..37e048ffd9 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_preupdate_hook.go @@ -59,13 +59,17 @@ func (d *SQLitePreUpdateData) row(dest []any, new bool) error { for i := 0; i < d.Count() && i < len(dest); i++ { var val *C.sqlite3_value var src any + var rc C.int // Initially I tried making this just a function pointer argument, but // it's absurdly complicated to pass C function pointers. if new { - C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) + rc = C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val) } else { - C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) + rc = C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val) + } + if rc != C.SQLITE_OK { + return Error{Code: ErrNo(rc)} } switch C.sqlite3_value_type(val) { diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize.go index 51dd9c8f02..60019bd5ed 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_serialize.go @@ -60,6 +60,9 @@ func (c *SQLiteConn) Deserialize(b []byte, schema string) error { defer C.free(unsafe.Pointer(zSchema)) tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b)))) + if tmpBuf == nil && len(b) > 0 { + return fmt.Errorf("deserialize failed: out of memory") + } copy(unsafe.Slice((*byte)(unsafe.Pointer(tmpBuf)), len(b)), b) rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)), diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go index 3ac8050a4a..dddb655da8 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_unlock_notify.go @@ -21,6 +21,7 @@ package sqlite3 extern void unlock_notify_callback(void *arg, int argc); */ import "C" + import ( "fmt" "math" @@ -82,7 +83,7 @@ func unlock_notify_wait(db *C.sqlite3) C.int { h := unt.add(c) defer unt.remove(h) - pargv := C.malloc(C.sizeof_uint) + pargv := C.malloc(C.size_t(unsafe.Sizeof(uint(0)))) defer C.free(pargv) argv := (*[1]uint)(pargv) diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go index 9761bf3570..b2b2404bcb 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go @@ -113,6 +113,9 @@ uintptr_t goVOpen(void *pVTab, char **pzErr); static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); + if (!vTabCursor) { + return SQLITE_ERROR; + } goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); if (!pCursor) { return SQLITE_NOMEM; @@ -270,7 +273,6 @@ import "C" import ( "fmt" "math" - "reflect" "unsafe" ) @@ -329,11 +331,7 @@ type InfoOrderBy struct { } func constraints(info *C.sqlite3_index_info) []InfoConstraint { - slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.aConstraint)), - Len: int(info.nConstraint), - Cap: int(info.nConstraint), - })) + slice := unsafe.Slice(info.aConstraint, int(info.nConstraint)) cst := make([]InfoConstraint, 0, len(slice)) for _, c := range slice { @@ -351,11 +349,7 @@ func constraints(info *C.sqlite3_index_info) []InfoConstraint { } func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { - slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.aOrderBy)), - Len: int(info.nOrderBy), - Cap: int(info.nOrderBy), - })) + slice := unsafe.Slice(info.aOrderBy, int(info.nOrderBy)) ob := make([]InfoOrderBy, 0, len(slice)) for _, c := range slice { @@ -400,10 +394,7 @@ func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr ** return 0 } args := make([]string, argc) - var A []*C.char - slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} - a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() - for i, s := range a.([]*C.char) { + for i, s := range unsafe.Slice(argv, int(argc)) { args[i] = C.GoString(s) } var vTab VTab @@ -432,6 +423,9 @@ func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { } else { err = vt.vTab.Disconnect() } + // The vtab is gone as far as SQLite is concerned regardless of the + // callback result, so release the handle either way. + deleteHandle(pVTab) if err != nil { return mPrintf("%s", err.Error()) } @@ -460,20 +454,23 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { if err != nil { return mPrintf("%s", err.Error()) } + if res == nil { + return mPrintf("%s", "BestIndex returned a nil IndexResult") + } if len(res.Used) != len(csts) { return mPrintf("Result.Used != expected value", "") } // Get a pointer to constraint_usage struct so we can update in place. - slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.aConstraintUsage)), - Len: int(info.nConstraint), - Cap: int(info.nConstraint), - })) + slice := unsafe.Slice(info.aConstraintUsage, int(info.nConstraint)) index := 1 for i := range slice { - if res.Used[i] { + // SQLite returns "xBestIndex malfunction" when an argvIndex is + // assigned to a constraint it marked as not usable, so ignore + // Used for those; they may become usable on a later xBestIndex + // invocation for a different plan. + if res.Used[i] && csts[i].Usable { slice[i].argvIndex = C.int(index) slice[i].omit = C.uchar(1) index++ @@ -488,19 +485,30 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { } info.needToFreeIdxStr = C.int(1) - idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(info.idxStr)), - Len: len(res.IdxStr) + 1, - Cap: len(res.IdxStr) + 1, - })) + idxStr := unsafe.Slice((*byte)(unsafe.Pointer(info.idxStr)), len(res.IdxStr)+1) copy(idxStr, res.IdxStr) idxStr[len(idxStr)-1] = 0 // null-terminated string if res.AlreadyOrdered { info.orderByConsumed = C.int(1) } - info.estimatedCost = C.double(res.EstimatedCost) - info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) + // SQLite pre-initializes estimatedCost and estimatedRows with sensible + // defaults; overwriting them with the Go zero value would make every + // candidate plan look free and break query planning, so only pass + // values the implementation actually set. + if res.EstimatedCost > 0 { + info.estimatedCost = C.double(res.EstimatedCost) + } + if res.EstimatedRows > 0 { + var rows int64 + if res.EstimatedRows >= float64(math.MaxInt64) { + rows = math.MaxInt64 + } else if rows = int64(res.EstimatedRows); rows < 1 { + // A positive fractional estimate must not truncate to 0. + rows = 1 + } + info.estimatedRows = C.sqlite3_int64(rows) + } return nil } @@ -509,6 +517,9 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { func goVClose(pCursor unsafe.Pointer) *C.char { vtc := lookupHandle(pCursor).(*sqliteVTabCursor) err := vtc.vTabCursor.Close() + // The cursor is gone as far as SQLite is concerned regardless of the + // callback result, so release the handle either way. + deleteHandle(pCursor) if err != nil { return mPrintf("%s", err.Error()) } @@ -519,6 +530,7 @@ func goVClose(pCursor unsafe.Pointer) *C.char { func goMDestroy(pClientData unsafe.Pointer) { m := lookupHandle(pClientData).(*sqliteModule) m.module.DestroyModule() + deleteHandle(pClientData) } //export goVFilter @@ -531,7 +543,14 @@ func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int if err != nil { return mPrintf("%s", err.Error()) } - vals = append(vals, conv.Interface()) + + // work around for SQLITE_NULL + x := conv.Interface() + if z, ok := x.([]byte); ok && z == nil { + x = nil + } + + vals = append(vals, x) } err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) if err != nil { @@ -625,7 +644,15 @@ func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid } case argc > 1: - err = v.Update(vals[1], vals[2:]) + // Per the xUpdate contract argv[0] identifies the row being + // updated while argv[1] is its new rowid. VTabUpdater has no + // way to convey a rowid change, so reject it instead of + // silently updating values under the old rowid. + if vals[0] != vals[1] { + err = fmt.Errorf("virtual %s table %sdoes not support changing the rowid", vt.module.name, tname) + } else { + err = v.Update(vals[0], vals[2:]) + } } } @@ -725,5 +752,5 @@ func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { } return nil } - return nil + return fmt.Errorf("sqlite3: CreateModule requires a non-nil module") } diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3ext.h b/vendor/github.com/mattn/go-sqlite3/sqlite3ext.h index 33eef8af62..e54ccb3dc4 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3ext.h +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3ext.h @@ -376,7 +376,11 @@ struct sqlite3_api_routines { /* Version 3.51.0 and later */ int (*set_errmsg)(sqlite3*,int,const char*); int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); - + /* Version 3.52.0 and later */ + void (*str_truncate)(sqlite3_str*,int); + void (*str_free)(sqlite3_str*); + int (*carray_bind)(sqlite3_stmt*,int,void*,int,int,void(*)(void*)); + int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*); }; /* @@ -715,6 +719,11 @@ typedef int (*sqlite3_loadext_entry)( /* Version 3.51.0 and later */ #define sqlite3_set_errmsg sqlite3_api->set_errmsg #define sqlite3_db_status64 sqlite3_api->db_status64 +/* Version 3.52.0 and later */ +#define sqlite3_str_truncate sqlite3_api->str_truncate +#define sqlite3_str_free sqlite3_api->str_free +#define sqlite3_carray_bind sqlite3_api->carray_bind +#define sqlite3_carray_bind_v2 sqlite3_api->carray_bind_v2 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go index b061992063..670c25f90b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go @@ -27,15 +27,15 @@ func NewNatsKeyValue(c Config, log *zerolog.Logger) (jetstream.KeyValue, error) } opts := []nats.Option{ nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { - log.Error().Err(err).Msg("Disconnected from NATS. Trying to reconnect...") + log.Error().Err(err).Str("database", c.Database).Msg("Disconnected from NATS. Trying to reconnect...") }), nats.ReconnectHandler(func(nc *nats.Conn) { - log.Error().Msgf("Successfully reconnected to NATS at: %s", nc.ConnectedUrl()) + log.Error().Str("database", c.Database).Msgf("Successfully reconnected to NATS at: %s", nc.ConnectedUrl()) }), nats.ClosedHandler(func(nc *nats.Conn) { // Alright, it's time to give up. Send ourselves a SIGTERM to trigger a graceful // shutdown of the service. - log.Error().Msg("NATS connection closed permanently. Shutting down...") + log.Error().Str("database", c.Database).Msg("NATS connection closed permanently. Shutting down...") pid := os.Getpid() process, err := os.FindProcess(pid) diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go index db0d306441..cc8989d2d8 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/hybrid_backend.go @@ -500,9 +500,21 @@ func (b HybridBackend) Lock(n MetadataNode) (UnlockFunc, error) { mlock, err := lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600) if err != nil { if errors.Is(err, os.ErrNotExist) { - // create the parent directory - err = os.MkdirAll(filepath.Dir(metaLockPath), 0700) - if err != nil { + // The lock file's parent directories don't exist yet (or anymore). + // We may only create the space's metadata directories, never the + // space's base directory itself: locking a node (i.e. the space + // root) whose space has been removed (or has not been created yet) + // must not resurrect the spaca. + base := b.metadataPathFunc(n) + if base == "" { + return nil, err + } + // base is the space's metadata directory (e.g. /.oc-nodes), + // so its parent is the space's base directory. + if _, statErr := os.Stat(filepath.Dir(base)); statErr != nil { + return nil, statErr + } + if err = os.MkdirAll(filepath.Dir(metaLockPath), 0700); err != nil { return nil, err } mlock, err = lockedfile.OpenFile(metaLockPath, os.O_RDWR|os.O_CREATE, 0600) diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go index 1035bc1cd0..5348b620c7 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/async.go @@ -282,8 +282,10 @@ func (p AsyncPropagator) propagate(ctx context.Context, spaceID, nodeID string, attrs := node.Attributes{} + // lock parent before reading treesize or tree time + // we deliberately allow reading disabled spaces so that the metadata is always consistent (see https://github.com/opencloud-eu/reva/issues/747) _, subspan = tracer.Start(ctx, "node.LockAndReadNode") - n, unlock, err := node.LockAndReadNode(ctx, p.lookup, spaceID, nodeID, "", false, nil, false) + n, unlock, err := node.LockAndReadNode(ctx, p.lookup, spaceID, nodeID, "", true, nil, false) subspan.End() if err != nil { if n != nil && !n.Exists { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/sync.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/sync.go index d57e890508..99aed8eefe 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/sync.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator/sync.go @@ -92,7 +92,8 @@ func (p SyncPropagator) propagateItem(ctx context.Context, n *node.Node, sTime t attrs := node.Attributes{} // lock parent before reading treesize or tree time - n, unlock, err := node.LockAndReadNode(ctx, p.lookup, n.SpaceID, n.ParentID, "", false, n.SpaceRoot, false) + // we deliberately allow reading disabled spaces so that the metadata is always consistent (see https://github.com/opencloud-eu/reva/issues/747) + n, unlock, err := node.LockAndReadNode(ctx, p.lookup, n.SpaceID, n.ParentID, "", true, n.SpaceRoot, false) if err != nil { return nil, true, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go index 14e59c7d86..57904d2c94 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go @@ -252,6 +252,12 @@ func IsSpaceRoot(ri *storageprovider.ResourceInfo) bool { return f.GetOpaqueId() == s.GetOpaqueId() && f.GetSpaceId() == s.GetSpaceId() } +// IsProcessing checks if the given resource info is still being processed +// (e.g. postprocessing after an upload has not finished yet). +func IsProcessing(ri *storageprovider.ResourceInfo) bool { + return ReadPlainFromOpaque(ri.GetOpaque(), "status") == "processing" +} + func checkStatusCode(reason, message string, code rpc.Code) error { if code == rpc.Code_CODE_OK { return nil diff --git a/vendor/github.com/sethvargo/go-diceware/diceware/generate.go b/vendor/github.com/sethvargo/go-diceware/diceware/generate.go index a4c3df442d..c171e22f5d 100644 --- a/vendor/github.com/sethvargo/go-diceware/diceware/generate.go +++ b/vendor/github.com/sethvargo/go-diceware/diceware/generate.go @@ -4,10 +4,12 @@ import ( "crypto/rand" "fmt" "io" - "math" "math/big" ) +// ErrNumWordsNegative is returned when a negative number of words is requested. +var ErrNumWordsNegative = fmt.Errorf("number of words cannot be negative") + // sides is the number of sides on a die. var sides = big.NewInt(6) @@ -68,6 +70,10 @@ func NewGenerator(i *GeneratorInput) (*Generator, error) { // non-overlapping words, use a single invocation of the function and split the // resulting string list. func (g *Generator) Generate(numWords int) ([]string, error) { + if numWords < 0 { + return nil, ErrNumWordsNegative + } + if typ, ok := g.wordList.(WordListNumWordser); ok { if l := typ.NumWords(); numWords > l { return nil, fmt.Errorf("number of requested words (%d) cannot exceed the size of the wordlist (%d)", @@ -182,13 +188,13 @@ func (g *Generator) RollDie() (int, error) { func (g *Generator) RollWord(d int) (int, error) { var final int - for i := d; i > 0; i-- { + for range d { res, err := g.RollDie() if err != nil { return 0, err } - final += res * int(math.Pow(10, float64(i-1))) + final = final*10 + res } return final, nil diff --git a/vendor/github.com/sethvargo/go-password/password/generate.go b/vendor/github.com/sethvargo/go-password/password/generate.go index 45c2247c5a..522c1f940f 100644 --- a/vendor/github.com/sethvargo/go-password/password/generate.go +++ b/vendor/github.com/sethvargo/go-password/password/generate.go @@ -5,14 +5,13 @@ // if err != nil { // log.Fatal(err) // } -// log.Printf(res) +// log.Print(res) // // Most functions are safe for concurrent use. package password import ( "crypto/rand" - "errors" "fmt" "io" "math/big" @@ -47,19 +46,23 @@ const ( var ( // ErrExceedsTotalLength is the error returned with the number of digits and // symbols is greater than the total length. - ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length") + ErrExceedsTotalLength = fmt.Errorf("number of digits and symbols must be less than total length") // ErrLettersExceedsAvailable is the error returned with the number of letters // exceeds the number of available letters and repeats are not allowed. - ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed") + ErrLettersExceedsAvailable = fmt.Errorf("number of letters exceeds available letters and repeats are not allowed") // ErrDigitsExceedsAvailable is the error returned with the number of digits // exceeds the number of available digits and repeats are not allowed. - ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed") + ErrDigitsExceedsAvailable = fmt.Errorf("number of digits exceeds available digits and repeats are not allowed") // ErrSymbolsExceedsAvailable is the error returned with the number of symbols // exceeds the number of available symbols and repeats are not allowed. - ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed") + ErrSymbolsExceedsAvailable = fmt.Errorf("number of symbols exceeds available symbols and repeats are not allowed") + + // ErrNegativeInput is the error returned when the number of digits or symbols + // is negative. + ErrNegativeInput = fmt.Errorf("number of digits and symbols must not be negative") ) // Generator is the stateful generator which can be used to customize the list @@ -129,6 +132,10 @@ func NewGenerator(i *GeneratorInput) (*Generator, error) { // The algorithm is fast, but it's not designed to be performant; it favors // entropy over speed. This function is safe for concurrent use. func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) { + if numDigits < 0 || numSymbols < 0 { + return "", ErrNegativeInput + } + letters := g.lowerLetters if !noUpper { letters += g.upperLetters diff --git a/vendor/modules.txt b/vendor/modules.txt index 64f125c0d9..5ec9ecb45a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -93,7 +93,7 @@ github.com/alexedwards/argon2id # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 ## explicit github.com/amoghe/go-crypt -# github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op +# github.com/antithesishq/antithesis-sdk-go v0.7.2 ## explicit; go 1.24.0 github.com/antithesishq/antithesis-sdk-go/assert github.com/antithesishq/antithesis-sdk-go/internal @@ -1013,16 +1013,16 @@ github.com/magiconair/properties # github.com/mattermost/xml-roundtrip-validator v0.1.0 ## explicit; go 1.14 github.com/mattermost/xml-roundtrip-validator -# github.com/mattn/go-colorable v0.1.14 +# github.com/mattn/go-colorable v0.1.15 ## explicit; go 1.18 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.20 -## explicit; go 1.15 +# github.com/mattn/go-isatty v0.0.22 +## explicit; go 1.21 github.com/mattn/go-isatty -# github.com/mattn/go-runewidth v0.0.23 +# github.com/mattn/go-runewidth v0.0.24 ## explicit; go 1.20 github.com/mattn/go-runewidth -# github.com/mattn/go-sqlite3 v1.14.42 +# github.com/mattn/go-sqlite3 v1.14.49 ## explicit; go 1.21 github.com/mattn/go-sqlite3 # github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b @@ -1364,7 +1364,7 @@ github.com/opencloud-eu/icap-client # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.47.0 +# github.com/opencloud-eu/reva/v2 v2.48.0 ## explicit; go 1.25.8 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime @@ -1979,11 +1979,11 @@ github.com/sercand/kuberesolver/v5 # github.com/sergi/go-diff v1.4.0 ## explicit; go 1.13 github.com/sergi/go-diff/diffmatchpatch -# github.com/sethvargo/go-diceware v0.5.0 -## explicit; go 1.22 +# github.com/sethvargo/go-diceware v0.6.0 +## explicit; go 1.25 github.com/sethvargo/go-diceware/diceware -# github.com/sethvargo/go-password v0.3.1 -## explicit; go 1.21 +# github.com/sethvargo/go-password v0.4.0 +## explicit; go 1.25 github.com/sethvargo/go-password/password # github.com/shamaton/msgpack/v2 v2.4.1 ## explicit; go 1.20 @@ -2572,7 +2572,7 @@ google.golang.org/genproto/protobuf/field_mask google.golang.org/genproto/googleapis/api google.golang.org/genproto/googleapis/api/annotations google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa +# google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 ## explicit; go 1.25.0 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status From a541a4f26f7903f077b59b580c3acb713c555519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Mon, 3 Aug 2026 16:29:30 +0200 Subject: [PATCH 26/27] Do not check ignored paths --- opencloud/pkg/command/posixfs_consistency.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/opencloud/pkg/command/posixfs_consistency.go b/opencloud/pkg/command/posixfs_consistency.go index f822143bbc..8688a8824c 100644 --- a/opencloud/pkg/command/posixfs_consistency.go +++ b/opencloud/pkg/command/posixfs_consistency.go @@ -57,6 +57,9 @@ func (c *consistencyChecker) Check(paths []string) error { fmt.Printf("Checking space '%s'...\n", path) c.checkSpace(path) case contained: + if c.ignorer.IsIgnored(path) { + continue + } fmt.Printf("Checking '%s'...\n", path) c.checkEntity(path) default: From b5140f6d736d5e024568e08155e1922ec5932e0b Mon Sep 17 00:00:00 2001 From: Pascal Bleser Date: Wed, 29 Jul 2026 11:18:39 +0200 Subject: [PATCH 27/27] feat(posixfs): #3182 add basepath option in the "posixfs scan" command * add support for specifying a set of resources when running the posixfs scan command scanning, or a singular file to scan, as opposed * refactors the resource walking function implemented in https://github.com/opencloud-eu/opencloud/pull/3220 in order to reuse it for the posixfs scan command * implements https://github.com/opencloud-eu/opencloud/issues/3182 * add --halt-on-error flag * collect errors and fail command when more than one error occured --- opencloud/pkg/command/posixfs.go | 258 +++++++++++++------ opencloud/pkg/command/posixfs_consistency.go | 42 ++- 2 files changed, 202 insertions(+), 98 deletions(-) diff --git a/opencloud/pkg/command/posixfs.go b/opencloud/pkg/command/posixfs.go index 751e3db1be..01bab8b75b 100644 --- a/opencloud/pkg/command/posixfs.go +++ b/opencloud/pkg/command/posixfs.go @@ -55,11 +55,23 @@ func init() { // scanCmd performs a posixfs id cache warmup scan func scanCmd(ocCfg *config.Config) *cobra.Command { - cmd := &cobra.Command{ - Use: "scan", + scanCmd := &cobra.Command{ + Use: "scan [path ...]", Short: "Perform a filesystem scan and update the ID and filemetadata cache", - PreRunE: func(cmd *cobra.Command, args []string) error { + Long: `Perform a filesystem scan and update the ID and filemetadata cache. + +You can specify one or more paths to limit the scope of the scan. +If no path is provided, the whole storage is checked, starting at the storage root directory. + +The provided arguments determines the scope of the check: + - a storage root: the whole storage (all personal and project spaces) is scanned + - a space root: only that space is scanned + - a file or directory: only that single resource is scanned (and its children, if it is a directory) +Any specified file or directory must be underneath the storage root directory and if that is not the case, +the command is aborted with an error before performing any scanning.`, + Args: cobra.ArbitraryArgs, + PreRunE: func(cmd *cobra.Command, args []string) error { if err := parser.ParseConfig(ocCfg, true); err != nil { return configlog.ReturnError(err) } @@ -76,91 +88,110 @@ func scanCmd(ocCfg *config.Config) *cobra.Command { os.Exit(1) } + haltOnError, err := cmd.Flags().GetBool("halt-on-error") + if err != nil { + return err + } + storageRoot := cfg.Drivers.Posix.Root - root := storageRoot - defaultRoot := true - if v, err := cmd.Flags().GetString("basepath"); err != nil { - fmt.Fprintf(os.Stderr, "Failed to parse command-line parameter '--basepath': %v\n", err) - os.Exit(1) - } else if v != "" { - root = v - if !filepath.IsAbs(v) { - if v, err = filepath.Abs(v); err != nil { - fmt.Fprintf(os.Stderr, "Failed to make the basepath mentioned using '--basepath' absolute: %v\n", err) - os.Exit(1) - } else { - root = v + paths := []string{storageRoot} + if len(args) > 0 { + paths = []string{} + for _, v := range args { + path := v + if !filepath.IsAbs(path) { + if v, err := filepath.Abs(path); err != nil { + fmt.Fprintf(os.Stderr, "Failed to make the specified path %q absolute: %v\n", v, err) + os.Exit(1) + } else { + path = v + } } - } else { - root = v + // not ensuring whether the path is under the storage root here, will be done when iterating over them + path = filepath.Clean(path) + paths = append(paths, path) } - root = filepath.Clean(root) - defaultRoot = false } - // ensure that, if a basepath has been indicated, it is under the storage root - if !defaultRoot { - if contained, err := filepathx.IsSameOrContainedBy(storageRoot, root); err != nil { - fmt.Fprintf(os.Stderr, "Failed to determine whether the specified basepath %q is contained by the storage root %q: %v\n", root, storageRoot, err) - os.Exit(1) - } else if !contained { - fmt.Fprintf(os.Stderr, "The specified basepath %q is neither the storage root %q, nor a subdirectory thereof, nor a file underneath it\n", root, storageRoot) + var scan func(path string) error = nil + { + // We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards + drivers := revaconfig.StorageProviderDrivers(cfg) + drivers["posix"] = revaconfig.Posix(cfg, false, false) + + var fsStream events.Stream + var err error + fsStream, err = event.NewStream(cfg) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create event stream for posix driver: %v\n", err) os.Exit(1) } - } - - // We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards - drivers := revaconfig.StorageProviderDrivers(cfg) - drivers["posix"] = revaconfig.Posix(cfg, false, false) + log := logger("posixfs") - var fsStream events.Stream - var err error - fsStream, err = event.NewStream(cfg) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create event stream for posix driver: %v\n", err) - os.Exit(1) - } - log := logger("posixfs") - - if !defaultRoot { - log = log.With().Str("basepath", root).Logger() - } + f, ok := registry.NewFuncs["posix"] + if !ok { + fmt.Fprintf(os.Stderr, "posix driver not found in registry\n") + os.Exit(1) + } - f, ok := registry.NewFuncs["posix"] - if !ok { - fmt.Fprintf(os.Stderr, "posix driver not found in registry\n") - os.Exit(1) - } + fs, err := f(drivers["posix"].(map[string]any), fsStream, &log) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to initialize filesystem driver '%s': %v\n", cfg.Driver, err) + return err + } - fs, err := f(drivers["posix"].(map[string]any), fsStream, &log) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to initialize filesystem driver '%s': %v\n", cfg.Driver, err) - return err - } + cacher, ok := fs.(IDCacher) + if !ok { + fmt.Fprintf(os.Stderr, "The posix driver does not expose WarmupIDCache.\n") + os.Exit(1) + } - cacher, ok := fs.(IDCacher) - if !ok { - fmt.Fprintf(os.Stderr, "The posix driver does not expose WarmupIDCache.\n") - os.Exit(1) + scan = func(path string) error { + err := cacher.WarmupIDCache(path, true, false) + if err != nil { + logFailure("Error scanning path '%s': %v", path, err) + } + return err + } } - if defaultRoot { - fmt.Println("Starting posixfs scan...") + errors := processPosixFsResources(paths, !haltOnError, + func(path string) error { + fmt.Println("Scanning personal spaces...") + return scan(path) + }, + func(path string) error { + fmt.Println("Scanning project spaces...") + return scan(path) + }, + func(path string) error { + fmt.Printf("Scanning space '%s'...\n", path) + return scan(path) + }, + func(path string) error { + fmt.Printf("Scanning '%s'...\n", path) + return scan(path) + }, + ) + + if len(errors) == 0 { + fmt.Println("Scan completed successfully.") + return nil } else { - fmt.Printf("Starting posixfs scan at '%s'...\n", root) - } - err = cacher.WarmupIDCache(root, true, false) - if err != nil { - fmt.Fprintf(os.Stderr, "Scan failed: %v\n", err) - return err + plural := "s" + if len(errors) == 1 { + plural = "" + } + verb := "completed" + if haltOnError { + verb = "aborted" + } + return fmt.Errorf("scan %s with %d error%s", verb, len(errors), plural) } - - fmt.Println("Scan completed successfully.") - return nil }, } - cmd.Flags().StringP("basepath", "p", "", "the root under which to scan files, which may be a directory or a file (when omitted, detaults to using the storage root)") - return cmd + scanCmd.Flags().BoolP("halt-on-error", "E", false, "Halt at once when an error occurs when processing one of the paths (default behaviour is to keep going and attempt to process all paths).") + return scanCmd } // consistencyCmd returns a command to check the consistency of the posixfs storage. @@ -195,7 +226,10 @@ The provided arguments determines the scope of the check: args = []string{cfg.Drivers.Posix.Root} } log := logger("posixfs") - recalculateChecksums, _ := cmd.Flags().GetBool("fix-checksums") + recalculateChecksums, err := cmd.Flags().GetBool("fix-checksums") + if err != nil { + return err + } drivers := revaconfig.StorageProviderDrivers(cfg) drivers["posix"] = revaconfig.Posix(cfg, false, false) @@ -215,7 +249,6 @@ The provided arguments determines the scope of the check: }, } consCmd.Flags().Bool("fix-checksums", false, "Recalculate and fix the file checksums. This reads every file and can be slow on large storages.") - return consCmd } @@ -254,3 +287,82 @@ func isSpaceRoot(path string) bool { spaceID, err := xattr.Get(path, prefixes.SpaceIDAttr) return err == nil && len(spaceID) > 0 } + +// iterates over a list of paths and processes them all, using the appropriate function +// depending on the type of resource +// +// note that whenever an error occurs, it collects that error and continues processing +// subsequent paths, and then returns a slice of errors at the end (or an empty slice +// if no errors occured) +func processPosixFsResources(paths []string, + keepGoing bool, + personalSpaceDir func(string) error, + projectSpaceDir func(string) error, + spaceRoot func(string) error, + entity func(string) error, +) []error { + // no need to guard this with a mutex for now, since the implementation is not parallelized + errors := []error{} + + for _, path := range paths { + rootPath, err := findStorageRoot(path) + if err != nil { + errors = append(errors, err) + logFailure("error: %s", err) + if keepGoing { + continue + } else { + return errors + } + } + path = filepath.Clean(path) + if _, err := os.Stat(path); err != nil { + errors = append(errors, err) + logFailure("error accessing '%s': %w", path, err) + if keepGoing { + continue + } else { + return errors + } + } + contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) + + switch { + case path == rootPath: + if err := personalSpaceDir(filepath.Join(path, "users")); err != nil { + errors = append(errors, err) + if !keepGoing { + return errors + } + } + if err := projectSpaceDir(filepath.Join(path, "projects")); err != nil { + errors = append(errors, err) + if !keepGoing { + return errors + } + } + case isSpaceRoot(path): + if err := spaceRoot(path); err != nil { + errors = append(errors, err) + if !keepGoing { + return errors + } + } + case contained: + if err := entity(path); err != nil { + errors = append(errors, err) + if !keepGoing { + return errors + } + } + default: + err := fmt.Errorf("error: the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) + errors = append(errors, err) + logFailure(err.Error()) + if !keepGoing { + return errors + } + } + } + return errors +} diff --git a/opencloud/pkg/command/posixfs_consistency.go b/opencloud/pkg/command/posixfs_consistency.go index 8688a8824c..939a51d185 100644 --- a/opencloud/pkg/command/posixfs_consistency.go +++ b/opencloud/pkg/command/posixfs_consistency.go @@ -14,7 +14,6 @@ import ( "strings" "time" - "github.com/opencloud-eu/opencloud/pkg/x/path/filepathx" storageUsersConfig "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config" "github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore" "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes" @@ -35,38 +34,31 @@ type consistencyChecker struct { // given path determines the scope of the check: the whole storage, a single // space or a single entity within a space. func (c *consistencyChecker) Check(paths []string) error { - for _, path := range paths { - rootPath, err := findStorageRoot(path) - if err != nil { - return err - } - path = filepath.Clean(path) - if _, err := os.Stat(path); err != nil { - return fmt.Errorf("error accessing '%s': %w", path, err) - } - contained, _ := filepathx.IsSameOrContainedBy(rootPath, path) - - switch { - case path == rootPath: + _ = processPosixFsResources(paths, true, + func(path string) error { fmt.Println("Checking personal spaces...") - c.checkSpaces(filepath.Join(path, "users")) - + c.checkSpaces(path) + return nil + }, + func(path string) error { fmt.Println("Checking project spaces...") - c.checkSpaces(filepath.Join(path, "projects")) - case isSpaceRoot(path): + c.checkSpaces(path) + return nil + }, + func(path string) error { fmt.Printf("Checking space '%s'...\n", path) c.checkSpace(path) - case contained: + return nil + }, + func(path string) error { if c.ignorer.IsIgnored(path) { - continue + return nil } fmt.Printf("Checking '%s'...\n", path) c.checkEntity(path) - default: - return fmt.Errorf("the provided path '%s' is neither a space root nor contained by the storage root '%s'", path, rootPath) - } - } - + return nil + }, + ) if c.restartRequired { fmt.Println("\n\n ⚠️ Please restart your openCloud instance to apply changes.") }