From cef0a40fa50154a5bf2f620a581e7760a594d563 Mon Sep 17 00:00:00 2001 From: grokspawn Date: Thu, 23 Jul 2026 14:52:18 -0500 Subject: [PATCH] chore: bump o-f deps Signed-off-by: grokspawn --- go.mod | 22 +- go.sum | 48 +- .../containerd/containerd/version/version.go | 2 +- vendor/github.com/docker/cli/AUTHORS | 16 +- vendor/github.com/google/cel-go/cel/env.go | 20 +- .../github.com/google/cel-go/cel/folding.go | 6 +- .../github.com/google/cel-go/cel/library.go | 40 +- .../github.com/google/cel-go/cel/options.go | 8 + .../github.com/google/cel-go/cel/program.go | 209 +---- vendor/github.com/google/cel-go/cel/prompt.go | 11 +- .../github.com/google/cel-go/checker/cost.go | 16 +- .../cel-go/common/containers/container.go | 15 +- .../cel-go/common/functions/functions.go | 32 +- .../google/cel-go/common/runes/buffer.go | 80 +- .../github.com/google/cel-go/common/source.go | 23 + .../google/cel-go/common/types/timestamp.go | 3 + .../google/cel-go/common/types/unknown.go | 14 + .../github.com/google/cel-go/ext/BUILD.bazel | 3 + vendor/github.com/google/cel-go/ext/README.md | 14 + .../github.com/google/cel-go/ext/bindings.go | 52 +- .../github.com/google/cel-go/ext/encoders.go | 45 +- vendor/github.com/google/cel-go/ext/lists.go | 33 +- .../github.com/google/cel-go/ext/network.go | 619 ++++++++++++++ .../github.com/google/cel-go/ext/strings.go | 28 +- .../google/cel-go/interpreter/BUILD.bazel | 2 + .../google/cel-go/interpreter/activation.go | 25 +- .../google/cel-go/interpreter/attributes.go | 26 +- .../google/cel-go/interpreter/decorators.go | 36 +- .../google/cel-go/interpreter/frame.go | 327 ++++++++ .../cel-go/interpreter/interpretable.go | 503 +++++++---- .../google/cel-go/interpreter/interpreter.go | 120 +-- .../google/cel-go/interpreter/planner.go | 62 +- .../google/cel-go/interpreter/runtimecost.go | 89 +- .../google/cel-go/parser/unparser.go | 2 +- .../joelanford/ignore/.golangci.yml | 12 +- vendor/github.com/joelanford/ignore/ignore.go | 2 +- .../mattn/go-sqlite3/.coderabbit.yaml | 15 + vendor/github.com/mattn/go-sqlite3/README.md | 13 +- .../github.com/mattn/go-sqlite3/callback.go | 115 ++- .../mattn/go-sqlite3/sqlite3-binding.c | 781 ++++++++++++------ .../mattn/go-sqlite3/sqlite3-binding.h | 16 +- vendor/github.com/mattn/go-sqlite3/sqlite3.go | 224 +++-- .../go-sqlite3/sqlite3_load_extension.go | 6 +- .../go-sqlite3/sqlite3_opt_preupdate_hook.go | 8 +- .../mattn/go-sqlite3/sqlite3_opt_serialize.go | 3 + .../mattn/go-sqlite3/sqlite3_opt_vtable.go | 3 + .../api/pkg/manifests/bundleloader.go | 3 + .../pkg/manifests/packagemanifestloader.go | 6 + .../pkg/lib/bundle/chartutil.go | 2 +- .../pkg/lib/bundle/generate.go | 14 +- .../operator-registry/pkg/sqlite/load.go | 33 + vendor/go.etcd.io/bbolt/.gitattributes | 4 + vendor/go.etcd.io/bbolt/.go-version | 2 +- vendor/go.etcd.io/bbolt/.golangci.yaml | 40 + vendor/go.etcd.io/bbolt/Makefile | 26 +- vendor/go.etcd.io/bbolt/OWNERS | 1 + vendor/go.etcd.io/bbolt/README.md | 18 +- vendor/go.etcd.io/bbolt/bucket.go | 24 +- vendor/go.etcd.io/bbolt/code-of-conduct.md | 3 + vendor/go.etcd.io/bbolt/db.go | 159 ++-- vendor/go.etcd.io/bbolt/errors/errors.go | 3 + .../go.etcd.io/bbolt/internal/common/page.go | 38 +- .../bbolt/internal/freelist/hashmap.go | 46 +- .../bbolt/internal/freelist/shared.go | 6 +- vendor/go.etcd.io/bbolt/tx.go | 18 +- vendor/go.etcd.io/bbolt/tx_check.go | 9 +- vendor/golang.org/x/mod/modfile/read.go | 2 +- vendor/golang.org/x/mod/modfile/rule.go | 6 +- vendor/modules.txt | 28 +- 69 files changed, 3028 insertions(+), 1212 deletions(-) create mode 100644 vendor/github.com/google/cel-go/ext/network.go create mode 100644 vendor/github.com/google/cel-go/interpreter/frame.go create mode 100644 vendor/github.com/mattn/go-sqlite3/.coderabbit.yaml create mode 100644 vendor/go.etcd.io/bbolt/.gitattributes create mode 100644 vendor/go.etcd.io/bbolt/.golangci.yaml create mode 100644 vendor/go.etcd.io/bbolt/code-of-conduct.md diff --git a/go.mod b/go.mod index aae8e0944b..30ed488d47 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,8 @@ require ( github.com/openshift/api v0.0.0-20260204104751-e09e5a4ebcd0 github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a - github.com/operator-framework/api v0.44.0 - github.com/operator-framework/operator-registry v1.72.0 + github.com/operator-framework/api v0.45.0 + github.com/operator-framework/operator-registry v1.73.0 github.com/otiai10/copy v1.14.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.24.0 @@ -68,7 +68,7 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/cgroups/v3 v3.1.2 // indirect - github.com/containerd/containerd v1.7.33 // indirect + github.com/containerd/containerd v1.7.34 // indirect github.com/containerd/containerd/api v1.10.0 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect @@ -81,7 +81,7 @@ require ( github.com/containers/ocicrypt v1.3.0 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/docker/cli v29.5.3+incompatible // indirect + github.com/docker/cli v29.6.2+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.7 // indirect github.com/docker/go-connections v0.7.0 // indirect @@ -116,7 +116,7 @@ require ( github.com/golang-migrate/migrate/v4 v4.19.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.28.1 // indirect + github.com/google/cel-go v0.29.2 // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/google/uuid v1.6.0 // indirect @@ -129,15 +129,15 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.8 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/joelanford/ignore v0.1.1 // indirect + github.com/joelanford/ignore v0.1.2 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.19.0 // indirect github.com/kylelemons/godebug v1.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-sqlite3 v1.14.45 // indirect + github.com/mattn/go-sqlite3 v1.14.48 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/moby/client v0.4.1 // indirect + github.com/moby/moby/client v0.5.0 // indirect github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.6.0 // indirect @@ -154,7 +154,7 @@ require ( github.com/prometheus/procfs v0.21.1 // indirect github.com/tidwall/btree v1.8.1 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/bbolt v1.4.3 // indirect + go.etcd.io/bbolt v1.5.0 // indirect go.etcd.io/etcd/api/v3 v3.6.8 // indirect go.etcd.io/etcd/client/pkg/v3 v3.6.8 // indirect go.etcd.io/etcd/client/v3 v3.6.8 // indirect @@ -169,14 +169,14 @@ require ( go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.podman.io/common v0.68.0 // indirect + go.podman.io/common v0.68.1 // indirect go.podman.io/storage v1.63.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect golang.org/x/crypto v0.54.0 // indirect golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect - golang.org/x/mod v0.37.0 // indirect + golang.org/x/mod v0.38.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect diff --git a/go.sum b/go.sum index 9f70b892a7..37534b3ea4 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4= github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= -github.com/containerd/containerd v1.7.33 h1:iAkYGC/ifR/V+0eR4iXWHNGYUF0DF2PmGV5iz4Irj5M= -github.com/containerd/containerd v1.7.33/go.mod h1:gSbSCVjPCdkfJCjyrzz7aRC+xFlqVbatNpfHfVCYGUM= +github.com/containerd/containerd v1.7.34 h1:Q35B4FUECxcoaMz9QrOlqp+0s72w8/0NWawVMhVdf5g= +github.com/containerd/containerd v1.7.34/go.mod h1:ozI//0TomTCLPhQREnx0IXDIQMg+Fk7yTtg9fNvU8EQ= github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= @@ -76,8 +76,8 @@ github.com/distribution/distribution/v3 v3.1.1 h1:KUbk7C8CfaLXy8kbf/hGq9cad/wCoL github.com/distribution/distribution/v3 v3.1.1/go.mod h1:d7lXwZpph0bVcOj4Aqn0nMrWHIwRQGdiV5TLeI+/w6Y= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v29.5.3+incompatible h1:nbEFfz774vBwQ5KRYv7c/AghjReqnGISvrRhzjV0evs= -github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.6.2+incompatible h1:/bjePvcbbFTnRrMfWJBY7AjfICdsiLVgHn6LwTVOcqw= +github.com/docker/cli v29.6.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.9.7 h1:jaPIxEIDz5bQeghNAdzz0ETwMMnM4vzjZlxz3pWP4JA= @@ -203,8 +203,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM= -github.com/google/cel-go v0.28.1/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/cel-go v0.29.2 h1:ZtDxkeiMmz0mxbKDYiNkE5Lk7V5edMRcaaDf2jX002k= +github.com/google/cel-go v0.29.2/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -254,8 +254,8 @@ github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo79 github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/joelanford/ignore v0.1.1 h1:vKky5RDoPT+WbONrbQBgOn95VV/UPh4ejlyAbbzgnQk= -github.com/joelanford/ignore v0.1.1/go.mod h1:8eho/D8fwQ3rIXrLwE23AaeaGDNXqLE9QJ3zJ4LIPCw= +github.com/joelanford/ignore v0.1.2 h1:n/9VnxthX0NVx8VOdB6quLFOV4AcAGqaZOonrXt55HU= +github.com/joelanford/ignore v0.1.2/go.mod h1:45jOCHVmjfCK/1ZhM05iog2HRkmfv22VqBUgPl+e2iA= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= @@ -284,8 +284,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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.45 h1:6KA/spDguL3KV8rnybG7ezSaE4SeMR3KC9VbUoAQaIk= -github.com/mattn/go-sqlite3 v1.14.45/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ= +github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs= +github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 h1:V23nK2R2B63g2GhygF9zVGpnigmhvoZoH8d0hrZwMGY= github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2/go.mod h1:Mr897yU9FmyKaQDPtRlVKibrjz40XXyOHUfyZBPSyZU= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= @@ -296,8 +296,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4 github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= -github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= +github.com/moby/moby/client v0.5.0 h1:5XhyPk2fuOWf6RlSFa3MkIIgDZkF25xToXW8Q/BH7cc= +github.com/moby/moby/client v0.5.0/go.mod h1:rcVpF8ncl9vo5gaIBdol6CnbEtSj1uxMvEV/UrykF/s= github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= @@ -336,10 +336,10 @@ github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 h1:6rd4zSo2UaW github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13/go.mod h1:YvOmPmV7wcJxpfhTDuFqqs2Xpb3M3ovsM6Qs/i2ptq4= github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a h1:YLnZtVfqGUfTbQ+M06QAslEmP4WrnRoPrk4AtoBJdm8= github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a/go.mod h1:DCRz1EgdayEmr9b6KXKDL+DWBN0rGHu/VYADeHzPoOk= -github.com/operator-framework/api v0.44.0 h1:UPPNVVI2HJyfH17cnL/8PADV4DCaNLqfL08e+AyYrO8= -github.com/operator-framework/api v0.44.0/go.mod h1:H46wpymo4j3kJZraarbuBUPLF5OzTCw4u0n2xb9sMNQ= -github.com/operator-framework/operator-registry v1.72.0 h1:Gvnsx3kLykduUga1bw3PEFg+c4u1VJB7ezG5PO5pByQ= -github.com/operator-framework/operator-registry v1.72.0/go.mod h1:ta17GdpLAXdDfz2RgBxvhKmfHMpetsTYj+To5OcfChc= +github.com/operator-framework/api v0.45.0 h1:hkROwtsLH3oszp4IW+WsXEFSDgveSahHI7DKStOtrUI= +github.com/operator-framework/api v0.45.0/go.mod h1:IQ4uuISTiIhV09oAurJSGD4KabayhY5nV6k1XmA235M= +github.com/operator-framework/operator-registry v1.73.0 h1:ZlgunOFoo0z2aR1teQs54sOkzBdEMmJKF0W6iQXNW90= +github.com/operator-framework/operator-registry v1.73.0/go.mod h1:3nfBxNTdxK1rKTBUSilyhLNHABv1N0HUVDpX08eaM58= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= @@ -429,8 +429,8 @@ github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chq github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= -go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU= +go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk= go.etcd.io/etcd/api/v3 v3.6.8 h1:gqb1VN92TAI6G2FiBvWcqKtHiIjr4SU2GdXxTwyexbM= go.etcd.io/etcd/api/v3 v3.6.8/go.mod h1:qyQj1HZPUV3B5cbAL8scG62+fyz5dSxxu0w8pn28N6Q= go.etcd.io/etcd/client/pkg/v3 v3.6.8 h1:Qs/5C0LNFiqXxYf2GU8MVjYUEXJ6sZaYOz0zEqQgy50= @@ -493,8 +493,8 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09 go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -go.podman.io/common v0.68.0 h1:6V8nZS33vLTPC047RfSGxARgS/Ui6CQtgdZXLo18qAc= -go.podman.io/common v0.68.0/go.mod h1:zVzufHkRpLueF6NW6N+fAs1C2METdzYcfD9zuw+oJKA= +go.podman.io/common v0.68.1 h1:y8NoHLidghpgHtWtwB9+pN7cHhIDe33g1sB4RFCRvKI= +go.podman.io/common v0.68.1/go.mod h1:zVzufHkRpLueF6NW6N+fAs1C2METdzYcfD9zuw+oJKA= go.podman.io/image/v5 v5.40.0 h1:gNQvj343Eb4juCitUBkuDz1T82Zpp6nhgMEXzNfCges= go.podman.io/image/v5 v5.40.0/go.mod h1:qgXf1abXJ+2l01pL8+CljaMKryeo6ahaHO7H51ooKIc= go.podman.io/storage v1.63.0 h1:bj/pAWFhChbuBmejzno0iQLhU7FevGVXepRXm5pFGeA= @@ -524,8 +524,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -663,8 +663,8 @@ k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98= k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 h1:kBawHLSnx/mYHmRnNUf9d4CpjREbeZuxoSGOX/J+aYM= k8s.io/utils v0.0.0-20260319190234-28399d86e0b5/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= -oras.land/oras-go/v2 v2.6.1 h1:bonOEkjLfp8tt6qXWRRWP6p1F+9octchOf2EqnWB4Zs= -oras.land/oras-go/v2 v2.6.1/go.mod h1:dhtFrFOuZuDtAVeZ9FUnaa5zfzplG3ZnFX9/uH1J/Yk= +oras.land/oras-go/v2 v2.6.2 h1:N04RXngAp1LJKTG6ifz3xHPipasEkWr+hFmInja5YKo= +oras.land/oras-go/v2 v2.6.2/go.mod h1:PlTtg4JTDJkDe8yVHpM2wz7/YDc00GVas+i4jAW2TZ4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4= diff --git a/vendor/github.com/containerd/containerd/version/version.go b/vendor/github.com/containerd/containerd/version/version.go index ab00acab9d..c4007b0d06 100644 --- a/vendor/github.com/containerd/containerd/version/version.go +++ b/vendor/github.com/containerd/containerd/version/version.go @@ -23,7 +23,7 @@ var ( Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.7.33+unknown" + Version = "1.7.34+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. diff --git a/vendor/github.com/docker/cli/AUTHORS b/vendor/github.com/docker/cli/AUTHORS index accbf6c52b..c42a3f8f4f 100644 --- a/vendor/github.com/docker/cli/AUTHORS +++ b/vendor/github.com/docker/cli/AUTHORS @@ -43,6 +43,7 @@ Alexander Larsson Alexander Morozov Alexander Ryabov Alexandre González +Alexandre Levavasseur Alexandre Vallières-Lagacé Alexey Igrychev Alexis Couvreur @@ -161,6 +162,7 @@ Chen Chuanliang Chen Hanxiao Chen Mingjie Chen Qiu +Ching Wei Kang <164879897+WilliamK112@users.noreply.github.com> Chris Chinchilla Chris Couzens Chris Gavin @@ -366,7 +368,7 @@ Hugo Gabriel Eyherabide huqun Huu Nguyen Hyzhou Zhy -Iain MacDonald +Iain MacDonald Iain Samuel McLean Elder Ian Campbell Ian Philpot @@ -552,6 +554,7 @@ Ludovic Temgoua Abanda Luis Henrique Mulinari Luka Hartwig Lukas Heeren +Lukas Michael Lukasz Zajaczkowski Luo Jiyin Lydell Manganti @@ -562,6 +565,7 @@ Maciej Kalisz Madhav Puri Madhu Venugopal Madhur Batra +Mahesh Thakur Malte Janduda Manjunath A Kumatagi Mansi Nahar @@ -589,10 +593,12 @@ Mathieu Rollet Matt Gucci Matt Robenolt Matteo Orefice +Matteo Panzeri Matthew Heon Matthieu Hauglustaine Matthieu MOREL Mauro Porras P +Max Morozov Max Shytikov Max-Julian Pogner Maxime Petazzoni @@ -617,6 +623,7 @@ Michael West Michael Zampani Michal Minář Michał Czeraszkiewicz +Mickael Emirkanian Miguel Angel Alvarez Cabrerizo Mihai Borobocea Mihuleacc Sergiu @@ -638,6 +645,7 @@ Mohammad Banikazemi Mohammad Hossein Mohammed Aaqib Ansari Mohammed Aminu Futa +Mohammed Thaha Mohini Anne Dsouza Moorthy RS Morgan Bauer @@ -689,6 +697,7 @@ Olli Janatuinen Oscar Wieman Otto Kekäläinen Ovidio Mallo +Park Jaeon Pascal Borreli Patrick Böänziger Patrick Daigle <114765035+pdaig@users.noreply.github.com> @@ -715,6 +724,7 @@ Peter Jaffe Peter Kehl Peter Nagy Peter Salvatore +Peter Valdemar Mørch Peter Waller Phil Estes Philip Alexander Etling @@ -739,6 +749,7 @@ Ray Tsang Reficul Remy Suen Renaud Gaubert +René Hermenau Ricardo N Feliciano Rich Moyse Richard Chen Zheng <58443436+rchenzheng@users.noreply.github.com> @@ -789,6 +800,7 @@ Scott Collier Sean Christopherson Sean Rodman Sebastiaan van Stijn +Seiya Miyata Sergey Tryuber Serhat Gülçiçek Sevki Hasirci @@ -889,8 +901,10 @@ Umesh Yadav Vaclav Struhar Valentin Lorentz Vardan Pogosian +Varun Hotani Venkateswara Reddy Bukkasamudram Veres Lajos +Vibhu Anan Victor Vieux Victoria Bialas Viktor Stanchev diff --git a/vendor/github.com/google/cel-go/cel/env.go b/vendor/github.com/google/cel-go/cel/env.go index e2de2ff6f8..7139e415ea 100644 --- a/vendor/github.com/google/cel-go/cel/env.go +++ b/vendor/github.com/google/cel-go/cel/env.go @@ -436,6 +436,15 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) { return ast, nil } +// configuredExpressionSizeLimit returns the effective expression size code point limit. +// A zero value means "use the parser default". +func (e *Env) configuredExpressionSizeLimit() int { + if l := e.limits[limitCodePointSize]; l != 0 { + return l + } + return 100_000 +} + // Compile combines the Parse and Check phases CEL program compilation to produce an Ast and // associated issues. // @@ -445,7 +454,11 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) { // // Note, for parse-only uses of CEL use Parse. func (e *Env) Compile(txt string) (*Ast, *Issues) { - return e.CompileSource(common.NewTextSource(txt)) + src, err := common.NewTextSourceWithLimit(txt, e.configuredExpressionSizeLimit()) + if err != nil { + return nil, ErrorAsIssues(err) + } + return e.CompileSource(src) } // CompileSource combines the Parse and Check phases CEL program compilation to produce an Ast and @@ -650,7 +663,10 @@ func (e *Env) Validators() []ASTValidator { // This form of Parse creates a Source value for the input `txt` and forwards to the // ParseSource method. func (e *Env) Parse(txt string) (*Ast, *Issues) { - src := common.NewTextSource(txt) + src, err := common.NewTextSourceWithLimit(txt, e.configuredExpressionSizeLimit()) + if err != nil { + return nil, ErrorAsIssues(err) + } return e.ParseSource(src) } diff --git a/vendor/github.com/google/cel-go/cel/folding.go b/vendor/github.com/google/cel-go/cel/folding.go index d1ea6b19db..0cff9517a4 100644 --- a/vendor/github.com/google/cel-go/cel/folding.go +++ b/vendor/github.com/google/cel-go/cel/folding.go @@ -97,7 +97,7 @@ func (opt *constantFoldingOptimizer) Optimize(ctx *OptimizerContext, a *ast.AST) continue } // Late-bound function calls cannot be folded. - if fold.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, a, fold) { + if fold.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, fold) { continue } // Otherwise, assume all context is needed to evaluate the expression. @@ -168,7 +168,7 @@ func (opt *constantFoldingOptimizer) tryFold(ctx *OptimizerContext, a *ast.AST, return nil } -func isLateBoundFunctionCall(ctx *OptimizerContext, a *ast.AST, expr ast.Expr) bool { +func isLateBoundFunctionCall(ctx *OptimizerContext, expr ast.Expr) bool { call := expr.AsCall() function := ctx.Functions()[call.FunctionName()] if function == nil { @@ -518,7 +518,7 @@ func (opt *constantFoldingOptimizer) constantExprMatcher(ctx *OptimizerContext, constantExprs = false } // Late-bound function calls cannot be folded. - if e.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, a, e) { + if e.Kind() == ast.CallKind && isLateBoundFunctionCall(ctx, e) { constantExprs = false } }) diff --git a/vendor/github.com/google/cel-go/cel/library.go b/vendor/github.com/google/cel-go/cel/library.go index 3c8b6ba343..332eb3f170 100644 --- a/vendor/github.com/google/cel-go/cel/library.go +++ b/vendor/github.com/google/cel-go/cel/library.go @@ -590,7 +590,7 @@ func (lib *optionalLib) CompileOptions() []EnvOption { // ProgramOptions implements the Library interface method. func (lib *optionalLib) ProgramOptions() []ProgramOption { return []ProgramOption{ - CustomDecorator(decorateOptionalOr), + CustomDecoratorV2(decorateOptionalOr), } } @@ -683,7 +683,7 @@ func EnableErrorOnBadPresenceTest(value bool) EnvOption { return features(featureEnableErrorOnBadPresenceTest, value) } -func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, error) { +func decorateOptionalOr(i interpreter.InterpretableV2) (interpreter.InterpretableV2, error) { call, ok := i.(interpreter.InterpretableCall) if !ok { return i, nil @@ -720,8 +720,8 @@ func decorateOptionalOr(i interpreter.Interpretable) (interpreter.Interpretable, // the second optional expression is evaluated and returned. type evalOptionalOr struct { id int64 - lhs interpreter.Interpretable - rhs interpreter.Interpretable + lhs interpreter.InterpretableV2 + rhs interpreter.InterpretableV2 } // ID implements the Interpretable interface method. @@ -729,11 +729,9 @@ func (opt *evalOptionalOr) ID() int64 { return opt.id } -// Eval evaluates the left-hand side optional to determine whether it contains a value, else -// proceeds with the right-hand side evaluation. -func (opt *evalOptionalOr) Eval(ctx interpreter.Activation) ref.Val { +func (opt *evalOptionalOr) Exec(frame *interpreter.ExecutionFrame) ref.Val { // short-circuit lhs. - optLHS := opt.lhs.Eval(ctx) + optLHS := opt.lhs.Exec(frame) switch val := optLHS.(type) { case *types.Err, *types.Unknown: return optLHS @@ -741,18 +739,24 @@ func (opt *evalOptionalOr) Eval(ctx interpreter.Activation) ref.Val { if val.HasValue() { return optLHS } - return opt.rhs.Eval(ctx) + return opt.rhs.Exec(frame) default: return types.NoSuchOverloadErr() } } +// Eval evaluates the left-hand side optional to determine whether it contains a value, else +// proceeds with the right-hand side evaluation. +func (opt *evalOptionalOr) Eval(ctx interpreter.Activation) ref.Val { + return opt.Exec(interpreter.AsFrame(ctx)) +} + // evalOptionalOrValue selects between an optional or a concrete value. If the optional has a value, // its value is returned, otherwise the alternative value expression is evaluated and returned. type evalOptionalOrValue struct { id int64 - lhs interpreter.Interpretable - rhs interpreter.Interpretable + lhs interpreter.InterpretableV2 + rhs interpreter.InterpretableV2 } // ID implements the Interpretable interface method. @@ -760,11 +764,9 @@ func (opt *evalOptionalOrValue) ID() int64 { return opt.id } -// Eval evaluates the left-hand side optional to determine whether it contains a value, else -// proceeds with the right-hand side evaluation. -func (opt *evalOptionalOrValue) Eval(ctx interpreter.Activation) ref.Val { +func (opt *evalOptionalOrValue) Exec(frame *interpreter.ExecutionFrame) ref.Val { // short-circuit lhs. - optLHS := opt.lhs.Eval(ctx) + optLHS := opt.lhs.Exec(frame) switch val := optLHS.(type) { case *types.Err, *types.Unknown: @@ -773,12 +775,18 @@ func (opt *evalOptionalOrValue) Eval(ctx interpreter.Activation) ref.Val { if val.HasValue() { return val.GetValue() } - return opt.rhs.Eval(ctx) + return opt.rhs.Exec(frame) default: return types.NoSuchOverloadErr() } } +// Eval evaluates the left-hand side optional to determine whether it contains a value, else +// proceeds with the right-hand side evaluation. +func (opt *evalOptionalOrValue) Eval(ctx interpreter.Activation) ref.Val { + return opt.Exec(interpreter.AsFrame(ctx)) +} + type timeLegacyLibrary struct{} func (timeLegacyLibrary) CompileOptions() []EnvOption { diff --git a/vendor/github.com/google/cel-go/cel/options.go b/vendor/github.com/google/cel-go/cel/options.go index d7d2ab0340..86a98d35db 100644 --- a/vendor/github.com/google/cel-go/cel/options.go +++ b/vendor/github.com/google/cel-go/cel/options.go @@ -456,6 +456,14 @@ func CustomDecorator(dec interpreter.InterpretableDecorator) ProgramOption { } } +// CustomDecoratorV2 appends an InterpreterDecoratorV2 to the program. +func CustomDecoratorV2(dec interpreter.InterpretableDecoratorV2) ProgramOption { + return func(p *prog) (*prog, error) { + p.plannerOptions = append(p.plannerOptions, interpreter.CustomDecoratorV2(dec)) + return p, nil + } +} + // Functions adds function overloads that extend or override the set of CEL built-ins. // // Deprecated: use Function() instead to declare the function, its overload signatures, diff --git a/vendor/github.com/google/cel-go/cel/program.go b/vendor/github.com/google/cel-go/cel/program.go index c8b7250454..1fb6587774 100644 --- a/vendor/github.com/google/cel-go/cel/program.go +++ b/vendor/github.com/google/cel-go/cel/program.go @@ -18,7 +18,6 @@ import ( "context" "errors" "fmt" - "sync" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/functions" @@ -160,7 +159,7 @@ type prog struct { regexOptimizations []*interpreter.RegexOptimization // Interpretable configured from an Ast and aggregate decorator set based on program options. - interpretable interpreter.Interpretable + interpretable interpreter.InterpretableV2 observable *interpreter.ObservableInterpretable callCostEstimator interpreter.ActualCostEstimator costOptions []interpreter.CostTrackerOption @@ -262,8 +261,16 @@ func newProgram(e *Env, a *ast.AST, opts []ProgramOption) (Program, error) { if p.costLimit != nil { costOpts = append(costOpts, interpreter.CostTrackerLimit(*p.costLimit)) } + // Creating a new cost tracker for each evaluation causes significant work that + // needs to be repeated for each evaluation even though the cost tracker is + // mostly read-only once constructed. Therefore it gets constructed + // once now and later a cheap clone is used for each evaluation. + tracker, err := interpreter.NewCostTracker(p.callCostEstimator, costOpts...) + if err != nil { + return nil, fmt.Errorf("construct cost tracker: %w", err) + } trackerFactory := func() (*interpreter.CostTracker, error) { - return interpreter.NewCostTracker(p.callCostEstimator, costOpts...) + return tracker.Clone() } var observers []interpreter.PlannerOption if p.evalOpts&(OptExhaustiveEval|OptTrackState) != 0 { @@ -313,22 +320,19 @@ func (p *prog) Eval(input any) (out ref.Val, det *EvalDetails, err error) { } }() // Build a hierarchical activation if there are default vars set. - var vars Activation - switch v := input.(type) { - case Activation: - vars = v - case map[string]any: - vars = activationPool.Setup(v) - defer activationPool.Put(vars) - default: - return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]any, got: (%T)%v", input, input) - } - if p.defaultVars != nil { - vars = interpreter.NewHierarchicalActivation(p.defaultVars, vars) + var frame *interpreter.ExecutionFrame + if f, ok := input.(*interpreter.ExecutionFrame); ok { + frame = f + } else { + frame, err = p.newExecutionFrame(input) + if err != nil { + return nil, nil, err + } + defer frame.Close() } if p.observable != nil { det = &EvalDetails{} - out = p.observable.ObserveEval(vars, func(observed any) { + out = p.observable.ObserveExec(frame, func(observed any) { switch o := observed.(type) { case interpreter.EvalState: det.state = o @@ -337,7 +341,7 @@ func (p *prog) Eval(input any) (out ref.Val, det *EvalDetails, err error) { } }) } else { - out = p.interpretable.Eval(vars) + out = p.interpretable.Exec(frame) } // The output of an internal Eval may have a value (`v`) that is a types.Err. This step // translates the CEL value to a Go error response. This interface does not quite match the @@ -353,164 +357,29 @@ func (p *prog) ContextEval(ctx context.Context, input any) (ref.Val, *EvalDetail if ctx == nil { return nil, nil, fmt.Errorf("context can not be nil") } - // Configure the input, making sure to wrap Activation inputs in the special ctxActivation which - // exposes the #interrupted variable and manages rate-limited checks of the ctx.Done() state. - var vars Activation - switch v := input.(type) { - case Activation: - vars = ctxActivationPool.Setup(v, ctx.Done(), p.interruptCheckFrequency) - defer ctxActivationPool.Put(vars) - case map[string]any: - rawVars := activationPool.Setup(v) - defer activationPool.Put(rawVars) - vars = ctxActivationPool.Setup(rawVars, ctx.Done(), p.interruptCheckFrequency) - defer ctxActivationPool.Put(vars) - default: - return nil, nil, fmt.Errorf("invalid input, wanted Activation or map[string]any, got: (%T)%v", input, input) - } - out, det, err := p.Eval(vars) - if err != nil && errors.Is(err, interpreter.InterruptError{}) { - return out, det, fmt.Errorf("%w: %w", err, context.Cause(ctx)) - } - return out, det, err -} - -type ctxEvalActivation struct { - parent Activation - interrupt <-chan struct{} - interruptCheckCount uint - interruptCheckFrequency uint -} - -// ResolveName implements the Activation interface method, but adds a special #interrupted variable -// which is capable of testing whether a 'done' signal is provided from a context.Context channel. -func (a *ctxEvalActivation) ResolveName(name string) (any, bool) { - if name == "#interrupted" { - a.interruptCheckCount++ - if a.interruptCheckCount%a.interruptCheckFrequency == 0 { - select { - case <-a.interrupt: - return true, true - default: - return nil, false - } - } - return nil, false + frame, err := p.newExecutionFrame(input) + if err != nil { + return nil, nil, err } - return a.parent.ResolveName(name) -} - -func (a *ctxEvalActivation) Parent() Activation { - return a.parent -} - -func (a *ctxEvalActivation) AsPartialActivation() (interpreter.PartialActivation, bool) { - pa, ok := a.parent.(interpreter.PartialActivation) - return pa, ok -} - -func newCtxEvalActivationPool() *ctxEvalActivationPool { - return &ctxEvalActivationPool{ - Pool: sync.Pool{ - New: func() any { - return &ctxEvalActivation{} - }, - }, + defer frame.Close() + frame.SetContext(ctx, p.interruptCheckFrequency) + out, det, errEval := p.Eval(frame) + if errEval != nil && errors.Is(errEval, interpreter.InterruptError{}) { + return out, det, fmt.Errorf("%w: %w", errEval, context.Cause(ctx)) } + return out, det, errEval } -type ctxEvalActivationPool struct { - sync.Pool -} - -// Setup initializes a pooled Activation with the ability check for context.Context cancellation -func (p *ctxEvalActivationPool) Setup(vars Activation, done <-chan struct{}, interruptCheckRate uint) *ctxEvalActivation { - a := p.Pool.Get().(*ctxEvalActivation) - a.parent = vars - a.interrupt = done - a.interruptCheckCount = 0 - a.interruptCheckFrequency = interruptCheckRate - return a -} - -type evalActivation struct { - vars map[string]any - lazyVars map[string]any -} - -// ResolveName looks up the value of the input variable name, if found. -// -// Lazy bindings may be supplied within the map-based input in either of the following forms: -// - func() any -// - func() ref.Val -// -// The lazy binding will only be invoked once per evaluation. -// -// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using -// the types.Adapter configured in the environment. -func (a *evalActivation) ResolveName(name string) (any, bool) { - v, found := a.vars[name] - if !found { - return nil, false - } - switch obj := v.(type) { - case func() ref.Val: - if resolved, found := a.lazyVars[name]; found { - return resolved, true - } - lazy := obj() - a.lazyVars[name] = lazy - return lazy, true - case func() any: - if resolved, found := a.lazyVars[name]; found { - return resolved, true - } - lazy := obj() - a.lazyVars[name] = lazy - return lazy, true - default: - return obj, true +// newExecutionFrame creates an ExecutionFrame for the given input without a timeout context. +func (p *prog) newExecutionFrame(input any) (*interpreter.ExecutionFrame, error) { + frame, err := interpreter.NewExecutionFrame(input) + if err != nil { + return nil, err } -} - -// Parent implements the Activation interface -func (a *evalActivation) Parent() Activation { - return nil -} - -func newEvalActivationPool() *evalActivationPool { - return &evalActivationPool{ - Pool: sync.Pool{ - New: func() any { - return &evalActivation{lazyVars: make(map[string]any)} - }, - }, + if p.defaultVars != nil { + // Update the frame's activation in place. + frame.Activation = interpreter.NewHierarchicalActivation(p.defaultVars, frame.Activation) } -} - -type evalActivationPool struct { - sync.Pool -} - -// Setup initializes a pooled Activation object with the map input. -func (p *evalActivationPool) Setup(vars map[string]any) *evalActivation { - a := p.Pool.Get().(*evalActivation) - a.vars = vars - return a -} -func (p *evalActivationPool) Put(value any) { - a := value.(*evalActivation) - for k := range a.lazyVars { - delete(a.lazyVars, k) - } - p.Pool.Put(a) + return frame, nil } - -var ( - // activationPool is an internally managed pool of Activation values that wrap map[string]any inputs - activationPool = newEvalActivationPool() - - // ctxActivationPool is an internally managed pool of Activation values that expose a special #interrupted variable - ctxActivationPool = newCtxEvalActivationPool() -) diff --git a/vendor/github.com/google/cel-go/cel/prompt.go b/vendor/github.com/google/cel-go/cel/prompt.go index 1529680fd9..f599348271 100644 --- a/vendor/github.com/google/cel-go/cel/prompt.go +++ b/vendor/github.com/google/cel-go/cel/prompt.go @@ -108,7 +108,7 @@ type Prompt struct { // tmpl is the text template base-configuration for rendering text. tmpl *template.Template - // fieldPaths is a flag to enable including reachable field paths in the prompt. + // fieldPaths is a flag to include reachable field paths in the prompt. fieldPaths bool // env reference used to collect variables, functions, and macros available to the prompt. @@ -131,6 +131,12 @@ type promptInst struct { // Render renders the user prompt with the associated context from the prompt template // for use with LLM generators. +// +// User-supplied input is passed as template data via the UserPrompt field, which +// Go's text/template renders as a literal string value. Template action delimiters +// such as {{.Persona}} in the user prompt are never evaluated as template directives +// because text/template only executes directives present in the template definition +// itself, not in data values interpolated at render time. func (p *Prompt) Render(userPrompt string) string { var buffer strings.Builder vars := make([]*promptVariable, len(p.env.Variables())) @@ -178,7 +184,8 @@ func (p *Prompt) Render(userPrompt string) string { Variables: vars, Macros: macs, Functions: funcs, - UserPrompt: userPrompt} + UserPrompt: userPrompt, + } p.tmpl.Execute(&buffer, inst) return buffer.String() } diff --git a/vendor/github.com/google/cel-go/checker/cost.go b/vendor/github.com/google/cel-go/checker/cost.go index 086dc40fa1..3d7dd7ec4a 100644 --- a/vendor/github.com/google/cel-go/checker/cost.go +++ b/vendor/github.com/google/cel-go/checker/cost.go @@ -791,18 +791,26 @@ func (c *coster) functionCost(e ast.Expr, function, overloadID string, target *A return CallEstimate{CostEstimate: c.sizeOrUnknown(args[1]).MultiplyByCostFactor(1).Add(argCostSum())} } // O(nm) functions - case overloads.MatchesString: + case overloads.Matches, overloads.MatchesString: // https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL - if target != nil && len(args) == 1 { + var strNode, regexNode AstNode + if overloadID == overloads.MatchesString && target != nil && len(args) == 1 { + strNode = *target + regexNode = args[0] + } else if overloadID == overloads.Matches && target == nil && len(args) == 2 { + strNode = args[0] + regexNode = args[1] + } + if strNode != nil && regexNode != nil { // Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0 // in case where string is empty but regex is still expensive. - strCost := c.sizeOrUnknown(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor) + strCost := c.sizeOrUnknown(strNode).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor) // We don't know how many expressions are in the regex, just the string length (a huge // improvement here would be to somehow get a count the number of expressions in the regex or // how many states are in the regex state machine and use that to measure regex cost). // For now, we're making a guess that each expression in a regex is typically at least 4 chars // in length. - regexCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor) + regexCost := c.sizeOrUnknown(regexNode).MultiplyByCostFactor(common.RegexStringLengthCostFactor) return CallEstimate{CostEstimate: strCost.Multiply(regexCost).Add(argCostSum())} } case overloads.ContainsString: diff --git a/vendor/github.com/google/cel-go/common/containers/container.go b/vendor/github.com/google/cel-go/common/containers/container.go index fc146b6fc4..fcfcdfc3f4 100644 --- a/vendor/github.com/google/cel-go/common/containers/container.go +++ b/vendor/github.com/google/cel-go/common/containers/container.go @@ -227,7 +227,7 @@ func Abbrevs(qualifiedNames ...string) ContainerOption { } alias := qn[ind+1:] var err error - c, err = aliasAs("abbreviation", qn, alias)(c) + c, err = aliasAs("abbreviation", qn, alias, true)(c) if err != nil { return nil, err } @@ -236,31 +236,32 @@ func Abbrevs(qualifiedNames ...string) ContainerOption { } } -// Alias associates a fully-qualified name with a user-defined alias. +// Alias associates a name with a user-defined alias. // // In general, Abbrevs is preferred to Alias since the names generated from the Abbrevs option // are more easily traced back to source code. The Alias option is useful for propagating alias // configuration from one Container instance to another, and may also be useful for remapping // poorly chosen protobuf message / package names. -// -// Note: all of the rules that apply to Abbrevs also apply to Alias. func Alias(qualifiedName, alias string) ContainerOption { - return aliasAs("alias", qualifiedName, alias) + return aliasAs("alias", qualifiedName, alias, false) } -func aliasAs(kind, qualifiedName, alias string) ContainerOption { +func aliasAs(kind, qualifiedName, alias string, requireQualified bool) ContainerOption { return func(c *Container) (*Container, error) { if len(alias) == 0 || strings.Contains(alias, ".") { return nil, fmt.Errorf( "%s must be non-empty and simple (not qualified): %s=%s", kind, kind, alias) } + if len(qualifiedName) == 0 { + return nil, fmt.Errorf("%s must refer to a valid name: %s", kind, qualifiedName) + } if qualifiedName[0:1] == "." { return nil, fmt.Errorf("qualified name must not begin with a leading '.': %s", qualifiedName) } ind := strings.LastIndex(qualifiedName, ".") - if ind <= 0 || ind == len(qualifiedName)-1 { + if ind == len(qualifiedName)-1 || (requireQualified && ind <= 0) { return nil, fmt.Errorf("%s must refer to a valid qualified name: %s", kind, qualifiedName) } diff --git a/vendor/github.com/google/cel-go/common/functions/functions.go b/vendor/github.com/google/cel-go/common/functions/functions.go index 67f4a5944e..0c00781d9c 100644 --- a/vendor/github.com/google/cel-go/common/functions/functions.go +++ b/vendor/github.com/google/cel-go/common/functions/functions.go @@ -15,7 +15,11 @@ // Package functions defines the standard builtin functions supported by the interpreter package functions -import "github.com/google/cel-go/common/types/ref" +import ( + "context" + + "github.com/google/cel-go/common/types/ref" +) // Overload defines a named overload of a function, indicating an operand trait // which must be present on the first argument to the overload as well as one @@ -41,21 +45,37 @@ type Overload struct { // Binary defines the overload with a BinaryOp implementation. May be nil. Binary BinaryOp - // Function defines the overload with a FunctionOp implementation. May be - // nil. + // Function defines the overload with a FunctionOp implementation. May be nil. Function FunctionOp + // Async defines the overload with an AsyncOp implementation. May be nil. + Async AsyncOp + // NonStrict specifies whether the Overload will tolerate arguments that // are types.Err or types.Unknown. NonStrict bool } // UnaryOp is a function that takes a single value and produces an output. -type UnaryOp func(value ref.Val) ref.Val +type UnaryOp func(ref.Val) ref.Val // BinaryOp is a function that takes two values and produces an output. -type BinaryOp func(lhs ref.Val, rhs ref.Val) ref.Val +type BinaryOp func(ref.Val, ref.Val) ref.Val // FunctionOp is a function with accepts zero or more arguments and produces // a value or error as a result. -type FunctionOp func(values ...ref.Val) ref.Val +type FunctionOp func(...ref.Val) ref.Val + +// AsyncOp is a function that accepts zero or more arguments and produces +// a value or error asynchronously via a channel. +// +// AsyncOp is an internal interface intended for use by CEL to manage goroutines and +// channels associated with async calls. For public API usage, use BlockingAsyncOp. +// Implementers should listen for context cancellation on the provided context for +// resource cleanup. +type AsyncOp func(context.Context, ...ref.Val) <-chan ref.Val + +// BlockingAsyncOp is a function that accepts zero or more arguments and blocks until +// the result is available. When used with AsyncBinding, the framework runs the function +// in its own goroutine and manages channel lifecycle internally. +type BlockingAsyncOp func(context.Context, ...ref.Val) ref.Val diff --git a/vendor/github.com/google/cel-go/common/runes/buffer.go b/vendor/github.com/google/cel-go/common/runes/buffer.go index 021198224d..58dd33e13e 100644 --- a/vendor/github.com/google/cel-go/common/runes/buffer.go +++ b/vendor/github.com/google/cel-go/common/runes/buffer.go @@ -16,6 +16,7 @@ package runes import ( + "fmt" "strings" "unicode/utf8" ) @@ -113,45 +114,64 @@ var _ Buffer = &supplementalBuffer{} var nilBuffer = &emptyBuffer{} +// SizeLimitError indicates that the input exceeded the configured code point limit. +type SizeLimitError struct { + Size int + Limit int +} + +func (e *SizeLimitError) Error() string { + return fmt.Sprintf("expression code point size exceeds limit: size: %d, limit %d", e.Size, e.Limit) +} + // NewBuffer returns an efficient implementation of Buffer for the given text based on the ranges of // the encoded code points contained within. -// -// Code points are represented as an array of byte, uint16, or rune. This approach ensures that -// each index represents a code point by itself without needing to use an array of rune. At first -// we assume all code points are less than or equal to '\u007f'. If this holds true, the -// underlying storage is a byte array containing only ASCII characters. If we encountered a code -// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the -// elements of previous byte array to the uint16 array, and continue. If this holds true, the -// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual -// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous -// elements of the byte or uint16 array, and continue. The underlying storage is an rune array -// containing any Unicode character. func NewBuffer(data string) Buffer { - buf, _ := newBuffer(data, false) + buf, _, _ := newBufferWithLimit(data, false, -1) return buf } // NewBufferAndLineOffsets returns an efficient implementation of Buffer for the given text based on // the ranges of the encoded code points contained within, as well as returning the line offsets. -// -// Code points are represented as an array of byte, uint16, or rune. This approach ensures that -// each index represents a code point by itself without needing to use an array of rune. At first -// we assume all code points are less than or equal to '\u007f'. If this holds true, the -// underlying storage is a byte array containing only ASCII characters. If we encountered a code -// point above this range but less than or equal to '\uffff' we allocate a uint16 array, copy the -// elements of previous byte array to the uint16 array, and continue. If this holds true, the -// underlying storage is a uint16 array containing only Unicode characters in the Basic Multilingual -// Plane. If we encounter a code point above '\uffff' we allocate an rune array, copy the previous -// elements of the byte or uint16 array, and continue. The underlying storage is an rune array -// containing any Unicode character. func NewBufferAndLineOffsets(data string) (Buffer, []int32) { - return newBuffer(data, true) + buf, offs, _ := newBufferWithLimit(data, true, -1) + return buf, offs +} + +// NewBufferAndLineOffsetsWithLimit returns an efficient implementation of Buffer for the given text +// and enforces a code point limit while constructing the buffer. +func NewBufferAndLineOffsetsWithLimit(data string, limit int) (Buffer, []int32, error) { + if limit < 0 || len(data) <= limit { + return newBufferWithLimit(data, true, -1) + } + return newBufferWithLimit(data, true, limit) +} + +func countRemainingCodePoints(data string, idx int, count int) int { + for idx < len(data) { + _, s := utf8.DecodeRuneInString(data[idx:]) + idx += s + count++ + } + return count } -func newBuffer(data string, lines bool) (Buffer, []int32) { +func newBufferWithLimit(data string, lines bool, limit int) (Buffer, []int32, error) { if len(data) == 0 { - return nilBuffer, []int32{0} + return nilBuffer, []int32{0}, nil + } + if limit >= 0 && len(data) > limit { + size := countRemainingCodePoints(data, 0, 0) + if size > limit { + return nil, nil, &SizeLimitError{ + Size: size, + Limit: limit, + } + } } + + // The resulting buffers store one element per code point, so the worst case + // element count never exceeds len(data). var ( idx = 0 off int32 = 0 @@ -195,7 +215,8 @@ func newBuffer(data string, lines bool) (Buffer, []int32) { } return &asciiBuffer{ arr: buf8, - }, offs + }, offs, nil + copy16: for idx < len(data) { r, s := utf8.DecodeRuneInString(data[idx:]) @@ -222,7 +243,8 @@ copy16: } return &basicBuffer{ arr: buf16, - }, offs + }, offs, nil + copy32: for idx < len(data) { r, s := utf8.DecodeRuneInString(data[idx:]) @@ -238,5 +260,5 @@ copy32: } return &supplementalBuffer{ arr: buf32, - }, offs + }, offs, nil } diff --git a/vendor/github.com/google/cel-go/common/source.go b/vendor/github.com/google/cel-go/common/source.go index ec79cb5454..9187e9b5c3 100644 --- a/vendor/github.com/google/cel-go/common/source.go +++ b/vendor/github.com/google/cel-go/common/source.go @@ -74,6 +74,12 @@ func NewTextSource(text string) Source { return NewStringSource(text, "") } +// NewTextSourceWithLimit creates a new Source from the input text string while +// enforcing a maximum code point count when needed. +func NewTextSourceWithLimit(text string, limit int) (Source, error) { + return NewStringSourceWithLimit(text, "", limit) +} + // NewStringSource creates a new Source from the given contents and description. func NewStringSource(contents string, description string) Source { // Compute line offsets up front as they are referred to frequently. @@ -85,6 +91,23 @@ func NewStringSource(contents string, description string) Source { } } +// NewStringSourceWithLimit creates a new Source from the given contents and +// description while enforcing a maximum code point count when needed. +func NewStringSourceWithLimit(contents string, description string, limit int) (Source, error) { + if limit < 0 || len(contents) <= limit { + return NewStringSource(contents, description), nil + } + buf, offs, err := runes.NewBufferAndLineOffsetsWithLimit(contents, limit) + if err != nil { + return nil, err + } + return &sourceImpl{ + Buffer: buf, + description: description, + lineOffsets: offs, + }, nil +} + // NewInfoSource creates a new Source from a SourceInfo. func NewInfoSource(info *exprpb.SourceInfo) Source { return &sourceImpl{ diff --git a/vendor/github.com/google/cel-go/common/types/timestamp.go b/vendor/github.com/google/cel-go/common/types/timestamp.go index 060caf6bbe..c4484291be 100644 --- a/vendor/github.com/google/cel-go/common/types/timestamp.go +++ b/vendor/github.com/google/cel-go/common/types/timestamp.go @@ -302,6 +302,9 @@ func timeZone(tz ref.Val, visitor timestampVisitor) timestampVisitor { if err != nil { return WrapErr(err) } + if min < 0 || min > 59 { + return WrapErr(fmt.Errorf("timezone offset minutes out of range [0, 59]: %s", val)) + } var offset int if string(val[0]) == "-" { offset = hr*60 - min diff --git a/vendor/github.com/google/cel-go/common/types/unknown.go b/vendor/github.com/google/cel-go/common/types/unknown.go index 9dd2b25794..24d86518b4 100644 --- a/vendor/github.com/google/cel-go/common/types/unknown.go +++ b/vendor/github.com/google/cel-go/common/types/unknown.go @@ -181,6 +181,20 @@ func (u *Unknown) GetAttributeTrails(id int64) ([]*AttributeTrail, bool) { return trails, found } +// HasUnknownFunction returns whether any of the attribute trails contained within the unknown +// are unspecified. Unspecified attributes typically indicate an unresolved function call +// or operation, rather than a missing variable. +func (u *Unknown) HasUnknownFunction() bool { + for _, trails := range u.attributeTrails { + for _, t := range trails { + if t.variable == "" { + return true + } + } + } + return false +} + // Contains returns true if the input unknown is a subset of the current unknown. func (u *Unknown) Contains(other *Unknown) bool { for id, otherTrails := range other.attributeTrails { diff --git a/vendor/github.com/google/cel-go/ext/BUILD.bazel b/vendor/github.com/google/cel-go/ext/BUILD.bazel index b993d39cbb..f362fd97b8 100644 --- a/vendor/github.com/google/cel-go/ext/BUILD.bazel +++ b/vendor/github.com/google/cel-go/ext/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "lists.go", "math.go", "native.go", + "network.go", "protos.go", "regex.go", "sets.go", @@ -40,6 +41,7 @@ go_library( "//common/types/traits:go_default_library", "//interpreter:go_default_library", "//parser:go_default_library", + "@org_golang_google_protobuf//encoding/protojson:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//reflect/protoreflect:go_default_library", "@org_golang_google_protobuf//types/known/structpb", @@ -61,6 +63,7 @@ go_test( "lists_test.go", "math_test.go", "native_test.go", + "network_test.go", "protos_test.go", "regex_test.go", "sets_test.go", diff --git a/vendor/github.com/google/cel-go/ext/README.md b/vendor/github.com/google/cel-go/ext/README.md index 6a7163de00..523d908564 100644 --- a/vendor/github.com/google/cel-go/ext/README.md +++ b/vendor/github.com/google/cel-go/ext/README.md @@ -55,6 +55,20 @@ Example: base64.encode(b'hello') // return 'aGVsbG8=' +### JSON.Encode + +Introduced at version: 1 + +Encodes a CEL value to a JSON string. + + json.encode() -> + +Examples: + + json.encode('hello') // return '"hello"' + json.encode([1, 'two', true]) // return '[1,"two",true]' + json.encode({'items': [1, 'two', false]}) // return '{"items":[1,"two",false]}' + ## Math Math helper macros and functions. diff --git a/vendor/github.com/google/cel-go/ext/bindings.go b/vendor/github.com/google/cel-go/ext/bindings.go index bef29ae270..89766d60aa 100644 --- a/vendor/github.com/google/cel-go/ext/bindings.go +++ b/vendor/github.com/google/cel-go/ext/bindings.go @@ -108,7 +108,7 @@ func (lib *celBindings) CompileOptions() []cel.EnvOption { func (lib *celBindings) ProgramOptions() []cel.ProgramOption { if lib.version >= 1 { - celBlockPlan := func(i interpreter.Interpretable) (interpreter.Interpretable, error) { + celBlockPlan := func(i interpreter.InterpretableV2) (interpreter.InterpretableV2, error) { call, ok := i.(interpreter.InterpretableCall) if !ok { return i, nil @@ -140,7 +140,7 @@ func (lib *celBindings) ProgramOptions() []cel.ProgramOption { return i, nil } } - return []cel.ProgramOption{cel.CustomDecorator(celBlockPlan)} + return []cel.ProgramOption{cel.CustomDecoratorV2(celBlockPlan)} } return []cel.ProgramOption{} } @@ -190,7 +190,7 @@ func celBind(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Ex ), nil } -func newDynamicBlock(slotExprs []interpreter.Interpretable, expr interpreter.Interpretable) interpreter.Interpretable { +func newDynamicBlock(slotExprs []interpreter.InterpretableV2, expr interpreter.InterpretableV2) interpreter.InterpretableV2 { bs := &dynamicBlock{ slotExprs: slotExprs, expr: expr, @@ -213,8 +213,8 @@ func newDynamicBlock(slotExprs []interpreter.Interpretable, expr interpreter.Int } type dynamicBlock struct { - slotExprs []interpreter.Interpretable - expr interpreter.Interpretable + slotExprs []interpreter.InterpretableV2 + expr interpreter.InterpretableV2 slotActivationPool *sync.Pool } @@ -223,12 +223,23 @@ func (b *dynamicBlock) ID() int64 { return b.expr.ID() } -// Eval implements the Interpretable interface method. -func (b *dynamicBlock) Eval(activation cel.Activation) ref.Val { +// Exec implements the Interpretable interface method and pushes a new frame onto the +// execution frame for the duration of the block execution. +func (b *dynamicBlock) Exec(frame *interpreter.ExecutionFrame) ref.Val { sa := b.slotActivationPool.Get().(*dynamicSlotActivation) - sa.Activation = activation + sa.frame = frame.Push(sa) + // Ensure the 'unwrapped' Activation points to the original one from the frame, + // and not the hierarchical activation which composes the original and the slot + // activation. + sa.Activation = frame.Activation + defer sa.frame.Pop() defer b.clearSlots(sa) - return b.expr.Eval(sa) + return b.expr.Exec(sa.frame) +} + +// Eval implements the Interpretable interface method. +func (b *dynamicBlock) Eval(activation cel.Activation) ref.Val { + return b.Exec(interpreter.AsFrame(activation)) } func (b *dynamicBlock) clearSlots(sa *dynamicSlotActivation) { @@ -243,7 +254,8 @@ type slotVal struct { type dynamicSlotActivation struct { cel.Activation - slotExprs []interpreter.Interpretable + frame *interpreter.ExecutionFrame + slotExprs []interpreter.InterpretableV2 slotCount int slotVals []*slotVal } @@ -267,7 +279,7 @@ func (sa *dynamicSlotActivation) ResolveName(name string) (any, bool) { return *v.value, true } v.visited = true - val := sa.slotExprs[idx].Eval(sa) + val := sa.slotExprs[idx].Exec(sa.frame) v.value = &val return val, true } @@ -276,13 +288,14 @@ func (sa *dynamicSlotActivation) ResolveName(name string) (any, bool) { func (sa *dynamicSlotActivation) reset() { sa.Activation = nil + sa.frame = nil for _, sv := range sa.slotVals { sv.visited = false sv.value = nil } } -func newConstantBlock(slots traits.Lister, expr interpreter.Interpretable) interpreter.Interpretable { +func newConstantBlock(slots traits.Lister, expr interpreter.InterpretableV2) interpreter.InterpretableV2 { count := slots.Size().(types.Int) return &constantBlock{slots: slots, slotCount: int(count), expr: expr} } @@ -290,7 +303,7 @@ func newConstantBlock(slots traits.Lister, expr interpreter.Interpretable) inter type constantBlock struct { slots traits.Lister slotCount int - expr interpreter.Interpretable + expr interpreter.InterpretableV2 } // ID implements the interpreter.Interpretable interface method. @@ -298,15 +311,24 @@ func (b *constantBlock) ID() int64 { return b.expr.ID() } +// Exec implements the Interpretable interface method and pushes a new frame onto the +// stack for the duration of the block execution. +func (b *constantBlock) Exec(frame *interpreter.ExecutionFrame) ref.Val { + sa := constantSlotActivation{Activation: frame.Activation, slots: b.slots, slotCount: b.slotCount} + sa.frame = frame.Push(sa) + defer sa.frame.Pop() + return b.expr.Exec(sa.frame) +} + // Eval implements the interpreter.Interpretable interface method, and will proxy @index prefixed variable // lookups into a set of constant slots determined from the plan step. func (b *constantBlock) Eval(activation cel.Activation) ref.Val { - vars := constantSlotActivation{Activation: activation, slots: b.slots, slotCount: b.slotCount} - return b.expr.Eval(vars) + return b.Exec(interpreter.AsFrame(activation)) } type constantSlotActivation struct { cel.Activation + frame *interpreter.ExecutionFrame slots traits.Lister slotCount int } diff --git a/vendor/github.com/google/cel-go/ext/encoders.go b/vendor/github.com/google/cel-go/ext/encoders.go index 731c3d095d..10beea4f13 100644 --- a/vendor/github.com/google/cel-go/ext/encoders.go +++ b/vendor/github.com/google/cel-go/ext/encoders.go @@ -16,11 +16,14 @@ package ext import ( "encoding/base64" + "fmt" "math" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/structpb" ) // Encoders returns a cel.EnvOption to configure extended functions for string, byte, and object @@ -48,6 +51,18 @@ import ( // Examples: // // base64.encode(b'hello') // return b'aGVsbG8=' +// +// # JSON.Encode +// +// Introduced at version: 1 +// +// Encodes a CEL value to a JSON string. +// +// json.encode() -> +// +// Examples: +// +// json.encode({'hello': 'world'}) // return '{"hello":"world"}' func Encoders(options ...EncodersOption) cel.EnvOption { l := &encoderLib{version: math.MaxUint32} for _, o := range options { @@ -75,8 +90,8 @@ func (*encoderLib) LibraryName() string { return "cel.lib.ext.encoders" } -func (*encoderLib) CompileOptions() []cel.EnvOption { - return []cel.EnvOption{ +func (lib *encoderLib) CompileOptions() []cel.EnvOption { + opts := []cel.EnvOption{ cel.Function("base64.decode", cel.Overload("base64_decode_string", []*cel.Type{cel.StringType}, cel.BytesType, cel.UnaryBinding(func(str ref.Val) ref.Val { @@ -90,6 +105,16 @@ func (*encoderLib) CompileOptions() []cel.EnvOption { return stringOrError(base64EncodeBytes([]byte(b))) }))), } + if lib.version >= 1 { + opts = append(opts, + cel.Function("json.encode", + cel.Overload("json_encode_dyn", []*cel.Type{cel.DynType}, cel.StringType, + cel.UnaryBinding(func(val ref.Val) ref.Val { + return stringOrError(jsonEncodeValue(val)) + }))), + ) + } + return opts } func (*encoderLib) ProgramOptions() []cel.ProgramOption { @@ -110,3 +135,19 @@ func base64DecodeString(str string) ([]byte, error) { func base64EncodeBytes(bytes []byte) (string, error) { return base64.StdEncoding.EncodeToString(bytes), nil } + +func jsonEncodeValue(val ref.Val) (string, error) { + native, err := val.ConvertToNative(types.JSONValueType) + if err != nil { + return "", err + } + jsonValue, ok := native.(*structpb.Value) + if !ok { + return "", fmt.Errorf("cannot convert %T to JSON value", native) + } + jsonBytes, err := protojson.Marshal(jsonValue) + if err != nil { + return "", err + } + return string(jsonBytes), nil +} diff --git a/vendor/github.com/google/cel-go/ext/lists.go b/vendor/github.com/google/cel-go/ext/lists.go index 7a7a33c622..b4ac258995 100644 --- a/vendor/github.com/google/cel-go/ext/lists.go +++ b/vendor/github.com/google/cel-go/ext/lists.go @@ -153,15 +153,18 @@ var comparableTypes = []*cel.Type{ // ].sortBy(e, e.score).map(e, e.name) // == ["bar", "foo", "baz"] func Lists(options ...ListsOption) cel.EnvOption { - l := &listsLib{version: math.MaxUint32} + l := &listsLib{version: math.MaxUint32, maxRangeSize: defaultMaxRangeSize} for _, o := range options { l = o(l) } return cel.Lib(l) } +const defaultMaxRangeSize = 1_000_000 + type listsLib struct { - version uint32 + version uint32 + maxRangeSize int64 } // LibraryName implements the SingletonLibrary interface method. @@ -188,6 +191,16 @@ func ListsVersion(version uint32) ListsOption { } } +// ListsMaxRangeSize sets the maximum number of elements lists.range() will +// allocate. If not set, the default is 10,000,000. Setting this to zero +// disables the limit (not recommended). +func ListsMaxRangeSize(size int64) ListsOption { + return func(lib *listsLib) *listsLib { + lib.maxRangeSize = size + return lib + } +} + // CompileOptions implements the Library interface method. func (lib listsLib) CompileOptions() []cel.EnvOption { listType := cel.ListType(cel.TypeParamType("T")) @@ -309,11 +322,12 @@ func (lib listsLib) CompileOptions() []cel.EnvOption { )..., )) + maxRange := lib.maxRangeSize opts = append(opts, cel.Function("lists.range", cel.Overload("lists_range", []*cel.Type{cel.IntType}, cel.ListType(cel.IntType), cel.UnaryBinding(func(n ref.Val) ref.Val { - result, err := genRange(n.(types.Int)) + result, err := genRange(n.(types.Int), maxRange) if err != nil { return types.WrapErr(err) } @@ -403,8 +417,14 @@ func (lib *listsLib) ProgramOptions() []cel.ProgramOption { return opts } -func genRange(n types.Int) (ref.Val, error) { - var newList []ref.Val +func genRange(n types.Int, maxSize int64) (ref.Val, error) { + if n < 0 { + return nil, fmt.Errorf("lists.range: size must be non-negative, got %d", n) + } + if maxSize > 0 && int64(n) > maxSize { + return nil, fmt.Errorf("lists.range: size %d exceeds maximum allowed (%d)", n, maxSize) + } + newList := make([]ref.Val, 0, n) for i := types.Int(0); i < n; i++ { newList = append(newList, i) } @@ -759,6 +779,9 @@ func trackListSelfCompare(l traits.Lister) *uint64 { // trackAllocatingListCall computes costs as a function of the size of the result list with a baseline cost // for the call dispatch and the associated list allocation. func trackAllocatingListCall(costFactor float64, size uint64) *uint64 { + if costFactor < 0.0 { + costFactor = 1.0 + } cost := safeAdd(uint64(float64(size)*costFactor), callCost, common.ListCreateBaseCost) return &cost } diff --git a/vendor/github.com/google/cel-go/ext/network.go b/vendor/github.com/google/cel-go/ext/network.go new file mode 100644 index 0000000000..affe59e2d3 --- /dev/null +++ b/vendor/github.com/google/cel-go/ext/network.go @@ -0,0 +1,619 @@ +// Copyright 2025 Google LLC +// +// 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 ext + +import ( + "fmt" + "net/netip" + "reflect" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/ast" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" +) + +const ( + // Version1 is the initial version of the Network library, providing + // parity with Kubernetes v1.30+ CEL network functions. + Version1 uint32 = 1 +) + +// Network returns a cel.EnvOption to configure extended functions for network +// address parsing, inspection, and CIDR range manipulation. +// +// Note: This library defines global functions `ip`, `cidr`, `isIP`, `isCIDR` +// and `ip.isCanonical`. If you are currently using variables named `ip` or +// `cidr`, these functions will likely work as intended, however there is a +// chance for collision. +// +// The library closely mirrors the behavior of the Kubernetes CEL network +// libraries, treating IP addresses and CIDR ranges as opaque types. It parses +// IPs strictly: IPv4-mapped IPv6 addresses and IP zones are not allowed. +// +// This library includes a TypeAdapter that allows `netip.Addr` and +// `netip.Prefix` Go types to be passed directly into the CEL environment. +// +// # IP Addresses +// +// The `ip` function converts a string to an IP address (IPv4 or IPv6). If the +// string is not a valid IP, an error is returned. The `isIP` function checks +// if a string is a valid IP address without throwing an error. +// +// ip(string) -> ip +// isIP(string) -> bool +// +// Examples: +// +// ip('127.0.0.1') +// ip('::1') +// isIP('1.2.3.4') // true +// isIP('invalid') // false +// +// # CIDR Ranges +// +// The `cidr` function converts a string to a Classless Inter-Domain Routing +// (CIDR) range. If the string is not valid, an error is returned. +// +// The `isCIDR` function checks if a string is a valid CIDR notation. Note that +// `isCIDR` allows CIDR values with or without host bits (e.g., '10.0.0.1/8' +// or '10.0.0.0/8'). +// +// cidr(string) -> cidr +// isCIDR(string) -> bool +// +// Examples: +// +// cidr('192.168.0.0/24') +// cidr('::1/128') +// isCIDR('10.0.0.0/8') // true +// isCIDR('10.0.0.1/8') // true +// +// # IP Inspection and Canonicalization +// +// IP objects support various inspection methods. +// +// .family() -> int +// .isLoopback() -> bool +// .isGlobalUnicast() -> bool +// .isLinkLocalMulticast() -> bool +// .isLinkLocalUnicast() -> bool +// .isUnspecified() -> bool +// +// The `ip.isCanonical` function takes a string and returns true if it matches +// the RFC 5952 canonical string representation of that address. +// +// ip.isCanonical(string) -> bool +// +// Examples: +// +// ip('127.0.0.1').family() == 4 +// ip('::1').family() == 6 +// ip('127.0.0.1').isLoopback() == true +// ip.isCanonical('2001:db8::1') == true // RFC 5952 format +// ip.isCanonical('2001:DB8::1') == false // Uppercase is not canonical +// ip.isCanonical('2001:db8:0:0:0:0:0:1') == false // Expanded is not canonical +// +// # CIDR Member Functions +// +// CIDR objects support containment checks and property extraction. +// +// .containsIP(ip|string) -> bool +// .containsCIDR(cidr|string) -> bool +// .ip() -> ip +// .isMask() -> bool +// .masked() -> cidr +// .prefixLength() -> int +// +// Examples: +// +// cidr('10.0.0.0/8').containsIP(ip('10.0.0.1')) == true +// cidr('10.0.0.0/8').containsIP('10.0.0.1') == true +// cidr('10.0.0.0/8').containsCIDR('10.1.0.0/16') == true +// cidr('192.168.1.5/24').ip() == ip('192.168.1.5') +// cidr('192.168.1.0/24').isMask() == true +// cidr('192.168.1.5/24').isMask() == false +// cidr('192.168.1.5/24').masked() == cidr('192.168.1.0/24') +// cidr('192.168.1.0/24').prefixLength() == 24 +func Network(opts ...NetworkOption) cel.EnvOption { + lib := &networkLib{version: Version1} + for _, o := range opts { + lib = o(lib) + } + return func(e *cel.Env) (*cel.Env, error) { + // Install the library (Types and Functions) + e, err := cel.Lib(lib)(e) + if err != nil { + return nil, err + } + + // Install the Adapter (Wrapping the existing one) + adapter := &networkAdapter{Adapter: e.CELTypeAdapter()} + return cel.CustomTypeAdapter(adapter)(e) + } +} + +// NetworkOption declares a functional operator for configuring the Network library behavior. +type NetworkOption func(*networkLib) *networkLib + +// NetworkVersion sets the version of the network library to an explicit version. +func NetworkVersion(version uint32) NetworkOption { + return func(lib *networkLib) *networkLib { + lib.version = version + return lib + } +} + +const ( + // Function names matching the original Kubernetes implementation of this networking library. + // isStrictCIDR and isInterfaceAddress are added to enable strict isCIDR parsing without breaking + // functionality for existing users. Ctx: https://github.com/kubernetes/kubernetes/issues/134224 + cidrFunc = "cidr" + cidrToString = "string" + containsCIDRFunc = "containsCIDR" + containsIPFunc = "containsIP" + familyFunc = "family" + ipFunc = "ip" + ipToString = "string" + isCanonicalFunc = "ip.isCanonical" + isCIDRFunc = "isCIDR" + isGlobalUnicastFunc = "isGlobalUnicast" + isIPFunc = "isIP" + isLinkLocalMcastFunc = "isLinkLocalMulticast" + isLinkLocalUcastFunc = "isLinkLocalUnicast" + isLoopbackFunc = "isLoopback" + isMaskFunc = "isMask" + isUnspecifiedFunc = "isUnspecified" + maskedFunc = "masked" + prefixLengthFunc = "prefixLength" +) + +var ( + // Definitions for the Opaque Types + IPType = types.NewOpaqueType("net.IP") + CIDRType = types.NewOpaqueType("net.CIDR") +) + +type networkLib struct { + version uint32 +} + +func (*networkLib) LibraryName() string { + return "cel.lib.ext.network" +} + +func (*networkLib) CompileOptions() []cel.EnvOption { + return []cel.EnvOption{ + // 1. Register Types + cel.Types( + IPType, + CIDRType, + ), + + // 2. Register Functions + cel.Function(cidrFunc, + // K8s Parity: Following the pattern, this is "string_to_cidr" + cel.Overload("string_to_cidr", []*cel.Type{cel.StringType}, CIDRType, + cel.UnaryBinding(netCIDRString)), + ), + cel.Function(cidrToString, + cel.Overload("cidr_to_string", []*cel.Type{CIDRType}, cel.StringType, + cel.UnaryBinding(netCIDRToString)), + ), + cel.Function(containsCIDRFunc, + cel.MemberOverload("cidr_contains_cidr", []*cel.Type{CIDRType, CIDRType}, cel.BoolType, + cel.BinaryBinding(netCIDRContainsCIDR)), + cel.MemberOverload("cidr_contains_cidr_string", []*cel.Type{CIDRType, cel.StringType}, cel.BoolType, + cel.BinaryBinding(netCIDRContainsCIDRString)), + ), + cel.Function(containsIPFunc, + cel.MemberOverload("cidr_contains_ip_ip", []*cel.Type{CIDRType, IPType}, cel.BoolType, + cel.BinaryBinding(netCIDRContainsIP)), + cel.MemberOverload("cidr_contains_ip_string", []*cel.Type{CIDRType, cel.StringType}, cel.BoolType, + cel.BinaryBinding(netCIDRContainsIPString)), + ), + cel.Function(familyFunc, + cel.MemberOverload("ip_family", []*cel.Type{IPType}, cel.IntType, + cel.UnaryBinding(netIPFamily)), + ), + cel.Function(ipFunc, + // K8s Parity: The global overload is named "string_to_ip" + cel.Overload("string_to_ip", []*cel.Type{cel.StringType}, IPType, + cel.UnaryBinding(netIPString)), + // K8s Parity: The member overload is named "cidr_ip" + cel.MemberOverload("cidr_ip", []*cel.Type{CIDRType}, IPType, + cel.UnaryBinding(netCIDRIP)), + ), + cel.Function(ipToString, + cel.Overload("ip_to_string", []*cel.Type{IPType}, cel.StringType, + cel.UnaryBinding(netIPToString)), + ), + cel.Function(isCanonicalFunc, + cel.Overload("ip_is_canonical", []*cel.Type{cel.StringType}, cel.BoolType, + cel.UnaryBinding(netIPIsCanonical)), + ), + cel.Function(isCIDRFunc, + cel.Overload("is_cidr", []*cel.Type{cel.StringType}, cel.BoolType, + cel.UnaryBinding(netIsCIDR)), + ), + cel.Function(isGlobalUnicastFunc, + cel.MemberOverload("ip_is_global_unicast", []*cel.Type{IPType}, cel.BoolType, + cel.UnaryBinding(netIPIsGlobalUnicast)), + ), + cel.Function(isIPFunc, + cel.Overload("is_ip", []*cel.Type{cel.StringType}, cel.BoolType, + cel.UnaryBinding(netIsIP)), + ), + cel.Function(isLinkLocalMcastFunc, + cel.MemberOverload("ip_is_link_local_multicast", []*cel.Type{IPType}, cel.BoolType, + cel.UnaryBinding(netIPIsLinkLocalMulticast)), + ), + cel.Function(isLinkLocalUcastFunc, + cel.MemberOverload("ip_is_link_local_unicast", []*cel.Type{IPType}, cel.BoolType, + cel.UnaryBinding(netIPIsLinkLocalUnicast)), + ), + cel.Function(isLoopbackFunc, + cel.MemberOverload("ip_is_loopback", []*cel.Type{IPType}, cel.BoolType, + cel.UnaryBinding(netIPIsLoopback)), + ), + cel.Function(isMaskFunc, + cel.MemberOverload("cidr_is_mask", []*cel.Type{CIDRType}, cel.BoolType, + cel.UnaryBinding(netCIDRIsMask)), + ), + cel.Function(isUnspecifiedFunc, + cel.MemberOverload("ip_is_unspecified", []*cel.Type{IPType}, cel.BoolType, + cel.UnaryBinding(netIPIsUnspecified)), + ), + cel.Function(maskedFunc, + cel.MemberOverload("cidr_masked", []*cel.Type{CIDRType}, CIDRType, + cel.UnaryBinding(netCIDRMasked)), + ), + cel.Function(prefixLengthFunc, + cel.MemberOverload("cidr_prefix_length", []*cel.Type{CIDRType}, cel.IntType, + cel.UnaryBinding(netCIDRPrefixLength)), + ), + cel.ASTValidators( + networkFormatValidator{funcName: ipFunc, argNum: 0, check: checkIP}, + networkFormatValidator{funcName: cidrFunc, argNum: 0, check: checkCIDR}, + ), + } +} + +func (*networkLib) ProgramOptions() []cel.ProgramOption { + return []cel.ProgramOption{} +} + +// networkAdapter adapts netip types while preserving existing adapters. +type networkAdapter struct { + types.Adapter +} + +func (a *networkAdapter) NativeToValue(value any) ref.Val { + switch v := value.(type) { + case netip.Addr: + return IP{Addr: v} + case netip.Prefix: + return CIDR{Prefix: v} + } + // Delegate to the wrapped adapter (e.g., Protobuf adapter) + return a.Adapter.NativeToValue(value) +} + +// --- Implementation Logic --- + +func netCIDRContainsCIDR(lhs, rhs ref.Val) ref.Val { + parent := lhs.(CIDR) + child := rhs.(CIDR) + return types.Bool(parent.Prefix.Overlaps(child.Prefix) && parent.Prefix.Bits() <= child.Prefix.Bits()) +} + +func netCIDRContainsCIDRString(lhs, rhs ref.Val) ref.Val { + parent := lhs.(CIDR) + s := rhs.(types.String) + childPrefix, err := parseCIDR(string(s)) + if err != nil { + return types.WrapErr(err) + } + return types.Bool(parent.Prefix.Overlaps(childPrefix) && parent.Prefix.Bits() <= childPrefix.Bits()) +} + +func netCIDRContainsIP(lhs, rhs ref.Val) ref.Val { + cidr := lhs.(CIDR) + ip := rhs.(IP) + return types.Bool(cidr.Prefix.Contains(ip.Addr)) +} + +func netCIDRContainsIPString(lhs, rhs ref.Val) ref.Val { + cidr := lhs.(CIDR) + s := rhs.(types.String) + addr, err := parseIPAddr(string(s)) + if err != nil { + return types.WrapErr(err) + } + return types.Bool(cidr.Prefix.Contains(addr)) +} + +func netCIDRIP(val ref.Val) ref.Val { + cidr := val.(CIDR) + return IP{Addr: cidr.Prefix.Addr()} +} + +func netCIDRMasked(val ref.Val) ref.Val { + cidr := val.(CIDR) + return CIDR{Prefix: cidr.Prefix.Masked()} +} + +func netCIDRPrefixLength(val ref.Val) ref.Val { + cidr := val.(CIDR) + return types.Int(cidr.Prefix.Bits()) +} + +func netCIDRString(val ref.Val) ref.Val { + s := val.(types.String) + str := string(s) + prefix, err := parseCIDR(str) + if err != nil { + return types.WrapErr(err) + } + return CIDR{Prefix: prefix} +} + +func netCIDRToString(val ref.Val) ref.Val { + cidr := val.(CIDR) + return types.String(cidr.Prefix.String()) +} + +func netIPFamily(val ref.Val) ref.Val { + ip := val.(IP) + if ip.Addr.Is4() { + return types.Int(4) + } + return types.Int(6) +} + +func netIPIsCanonical(val ref.Val) ref.Val { + s := val.(types.String) + str := string(s) + addr, err := parseIPAddr(str) + if err != nil { + return types.WrapErr(err) + } + return types.Bool(addr.String() == str) +} + +func netIPIsGlobalUnicast(val ref.Val) ref.Val { + ip := val.(IP) + return types.Bool(ip.Addr.IsGlobalUnicast()) +} + +func netIPIsLinkLocalMulticast(val ref.Val) ref.Val { + ip := val.(IP) + return types.Bool(ip.Addr.IsLinkLocalMulticast()) +} + +func netIPIsLinkLocalUnicast(val ref.Val) ref.Val { + ip := val.(IP) + return types.Bool(ip.Addr.IsLinkLocalUnicast()) +} + +func netIPIsLoopback(val ref.Val) ref.Val { + ip := val.(IP) + return types.Bool(ip.Addr.IsLoopback()) +} + +func netIPIsUnspecified(val ref.Val) ref.Val { + ip := val.(IP) + return types.Bool(ip.Addr.IsUnspecified()) +} + +func netIPString(val ref.Val) ref.Val { + s := val.(types.String) + str := string(s) + addr, err := parseIPAddr(str) + if err != nil { + return types.WrapErr(err) + } + return IP{Addr: addr} +} + +func netIPToString(val ref.Val) ref.Val { + ip := val.(IP) + return types.String(ip.Addr.String()) +} + +func netIsCIDR(val ref.Val) ref.Val { + s := val.(types.String) + _, err := parseCIDR(string(s)) + return types.Bool(err == nil) +} + +func netIsIP(val ref.Val) ref.Val { + s := val.(types.String) + _, err := parseIPAddr(string(s)) + return types.Bool(err == nil) +} + +func netCIDRIsMask(val ref.Val) ref.Val { + cidr := val.(CIDR) + return types.Bool(cidr.Prefix.Addr() == cidr.Prefix.Masked().Addr()) +} + +func parseCIDR(raw string) (netip.Prefix, error) { + prefix, err := netip.ParsePrefix(raw) + if err != nil { + return netip.Prefix{}, fmt.Errorf("CIDR %q parse error during conversion from string: %v", raw, err) + } + if prefix.Addr().Zone() != "" { + return netip.Prefix{}, fmt.Errorf("CIDR %q with zone value is not allowed", raw) + } + if prefix.Addr().Is4In6() { + return netip.Prefix{}, fmt.Errorf("IPv4-mapped IPv6 address %q is not allowed", raw) + } + return prefix, nil +} + +func parseIPAddr(raw string) (netip.Addr, error) { + addr, err := netip.ParseAddr(raw) + if err != nil { + return netip.Addr{}, fmt.Errorf("IP Address %q parse error during conversion from string: %v", raw, err) + } + if addr.Zone() != "" { + return netip.Addr{}, fmt.Errorf("IP address %q with zone value is not allowed", raw) + } + if addr.Is4In6() { + return netip.Addr{}, fmt.Errorf("IPv4-mapped IPv6 address %q is not allowed", raw) + } + return addr, nil +} + +// --- Opaque Type Wrappers --- + +type IP struct { + netip.Addr +} + +// ConvertToNative converts the IP value to a native Go type. +func (i IP) ConvertToNative(typeDesc reflect.Type) (any, error) { + if typeDesc == reflect.TypeFor[netip.Addr]() { + return i.Addr, nil + } + if typeDesc.Kind() == reflect.String { + return i.Addr.String(), nil + } + return nil, fmt.Errorf("unsupported type conversion to '%v'", typeDesc) +} + +// ConvertToType converts the IP value to a CEL type. +func (i IP) ConvertToType(typeValue ref.Type) ref.Val { + switch typeValue { + case types.StringType: + return types.String(i.Addr.String()) + case IPType: + return i + case types.TypeType: + return IPType + } + return types.NewErr("type conversion error from '%s' to '%s'", IPType, typeValue) +} + +// Equal returns true if this IP is equal to the other ref.Val. +func (i IP) Equal(other ref.Val) ref.Val { + o, ok := other.(IP) + if !ok { + return types.False + } + return types.Bool(i.Addr == o.Addr) +} + +// Type returns the CEL type of the IP. +func (i IP) Type() ref.Type { + return IPType +} + +// Value returns the raw Go value (netip.Addr) of the IP. +func (i IP) Value() any { + return i.Addr +} + +type CIDR struct { + netip.Prefix +} + +// ConvertToNative converts the CIDR value to a native Go type. +func (c CIDR) ConvertToNative(typeDesc reflect.Type) (any, error) { + if typeDesc == reflect.TypeFor[netip.Prefix]() { + return c.Prefix, nil + } + if typeDesc.Kind() == reflect.String { + return c.Prefix.String(), nil + } + return nil, fmt.Errorf("unsupported type conversion to '%v'", typeDesc) +} + +// ConvertToType converts the CIDR value to a CEL type. +func (c CIDR) ConvertToType(typeValue ref.Type) ref.Val { + switch typeValue { + case types.StringType: + return types.String(c.Prefix.String()) + case CIDRType: + return c + case types.TypeType: + return CIDRType + } + return types.NewErr("type conversion error from '%s' to '%s'", CIDRType, typeValue) +} + +// Equal returns true if this CIDR is equal to the other ref.Val. +func (c CIDR) Equal(other ref.Val) ref.Val { + o, ok := other.(CIDR) + if !ok { + return types.False + } + return types.Bool(c.Prefix == o.Prefix) +} + +// Type returns the CEL type of the CIDR. +func (c CIDR) Type() ref.Type { + return CIDRType +} + +// Value returns the raw Go value (netip.Prefix) of the CIDR. +func (c CIDR) Value() any { + return c.Prefix +} + +// --- Static Validators --- + +type argChecker func(e *cel.Env, call, arg ast.Expr) error + +type networkFormatValidator struct { + funcName string + argNum int + check argChecker +} + +func (v networkFormatValidator) Name() string { + return fmt.Sprintf("cel.validator.network.%s", v.funcName) +} + +func (v networkFormatValidator) Validate(e *cel.Env, _ cel.ValidatorConfig, a *ast.AST, iss *cel.Issues) { + root := ast.NavigateAST(a) + funcCalls := ast.MatchDescendants(root, ast.FunctionMatcher(v.funcName)) + for _, call := range funcCalls { + callArgs := call.AsCall().Args() + if len(callArgs) <= v.argNum { + continue + } + litArg := callArgs[v.argNum] + if litArg.Kind() != ast.LiteralKind { + continue + } + if err := v.check(e, call, litArg); err != nil { + iss.ReportErrorAtID(litArg.ID(), "invalid %s argument: %v", v.funcName, err) + } + } +} + +func checkIP(e *cel.Env, call, arg ast.Expr) error { + pattern := arg.AsLiteral().Value().(string) + _, err := parseIPAddr(pattern) + return err +} + +func checkCIDR(e *cel.Env, call, arg ast.Expr) error { + pattern := arg.AsLiteral().Value().(string) + _, err := parseCIDR(pattern) + return err +} diff --git a/vendor/github.com/google/cel-go/ext/strings.go b/vendor/github.com/google/cel-go/ext/strings.go index 0731a08174..1f7732f2fb 100644 --- a/vendor/github.com/google/cel-go/ext/strings.go +++ b/vendor/github.com/google/cel-go/ext/strings.go @@ -663,15 +663,19 @@ func indexOf(str, substr string) (int64, error) { } func indexOfOffset(str, substr string, offset int64) (int64, error) { - if substr == "" { - return offset, nil - } off := int(offset) - runes := []rune(str) - subrunes := []rune(substr) if off < 0 { return -1, fmt.Errorf("index out of range: %d", off) } + runes := []rune(str) + if substr == "" { + // The empty string matches at the search offset, clamped to the end of the string. + if off > len(runes) { + return int64(len(runes)), nil + } + return offset, nil + } + subrunes := []rune(substr) // If the offset exceeds the length, return -1 rather than error. if off >= len(runes) { return -1, nil @@ -704,15 +708,19 @@ func lastIndexOf(str, substr string) (int64, error) { } func lastIndexOfOffset(str, substr string, offset int64) (int64, error) { - if substr == "" { - return offset, nil - } off := int(offset) - runes := []rune(str) - subrunes := []rune(substr) if off < 0 { return -1, fmt.Errorf("index out of range: %d", off) } + runes := []rune(str) + if substr == "" { + // The empty string matches at the search offset, clamped to the end of the string. + if off > len(runes) { + return int64(len(runes)), nil + } + return offset, nil + } + subrunes := []rune(substr) // If the offset is far greater than the length return -1 if off >= len(runes) { return -1, nil diff --git a/vendor/github.com/google/cel-go/interpreter/BUILD.bazel b/vendor/github.com/google/cel-go/interpreter/BUILD.bazel index 220e23d475..1274a6f12d 100644 --- a/vendor/github.com/google/cel-go/interpreter/BUILD.bazel +++ b/vendor/github.com/google/cel-go/interpreter/BUILD.bazel @@ -14,6 +14,7 @@ go_library( "decorators.go", "dispatcher.go", "evalstate.go", + "frame.go", "interpretable.go", "interpreter.go", "optimizations.go", @@ -47,6 +48,7 @@ go_test( "activation_test.go", "attribute_patterns_test.go", "attributes_test.go", + "frame_test.go", "interpreter_test.go", "prune_test.go", "runtimecost_test.go", diff --git a/vendor/github.com/google/cel-go/interpreter/activation.go b/vendor/github.com/google/cel-go/interpreter/activation.go index dd40619ee1..023efbb807 100644 --- a/vendor/github.com/google/cel-go/interpreter/activation.go +++ b/vendor/github.com/google/cel-go/interpreter/activation.go @@ -110,8 +110,9 @@ func (a *mapActivation) ResolveName(name string) (any, bool) { // hierarchicalActivation which implements Activation and contains a parent and // child activation. type hierarchicalActivation struct { - parent Activation - child Activation + parent Activation + child Activation + poolAllocated bool } // Parent implements the Activation interface method. @@ -127,10 +128,28 @@ func (a *hierarchicalActivation) ResolveName(name string) (any, bool) { return a.parent.ResolveName(name) } +// Unwrap returns the parent activation, stripping the local child scope. +// This allows global disambiguation to skip past locally introduced variables. +func (a *hierarchicalActivation) Unwrap() Activation { + return a.parent +} + +// AsPartialActivation checks the child first via direct type assertion (to +// avoid recursion through the folder → frame → hierarchicalActivation cycle), +// then walks the parent hierarchy via the free function. +func (a *hierarchicalActivation) AsPartialActivation() (PartialActivation, bool) { + if pv, ok := a.child.(partialActivationConverter); ok { + if p, ok := pv.AsPartialActivation(); ok { + return p, true + } + } + return AsPartialActivation(a.parent) +} + // NewHierarchicalActivation takes two activations and produces a new one which prioritizes // resolution in the child first and parent(s) second. func NewHierarchicalActivation(parent Activation, child Activation) Activation { - return &hierarchicalActivation{parent, child} + return &hierarchicalActivation{parent: parent, child: child, poolAllocated: false} } // NewPartialActivation returns an Activation which contains a list of AttributePattern values diff --git a/vendor/github.com/google/cel-go/interpreter/attributes.go b/vendor/github.com/google/cel-go/interpreter/attributes.go index 6b8b5c1b6d..26d8eb0f3e 100644 --- a/vendor/github.com/google/cel-go/interpreter/attributes.go +++ b/vendor/github.com/google/cel-go/interpreter/attributes.go @@ -190,7 +190,7 @@ func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAtt func (r *attrFactory) ConditionalAttribute(id int64, expr Interpretable, t, f Attribute) Attribute { return &conditionalAttribute{ id: id, - expr: expr, + expr: adaptToV2(expr), truthy: t, falsy: f, adapter: r.adapter, @@ -225,7 +225,7 @@ func (r *attrFactory) MaybeAttribute(id int64, name string) Attribute { func (r *attrFactory) RelativeAttribute(id int64, operand Interpretable) Attribute { return &relativeAttribute{ id: id, - operand: operand, + operand: adaptToV2(operand), qualifiers: []Qualifier{}, adapter: r.adapter, fac: r, @@ -384,7 +384,7 @@ func (a *absoluteAttribute) Resolve(vars Activation) (any, error) { type conditionalAttribute struct { id int64 - expr Interpretable + expr InterpretableV2 truthy Attribute falsy Attribute adapter types.Adapter @@ -571,7 +571,7 @@ func (a *maybeAttribute) String() string { type relativeAttribute struct { id int64 - operand Interpretable + operand InterpretableV2 qualifiers []Qualifier adapter types.Adapter fac AttributeFactory @@ -964,9 +964,11 @@ func (q *intQualifier) qualifyInternal(vars Activation, obj any, presenceTest, p } case map[int32]any: isMap = true - obj, isKey := o[int32(i)] - if isKey { - return obj, true, nil + if i32 := int32(i); int64(i32) == i { + obj, isKey := o[i32] + if isKey { + return obj, true, nil + } } case map[int64]any: isMap = true @@ -1089,9 +1091,11 @@ func (q *uintQualifier) qualifyInternal(vars Activation, obj any, presenceTest, return obj, true, nil } case map[uint32]any: - obj, isKey := o[uint32(u)] - if isKey { - return obj, true, nil + if u32 := uint32(u); uint64(u32) == u { + obj, isKey := o[u32] + if isKey { + return obj, true, nil + } } case map[uint64]any: obj, isKey := o[u] @@ -1301,7 +1305,7 @@ func applyQualifiers(vars Activation, obj any, qualifiers []Qualifier) (any, boo if !optObj.HasValue() { return optObj, false, nil } - obj = optObj.GetValue().Value() + obj = optObj.GetValue() } var err error diff --git a/vendor/github.com/google/cel-go/interpreter/decorators.go b/vendor/github.com/google/cel-go/interpreter/decorators.go index 502db35fc0..9c973664ac 100644 --- a/vendor/github.com/google/cel-go/interpreter/decorators.go +++ b/vendor/github.com/google/cel-go/interpreter/decorators.go @@ -25,9 +25,13 @@ import ( // Interpretable expression nodes at construction time. type InterpretableDecorator func(Interpretable) (Interpretable, error) +// InterpretableDecoratorV2 is a functional interface for decorating or replacing +// InterpretableV2 expression nodes at construction time. +type InterpretableDecoratorV2 func(InterpretableV2) (InterpretableV2, error) + // decObserveEval records evaluation state into an EvalState object. -func decObserveEval(observer EvalObserver) InterpretableDecorator { - return func(i Interpretable) (Interpretable, error) { +func decObserveEval(observer EvalObserver) InterpretableDecoratorV2 { + return func(i InterpretableV2) (InterpretableV2, error) { switch inst := i.(type) { case *evalWatch, *evalWatchAttr, *evalWatchConst, *evalWatchConstructor: // these instruction are already watching, return straight-away. @@ -49,8 +53,8 @@ func decObserveEval(observer EvalObserver) InterpretableDecorator { }, nil default: return &evalWatch{ - Interpretable: i, - observer: observer, + InterpretableV2: i, + observer: observer, }, nil } } @@ -58,8 +62,8 @@ func decObserveEval(observer EvalObserver) InterpretableDecorator { // decInterruptFolds creates an intepretable decorator which marks comprehensions as interruptable // where the interrupt state is communicated via a hidden variable on the Activation. -func decInterruptFolds() InterpretableDecorator { - return func(i Interpretable) (Interpretable, error) { +func decInterruptFolds() InterpretableDecoratorV2 { + return func(i InterpretableV2) (InterpretableV2, error) { fold, ok := i.(*evalFold) if !ok { return i, nil @@ -70,8 +74,8 @@ func decInterruptFolds() InterpretableDecorator { } // decDisableShortcircuits ensures that all branches of an expression will be evaluated, no short-circuiting. -func decDisableShortcircuits() InterpretableDecorator { - return func(i Interpretable) (Interpretable, error) { +func decDisableShortcircuits() InterpretableDecoratorV2 { + return func(i InterpretableV2) (InterpretableV2, error) { switch expr := i.(type) { case *evalOr: return &evalExhaustiveOr{ @@ -104,8 +108,8 @@ func decDisableShortcircuits() InterpretableDecorator { // conditionally precomputing the result. // - build list and map values with constant elements. // - convert 'in' operations to set membership tests if possible. -func decOptimize() InterpretableDecorator { - return func(i Interpretable) (Interpretable, error) { +func decOptimize() InterpretableDecoratorV2 { + return func(i InterpretableV2) (InterpretableV2, error) { switch inst := i.(type) { case *evalList: return maybeBuildListLiteral(i, inst) @@ -124,7 +128,7 @@ func decOptimize() InterpretableDecorator { } // decRegexOptimizer compiles regex pattern string constants. -func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDecorator { +func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDecoratorV2 { functionMatchMap := make(map[string]*RegexOptimization) overloadMatchMap := make(map[string]*RegexOptimization) for _, m := range regexOptimizations { @@ -134,7 +138,7 @@ func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDe } } - return func(i Interpretable) (Interpretable, error) { + return func(i InterpretableV2) (InterpretableV2, error) { call, ok := i.(InterpretableCall) if !ok { return i, nil @@ -165,7 +169,7 @@ func decRegexOptimizer(regexOptimizations ...*RegexOptimization) InterpretableDe } } -func maybeOptimizeConstUnary(i Interpretable, call InterpretableCall) (Interpretable, error) { +func maybeOptimizeConstUnary(i InterpretableV2, call InterpretableCall) (InterpretableV2, error) { args := call.Args() if len(args) != 1 { return i, nil @@ -181,7 +185,7 @@ func maybeOptimizeConstUnary(i Interpretable, call InterpretableCall) (Interpret return NewConstValue(call.ID(), val), nil } -func maybeBuildListLiteral(i Interpretable, l *evalList) (Interpretable, error) { +func maybeBuildListLiteral(i InterpretableV2, l *evalList) (InterpretableV2, error) { for _, elem := range l.elems { _, isConst := elem.(InterpretableConst) if !isConst { @@ -191,7 +195,7 @@ func maybeBuildListLiteral(i Interpretable, l *evalList) (Interpretable, error) return NewConstValue(l.ID(), l.Eval(EmptyActivation())), nil } -func maybeBuildMapLiteral(i Interpretable, mp *evalMap) (Interpretable, error) { +func maybeBuildMapLiteral(i InterpretableV2, mp *evalMap) (InterpretableV2, error) { for idx, key := range mp.keys { _, isConst := key.(InterpretableConst) if !isConst { @@ -209,7 +213,7 @@ func maybeBuildMapLiteral(i Interpretable, mp *evalMap) (Interpretable, error) { // test if the following conditions are true: // - the list is a constant with homogeneous element types. // - the elements are all of primitive type. -func maybeOptimizeSetMembership(i Interpretable, inlist InterpretableCall) (Interpretable, error) { +func maybeOptimizeSetMembership(i InterpretableV2, inlist InterpretableCall) (InterpretableV2, error) { args := inlist.Args() lhs := args[0] rhs := args[1] diff --git a/vendor/github.com/google/cel-go/interpreter/frame.go b/vendor/github.com/google/cel-go/interpreter/frame.go new file mode 100644 index 0000000000..3cf2d6b6b5 --- /dev/null +++ b/vendor/github.com/google/cel-go/interpreter/frame.go @@ -0,0 +1,327 @@ +// Copyright 2026 Google LLC +// +// 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 interpreter + +import ( + "context" + "errors" + "fmt" + "sync" + "sync/atomic" + + "github.com/google/cel-go/common/types/ref" +) + +// evalContext contains the stateful information needed for a single evaluation. +// +// This state is shared across all frames within a single evaluation, including +// child frames created for comprehension blocks. +type evalContext struct { + // interrupt exposes a callback channel for cancellation. + interrupt <-chan struct{} + + // interruptCheckCount is the number of times the interrupt channel has been checked. + interruptCheckCount atomic.Uint64 + + // interruptCheckFrequency is the frequency at which the interrupt channel is checked. + interruptCheckFrequency uint + + // interrupted indicates whether the evaluation has been interrupted. + interrupted atomic.Bool + + // state provides the context for tracking the evaluation state. + state EvalState + + // costs provides the context for tracking the evaluation costs. + costs *CostTracker + + // ctx is the context for async call implementations to use. + ctx context.Context + + // cancel cancels the context when the evaluation is finished. + cancel context.CancelFunc +} + +// ExecutionFrame provides the context for a single evaluation of an expression. +// +// The execution frame must not be stored in any fashion as its lifecycle is completely +// controlled by the CEL evaluation process. +type ExecutionFrame struct { + // Activation provides the context for resolving variables by name. + Activation + + // parent provides the context for parent scopes (used for comprehension iterators). + parent *ExecutionFrame + + // ctx provides the shared evaluation state across frames. + ctx *evalContext +} + +// NewExecutionFrame creates a new execution frame from the pool. +func NewExecutionFrame(input any) (*ExecutionFrame, error) { + f := frameStack.Get().(*ExecutionFrame) + switch v := input.(type) { + case Activation: + f.Activation = v + case map[string]any: + f.Activation = activationInput.create(v) + default: + return nil, fmt.Errorf("invalid input, wanted Activation or map[string]any, got: (%T)%v", input, input) + } + return f, nil +} + +// SetContext sets the context for the execution frame. +func (f *ExecutionFrame) SetContext(ctx context.Context, interruptCheckFrequency uint) error { + if f.parent != nil { + return errors.New("SetContext() called on child frame") + } + if f.ctx != nil { + return errors.New("SetContext() called more than once") + } + f.ctx = evalContextPool.Get().(*evalContext) + f.ctx.ctx, f.ctx.cancel = context.WithCancel(ctx) + f.ctx.interrupt = ctx.Done() + f.ctx.interruptCheckFrequency = interruptCheckFrequency + f.ctx.interruptCheckCount.Store(0) + f.ctx.interrupted.Store(false) + return nil +} + +// Close releases the resources held by the execution frame and returns it to the pool. +func (f *ExecutionFrame) Close() { + if f.parent == nil && f.ctx != nil { + if f.ctx.cancel != nil { + f.ctx.cancel() + f.ctx.cancel = nil + } + f.ctx.ctx = nil + f.ctx.interrupt = nil + f.ctx.state = nil + f.ctx.costs = nil + f.ctx.interrupted.Store(false) + f.ctx.interruptCheckCount.Store(0) + f.ctx.interruptCheckFrequency = 0 + evalContextPool.Put(f.ctx) + } + f.ctx = nil + f.parent = nil + switch a := f.Activation.(type) { + case *hierarchicalActivation: + if child, ok := a.child.(*inputActivation); ok { + activationInput.release(child) + } + activationStack.release(a) + case *inputActivation: + activationInput.release(a) + } + f.Activation = nil + frameStack.Put(f) +} + +// Push pushes the given activation onto the activation stack and returns the new frame. +// +// This operation is internal to the interpreter and is used to handle comprehension +// scoping. The child frame inherits the shared evalContext from the parent. +func (f *ExecutionFrame) Push(activation Activation) *ExecutionFrame { + child := frameStack.Get().(*ExecutionFrame) + child.parent = f + child.ctx = f.ctx + child.Activation = activationStack.create(f.Activation, activation) + return child +} + +// Pop returns the parent frame, releasing the current frame back to the pool. +func (f *ExecutionFrame) Pop() *ExecutionFrame { + if f.parent == nil { + return f + } + parent := f.parent + activationStack.release(f.Activation) + f.Activation = nil + f.parent = nil + f.ctx = nil + frameStack.Put(f) + return parent +} + +// ResolveName implements the Activation interface by proxying to the internal activation. +func (f *ExecutionFrame) ResolveName(name string) (any, bool) { + return f.Activation.ResolveName(name) +} + +// Parent implements the Activation interface by proxying to the internal activation. +func (f *ExecutionFrame) Parent() Activation { + return f.Activation.Parent() +} + +// AsPartialActivation implements the PartialActivation interface by proxying to the internal activation. +func (f *ExecutionFrame) AsPartialActivation() (PartialActivation, bool) { + return AsPartialActivation(f.Activation) +} + +// Unwrap returns the internal activation. +func (f *ExecutionFrame) Unwrap() Activation { + return f.Activation +} + +// CheckInterrupt returns whether the evaluation has been interrupted. +func (f *ExecutionFrame) CheckInterrupt() bool { + if f.ctx == nil { + return false + } + if f.ctx.interrupted.Load() { + return true + } + count := f.ctx.interruptCheckCount.Add(1) + if f.ctx.interruptCheckFrequency > 0 && count%uint64(f.ctx.interruptCheckFrequency) == 0 { + select { + case <-f.ctx.interrupt: + f.ctx.interrupted.Store(true) + return true + default: + return false + } + } + return false +} + +// frameStack provides a synchronized pool of ExecutionFrames. +var frameStack = &sync.Pool{ + New: func() any { + return &ExecutionFrame{} + }, +} + +// evalContextPool provides a synchronized pool of evalContexts. +var evalContextPool = &sync.Pool{ + New: func() any { + return &evalContext{} + }, +} + +type activationStackPool struct { + sync.Pool +} + +func (pool *activationStackPool) create(parent, child Activation) Activation { + h := pool.Get().(*hierarchicalActivation) + h.child = child + h.parent = parent + h.poolAllocated = true + return h +} + +func (pool *activationStackPool) release(activation Activation) { + h, ok := activation.(*hierarchicalActivation) + if !ok || !h.poolAllocated { + return + } + h.parent = nil + h.child = nil + pool.Pool.Put(h) +} + +func newActivationStackPool() *activationStackPool { + return &activationStackPool{ + Pool: sync.Pool{ + New: func() any { + return &hierarchicalActivation{} + }, + }, + } +} + +type inputActivation struct { + vars map[string]any + lazyVars map[string]any +} + +// ResolveName looks up the value of the input variable name, if found. +// +// Lazy bindings may be supplied within the map-based input in either of the following forms: +// - func() any +// - func() ref.Val +// +// The lazy binding will only be invoked once per evaluation. +// +// Values which are not represented as ref.Val types on input may be adapted to a ref.Val using +// the types.Adapter configured in the environment. +func (a *inputActivation) ResolveName(name string) (any, bool) { + v, found := a.vars[name] + if !found { + return nil, false + } + switch obj := v.(type) { + case func() ref.Val: + if resolved, found := a.lazyVars[name]; found { + return resolved, true + } + lazy := obj() + a.lazyVars[name] = lazy + return lazy, true + case func() any: + if resolved, found := a.lazyVars[name]; found { + return resolved, true + } + lazy := obj() + a.lazyVars[name] = lazy + return lazy, true + default: + return obj, true + } +} + +// Parent implements the Activation interface +func (a *inputActivation) Parent() Activation { + return nil +} + +func newActivationInputPool() *activationInputPool { + return &activationInputPool{ + Pool: sync.Pool{ + New: func() any { + return &inputActivation{ + lazyVars: make(map[string]any), + } + }, + }, + } +} + +type activationInputPool struct { + sync.Pool +} + +// create initializes a pooled Activation object with the map input. +func (p *activationInputPool) create(vars map[string]any) *inputActivation { + a := p.Pool.Get().(*inputActivation) + a.vars = vars + return a +} + +func (p *activationInputPool) release(value any) { + a := value.(*inputActivation) + for k := range a.lazyVars { + delete(a.lazyVars, k) + } + a.vars = nil + p.Pool.Put(a) +} + +var ( + activationStack = newActivationStackPool() + activationInput = newActivationInputPool() +) diff --git a/vendor/github.com/google/cel-go/interpreter/interpretable.go b/vendor/github.com/google/cel-go/interpreter/interpretable.go index 50e66d6373..5b94e23e6c 100644 --- a/vendor/github.com/google/cel-go/interpreter/interpretable.go +++ b/vendor/github.com/google/cel-go/interpreter/interpretable.go @@ -26,20 +26,53 @@ import ( "github.com/google/cel-go/common/types/traits" ) -// Interpretable can accept a given Activation and produce a value along with -// an accompanying EvalState which can be used to inspect whether additional -// data might be necessary to complete the evaluation. +// Interpretable evaluates an Activation and produces a value. type Interpretable interface { // ID value corresponding to the expression node. ID() int64 - // Eval an Activation to produce an output. + // Eval evaluates an Activation and produces an output. Eval(activation Activation) ref.Val } +// InterpretableV2 evaluates an ExecutionFrame and produces a value. +// +// The ExecutionFrame should not be stored and should always be passed as the first +// argument to any function as it behaves like Golang's context.Context. +type InterpretableV2 interface { + Interpretable + + // Exec evaluates the expression within the given ExecutionFrame. + Exec(frame *ExecutionFrame) ref.Val +} + +// adaptToV2 adapts a V1 Interpretable implementation to the V2 interface. +// +// This adapter is used to bridge the legacy Interpretable interface to the +// modern InterpretableV2 interface, providing a shim that allows the use of +// both interfaces in the same system. +func adaptToV2(i Interpretable) InterpretableV2 { + switch v := i.(type) { + case InterpretableV2: + return v + default: + return &v1Adapter{Interpretable: v} + } +} + +// v1Adapter handles bridging a V1 Interpretable implementation to the V2 interface. +type v1Adapter struct { + Interpretable +} + +// Exec implements the InterpretableV2 interface method. +func (a *v1Adapter) Exec(f *ExecutionFrame) ref.Val { + return a.Eval(f) +} + // InterpretableConst interface for tracking whether the Interpretable is a constant value. type InterpretableConst interface { - Interpretable + InterpretableV2 // Value returns the constant value of the instruction. Value() ref.Val @@ -47,7 +80,7 @@ type InterpretableConst interface { // InterpretableAttribute interface for tracking whether the Interpretable is an attribute. type InterpretableAttribute interface { - Interpretable + InterpretableV2 // Attr returns the Attribute value. Attr() Attribute @@ -81,7 +114,7 @@ type InterpretableAttribute interface { // InterpretableCall interface for inspecting Interpretable instructions related to function calls. type InterpretableCall interface { - Interpretable + InterpretableV2 // Function returns the function name as it appears in text or mangled operator name as it // appears in the operators.go file. @@ -94,16 +127,16 @@ type InterpretableCall interface { // Args returns the normalized arguments to the function overload. // For receiver-style functions, the receiver target is arg 0. - Args() []Interpretable + Args() []InterpretableV2 } // InterpretableConstructor interface for inspecting Interpretable instructions that initialize a list, map // or struct. type InterpretableConstructor interface { - Interpretable + InterpretableV2 // InitVals returns all the list elements, map key and values or struct field values. - InitVals() []Interpretable + InitVals() []InterpretableV2 // Type returns the type constructed. Type() ref.Type @@ -112,18 +145,23 @@ type InterpretableConstructor interface { // ObservableInterpretable is an Interpretable which supports stateful observation, such as tracing // or cost-tracking. type ObservableInterpretable struct { - Interpretable + InterpretableV2 observers []StatefulObserver } // ID implements the Interpretable method to get the expression id associated with the step. func (oi *ObservableInterpretable) ID() int64 { - return oi.Interpretable.ID() + return oi.InterpretableV2.ID() +} + +// Exec implements the InterpretableV2 interface method. +func (oi *ObservableInterpretable) Exec(frame *ExecutionFrame) ref.Val { + return oi.ObserveExec(frame, func(any) {}) } // Eval proxies to the ObserveEval method while invoking a no-op callback to report the observations. func (oi *ObservableInterpretable) Eval(vars Activation) ref.Val { - return oi.ObserveEval(vars, func(any) {}) + return oi.ObserveExec(AsFrame(vars), func(any) {}) } // ObserveEval evaluates an interpretable and performs per-evaluation state-tracking. @@ -131,25 +169,65 @@ func (oi *ObservableInterpretable) Eval(vars Activation) ref.Val { // This method is concurrency safe and the expectation is that the observer function will use // a switch statement to determine the type of the state which has been reported back from the call. func (oi *ObservableInterpretable) ObserveEval(vars Activation, observer func(any)) ref.Val { - var err error + return oi.ObserveExec(AsFrame(vars), observer) +} + +// ObserveExec evaluates an interpretable and performs per-evaluation state-tracking. +// +// This method is concurrency safe and the expectation is that the observer function will use +// a switch statement to determine the type of the state which has been reported back from the call. +func (oi *ObservableInterpretable) ObserveExec(frame *ExecutionFrame, observer func(any)) ref.Val { // Initialize the state needed for the observers to function. for _, obs := range oi.observers { - vars, err = obs.InitState(vars) + state, err := obs.InitState(frame) if err != nil { return types.WrapErr(err) } // Provide an initial reference to the state to ensure state is available // even in cases of interrupting errors generated during evaluation. - observer(obs.GetState(vars)) + observer(state) } - result := oi.Interpretable.Eval(vars) + result := oi.InterpretableV2.Exec(frame) // Get the state which needs to be reported back as having been observed. for _, obs := range oi.observers { - observer(obs.GetState(vars)) + observer(obs.GetState(frame)) } return result } +// AsFrame promotes an Activation to an ExecutionFrame. +func AsFrame(a Activation) *ExecutionFrame { + if f, ok := a.(*ExecutionFrame); ok { + return f + } + frame := &ExecutionFrame{Activation: a} + // Walk the activation hierarchy to find a parent ExecutionFrame and inherit + // its shared context. + if parent := findFrame(a); parent != nil { + frame.ctx = parent.ctx + } + return frame +} + +// findFrame walks the activation hierarchy via Unwrap and Parent to locate an +// existing ExecutionFrame, if one exists. +func findFrame(a Activation) *ExecutionFrame { + if wrapper, ok := a.(activationWrapper); ok { + unwrapped := wrapper.Unwrap() + if f, ok := unwrapped.(*ExecutionFrame); ok { + return f + } + return findFrame(unwrapped) + } + if p := a.Parent(); p != nil { + if f, ok := p.(*ExecutionFrame); ok { + return f + } + return findFrame(p) + } + return nil +} + // Core Interpretable implementations used during the program planning phase. type evalTestOnly struct { @@ -162,9 +240,9 @@ func (test *evalTestOnly) ID() int64 { return test.id } -// Eval implements the Interpretable interface method. -func (test *evalTestOnly) Eval(ctx Activation) ref.Val { - val, err := test.Resolve(ctx) +// Exec implements the InterpretableV2 interface method. +func (test *evalTestOnly) Exec(frame *ExecutionFrame) ref.Val { + val, err := test.Resolve(frame) // Return an error if the resolve step fails if err != nil { return types.LabelErrNode(test.id, types.WrapErr(err)) @@ -175,6 +253,11 @@ func (test *evalTestOnly) Eval(ctx Activation) ref.Val { return test.Adapter().NativeToValue(val) } +// Eval implements the Interpretable interface method. +func (test *evalTestOnly) Eval(ctx Activation) ref.Val { + return test.Exec(AsFrame(ctx)) +} + // AddQualifier appends a qualifier that will always and only perform a presence test. func (test *evalTestOnly) AddQualifier(q Qualifier) (Attribute, error) { cq, ok := q.(ConstantQualifier) @@ -194,7 +277,7 @@ func (q *testOnlyQualifier) Qualify(vars Activation, obj any) (any, error) { if err != nil { return nil, err } - if unk, isUnk := out.(types.Unknown); isUnk { + if unk, isUnk := out.(*types.Unknown); isUnk { return unk, nil } return present, nil @@ -230,6 +313,11 @@ func (cons *evalConst) ID() int64 { return cons.id } +// Exec implements the InterpretableV2 interface method. +func (cons *evalConst) Exec(frame *ExecutionFrame) ref.Val { + return cons.val +} + // Eval implements the Interpretable interface method. func (cons *evalConst) Eval(ctx Activation) ref.Val { return cons.val @@ -242,7 +330,7 @@ func (cons *evalConst) Value() ref.Val { type evalOr struct { id int64 - terms []Interpretable + terms []InterpretableV2 } // ID implements the Interpretable interface method. @@ -250,12 +338,12 @@ func (or *evalOr) ID() int64 { return or.id } -// Eval implements the Interpretable interface method. -func (or *evalOr) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (or *evalOr) Exec(frame *ExecutionFrame) ref.Val { var err ref.Val = nil var unk *types.Unknown for _, term := range or.terms { - val := term.Eval(ctx) + val := term.Exec(frame) boolVal, ok := val.(types.Bool) // short-circuit on true. if ok && boolVal == types.True { @@ -283,9 +371,14 @@ func (or *evalOr) Eval(ctx Activation) ref.Val { return types.False } +// Eval implements the Interpretable interface method. +func (or *evalOr) Eval(ctx Activation) ref.Val { + return or.Exec(AsFrame(ctx)) +} + type evalAnd struct { id int64 - terms []Interpretable + terms []InterpretableV2 } // ID implements the Interpretable interface method. @@ -293,12 +386,12 @@ func (and *evalAnd) ID() int64 { return and.id } -// Eval implements the Interpretable interface method. -func (and *evalAnd) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (and *evalAnd) Exec(frame *ExecutionFrame) ref.Val { var err ref.Val = nil var unk *types.Unknown for _, term := range and.terms { - val := term.Eval(ctx) + val := term.Exec(frame) boolVal, ok := val.(types.Bool) // short-circuit on false. if ok && boolVal == types.False { @@ -326,10 +419,15 @@ func (and *evalAnd) Eval(ctx Activation) ref.Val { return types.True } +// Eval implements the Interpretable interface method. +func (and *evalAnd) Eval(ctx Activation) ref.Val { + return and.Exec(AsFrame(ctx)) +} + type evalEq struct { id int64 - lhs Interpretable - rhs Interpretable + lhs InterpretableV2 + rhs InterpretableV2 } // ID implements the Interpretable interface method. @@ -337,10 +435,10 @@ func (eq *evalEq) ID() int64 { return eq.id } -// Eval implements the Interpretable interface method. -func (eq *evalEq) Eval(ctx Activation) ref.Val { - lVal := eq.lhs.Eval(ctx) - rVal := eq.rhs.Eval(ctx) +// Exec implements the InterpretableV2 interface method. +func (eq *evalEq) Exec(frame *ExecutionFrame) ref.Val { + lVal := eq.lhs.Exec(frame) + rVal := eq.rhs.Exec(frame) if types.IsUnknownOrError(lVal) { return lVal } @@ -350,6 +448,11 @@ func (eq *evalEq) Eval(ctx Activation) ref.Val { return types.Equal(lVal, rVal) } +// Eval implements the Interpretable interface method. +func (eq *evalEq) Eval(ctx Activation) ref.Val { + return eq.Exec(AsFrame(ctx)) +} + // Function implements the InterpretableCall interface method. func (*evalEq) Function() string { return operators.Equals @@ -361,14 +464,14 @@ func (*evalEq) OverloadID() string { } // Args implements the InterpretableCall interface method. -func (eq *evalEq) Args() []Interpretable { - return []Interpretable{eq.lhs, eq.rhs} +func (eq *evalEq) Args() []InterpretableV2 { + return []InterpretableV2{eq.lhs, eq.rhs} } type evalNe struct { id int64 - lhs Interpretable - rhs Interpretable + lhs InterpretableV2 + rhs InterpretableV2 } // ID implements the Interpretable interface method. @@ -376,10 +479,10 @@ func (ne *evalNe) ID() int64 { return ne.id } -// Eval implements the Interpretable interface method. -func (ne *evalNe) Eval(ctx Activation) ref.Val { - lVal := ne.lhs.Eval(ctx) - rVal := ne.rhs.Eval(ctx) +// Exec implements the InterpretableV2 interface method. +func (ne *evalNe) Exec(frame *ExecutionFrame) ref.Val { + lVal := ne.lhs.Exec(frame) + rVal := ne.rhs.Exec(frame) if types.IsUnknownOrError(lVal) { return lVal } @@ -389,6 +492,11 @@ func (ne *evalNe) Eval(ctx Activation) ref.Val { return types.Bool(types.Equal(lVal, rVal) != types.True) } +// Eval implements the Interpretable interface method. +func (ne *evalNe) Eval(ctx Activation) ref.Val { + return ne.Exec(AsFrame(ctx)) +} + // Function implements the InterpretableCall interface method. func (*evalNe) Function() string { return operators.NotEquals @@ -400,8 +508,8 @@ func (*evalNe) OverloadID() string { } // Args implements the InterpretableCall interface method. -func (ne *evalNe) Args() []Interpretable { - return []Interpretable{ne.lhs, ne.rhs} +func (ne *evalNe) Args() []InterpretableV2 { + return []InterpretableV2{ne.lhs, ne.rhs} } type evalZeroArity struct { @@ -416,9 +524,14 @@ func (zero *evalZeroArity) ID() int64 { return zero.id } +// Exec implements the InterpretableV2 interface method. +func (zero *evalZeroArity) Exec(frame *ExecutionFrame) ref.Val { + return types.LabelErrNode(zero.id, zero.impl()) +} + // Eval implements the Interpretable interface method. func (zero *evalZeroArity) Eval(ctx Activation) ref.Val { - return types.LabelErrNode(zero.id, zero.impl()) + return zero.Exec(AsFrame(ctx)) } // Function implements the InterpretableCall interface method. @@ -432,15 +545,15 @@ func (zero *evalZeroArity) OverloadID() string { } // Args returns the argument to the unary function. -func (zero *evalZeroArity) Args() []Interpretable { - return []Interpretable{} +func (zero *evalZeroArity) Args() []InterpretableV2 { + return []InterpretableV2{} } type evalUnary struct { id int64 function string overload string - arg Interpretable + arg InterpretableV2 trait int impl functions.UnaryOp nonStrict bool @@ -451,9 +564,9 @@ func (un *evalUnary) ID() int64 { return un.id } -// Eval implements the Interpretable interface method. -func (un *evalUnary) Eval(ctx Activation) ref.Val { - argVal := un.arg.Eval(ctx) +// Exec implements the InterpretableV2 interface method. +func (un *evalUnary) Exec(frame *ExecutionFrame) ref.Val { + argVal := un.arg.Exec(frame) // Early return if the argument to the function is unknown or error. strict := !un.nonStrict if strict && types.IsUnknownOrError(argVal) { @@ -472,6 +585,11 @@ func (un *evalUnary) Eval(ctx Activation) ref.Val { return types.NewErrWithNodeID(un.id, "no such overload: %s", un.function) } +// Eval implements the Interpretable interface method. +func (un *evalUnary) Eval(ctx Activation) ref.Val { + return un.Exec(AsFrame(ctx)) +} + // Function implements the InterpretableCall interface method. func (un *evalUnary) Function() string { return un.function @@ -483,16 +601,16 @@ func (un *evalUnary) OverloadID() string { } // Args returns the argument to the unary function. -func (un *evalUnary) Args() []Interpretable { - return []Interpretable{un.arg} +func (un *evalUnary) Args() []InterpretableV2 { + return []InterpretableV2{un.arg} } type evalBinary struct { id int64 function string overload string - lhs Interpretable - rhs Interpretable + lhs InterpretableV2 + rhs InterpretableV2 trait int impl functions.BinaryOp nonStrict bool @@ -503,10 +621,10 @@ func (bin *evalBinary) ID() int64 { return bin.id } -// Eval implements the Interpretable interface method. -func (bin *evalBinary) Eval(ctx Activation) ref.Val { - lVal := bin.lhs.Eval(ctx) - rVal := bin.rhs.Eval(ctx) +// Exec implements the InterpretableV2 interface method. +func (bin *evalBinary) Exec(frame *ExecutionFrame) ref.Val { + lVal := bin.lhs.Exec(frame) + rVal := bin.rhs.Exec(frame) // Early return if any argument to the function is unknown or error. strict := !bin.nonStrict if strict { @@ -530,6 +648,11 @@ func (bin *evalBinary) Eval(ctx Activation) ref.Val { return types.NewErrWithNodeID(bin.id, "no such overload: %s", bin.function) } +// Eval implements the Interpretable interface method. +func (bin *evalBinary) Eval(ctx Activation) ref.Val { + return bin.Exec(AsFrame(ctx)) +} + // Function implements the InterpretableCall interface method. func (bin *evalBinary) Function() string { return bin.function @@ -541,22 +664,22 @@ func (bin *evalBinary) OverloadID() string { } // Args returns the argument to the unary function. -func (bin *evalBinary) Args() []Interpretable { - return []Interpretable{bin.lhs, bin.rhs} +func (bin *evalBinary) Args() []InterpretableV2 { + return []InterpretableV2{bin.lhs, bin.rhs} } type evalVarArgs struct { id int64 function string overload string - args []Interpretable + args []InterpretableV2 trait int impl functions.FunctionOp nonStrict bool } // NewCall creates a new call Interpretable. -func NewCall(id int64, function, overload string, args []Interpretable, impl functions.FunctionOp) InterpretableCall { +func NewCall(id int64, function, overload string, args []InterpretableV2, impl functions.FunctionOp) InterpretableCall { return &evalVarArgs{ id: id, function: function, @@ -571,13 +694,13 @@ func (fn *evalVarArgs) ID() int64 { return fn.id } -// Eval implements the Interpretable interface method. -func (fn *evalVarArgs) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (fn *evalVarArgs) Exec(frame *ExecutionFrame) ref.Val { argVals := make([]ref.Val, len(fn.args)) // Early return if any argument to the function is unknown or error. strict := !fn.nonStrict for i, arg := range fn.args { - argVals[i] = arg.Eval(ctx) + argVals[i] = arg.Exec(frame) if strict && types.IsUnknownOrError(argVals[i]) { return argVals[i] } @@ -596,6 +719,11 @@ func (fn *evalVarArgs) Eval(ctx Activation) ref.Val { return types.NewErrWithNodeID(fn.id, "no such overload: %s %d", fn.function, fn.id) } +// Eval implements the Interpretable interface method. +func (fn *evalVarArgs) Eval(ctx Activation) ref.Val { + return fn.Exec(AsFrame(ctx)) +} + // Function implements the InterpretableCall interface method. func (fn *evalVarArgs) Function() string { return fn.function @@ -607,13 +735,13 @@ func (fn *evalVarArgs) OverloadID() string { } // Args returns the argument to the unary function. -func (fn *evalVarArgs) Args() []Interpretable { +func (fn *evalVarArgs) Args() []InterpretableV2 { return fn.args } type evalList struct { id int64 - elems []Interpretable + elems []InterpretableV2 optionals []bool hasOptionals bool adapter types.Adapter @@ -624,12 +752,12 @@ func (l *evalList) ID() int64 { return l.id } -// Eval implements the Interpretable interface method. -func (l *evalList) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (l *evalList) Exec(frame *ExecutionFrame) ref.Val { elemVals := make([]ref.Val, 0, len(l.elems)) // If any argument is unknown or error early terminate. for i, elem := range l.elems { - elemVal := elem.Eval(ctx) + elemVal := elem.Exec(frame) if types.IsUnknownOrError(elemVal) { return elemVal } @@ -645,10 +773,15 @@ func (l *evalList) Eval(ctx Activation) ref.Val { } elemVals = append(elemVals, elemVal) } - return l.adapter.NativeToValue(elemVals) + return types.NewRefValList(l.adapter, elemVals) } -func (l *evalList) InitVals() []Interpretable { +// Eval implements the Interpretable interface method. +func (l *evalList) Eval(ctx Activation) ref.Val { + return l.Exec(AsFrame(ctx)) +} + +func (l *evalList) InitVals() []InterpretableV2 { return l.elems } @@ -658,8 +791,8 @@ func (l *evalList) Type() ref.Type { type evalMap struct { id int64 - keys []Interpretable - vals []Interpretable + keys []InterpretableV2 + vals []InterpretableV2 optionals []bool hasOptionals bool adapter types.Adapter @@ -670,16 +803,16 @@ func (m *evalMap) ID() int64 { return m.id } -// Eval implements the Interpretable interface method. -func (m *evalMap) Eval(ctx Activation) ref.Val { - entries := make(map[ref.Val]ref.Val) +// Exec implements the InterpretableV2 interface method. +func (m *evalMap) Exec(frame *ExecutionFrame) ref.Val { + entries := make(map[ref.Val]ref.Val, len(m.keys)) // If any argument is unknown or error early terminate. for i, key := range m.keys { - keyVal := key.Eval(ctx) + keyVal := key.Exec(frame) if types.IsUnknownOrError(keyVal) { return keyVal } - valVal := m.vals[i].Eval(ctx) + valVal := m.vals[i].Exec(frame) if types.IsUnknownOrError(valVal) { return valVal } @@ -696,14 +829,19 @@ func (m *evalMap) Eval(ctx Activation) ref.Val { } entries[keyVal] = valVal } - return m.adapter.NativeToValue(entries) + return types.NewRefValMap(m.adapter, entries) } -func (m *evalMap) InitVals() []Interpretable { +// Eval implements the Interpretable interface method. +func (m *evalMap) Eval(ctx Activation) ref.Val { + return m.Exec(AsFrame(ctx)) +} + +func (m *evalMap) InitVals() []InterpretableV2 { if len(m.keys) != len(m.vals) { return nil } - result := make([]Interpretable, len(m.keys)+len(m.vals)) + result := make([]InterpretableV2, len(m.keys)+len(m.vals)) idx := 0 for i, k := range m.keys { v := m.vals[i] @@ -723,7 +861,7 @@ type evalObj struct { id int64 typeName string fields []string - vals []Interpretable + vals []InterpretableV2 optionals []bool hasOptionals bool provider types.Provider @@ -734,12 +872,12 @@ func (o *evalObj) ID() int64 { return o.id } -// Eval implements the Interpretable interface method. -func (o *evalObj) Eval(ctx Activation) ref.Val { - fieldVals := make(map[string]ref.Val) +// Exec implements the InterpretableV2 interface method. +func (o *evalObj) Exec(frame *ExecutionFrame) ref.Val { + fieldVals := make(map[string]ref.Val, len(o.fields)) // If any argument is unknown or error early terminate. for i, field := range o.fields { - val := o.vals[i].Eval(ctx) + val := o.vals[i].Exec(frame) if types.IsUnknownOrError(val) { return val } @@ -759,8 +897,13 @@ func (o *evalObj) Eval(ctx Activation) ref.Val { return types.LabelErrNode(o.id, o.provider.NewValue(o.typeName, fieldVals)) } +// Eval implements the Interpretable interface method. +func (o *evalObj) Eval(ctx Activation) ref.Val { + return o.Exec(AsFrame(ctx)) +} + // InitVals implements the InterpretableConstructor interface method. -func (o *evalObj) InitVals() []Interpretable { +func (o *evalObj) InitVals() []InterpretableV2 { return o.vals } @@ -774,11 +917,11 @@ type evalFold struct { accuVar string iterVar string iterVar2 string - iterRange Interpretable - accu Interpretable - cond Interpretable - step Interpretable - result Interpretable + iterRange InterpretableV2 + accu InterpretableV2 + cond InterpretableV2 + step InterpretableV2 + result InterpretableV2 adapter types.Adapter // note an exhaustive fold will ensure that all branches are evaluated @@ -793,13 +936,13 @@ func (fold *evalFold) ID() int64 { return fold.id } -// Eval implements the Interpretable interface method. -func (fold *evalFold) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (fold *evalFold) Exec(frame *ExecutionFrame) ref.Val { // Initialize the folder interface - f := newFolder(fold, ctx) + f := newFolder(fold, frame) defer releaseFolder(f) - foldRange := fold.iterRange.Eval(ctx) + foldRange := fold.iterRange.Exec(frame) if types.IsUnknownOrError(foldRange) { return foldRange } @@ -824,14 +967,19 @@ func (fold *evalFold) Eval(ctx Activation) ref.Val { return f.foldIterable(iterable) } +// Eval implements the Interpretable interface method. +func (fold *evalFold) Eval(ctx Activation) ref.Val { + return fold.Exec(AsFrame(ctx)) +} + // Optional Interpretable implementations that specialize, subsume, or extend the core evaluation // plan via decorators. // evalSetMembership is an Interpretable implementation which tests whether an input value // exists within the set of map keys used to model a set. type evalSetMembership struct { - inst Interpretable - arg Interpretable + inst InterpretableV2 + arg InterpretableV2 valueSet map[ref.Val]ref.Val } @@ -840,9 +988,9 @@ func (e *evalSetMembership) ID() int64 { return e.inst.ID() } -// Eval implements the Interpretable interface method. -func (e *evalSetMembership) Eval(ctx Activation) ref.Val { - val := e.arg.Eval(ctx) +// Exec implements the InterpretableV2 interface method. +func (e *evalSetMembership) Exec(frame *ExecutionFrame) ref.Val { + val := e.arg.Exec(frame) if types.IsUnknownOrError(val) { return val } @@ -852,18 +1000,28 @@ func (e *evalSetMembership) Eval(ctx Activation) ref.Val { return types.False } +// Eval implements the Interpretable interface method. +func (e *evalSetMembership) Eval(ctx Activation) ref.Val { + return e.Exec(AsFrame(ctx)) +} + // evalWatch is an Interpretable implementation that wraps the execution of a given // expression so that it may observe the computed value and send it to an observer. type evalWatch struct { - Interpretable + InterpretableV2 observer EvalObserver } +// Exec implements the InterpretableV2 interface method. +func (e *evalWatch) Exec(frame *ExecutionFrame) ref.Val { + val := e.InterpretableV2.Exec(frame) + e.observer(frame, e.ID(), e.InterpretableV2, val) + return val +} + // Eval implements the Interpretable interface method. func (e *evalWatch) Eval(vars Activation) ref.Val { - val := e.Interpretable.Eval(vars) - e.observer(vars, e.ID(), e.Interpretable, val) - return val + return e.Exec(AsFrame(vars)) } // evalWatchAttr describes a watcher of an InterpretableAttribute Interpretable. @@ -918,11 +1076,16 @@ func (e *evalWatchAttr) AddQualifier(q Qualifier) (Attribute, error) { return e, err } +// Exec implements the InterpretableV2 interface method. +func (e *evalWatchAttr) Exec(frame *ExecutionFrame) ref.Val { + val := e.InterpretableAttribute.Exec(frame) + e.observer(frame, e.ID(), e.InterpretableAttribute, val) + return val +} + // Eval implements the Interpretable interface method. func (e *evalWatchAttr) Eval(vars Activation) ref.Val { - val := e.InterpretableAttribute.Eval(vars) - e.observer(vars, e.ID(), e.InterpretableAttribute, val) - return val + return e.Exec(AsFrame(vars)) } // evalWatchConstQual observes the qualification of an object using a constant boolean, int, @@ -1049,17 +1212,22 @@ type evalWatchConst struct { observer EvalObserver } -// Eval implements the Interpretable interface method. -func (e *evalWatchConst) Eval(vars Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (e *evalWatchConst) Exec(frame *ExecutionFrame) ref.Val { val := e.Value() - e.observer(vars, e.ID(), e.InterpretableConst, val) + e.observer(frame, e.ID(), e.InterpretableConst, val) return val } +// Eval implements the Interpretable interface method. +func (e *evalWatchConst) Eval(vars Activation) ref.Val { + return e.Exec(AsFrame(vars)) +} + // evalExhaustiveOr is just like evalOr, but does not short-circuit argument evaluation. type evalExhaustiveOr struct { id int64 - terms []Interpretable + terms []InterpretableV2 } // ID implements the Interpretable interface method. @@ -1067,13 +1235,13 @@ func (or *evalExhaustiveOr) ID() int64 { return or.id } -// Eval implements the Interpretable interface method. -func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (or *evalExhaustiveOr) Exec(frame *ExecutionFrame) ref.Val { var err ref.Val = nil var unk *types.Unknown isTrue := false for _, term := range or.terms { - val := term.Eval(ctx) + val := term.Exec(frame) boolVal, ok := val.(types.Bool) // flag the result as true if ok && boolVal == types.True { @@ -1103,10 +1271,15 @@ func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val { return types.False } +// Eval implements the Interpretable interface method. +func (or *evalExhaustiveOr) Eval(ctx Activation) ref.Val { + return or.Exec(AsFrame(ctx)) +} + // evalExhaustiveAnd is just like evalAnd, but does not short-circuit argument evaluation. type evalExhaustiveAnd struct { id int64 - terms []Interpretable + terms []InterpretableV2 } // ID implements the Interpretable interface method. @@ -1114,13 +1287,13 @@ func (and *evalExhaustiveAnd) ID() int64 { return and.id } -// Eval implements the Interpretable interface method. -func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val { +// Exec implements the InterpretableV2 interface method. +func (and *evalExhaustiveAnd) Exec(frame *ExecutionFrame) ref.Val { var err ref.Val = nil var unk *types.Unknown isFalse := false for _, term := range and.terms { - val := term.Eval(ctx) + val := term.Exec(frame) boolVal, ok := val.(types.Bool) // short-circuit on false. if ok && boolVal == types.False { @@ -1150,6 +1323,11 @@ func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val { return types.True } +// Eval implements the Interpretable interface method. +func (and *evalExhaustiveAnd) Eval(ctx Activation) ref.Val { + return and.Exec(AsFrame(ctx)) +} + // evalExhaustiveConditional is like evalConditional, but does not short-circuit argument // evaluation. type evalExhaustiveConditional struct { @@ -1163,11 +1341,11 @@ func (cond *evalExhaustiveConditional) ID() int64 { return cond.id } -// Eval implements the Interpretable interface method. -func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val { - cVal := cond.attr.expr.Eval(ctx) - tVal, tErr := cond.attr.truthy.Resolve(ctx) - fVal, fErr := cond.attr.falsy.Resolve(ctx) +// Exec implements the InterpretableV2 interface method. +func (cond *evalExhaustiveConditional) Exec(frame *ExecutionFrame) ref.Val { + cVal := cond.attr.expr.Exec(frame) + tVal, tErr := cond.attr.truthy.Resolve(frame) + fVal, fErr := cond.attr.falsy.Resolve(frame) cBool, ok := cVal.(types.Bool) if !ok { return types.ValOrErr(cVal, "no such overload") @@ -1184,6 +1362,11 @@ func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val { return cond.adapter.NativeToValue(fVal) } +// Eval implements the Interpretable interface method. +func (cond *evalExhaustiveConditional) Eval(ctx Activation) ref.Val { + return cond.Exec(AsFrame(ctx)) +} + // evalAttr evaluates an Attribute value. type evalAttr struct { adapter types.Adapter @@ -1215,15 +1398,20 @@ func (a *evalAttr) Adapter() types.Adapter { return a.adapter } -// Eval implements the Interpretable interface method. -func (a *evalAttr) Eval(ctx Activation) ref.Val { - v, err := a.attr.Resolve(ctx) +// Exec implements the InterpretableV2 interface method. +func (a *evalAttr) Exec(frame *ExecutionFrame) ref.Val { + v, err := a.attr.Resolve(frame) if err != nil { return types.LabelErrNode(a.ID(), types.WrapErr(err)) } return a.adapter.NativeToValue(v) } +// Eval implements the Interpretable interface method. +func (a *evalAttr) Eval(ctx Activation) ref.Val { + return a.Exec(AsFrame(ctx)) +} + // Qualify proxies to the Attribute's Qualify method. func (a *evalAttr) Qualify(vars Activation, obj any) (any, error) { return a.attr.Qualify(vars, obj) @@ -1249,7 +1437,7 @@ type evalWatchConstructor struct { } // InitVals implements the InterpretableConstructor InitVals function. -func (c *evalWatchConstructor) InitVals() []Interpretable { +func (c *evalWatchConstructor) InitVals() []InterpretableV2 { return c.constructor.InitVals() } @@ -1263,11 +1451,16 @@ func (c *evalWatchConstructor) ID() int64 { return c.constructor.ID() } +// Exec implements the InterpretableV2 interface method. +func (c *evalWatchConstructor) Exec(frame *ExecutionFrame) ref.Val { + val := c.constructor.Exec(frame) + c.observer(frame, c.ID(), c.constructor, val) + return val +} + // Eval implements the Interpretable Eval function. func (c *evalWatchConstructor) Eval(vars Activation) ref.Val { - val := c.constructor.Eval(vars) - c.observer(vars, c.ID(), c.constructor, val) - return val + return c.Exec(AsFrame(vars)) } func invalidOptionalEntryInit(field any, value ref.Val) ref.Val { @@ -1279,10 +1472,10 @@ func invalidOptionalElementInit(value ref.Val) ref.Val { } // newFolder creates or initializes a pooled folder instance. -func newFolder(eval *evalFold, ctx Activation) *folder { +func newFolder(eval *evalFold, frame *ExecutionFrame) *folder { f := folderPool.Get().(*folder) f.evalFold = eval - f.activation = ctx + f.frame = frame.Push(f) return f } @@ -1303,7 +1496,7 @@ func releaseFolder(f *folder) { // cel.bind or cel.@block. type folder struct { *evalFold - activation Activation + frame *ExecutionFrame // fold state objects. accuVal ref.Val @@ -1322,16 +1515,16 @@ func (f *folder) foldIterable(iterable traits.Iterable) ref.Val { for it.HasNext() == types.True { f.iterVar1Val = it.Next() - cond := f.cond.Eval(f) + cond := f.cond.Exec(f.frame) condBool, ok := cond.(types.Bool) if f.interrupted || (!f.exhaustive && ok && condBool != types.True) { return f.evalResult() } // Update the accumulation value and check for eval interuption. - f.accuVal = f.step.Eval(f) + f.accuVal = f.step.Exec(f.frame) f.initialized = true - if f.interruptable && checkInterrupt(f.activation) { + if f.interruptable && f.frame.CheckInterrupt() { f.interrupted = true return f.evalResult() } @@ -1348,16 +1541,16 @@ func (f *folder) FoldEntry(key, val any) bool { // Terminate evaluation if evaluation is interrupted or the condition is not true and exhaustive // eval is not enabled. - cond := f.cond.Eval(f) + cond := f.cond.Exec(f.frame) condBool, ok := cond.(types.Bool) if f.interrupted || (!f.exhaustive && ok && condBool != types.True) { return false } // Update the accumulation value and check for eval interuption. - f.accuVal = f.step.Eval(f) + f.accuVal = f.step.Exec(f.frame) f.initialized = true - if f.interruptable && checkInterrupt(f.activation) { + if f.interruptable && f.frame.CheckInterrupt() { f.interrupted = true return false } @@ -1371,7 +1564,7 @@ func (f *folder) ResolveName(name string) (any, bool) { if name == f.accuVar { if !f.initialized { f.initialized = true - initVal := f.accu.Eval(f.activation) + initVal := f.accu.Exec(f.frame.parent) if !f.exhaustive { if l, isList := initVal.(traits.Lister); isList && l.Size() == types.IntZero { initVal = types.NewMutableList(f.adapter) @@ -1396,23 +1589,23 @@ func (f *folder) ResolveName(name string) (any, bool) { return f.iterVar2Val, true } } - return f.activation.ResolveName(name) + return f.frame.parent.ResolveName(name) } // Parent returns the activation embedded into the folder. func (f *folder) Parent() Activation { - return f.activation + return f.frame.parent } // Unwrap returns the parent activation, thus omitting access to local state func (f *folder) Unwrap() Activation { - return f.activation + return f.frame.parent } // UnknownAttributePatterns implements the PartialActivation interface returning the unknown patterns // if they were provided to the input activation, or an empty set if the proxied activation is not partial. func (f *folder) UnknownAttributePatterns() []*AttributePattern { - if pv, ok := f.activation.(partialActivationConverter); ok { + if pv, ok := f.frame.parent.Activation.(partialActivationConverter); ok { if partial, isPartial := pv.AsPartialActivation(); isPartial { return partial.UnknownAttributePatterns() } @@ -1421,7 +1614,7 @@ func (f *folder) UnknownAttributePatterns() []*AttributePattern { } func (f *folder) AsPartialActivation() (PartialActivation, bool) { - if pv, ok := f.activation.(partialActivationConverter); ok { + if pv, ok := f.frame.parent.Activation.(partialActivationConverter); ok { if _, isPartial := pv.AsPartialActivation(); isPartial { return f, true } @@ -1435,7 +1628,7 @@ func (f *folder) evalResult() ref.Val { if f.interrupted { return types.WrapErr(InterruptError{}) } - res := f.result.Eval(f) + res := f.result.Exec(f.frame) // Convert a mutable list or map to an immutable one if the comprehension has generated a list or // map as a result. if !types.IsUnknownOrError(res) && f.mutableValue { @@ -1452,7 +1645,8 @@ func (f *folder) evalResult() ref.Val { // reset clears any state associated with folder evaluation. func (f *folder) reset() { f.evalFold = nil - f.activation = nil + f.frame.Pop() + f.frame = nil f.accuVal = nil f.iterVar1Val = nil f.iterVar2Val = nil @@ -1463,11 +1657,6 @@ func (f *folder) reset() { f.computeResult = false } -func checkInterrupt(a Activation) bool { - stop, found := a.ResolveName("#interrupted") - return found && stop == true -} - // InterruptError is a specialized error type used to signal that program evaluation should check // whether a context cancellation is responsible for the error. type InterruptError struct{} diff --git a/vendor/github.com/google/cel-go/interpreter/interpreter.go b/vendor/github.com/google/cel-go/interpreter/interpreter.go index d81ef1280f..38174aff69 100644 --- a/vendor/github.com/google/cel-go/interpreter/interpreter.go +++ b/vendor/github.com/google/cel-go/interpreter/interpreter.go @@ -29,11 +29,11 @@ import ( // PlannerOption configures the program plan options during interpretable setup. type PlannerOption func(*planner) (*planner, error) -// Interpreter generates a new Interpretable from a checked or unchecked expression. +// Interpreter generates a new InterpretableV2 from a checked or unchecked expression. type Interpreter interface { - // NewInterpretable creates an Interpretable from a checked expression and an + // NewInterpretable creates an InterpretableV2 from a checked expression and an // optional list of PlannerOption values. - NewInterpretable(exprAST *ast.AST, opts ...PlannerOption) (Interpretable, error) + NewInterpretable(exprAST *ast.AST, opts ...PlannerOption) (InterpretableV2, error) } // EvalObserver is a functional interface that accepts an expression id and an observed value. @@ -43,16 +43,16 @@ type EvalObserver func(vars Activation, id int64, programStep any, value ref.Val // StatefulObserver observes evaluation while tracking or utilizing stateful behavior. type StatefulObserver interface { - // InitState configures stateful metadata on the activation. - InitState(Activation) (Activation, error) + // InitState configures stateful metadata on the execution frame. + InitState(*ExecutionFrame) (any, error) - // GetState retrieves the stateful metadata from the activation. - GetState(Activation) any + // GetState retrieves the stateful metadata from the execution frame. + GetState(*ExecutionFrame) any // Observe passes the activation and relevant evaluation metadata to the observer. - // The observe method is expected to do the equivalent of GetState(vars) in order + // The observe method is expected to do the equivalent of GetState(AsFrame(activation)) // to find the metadata that needs to be updated upon invocation. - Observe(vars Activation, id int64, programStep any, value ref.Val) + Observe(Activation, int64, any, ref.Val) } // EvalCancelledError represents a cancelled program evaluation operation. @@ -106,37 +106,6 @@ func EvalStateObserver(opts ...evalStateOption) PlannerOption { } } -// evalStateConverter identifies an object which is convertible to an EvalState instance. -type evalStateConverter interface { - asEvalState() EvalState -} - -// evalStateActivation hides state in the Activation in a manner not accessible to expressions. -type evalStateActivation struct { - vars Activation - state EvalState -} - -// ResolveName proxies variable lookups to the backing activation. -func (esa evalStateActivation) ResolveName(name string) (any, bool) { - return esa.vars.ResolveName(name) -} - -// Parent proxies parent lookups to the backing activation. -func (esa evalStateActivation) Parent() Activation { - return esa.vars -} - -// AsPartialActivation supports conversion to a partial activation in order to detect unknown attributes. -func (esa evalStateActivation) AsPartialActivation() (PartialActivation, bool) { - return AsPartialActivation(esa.vars) -} - -// asEvalState implements the evalStateConverter method. -func (esa evalStateActivation) asEvalState() EvalState { - return esa.state -} - // activationWrapper identifies an object carrying local variables which should not be exposed to the user // Activations used for such purposes can be unwrapped to return the activation which omits local state. type activationWrapper interface { @@ -144,57 +113,56 @@ type activationWrapper interface { Unwrap() Activation } -// asEvalState walks the Activation hierarchy and returns the first EvalState found, if present. -func asEvalState(vars Activation) (EvalState, bool) { - if conv, ok := vars.(evalStateConverter); ok { - return conv.asEvalState(), true - } - // Check if the current activation wraps another activation. This is used to support - // wrappers such as the @block() activation which may be composed of a dynamicSlotActivation or a - // constantSlotActivation. In this case, the underlying activation is the portion which interacts - // with the EvalState. - if wrapper, ok := vars.(activationWrapper); ok { - unwrapped := wrapper.Unwrap() - // Recursively call asEvalState on the unwrapped activation. This will check the unwrapped value and its parents. - return asEvalState(unwrapped) - } - if vars.Parent() != nil { - return asEvalState(vars.Parent()) - } - return nil, false -} - // evalStateFactory holds a reference to a factory function that produces an EvalState instance. type evalStateFactory struct { factory func() EvalState } -// InitState produces an EvalState instance and bundles it into the Activation in a way which is +// InitState produces an EvalState instance and bundles it into the ExecutionFrame in a way which is // not visible to expression evaluation. -func (et *evalStateFactory) InitState(vars Activation) (Activation, error) { +func (et *evalStateFactory) InitState(frame *ExecutionFrame) (any, error) { state := et.factory() - return evalStateActivation{vars: vars, state: state}, nil + if frame.ctx == nil { + frame.ctx = evalContextPool.Get().(*evalContext) + } + frame.ctx.state = state + return state, nil } // GetState extracts the EvalState from the Activation. -func (et *evalStateFactory) GetState(vars Activation) any { - if state, found := asEvalState(vars); found { - return state +func (et *evalStateFactory) GetState(frame *ExecutionFrame) any { + if frame.ctx == nil { + return nil } - return nil + return frame.ctx.state } // Observe records the evaluation state for a given expression node and program step. func (et *evalStateFactory) Observe(vars Activation, id int64, programStep any, val ref.Val) { - state, found := asEvalState(vars) - if !found { + frame := AsFrame(vars) + if frame.ctx == nil || frame.ctx.state == nil { return } - state.SetValue(id, val) + frame.ctx.state.SetValue(id, val) } // CustomDecorator configures a custom interpretable decorator for the program. func CustomDecorator(dec InterpretableDecorator) PlannerOption { + return func(p *planner) (*planner, error) { + dec2 := func(i InterpretableV2) (InterpretableV2, error) { + legacy, err := dec(i) + if err != nil { + return nil, err + } + return adaptToV2(legacy), nil + } + p.decorators = append(p.decorators, dec2) + return p, nil + } +} + +// CustomDecoratorV2 configures a custom V2 interpretable decorator for the program. +func CustomDecoratorV2(dec InterpretableDecoratorV2) PlannerOption { return func(p *planner) (*planner, error) { p.decorators = append(p.decorators, dec) return p, nil @@ -207,7 +175,7 @@ func CustomDecorator(dec InterpretableDecorator) PlannerOption { // provided to the decorator. This decorator is not thread-safe, and the EvalState // must be reset between Eval() calls. func ExhaustiveEval() PlannerOption { - return CustomDecorator(decDisableShortcircuits()) + return CustomDecoratorV2(decDisableShortcircuits()) } // InterruptableEval annotates comprehension loops with information that indicates they @@ -216,13 +184,13 @@ func ExhaustiveEval() PlannerOption { // The custom activation is currently managed higher up in the stack within the 'cel' package // and should not require any custom support on behalf of callers. func InterruptableEval() PlannerOption { - return CustomDecorator(decInterruptFolds()) + return CustomDecoratorV2(decInterruptFolds()) } // Optimize will pre-compute operations such as list and map construction and optimize // call arguments to set membership tests. The set of optimizations will increase over time. func Optimize() PlannerOption { - return CustomDecorator(decOptimize()) + return CustomDecoratorV2(decOptimize()) } // RegexOptimization provides a way to replace an InterpretableCall for a regex function when the @@ -247,7 +215,7 @@ type RegexOptimization struct { // CompileRegexConstants compiles regex pattern string constants at program creation time and reports any regex pattern // compile errors. func CompileRegexConstants(regexOptimizations ...*RegexOptimization) PlannerOption { - return CustomDecorator(decRegexOptimizer(regexOptimizations...)) + return CustomDecoratorV2(decRegexOptimizer(regexOptimizations...)) } type exprInterpreter struct { @@ -273,10 +241,10 @@ func NewInterpreter(dispatcher Dispatcher, attrFactory: attrFactory} } -// NewIntepretable implements the Interpreter interface method. +// NewInterpretable implements the Interpreter interface method. func (i *exprInterpreter) NewInterpretable( checked *ast.AST, - opts ...PlannerOption) (Interpretable, error) { + opts ...PlannerOption) (InterpretableV2, error) { p := newPlanner(i.dispatcher, i.provider, i.adapter, i.attrFactory, i.container, checked) var err error for _, o := range opts { diff --git a/vendor/github.com/google/cel-go/interpreter/planner.go b/vendor/github.com/google/cel-go/interpreter/planner.go index 0bc38449ce..95fd34551c 100644 --- a/vendor/github.com/google/cel-go/interpreter/planner.go +++ b/vendor/github.com/google/cel-go/interpreter/planner.go @@ -43,7 +43,7 @@ func newPlanner(disp Dispatcher, container: cont, refMap: exprAST.ReferenceMap(), typeMap: exprAST.TypeMap(), - decorators: make([]InterpretableDecorator, 0), + decorators: make([]InterpretableDecoratorV2, 0), observers: make([]StatefulObserver, 0), } } @@ -57,7 +57,7 @@ type planner struct { container *containers.Container refMap map[int64]*ast.ReferenceInfo typeMap map[int64]*types.Type - decorators []InterpretableDecorator + decorators []InterpretableDecoratorV2 observers []StatefulObserver } @@ -72,7 +72,7 @@ type planBuilder struct { // useful for layering functionality into the evaluation that is not natively understood by CEL, // such as state-tracking, expression re-write, and possibly efficient thread-safe memoization of // repeated expressions. -func (p *planner) Plan(expr ast.Expr) (Interpretable, error) { +func (p *planner) Plan(expr ast.Expr) (InterpretableV2, error) { pb := &planBuilder{planner: p, localVars: make(map[string]int)} i, err := pb.plan(expr) if err != nil { @@ -81,10 +81,10 @@ func (p *planner) Plan(expr ast.Expr) (Interpretable, error) { if len(p.observers) == 0 { return i, nil } - return &ObservableInterpretable{Interpretable: i, observers: p.observers}, nil + return &ObservableInterpretable{InterpretableV2: i, observers: p.observers}, nil } -func (p *planBuilder) plan(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) plan(expr ast.Expr) (InterpretableV2, error) { switch expr.Kind() { case ast.CallKind: return p.decorate(p.planCall(expr)) @@ -109,7 +109,7 @@ func (p *planBuilder) plan(expr ast.Expr) (Interpretable, error) { // decorate applies the InterpretableDecorator functions to the given Interpretable. // Both the Interpretable and error generated by a Plan step are accepted as arguments // for convenience. -func (p *planBuilder) decorate(i Interpretable, err error) (Interpretable, error) { +func (p *planBuilder) decorate(i InterpretableV2, err error) (InterpretableV2, error) { if err != nil { return nil, err } @@ -123,7 +123,7 @@ func (p *planBuilder) decorate(i Interpretable, err error) (Interpretable, error } // planIdent creates an Interpretable that resolves an identifier from an Activation. -func (p *planBuilder) planIdent(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planIdent(expr ast.Expr) (InterpretableV2, error) { // Establish whether the identifier is in the reference map. if identRef, found := p.refMap[expr.ID()]; found { return p.planCheckedIdent(expr.ID(), identRef) @@ -142,7 +142,7 @@ func (p *planBuilder) planIdent(expr ast.Expr) (Interpretable, error) { }, nil } -func (p *planBuilder) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (Interpretable, error) { +func (p *planBuilder) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (InterpretableV2, error) { // Plan a constant reference if this is the case for this simple identifier. if identRef.Value != nil { return NewConstValue(id, identRef.Value), nil @@ -171,7 +171,7 @@ func (p *planBuilder) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (I // a) selects a field from a map or proto. // b) creates a field presence test for a select within a has() macro. // c) resolves the select expression to a namespaced identifier. -func (p *planBuilder) planSelect(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planSelect(expr ast.Expr) (InterpretableV2, error) { // If the Select id appears in the reference map from the CheckedExpr proto then it is either // a namespaced identifier or enum value. if identRef, found := p.refMap[expr.ID()]; found { @@ -227,7 +227,7 @@ func (p *planBuilder) planSelect(expr ast.Expr) (Interpretable, error) { // planCall creates a callable Interpretable while specializing for common functions and invocation // patterns. Specifically, conditional operators &&, ||, ?:, and (in)equality functions result in // optimized Interpretable values. -func (p *planBuilder) planCall(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCall(expr ast.Expr) (InterpretableV2, error) { call := expr.AsCall() target, fnName, oName := p.resolveFunction(expr) argCount := len(call.Args()) @@ -237,7 +237,7 @@ func (p *planBuilder) planCall(expr ast.Expr) (Interpretable, error) { offset++ } - args := make([]Interpretable, argCount) + args := make([]InterpretableV2, argCount) if target != nil { arg, err := p.plan(target) if err != nil { @@ -307,7 +307,7 @@ func (p *planBuilder) planCall(expr ast.Expr) (Interpretable, error) { func (p *planBuilder) planCallZero(expr ast.Expr, function string, overload string, - impl *functions.Overload) (Interpretable, error) { + impl *functions.Overload) (InterpretableV2, error) { if impl == nil || impl.Function == nil { return nil, fmt.Errorf("no such overload: %s()", function) } @@ -324,7 +324,7 @@ func (p *planBuilder) planCallUnary(expr ast.Expr, function string, overload string, impl *functions.Overload, - args []Interpretable) (Interpretable, error) { + args []InterpretableV2) (InterpretableV2, error) { var fn functions.UnaryOp var trait int var nonStrict bool @@ -352,7 +352,7 @@ func (p *planBuilder) planCallBinary(expr ast.Expr, function string, overload string, impl *functions.Overload, - args []Interpretable) (Interpretable, error) { + args []InterpretableV2) (InterpretableV2, error) { var fn functions.BinaryOp var trait int var nonStrict bool @@ -381,7 +381,7 @@ func (p *planBuilder) planCallVarArgs(expr ast.Expr, function string, overload string, impl *functions.Overload, - args []Interpretable) (Interpretable, error) { + args []InterpretableV2) (InterpretableV2, error) { var fn functions.FunctionOp var trait int var nonStrict bool @@ -405,7 +405,7 @@ func (p *planBuilder) planCallVarArgs(expr ast.Expr, } // planCallEqual generates an equals (==) Interpretable. -func (p *planBuilder) planCallEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallEqual(expr ast.Expr, args []InterpretableV2) (InterpretableV2, error) { return &evalEq{ id: expr.ID(), lhs: args[0], @@ -414,7 +414,7 @@ func (p *planBuilder) planCallEqual(expr ast.Expr, args []Interpretable) (Interp } // planCallNotEqual generates a not equals (!=) Interpretable. -func (p *planBuilder) planCallNotEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallNotEqual(expr ast.Expr, args []InterpretableV2) (InterpretableV2, error) { return &evalNe{ id: expr.ID(), lhs: args[0], @@ -423,7 +423,7 @@ func (p *planBuilder) planCallNotEqual(expr ast.Expr, args []Interpretable) (Int } // planCallLogicalAnd generates a logical and (&&) Interpretable. -func (p *planBuilder) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallLogicalAnd(expr ast.Expr, args []InterpretableV2) (InterpretableV2, error) { return &evalAnd{ id: expr.ID(), terms: args, @@ -431,7 +431,7 @@ func (p *planBuilder) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (I } // planCallLogicalOr generates a logical or (||) Interpretable. -func (p *planBuilder) planCallLogicalOr(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallLogicalOr(expr ast.Expr, args []InterpretableV2) (InterpretableV2, error) { return &evalOr{ id: expr.ID(), terms: args, @@ -439,7 +439,7 @@ func (p *planBuilder) planCallLogicalOr(expr ast.Expr, args []Interpretable) (In } // planCallConditional generates a conditional / ternary (c ? t : f) Interpretable. -func (p *planBuilder) planCallConditional(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallConditional(expr ast.Expr, args []InterpretableV2) (InterpretableV2, error) { cond := args[0] t := args[1] var tAttr Attribute @@ -467,7 +467,7 @@ func (p *planBuilder) planCallConditional(expr ast.Expr, args []Interpretable) ( // planCallIndex either extends an attribute with the argument to the index operation, or creates // a relative attribute based on the return of a function call or operation. -func (p *planBuilder) planCallIndex(expr ast.Expr, args []Interpretable, optional bool) (Interpretable, error) { +func (p *planBuilder) planCallIndex(expr ast.Expr, args []InterpretableV2, optional bool) (InterpretableV2, error) { op := args[0] ind := args[1] opType := p.typeMap[op.ID()] @@ -502,7 +502,7 @@ func (p *planBuilder) planCallIndex(expr ast.Expr, args []Interpretable, optiona } // planCreateList generates a list construction Interpretable. -func (p *planBuilder) planCreateList(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateList(expr ast.Expr) (InterpretableV2, error) { list := expr.AsList() optionalIndices := list.OptionalIndices() elements := list.Elements() @@ -513,7 +513,7 @@ func (p *planBuilder) planCreateList(expr ast.Expr) (Interpretable, error) { } optionals[index] = true } - elems := make([]Interpretable, len(elements)) + elems := make([]InterpretableV2, len(elements)) for i, elem := range elements { elemVal, err := p.plan(elem) if err != nil { @@ -531,12 +531,12 @@ func (p *planBuilder) planCreateList(expr ast.Expr) (Interpretable, error) { } // planCreateStruct generates a map or object construction Interpretable. -func (p *planBuilder) planCreateMap(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateMap(expr ast.Expr) (InterpretableV2, error) { m := expr.AsMap() entries := m.Entries() optionals := make([]bool, len(entries)) - keys := make([]Interpretable, len(entries)) - vals := make([]Interpretable, len(entries)) + keys := make([]InterpretableV2, len(entries)) + vals := make([]InterpretableV2, len(entries)) hasOptionals := false for i, e := range entries { entry := e.AsMapEntry() @@ -565,7 +565,7 @@ func (p *planBuilder) planCreateMap(expr ast.Expr) (Interpretable, error) { } // planCreateObj generates an object construction Interpretable. -func (p *planBuilder) planCreateStruct(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateStruct(expr ast.Expr) (InterpretableV2, error) { obj := expr.AsStruct() typeName, defined := p.resolveTypeName(obj.TypeName()) if !defined { @@ -574,7 +574,7 @@ func (p *planBuilder) planCreateStruct(expr ast.Expr) (Interpretable, error) { objFields := obj.Fields() optionals := make([]bool, len(objFields)) fields := make([]string, len(objFields)) - vals := make([]Interpretable, len(objFields)) + vals := make([]InterpretableV2, len(objFields)) hasOptionals := false for i, f := range objFields { field := f.AsStructField() @@ -599,7 +599,7 @@ func (p *planBuilder) planCreateStruct(expr ast.Expr) (Interpretable, error) { } // planComprehension generates an Interpretable fold operation. -func (p *planBuilder) planComprehension(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planComprehension(expr ast.Expr) (InterpretableV2, error) { fold := expr.AsComprehension() accu, err := p.plan(fold.AccuInit()) if err != nil { @@ -639,7 +639,7 @@ func (p *planBuilder) planComprehension(expr ast.Expr) (Interpretable, error) { } // planConst generates a constant valued Interpretable. -func (p *planBuilder) planConst(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planConst(expr ast.Expr) (InterpretableV2, error) { return NewConstValue(expr.ID(), expr.AsLiteral()), nil } @@ -726,7 +726,7 @@ func (p *planBuilder) resolveFunction(expr ast.Expr) (ast.Expr, string, string) // relativeAttr indicates that the attribute in this case acts as a qualifier and as such needs to // be observed to ensure that it's evaluation value is properly recorded for state tracking. -func (p *planBuilder) relativeAttr(id int64, eval Interpretable, opt bool) (InterpretableAttribute, error) { +func (p *planBuilder) relativeAttr(id int64, eval InterpretableV2, opt bool) (InterpretableAttribute, error) { eAttr, ok := eval.(InterpretableAttribute) if !ok { eAttr = &evalAttr{ diff --git a/vendor/github.com/google/cel-go/interpreter/runtimecost.go b/vendor/github.com/google/cel-go/interpreter/runtimecost.go index 6c44cd7986..68e4310106 100644 --- a/vendor/github.com/google/cel-go/interpreter/runtimecost.go +++ b/vendor/github.com/google/cel-go/interpreter/runtimecost.go @@ -62,48 +62,6 @@ func CostObserver(opts ...costTrackPlanOption) PlannerOption { } } -// costTrackerConverter identifies an object which is convertible to a CostTracker instance. -type costTrackerConverter interface { - asCostTracker() *CostTracker -} - -// costTrackActivation hides state in the Activation in a manner not accessible to expressions. -type costTrackActivation struct { - vars Activation - costTracker *CostTracker -} - -// ResolveName proxies variable lookups to the backing activation. -func (cta costTrackActivation) ResolveName(name string) (any, bool) { - return cta.vars.ResolveName(name) -} - -// Parent proxies parent lookups to the backing activation. -func (cta costTrackActivation) Parent() Activation { - return cta.vars -} - -// AsPartialActivation supports conversion to a partial activation in order to detect unknown attributes. -func (cta costTrackActivation) AsPartialActivation() (PartialActivation, bool) { - return AsPartialActivation(cta.vars) -} - -// asCostTracker implements the costTrackerConverter method. -func (cta costTrackActivation) asCostTracker() *CostTracker { - return cta.costTracker -} - -// asCostTracker walks the Activation hierarchy and returns the first cost tracker found, if present. -func asCostTracker(vars Activation) (*CostTracker, bool) { - if conv, ok := vars.(costTrackerConverter); ok { - return conv.asCostTracker(), true - } - if vars.Parent() != nil { - return asCostTracker(vars.Parent()) - } - return nil, false -} - // costTrackerFactory holds a factory for producing new CostTracker instances on each Eval call. type costTrackerFactory struct { factory func() (*CostTracker, error) @@ -111,27 +69,37 @@ type costTrackerFactory struct { // InitState produces a CostTracker and bundles it into an Activation in a way which is not visible // to expression evaluation. -func (ct *costTrackerFactory) InitState(vars Activation) (Activation, error) { +func (ct *costTrackerFactory) InitState(frame *ExecutionFrame) (any, error) { tracker, err := ct.factory() if err != nil { return nil, err } - return costTrackActivation{vars: vars, costTracker: tracker}, nil + if frame.ctx == nil { + frame.ctx = evalContextPool.Get().(*evalContext) + } + frame.ctx.costs = tracker + return tracker, nil } // GetState extracts the CostTracker from the Activation. -func (ct *costTrackerFactory) GetState(vars Activation) any { - if tracker, found := asCostTracker(vars); found { - return tracker +func (ct *costTrackerFactory) GetState(frame *ExecutionFrame) any { + if frame == nil || frame.ctx == nil { + return nil } - return nil + return frame.ctx.costs } // Observe computes the incremental cost of each step and records it into the CostTracker associated // with the evaluation. func (ct *costTrackerFactory) Observe(vars Activation, id int64, programStep any, val ref.Val) { - tracker, found := asCostTracker(vars) - if !found { + frame := AsFrame(vars) + state := ct.GetState(frame) + if state == nil { + return + } + tracker, ok := state.(*CostTracker) + if !ok { + // The state is configured with CostTrackFactory so this shouldn't happen. return } switch t := programStep.(type) { @@ -265,6 +233,19 @@ type CostTracker struct { stack refValStack } +// Clone makes a shallow copy of the tracker. +// The different clones can be used independently from +// each other. +func (c *CostTracker) Clone() (*CostTracker, error) { + tracker := &CostTracker{ + Estimator: c.Estimator, + overloadTrackers: c.overloadTrackers, + Limit: c.Limit, + presenceTestHasCost: c.presenceTestHasCost, + } + return tracker, nil +} + // ActualCost returns the runtime cost func (c *CostTracker) ActualCost() uint64 { return c.cost @@ -292,7 +273,9 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re // if user has their own implementation of ActualCostEstimator, make sure to cover the mapping between overloadId and cost calculation switch call.OverloadID() { // O(n) functions - case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString: + case overloads.StartsWithString, overloads.EndsWithString: + cost += uint64(math.Ceil(float64(actualSize(args[1])) * common.StringTraversalCostFactor)) + case overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString: cost += uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor)) case overloads.InList: // If a list is composed entirely of constant values this is O(1), but we don't account for that here. @@ -317,7 +300,7 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re // In the worst case scenario, we would need to reallocate a new backing store and copy both operands over. cost += uint64(math.Ceil(float64(actualSize(args[0])+actualSize(args[1])) * common.StringTraversalCostFactor)) // O(nm) functions - case overloads.MatchesString: + case overloads.Matches, overloads.MatchesString: // https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL // Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0 // in case where string is empty but regex is still expensive. @@ -397,7 +380,7 @@ func (s *refValStack) drop(ids ...int64) { // the stack. // WARNING: It is possible for multiple expressions with the same ID to exist (due to how macros are implemented) so it's // possible that a dropped ID will remain on the stack. They should be removed when IDs on the stack are popped. -func (s *refValStack) dropArgs(args []Interpretable) ([]ref.Val, bool) { +func (s *refValStack) dropArgs(args []InterpretableV2) ([]ref.Val, bool) { result := make([]ref.Val, len(args)) argloop: for nIdx := len(args) - 1; nIdx >= 0; nIdx-- { diff --git a/vendor/github.com/google/cel-go/parser/unparser.go b/vendor/github.com/google/cel-go/parser/unparser.go index ffd5b18e47..d503a450e9 100644 --- a/vendor/github.com/google/cel-go/parser/unparser.go +++ b/vendor/github.com/google/cel-go/parser/unparser.go @@ -297,7 +297,7 @@ func (un *unparser) visitConstVal(val ref.Val) error { // represent the float using the minimum required digits d := strconv.FormatFloat(float64(val), 'g', -1, 64) un.str.WriteString(d) - if !strings.Contains(d, ".") { + if !strings.ContainsAny(d, ".eE") { un.str.WriteString(".0") } case types.Int: diff --git a/vendor/github.com/joelanford/ignore/.golangci.yml b/vendor/github.com/joelanford/ignore/.golangci.yml index 74cfe977e7..8325650b17 100644 --- a/vendor/github.com/joelanford/ignore/.golangci.yml +++ b/vendor/github.com/joelanford/ignore/.golangci.yml @@ -1,17 +1,15 @@ +version: "2" run: timeout: 5m linters: - disable-all: true + default: none enable: - govet - errcheck - staticcheck - unused - - gosimple - - structcheck - - varcheck - ineffassign - - deadcode - - typecheck - - gofmt - goconst +formatters: + enable: + - gofmt diff --git a/vendor/github.com/joelanford/ignore/ignore.go b/vendor/github.com/joelanford/ignore/ignore.go index 5245b6a490..07c6c92198 100644 --- a/vendor/github.com/joelanford/ignore/ignore.go +++ b/vendor/github.com/joelanford/ignore/ignore.go @@ -51,7 +51,7 @@ func loadPatterns(root fs.FS, path string) ([]gitignore.Pattern, error) { if err != nil { return nil, err } - defer f.Close() + defer func() { _ = f.Close() }() patterns := []gitignore.Pattern{} scanner := bufio.NewScanner(f) 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 fd2ef5524d..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 diff --git a/vendor/github.com/mattn/go-sqlite3/callback.go b/vendor/github.com/mattn/go-sqlite3/callback.go index 0c518fa2c1..8ea17db9d2 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,24 +105,26 @@ 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 { @@ -131,12 +134,34 @@ func lookupHandle(handle unsafe.Pointer) any { 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 +229,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 +260,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 +281,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 +336,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 +345,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 +358,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 +366,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 +383,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 28c9106004..337af9e47c 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.53.2. By combining all the individual C code files into this +** version 3.53.3. 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 -** d6e03d8c777cfa2d35e3b60d8ec3e0187f3e with changes in files: +** d4c0e51e4aeb96955b99185ab9cde75c339e 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.53.2" -#define SQLITE_VERSION_NUMBER 3053002 -#define SQLITE_SOURCE_ID "2026-06-03 19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24" +#define SQLITE_VERSION "3.53.3" +#define SQLITE_VERSION_NUMBER 3053003 +#define SQLITE_SOURCE_ID "2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62" #define SQLITE_SCM_BRANCH "branch-3.53" -#define SQLITE_SCM_TAGS "release version-3.53.2" -#define SQLITE_SCM_DATETIME "2026-06-03T19:12:13.350Z" +#define SQLITE_SCM_TAGS "release version-3.53.3" +#define SQLITE_SCM_DATETIME "2026-06-26T20:14:12.354Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -4688,7 +4688,8 @@ 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 @@ -4719,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 @@ -15801,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. @@ -17162,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 *); @@ -20920,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 */ @@ -39576,16 +39586,17 @@ int kvvfsDecode(const char *a, char *aOut, int nOut){ while( 1 ){ c = kvvfsHexValue[aIn[i]]; if( c<0 ){ - int n = 0; - int mult = 1; + sqlite3_int64 n = 0; + sqlite3_int64 mult = 1; c = aIn[i]; if( c==0 ) break; while( c>='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 */ @@ -39621,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); @@ -39667,9 +39678,7 @@ static int kvvfsClose(sqlite3_file *pProtoFile){ pFile->isJournal ? "journal" : "db")); sqlite3_free(pFile->aJrnl); sqlite3_free(pFile->aData); -#ifdef SQLITE_WASM memset(pFile, 0, sizeof(*pFile)); -#endif return SQLITE_OK; } @@ -39699,6 +39708,7 @@ static int kvvfsReadJrnl( aTxt, szTxt+1); if( rc>=0 ){ kvvfsDecodeJournal(pFile, aTxt, szTxt); + rc = 0; } sqlite3_free(aTxt); if( rc ) return rc; @@ -49762,10 +49772,8 @@ static struct win_syscall { #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ BOOL))aSyscall[63].pCurrent) - { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, - -#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ - LPSYSTEM_INFO))aSyscall[64].pCurrent) + { "GetNativeSystemInfo", (SYSCALL)0, 0 }, + /* ^^^^^^^^^^^^^^^^^^^----------------^------- placeholder only */ #if defined(SQLITE_WIN32_HAS_ANSI) { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, @@ -53020,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; } @@ -56040,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; @@ -56050,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 ); @@ -56066,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); @@ -56112,6 +56137,9 @@ SQLITE_API unsigned char *sqlite3_serialize( } } sqlite3_finalize(pStmt); + + serialize_out: + sqlite3_mutex_leave(db->mutex); return pOut; } @@ -57965,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; } @@ -60878,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)) @@ -60920,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. @@ -62170,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 + 2 + nSuperPtr + 2); + assert( nSuperJournal>=0 ); + zFree = sqlite3Malloc(4 + nSuperJournal + 2); if( !zFree ){ rc = SQLITE_NOMEM_BKPT; goto delsuper_out; @@ -62181,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; @@ -62189,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); @@ -62430,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+8) >= pPager->pVfs->mxPathname+1). Using os_unix.c, - ** mxPathname is 512, which is the same as the minimum allowable value - ** for pageSize, and so this assumption holds. But it might not for some - ** custom VFS. */ - 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; } @@ -62571,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 ); } @@ -62607,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; } @@ -68466,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 @@ -70320,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 ){ @@ -72818,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 }; /* @@ -75279,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 ){ @@ -79113,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 @@ -79282,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 @@ -84154,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)); } } @@ -84173,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)); } } @@ -84189,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 @@ -84323,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; @@ -84740,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 */ @@ -84829,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); } /* @@ -84889,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 @@ -85033,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 */ @@ -85049,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 */ @@ -85063,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. @@ -85072,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 @@ -85317,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; @@ -93585,8 +93710,14 @@ SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context *pCtx){ ** added or changed. */ SQLITE_API int sqlite3_expired(sqlite3_stmt *pStmt){ - Vdbe *p = (Vdbe*)pStmt; - return p==0 || p->expired; + 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 @@ -116881,7 +117012,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); } } @@ -116965,8 +117096,8 @@ static void sqlite3ExprCodeIN( ** ...)" is the collating sequence of x.". */ pColl = sqlite3ExprCollSeq(pParse, p); } - sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3); - sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3, + 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); @@ -125305,9 +125436,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 ){ @@ -125315,7 +125446,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; @@ -136787,7 +136918,7 @@ static void percentSort(double *a, unsigned int n){ i++; } }while( in/2 ){ + if( iLt>(int)(n/2) ){ if( n-iGt>=2 ) percentSort(a+iGt, n-iGt); n = iLt; }else{ @@ -151304,6 +151435,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; @@ -151325,6 +151463,8 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, c sqlite3DeleteTable(db, pTab); return 0; } + pParse->nNestSel--; + assert( pParse->nNestSel>=0 ); return pTab; } @@ -159268,7 +159408,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 */ @@ -159277,10 +159417,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. */ @@ -161281,7 +161435,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), @@ -161869,6 +162024,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); @@ -161879,6 +162035,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; } @@ -167253,7 +167410,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) ); + } /* @@ -168030,6 +168190,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; @@ -168240,8 +168401,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); @@ -188310,13 +188474,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; } /* @@ -188338,13 +188506,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); @@ -188354,13 +188526,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); @@ -189043,6 +189219,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); @@ -189053,6 +189230,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; } @@ -189958,9 +190136,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; @@ -190014,25 +190194,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; } /* @@ -190227,6 +190425,7 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ 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] ){ @@ -190236,6 +190435,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 */ } @@ -190278,7 +190478,7 @@ SQLITE_PRIVATE int sqlite3ParseUri( const char *zVfs = zDefaultVfs; char *zFile; char c; - int nUri = sqlite3Strlen30(zUri); + i64 nUri = strlen(zUri); assert( *pzErrMsg==0 ); @@ -190288,8 +190488,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 @@ -190323,7 +190523,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; } @@ -190398,11 +190598,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; @@ -190448,7 +190648,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; } @@ -191133,13 +191333,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; } /* @@ -192164,17 +192368,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; } /* @@ -195796,8 +196002,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; + } } /* @@ -198151,6 +198362,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; @@ -198162,7 +198374,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; @@ -207217,6 +207429,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); } } @@ -208443,7 +208659,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; @@ -218557,7 +218773,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 */ @@ -219012,6 +219228,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 ){ @@ -219037,7 +219256,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); } @@ -226600,16 +226819,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; } @@ -226685,7 +226914,7 @@ static int rbuDeltaApply( #endif limit = rbuDeltaGetInt(&zDelta, &lenDelta); - if( *zDelta!='\n' ){ + if( lenDelta<=0 || *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } @@ -226693,11 +226922,12 @@ static int rbuDeltaApply( while( *zDelta && lenDelta>0 ){ unsigned int cnt, ofst; cnt = rbuDeltaGetInt(&zDelta, &lenDelta); + if( lenDelta<=0 ) return -1; switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; ofst = rbuDeltaGetInt(&zDelta, &lenDelta); - if( lenDelta>0 && zDelta[0]!=',' ){ + if( lenDelta>0 || zDelta[0]!=',' ){ /* ERROR: copy command not terminated by ',' */ return -1; } @@ -226722,7 +226952,7 @@ static int rbuDeltaApply( /* ERROR: insert command gives an output larger than predicted */ return -1; } - if( (int)cnt>lenDelta ){ + if( (i64)cnt>(i64)lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } @@ -226760,7 +226990,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; } @@ -226808,7 +227038,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{ @@ -234987,7 +235217,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; @@ -240385,14 +240615,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{ @@ -240434,7 +240667,7 @@ 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]; @@ -244774,7 +245007,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 */ @@ -244799,7 +245032,7 @@ 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); @@ -247515,7 +247748,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); } } @@ -248466,10 +248699,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; } @@ -251752,7 +251985,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; } @@ -252708,7 +252941,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 ){ @@ -258576,8 +258809,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: @@ -258602,23 +258835,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 */ @@ -259123,9 +259359,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( iOff+nTerm>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); @@ -259156,7 +259396,7 @@ static void fts5IndexIntegrityCheckSegment( /* Check any rowid-less pages that occur before the current leaf. */ for(iPg=iPrevLeaf+1; iPgeContent==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 ){ diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h b/vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h index 7ea576038a..9417bce8d5 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.53.2" -#define SQLITE_VERSION_NUMBER 3053002 -#define SQLITE_SOURCE_ID "2026-06-03 19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24" +#define SQLITE_VERSION "3.53.3" +#define SQLITE_VERSION_NUMBER 3053003 +#define SQLITE_SOURCE_ID "2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62" #define SQLITE_SCM_BRANCH "branch-3.53" -#define SQLITE_SCM_TAGS "release version-3.53.2" -#define SQLITE_SCM_DATETIME "2026-06-03T19:12:13.350Z" +#define SQLITE_SCM_TAGS "release version-3.53.3" +#define SQLITE_SCM_DATETIME "2026-06-26T20:14:12.354Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -4367,7 +4367,8 @@ 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 @@ -4398,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 diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3.go b/vendor/github.com/mattn/go-sqlite3/sqlite3.go index b07bfc7c1f..c30bb8f1e7 100644 --- a/vendor/github.com/mattn/go-sqlite3/sqlite3.go +++ b/vendor/github.com/mattn/go-sqlite3/sqlite3.go @@ -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,12 +445,12 @@ 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 + 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 @@ -476,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. @@ -1589,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) @@ -1601,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 @@ -1627,66 +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} - if stmtCacheSize > 0 { - conn.stmtCache = make([]*SQLiteStmt, 0, stmtCacheSize) - conn.stmtCacheEnabled = true - } - // 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 @@ -1704,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 @@ -1717,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 @@ -1727,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 @@ -1737,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 @@ -1754,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) } } @@ -1765,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) } } } @@ -1783,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) } } @@ -1824,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) } } @@ -1851,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) } } @@ -1860,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) @@ -1964,6 +1961,10 @@ func (c *SQLiteConn) putCachedStmt(s *SQLiteStmt) bool { c.mu.Lock() defer c.mu.Unlock() + return c.putCachedStmtLocked(s) +} + +func (c *SQLiteConn) putCachedStmtLocked(s *SQLiteStmt) bool { if c.db == nil { return false } @@ -2069,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. @@ -2156,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) @@ -2274,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 { @@ -2293,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 @@ -2303,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 } @@ -2314,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) } } } @@ -2475,6 +2497,36 @@ 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 { @@ -2483,9 +2535,13 @@ func (rc *SQLiteRows) Columns() []string { 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 @@ -2493,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 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_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_vtable.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go index 7c2ae58e5d..9e916ea8cb 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; diff --git a/vendor/github.com/operator-framework/api/pkg/manifests/bundleloader.go b/vendor/github.com/operator-framework/api/pkg/manifests/bundleloader.go index f557c4f354..34ce20762a 100644 --- a/vendor/github.com/operator-framework/api/pkg/manifests/bundleloader.go +++ b/vendor/github.com/operator-framework/api/pkg/manifests/bundleloader.go @@ -122,6 +122,9 @@ func collectWalkErrs(walk filepath.WalkFunc, errs *[]error) filepath.WalkFunc { } func (b *bundleLoader) LoadBundleWalkFunc(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } if f == nil { return fmt.Errorf("invalid file: %v", f) } diff --git a/vendor/github.com/operator-framework/api/pkg/manifests/packagemanifestloader.go b/vendor/github.com/operator-framework/api/pkg/manifests/packagemanifestloader.go index d3597a97d0..37185e1e26 100644 --- a/vendor/github.com/operator-framework/api/pkg/manifests/packagemanifestloader.go +++ b/vendor/github.com/operator-framework/api/pkg/manifests/packagemanifestloader.go @@ -42,6 +42,9 @@ func (p *packageManifestLoader) LoadPackage() error { // LoadPackagesWalkFunc attempts to unmarshal the file at the given path into a PackageManifest resource. // If unmarshaling is successful, the PackageManifest is added to the loader's store. func (p *packageManifestLoader) LoadPackagesWalkFunc(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } if f == nil { return fmt.Errorf("invalid file: %v", f) } @@ -85,6 +88,9 @@ func (p *packageManifestLoader) LoadPackagesWalkFunc(path string, f os.FileInfo, } func (p *packageManifestLoader) LoadBundleWalkFunc(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } if f == nil { return fmt.Errorf("invalid file: %v", f) } diff --git a/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/chartutil.go b/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/chartutil.go index f7090446a8..49fe3bf9b8 100644 --- a/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/chartutil.go +++ b/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/chartutil.go @@ -23,7 +23,7 @@ import ( "path/filepath" "github.com/pkg/errors" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v3" ) // Maintainer describes a Chart maintainer. diff --git a/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/generate.go b/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/generate.go index 7895126b6f..d3d39f54c6 100644 --- a/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/generate.go +++ b/vendor/github.com/operator-framework/operator-registry/pkg/lib/bundle/generate.go @@ -1,6 +1,7 @@ package bundle import ( + "bytes" "fmt" "io" "os" @@ -8,7 +9,7 @@ import ( "strings" log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" + "go.yaml.in/yaml/v3" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" utilerrors "k8s.io/apimachinery/pkg/util/errors" k8syaml "k8s.io/apimachinery/pkg/util/yaml" @@ -315,12 +316,17 @@ func GenerateAnnotations(mediaType, manifests, metadata, packageName, channels, annotations.Annotations[ChannelDefaultLabel] = channelDefault } - afile, err := yaml.Marshal(annotations) - if err != nil { + var buf bytes.Buffer + encoder := yaml.NewEncoder(&buf) + encoder.SetIndent(2) + if err := encoder.Encode(annotations); err != nil { + return nil, err + } + if err := encoder.Close(); err != nil { return nil, err } - return afile, nil + return buf.Bytes(), nil } // GenerateDockerfile builds Dockerfile with mediatype, manifests & diff --git a/vendor/github.com/operator-framework/operator-registry/pkg/sqlite/load.go b/vendor/github.com/operator-framework/operator-registry/pkg/sqlite/load.go index 190144eaf2..f77a5d7cc9 100644 --- a/vendor/github.com/operator-framework/operator-registry/pkg/sqlite/load.go +++ b/vendor/github.com/operator-framework/operator-registry/pkg/sqlite/load.go @@ -162,6 +162,18 @@ func (s *sqlLoader) addOperatorBundle(tx *sql.Tx, bundle *registry.Bundle) error return s.addAPIs(tx, bundle) } +// markSeen records name in seen, erroring if it was already there. It guards +// the substitutesFor chain walks below against cyclic references (e.g. a +// bundle substituting for itself), which would otherwise loop until the +// process runs out of memory (OCPBUGS-37284). +func markSeen(seen map[string]struct{}, name string) error { + if _, ok := seen[name]; ok { + return fmt.Errorf("cyclic substitutesFor chain detected involving %q", name) + } + seen[name] = struct{}{} + return nil +} + func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error { updateBundleReplaces, err := tx.Prepare("update operatorbundle set replaces = ? where replaces = ?") if err != nil { @@ -217,11 +229,15 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error if err != nil { return err } + otherSeen := map[string]struct{}{csvName: {}} for len(otherSubstitutions) > 0 { // consume the slice of substitutions otherSubstitution := otherSubstitutions[0] otherSubstitutions = otherSubstitutions[1:] if otherSubstitution != csvName { + if err := markSeen(otherSeen, otherSubstitution); err != nil { + return err + } // Another bundle is substituting for that same bundle // Get other bundle's version _, _, rawVersion, err := s.getBundleSkipsReplacesVersion(tx, otherSubstitution) @@ -288,7 +304,11 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error // If the substituted-for of the current bundle substitutes for another bundle // it should also be added to the skips of the substitutesFor bundle + seen := map[string]struct{}{csvName: {}} for substitutesFor != "" { + if err := markSeen(seen, substitutesFor); err != nil { + return err + } skips = append(skips, substitutesFor) substitutesFor, err = s.getBundleSubstitution(tx, substitutesFor) if err != nil { @@ -305,7 +325,11 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error if err != nil || len(substitutesFors) > 1 { return err } + seen = map[string]struct{}{csvName: {}} for len(substitutesFors) > 0 { + if err := markSeen(seen, substitutesFors[0]); err != nil { + return err + } err = s.appendSkips(tx, append(skips, csvName), substitutesFors[0]) if err != nil { return err @@ -318,6 +342,7 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error // Bundles that skip a bundle that is substituted for // should also skip the substituted-for bundle + // nolint:nestif if len(skips) != 0 { // ensure slice of skips doesn't contain duplicates substitutesSkips := make(map[string]struct{}) @@ -328,12 +353,16 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error if err != nil || len(substitutesFors) > 1 { return err } + seen = map[string]struct{}{skip: {}} for len(substitutesFors) > 0 { // consume the slice of substitutions substitutesFor = substitutesFors[0] substitutesFors = substitutesFors[1:] // shouldn't skip yourself if substitutesFor != csvName { + if err := markSeen(seen, substitutesFor); err != nil { + return err + } substitutesSkips[substitutesFor] = struct{}{} substitutesFors, err = s.getBundlesThatSubstitutesFor(tx, substitutesFor) if err != nil || len(substitutesFors) > 1 { @@ -356,9 +385,13 @@ func (s *sqlLoader) addSubstitutesFor(tx *sql.Tx, bundle *registry.Bundle) error if err != nil { return err } + seen = map[string]struct{}{replaces: {}} for len(substitutesFors) > 0 { // update the replaces to a newer substitution replaces = substitutesFors[0] + if err := markSeen(seen, replaces); err != nil { + return err + } // try to get the substitution of the substitution substitutesFors, err = s.getBundlesThatSubstitutesFor(tx, replaces) if err != nil || len(substitutesFors) > 1 { diff --git a/vendor/go.etcd.io/bbolt/.gitattributes b/vendor/go.etcd.io/bbolt/.gitattributes new file mode 100644 index 0000000000..a681ce365f --- /dev/null +++ b/vendor/go.etcd.io/bbolt/.gitattributes @@ -0,0 +1,4 @@ +# ensure that line endings for Windows builds are properly formatted +# see https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#how-to-use +# at "Multiple OS Example" section +*.go text eol=lf diff --git a/vendor/go.etcd.io/bbolt/.go-version b/vendor/go.etcd.io/bbolt/.go-version index 7bdcec52d0..4fd1625308 100644 --- a/vendor/go.etcd.io/bbolt/.go-version +++ b/vendor/go.etcd.io/bbolt/.go-version @@ -1 +1 @@ -1.23.12 +1.25.11 diff --git a/vendor/go.etcd.io/bbolt/.golangci.yaml b/vendor/go.etcd.io/bbolt/.golangci.yaml new file mode 100644 index 0000000000..bef3f6de7a --- /dev/null +++ b/vendor/go.etcd.io/bbolt/.golangci.yaml @@ -0,0 +1,40 @@ +formatters: + enable: + - gci + - gofmt + - goimports + settings: # please keep this alphabetized + gci: + sections: + - standard + - default + - prefix(go.etcd.io) + goimports: + local-prefixes: + - go.etcd.io # Put imports beginning with prefix after 3rd-party packages. +issues: + max-same-issues: 0 +linters: + default: none + enable: # please keep this alphabetized + - errcheck + - govet + - ineffassign + - staticcheck + - unused + exclusions: + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + settings: # please keep this alphabetized + staticcheck: + checks: + - all + - -QF1003 # Convert if/else-if chain to tagged switch + - -QF1010 # Convert slice of bytes to string when printing it + - -ST1003 # Poorly chosen identifier + - -ST1005 # Incorrectly formatted error string + - -ST1012 # Poorly chosen name for error variable +version: "2" diff --git a/vendor/go.etcd.io/bbolt/Makefile b/vendor/go.etcd.io/bbolt/Makefile index f5a6703a0b..f68708cf5b 100644 --- a/vendor/go.etcd.io/bbolt/Makefile +++ b/vendor/go.etcd.io/bbolt/Makefile @@ -2,6 +2,8 @@ BRANCH=`git rev-parse --abbrev-ref HEAD` COMMIT=`git rev-parse --short HEAD` GOLDFLAGS="-X main.branch $(BRANCH) -X main.commit $(COMMIT)" GOFILES = $(shell find . -name \*.go) +REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel) +GOTOOLCHAIN ?= go$(shell cat $(REPOSITORY_ROOT)/.go-version) TESTFLAGS_RACE=-race=false ifdef ENABLE_RACE @@ -33,7 +35,7 @@ fmt: @!(gofmt -l -s -d ${GOFILES} | grep '[a-z]') @echo "Verifying goimports, failures can be fixed with ./scripts/fix.sh" - @!(go run golang.org/x/tools/cmd/goimports@latest -l -d ${GOFILES} | grep '[a-z]') + @!(go tool golang.org/x/tools/cmd/goimports -l -d ${GOFILES} | grep '[a-z]') .PHONY: lint lint: @@ -44,12 +46,12 @@ test: @echo "hashmap freelist test" BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT} BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./internal/... - BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./cmd/bbolt + BBOLT_VERIFY=all TEST_FREELIST_TYPE=hashmap go test -v ${TESTFLAGS} ./cmd/bbolt/... @echo "array freelist test" BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} -timeout ${TESTFLAGS_TIMEOUT} BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./internal/... - BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./cmd/bbolt + BBOLT_VERIFY=all TEST_FREELIST_TYPE=array go test -v ${TESTFLAGS} ./cmd/bbolt/... .PHONY: coverage coverage: @@ -71,16 +73,12 @@ clean: # Clean binaries rm -f ./bin/${BOLT_CMD} .PHONY: gofail-enable -gofail-enable: install-gofail - gofail enable . +gofail-enable: + go tool go.etcd.io/gofail enable . .PHONY: gofail-disable -gofail-disable: install-gofail - gofail disable . - -.PHONY: install-gofail -install-gofail: - go install go.etcd.io/gofail +gofail-disable: + go tool go.etcd.io/gofail disable . .PHONY: test-failpoint test-failpoint: @@ -99,10 +97,6 @@ test-robustness: gofail-enable build .PHONY: test-benchmark-compare # Runs benchmark tests on the current git ref and the given REF, and compares # the two. -test-benchmark-compare: install-benchstat +test-benchmark-compare: @git fetch ./scripts/compare_benchmarks.sh $(REF) - -.PHONY: install-benchstat -install-benchstat: - go install golang.org/x/perf/cmd/benchstat@latest diff --git a/vendor/go.etcd.io/bbolt/OWNERS b/vendor/go.etcd.io/bbolt/OWNERS index 91f168a798..f8ab19db29 100644 --- a/vendor/go.etcd.io/bbolt/OWNERS +++ b/vendor/go.etcd.io/bbolt/OWNERS @@ -6,5 +6,6 @@ approvers: - ptabor # Piotr Tabor - spzala # Sahdev Zala reviewers: + - elbehery # Mustafa Elbehery - fuweid # Wei Fu - tjungblu # Thomas Jungblut diff --git a/vendor/go.etcd.io/bbolt/README.md b/vendor/go.etcd.io/bbolt/README.md index f365e51e3e..7f6468e73b 100644 --- a/vendor/go.etcd.io/bbolt/README.md +++ b/vendor/go.etcd.io/bbolt/README.md @@ -890,13 +890,19 @@ Here are a few things to note when evaluating and using Bolt: to grow. However, it's important to note that deleting large chunks of data will not allow you to reclaim that space on disk. + For more information on page allocation, [see this comment][page-allocation]. + * Removing key/values pairs in a bucket during iteration on the bucket using cursor may not work properly. Each time when removing a key/value pair, the cursor may automatically move to the next position if present. When users call `c.Next()` after removing a key, it may skip one key/value pair. Refer to https://github.com/etcd-io/bbolt/pull/611 for more detailed info. - For more information on page allocation, [see this comment][page-allocation]. +* Bolt db can be corrupted during the initialization phase due to abrupt power failure. + - Please note: This issue can only be reproduced during the very first initialization phase, when there is + no existing data in bolt database. + - In normal production environment, it is difficult to reproduce this. Once the database file has been initialized, it can no longer occur. + - Please refer to this issue for more details: https://github.com/etcd-io/etcd/issues/16596. [page-allocation]: https://github.com/boltdb/bolt/issues/308#issuecomment-74811638 @@ -954,11 +960,16 @@ them via pull request. - bbolt might run into data corruption issue on Linux when the feature [ext4: fast commit](https://lwn.net/Articles/842385/), which was introduced in - linux kernel version v5.10, is enabled. The fixes to the issue were included in - linux kernel version v5.17, please refer to links below, + linux kernel version v5.10, is enabled. The fixes to the issue are included in + stable LTS patchlevels 5.10.94+ and 5.15.17+ (ftruncate tracking), plus + 5.15.27+ (ineligible-commit fallback). Linux 5.17 includes these fixes as + well, but 5.17 is not an LTS release. Please refer to links below, * [ext4: fast commit may miss tracking unwritten range during ftruncate](https://lore.kernel.org/linux-ext4/20211223032337.5198-3-yinxin.x@bytedance.com/) + * [5.10.94 stable backport](https://lore.kernel.org/stable/20220124184041.063143682@linuxfoundation.org/) + * [5.15.17 stable backport](https://lore.kernel.org/stable/20220124184125.887304707@linuxfoundation.org/) * [ext4: fast commit may not fallback for ineligible commit](https://lore.kernel.org/lkml/202201091544.W5HHEXAp-lkp@intel.com/T/#ma0768815e4b5f671e9e451d578256ef9a76fe30e) + * [5.15.27 stable backport](https://lore.kernel.org/stable/20220307091703.544901888@linuxfoundation.org/) * [ext4 updates for 5.17](https://lore.kernel.org/lkml/YdyxjTFaLWif6BCM@mit.edu/) Please also refer to the discussion in https://github.com/etcd-io/bbolt/issues/562. @@ -1000,6 +1011,7 @@ Below is a list of public, open source projects that use Bolt: * [GoShort](https://github.com/pankajkhairnar/goShort) - GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter. * [gopherpit](https://github.com/gopherpit/gopherpit) - A web service to manage Go remote import paths with custom domains * [gokv](https://github.com/philippgille/gokv) - Simple key-value store abstraction and implementations for Go (Redis, Consul, etcd, bbolt, BadgerDB, LevelDB, Memcached, DynamoDB, S3, PostgreSQL, MongoDB, CockroachDB and many more) +* [goraphdb](https://github.com/mstrYoda/goraphdb) - A graph database provides Cypher query, fluent builder and management UI. * [Gitchain](https://github.com/gitchain/gitchain) - Decentralized, peer-to-peer Git repositories aka "Git meets Bitcoin". * [InfluxDB](https://influxdata.com) - Scalable datastore for metrics, events, and real-time analytics. * [ipLocator](https://github.com/AndreasBriese/ipLocator) - A fast ip-geo-location-server using bolt with bloom filters. diff --git a/vendor/go.etcd.io/bbolt/bucket.go b/vendor/go.etcd.io/bbolt/bucket.go index 6371ace972..fec2d6c957 100644 --- a/vendor/go.etcd.io/bbolt/bucket.go +++ b/vendor/go.etcd.io/bbolt/bucket.go @@ -661,16 +661,20 @@ func (b *Bucket) Stats() BucketStats { } } else if p.IsBranchPage() { s.BranchPageN++ - lastElement := p.BranchPageElement(p.Count() - 1) - // used totals the used bytes for the page - // Add header and all element headers. - used := common.PageHeaderSize + (common.BranchPageElementSize * uintptr(p.Count()-1)) + used := common.PageHeaderSize + if p.Count() != 0 { + lastElement := p.BranchPageElement(p.Count() - 1) + + // Add all element headers. + used += common.BranchPageElementSize * uintptr(p.Count()-1) + + // Add size of all keys and values. + // Again, use the fact that last element's position equals to + // the total of key, value sizes of all previous elements. + used += uintptr(lastElement.Pos() + lastElement.Ksize()) + } - // Add size of all keys and values. - // Again, use the fact that last element's position equals to - // the total of key, value sizes of all previous elements. - used += uintptr(lastElement.Pos() + lastElement.Ksize()) s.BranchInuse += int(used) s.BranchOverflowN += int(p.Overflow()) } @@ -880,7 +884,9 @@ func (b *Bucket) node(pgId common.Pgid, parent *node) *node { // if p isn't nil, then it's an inline bucket. // The pgId must be 0 in this case. common.Verify(func() { - common.Assert(pgId == 0, "The page ID (%d) isn't 0 for an inline bucket", pgId) + if pgId != 0 { + panic(fmt.Sprintf("assertion failed: The page ID (%d) isn't 0 for an inline bucket", pgId)) + } }) } diff --git a/vendor/go.etcd.io/bbolt/code-of-conduct.md b/vendor/go.etcd.io/bbolt/code-of-conduct.md new file mode 100644 index 0000000000..f78dd84bc2 --- /dev/null +++ b/vendor/go.etcd.io/bbolt/code-of-conduct.md @@ -0,0 +1,3 @@ +# etcd Community Code of Conduct + +Please refer to [etcd Community Code of Conduct](https://github.com/etcd-io/etcd/blob/main/code-of-conduct.md). diff --git a/vendor/go.etcd.io/bbolt/db.go b/vendor/go.etcd.io/bbolt/db.go index 622947d9cb..5babb6ab16 100644 --- a/vendor/go.etcd.io/bbolt/db.go +++ b/vendor/go.etcd.io/bbolt/db.go @@ -36,12 +36,6 @@ const ( // All data access is performed through transactions which can be obtained through the DB. // All the functions on DB will return a ErrDatabaseNotOpen if accessed before Open() is called. type DB struct { - // Put `stats` at the first field to ensure it's 64-bit aligned. Note that - // the first word in an allocated struct can be relied upon to be 64-bit - // aligned. Refer to https://pkg.go.dev/sync/atomic#pkg-note-BUG. Also - // refer to discussion in https://github.com/etcd-io/bbolt/issues/577. - stats Stats - // When enabled, the database will perform a Check() after every commit. // A panic is issued if the database is in an inconsistent state. This // flag has a large performance impact so it should only be used for @@ -110,6 +104,12 @@ type DB struct { // of truncate() and fsync() when growing the data file. AllocSize int + // MaxSize is the maximum size (in bytes) allowed for the data file. + // If a caller's attempt to add data results in the need to grow + // the data file, an error will be returned and the data file will not grow. + // <=0 means no limit. + MaxSize int + // Mlock locks database file in memory when set to true. // It prevents major page faults, however used memory can't be reclaimed. // @@ -132,7 +132,7 @@ type DB struct { pageSize int opened bool rwtx *Tx - txs []*Tx + stats *Stats freelist fl.Interface freelistLoad sync.Once @@ -191,12 +191,17 @@ func Open(path string, mode os.FileMode, options *Options) (db *DB, err error) { db.PreLoadFreelist = options.PreLoadFreelist db.FreelistType = options.FreelistType db.Mlock = options.Mlock + db.MaxSize = options.MaxSize // Set default values for later DB operations. db.MaxBatchSize = common.DefaultMaxBatchSize db.MaxBatchDelay = common.DefaultMaxBatchDelay db.AllocSize = common.DefaultAllocSize + if !options.NoStatistics { + db.stats = new(Stats) + } + if options.Logger == nil { db.logger = getDiscardLogger() } else { @@ -424,7 +429,9 @@ func (db *DB) loadFreelist() { // Read free list from freelist page. db.freelist.Read(db.page(db.meta().Freelist())) } - db.stats.FreePageN = db.freelist.FreeCount() + if db.stats != nil { + db.stats.FreePageN = db.freelist.FreeCount() + } }) } @@ -469,6 +476,23 @@ func (db *DB) mmap(minsz int) (err error) { return err } + if db.MaxSize > 0 && size > db.MaxSize { + // On Windows, the data file is expanded to the full mapped size during mmap, + // so we must reject any mmap size larger than the configured max size. + // + // On other platforms, mmap itself does not grow the file immediately, so the + // mapped size may exceed the max size temporarily. The file size limit is + // enforced later when the file actually grows. + // + // In practice, this check mainly applies when opening a database with a large + // InitialMmapSize. In all other cases, file growth is already guarded during + // page allocation. + if size > fileSize && runtime.GOOS == "windows" { + db.Logger().Errorf("[GOOS: %s, GOARCH: %s] maximum db size reached, size: %d, db.MaxSize: %d", runtime.GOOS, runtime.GOARCH, size, db.MaxSize) + return berrors.ErrMaxSizeReached + } + } + if db.Mlock { // Unlock db memory if err := db.munlock(fileSize); err != nil { @@ -545,7 +569,7 @@ func (db *DB) munmap() error { // return errors.New(unmapError) if err := munmap(db); err != nil { db.Logger().Errorf("[GOOS: %s, GOARCH: %s] munmap failed, db.datasz: %d, error: %v", runtime.GOOS, runtime.GOARCH, db.datasz, err) - return fmt.Errorf("unmap error: %v", err.Error()) + return fmt.Errorf("unmap error: %w", err) } return nil @@ -593,7 +617,7 @@ func (db *DB) munlock(fileSize int) error { // return errors.New(munlockError) if err := munlock(db, fileSize); err != nil { db.Logger().Errorf("[GOOS: %s, GOARCH: %s] munlock failed, fileSize: %d, db.datasz: %d, error: %v", runtime.GOOS, runtime.GOARCH, fileSize, db.datasz, err) - return fmt.Errorf("munlock error: %v", err.Error()) + return fmt.Errorf("munlock error: %w", err) } return nil } @@ -603,7 +627,7 @@ func (db *DB) mlock(fileSize int) error { // return errors.New(mlockError) if err := mlock(db, fileSize); err != nil { db.Logger().Errorf("[GOOS: %s, GOARCH: %s] mlock failed, fileSize: %d, db.datasz: %d, error: %v", runtime.GOOS, runtime.GOARCH, fileSize, db.datasz, err) - return fmt.Errorf("mlock error: %v", err.Error()) + return fmt.Errorf("mlock error: %w", err) } return nil } @@ -794,9 +818,6 @@ func (db *DB) beginTx() (*Tx, error) { t := &Tx{} t.init(db) - // Keep track of transaction until it closes. - db.txs = append(db.txs, t) - n := len(db.txs) if db.freelist != nil { db.freelist.AddReadonlyTXID(t.meta.Txid()) } @@ -805,10 +826,12 @@ func (db *DB) beginTx() (*Tx, error) { db.metalock.Unlock() // Update the transaction stats. - db.statlock.Lock() - db.stats.TxN++ - db.stats.OpenTxN = n - db.statlock.Unlock() + if db.stats != nil { + db.statlock.Lock() + db.stats.TxN++ + db.stats.OpenTxN++ + db.statlock.Unlock() + } return t, nil } @@ -856,17 +879,6 @@ func (db *DB) removeTx(tx *Tx) { // Use the meta lock to restrict access to the DB object. db.metalock.Lock() - // Remove the transaction. - for i, t := range db.txs { - if t == tx { - last := len(db.txs) - 1 - db.txs[i] = db.txs[last] - db.txs[last] = nil - db.txs = db.txs[:last] - break - } - } - n := len(db.txs) if db.freelist != nil { db.freelist.RemoveReadonlyTXID(tx.meta.Txid()) } @@ -875,10 +887,12 @@ func (db *DB) removeTx(tx *Tx) { db.metalock.Unlock() // Merge statistics. - db.statlock.Lock() - db.stats.OpenTxN = n - db.stats.TxStats.add(&tx.stats) - db.statlock.Unlock() + if db.stats != nil { + db.statlock.Lock() + db.stats.OpenTxN-- + db.stats.TxStats.add(&tx.stats) + db.statlock.Unlock() + } } // Update executes a function within the context of a read-write managed transaction. @@ -1096,9 +1110,13 @@ func (db *DB) Sync() (err error) { // Stats retrieves ongoing performance stats for the database. // This is only updated when a transaction closes. func (db *DB) Stats() Stats { - db.statlock.RLock() - defer db.statlock.RUnlock() - return db.stats + var s Stats + if db.stats != nil { + db.statlock.RLock() + s = *db.stats + db.statlock.RUnlock() + } + return s } // This is for internal access to the raw data bytes from the C cursor, use @@ -1164,9 +1182,33 @@ func (db *DB) allocate(txid common.Txid, count int) (*common.Page, error) { // Resize mmap() if we're at the end. p.SetId(db.rwtx.meta.Pgid()) var minsz = int((p.Id()+common.Pgid(count))+1) * db.pageSize + if db.MaxSize > 0 { + nextAllocSize := minsz + nextMmapSize, err := db.mmapSize(minsz) + if err != nil { + return nil, fmt.Errorf("mmap size calculation error: %w", err) + } + if runtime.GOOS == "windows" { + // nextAllocSize may not exactly match nextMmapSize. + // On Windows, this mismatch may cause the file size to slightly exceed maxSize, + // while it is harmless on other platforms. + nextAllocSize = nextMmapSize + } else { + // On non-Windows platforms, the database file is only grown explicitly in grow calls. + nextAllocSize = db.growSize(nextMmapSize, nextAllocSize) + } + if nextAllocSize > db.MaxSize { + db.Logger().Errorf("[GOOS: %s, GOARCH: %s] maximum db size reached, minSize: %d (allocSize: %d), db.MaxSize: %d", runtime.GOOS, runtime.GOARCH, minsz, nextAllocSize, db.MaxSize) + return nil, berrors.ErrMaxSizeReached + } + } if minsz >= db.datasz { if err := db.mmap(minsz); err != nil { - return nil, fmt.Errorf("mmap allocate error: %s", err) + if err == berrors.ErrMaxSizeReached { + return nil, err + } else { + return nil, fmt.Errorf("mmap allocate error: %s", err) + } } } @@ -1190,13 +1232,7 @@ func (db *DB) grow(sz int) error { return nil } - // If the data is smaller than the alloc size then only allocate what's needed. - // Once it goes over the allocation size then allocate in chunks. - if db.datasz <= db.AllocSize { - sz = db.datasz - } else { - sz += db.AllocSize - } + sz = db.growSize(db.datasz, sz) // Truncate and fsync to ensure file size metadata is flushed. // https://github.com/boltdb/bolt/issues/284 @@ -1224,6 +1260,16 @@ func (db *DB) grow(sz int) error { return nil } +func (db *DB) growSize(mmapSize, growSize int) int { + // If the data is smaller than the alloc size then only allocate what's needed. + // Once it goes over the allocation size then allocate in chunks. + if mmapSize <= db.AllocSize { + return mmapSize + } else { + return growSize + db.AllocSize + } +} + func (db *DB) IsReadOnly() bool { return db.readOnly } @@ -1243,13 +1289,16 @@ func (db *DB) freepages() []common.Pgid { reachable := make(map[common.Pgid]*common.Page) nofreed := make(map[common.Pgid]bool) ech := make(chan error) + go func() { - for e := range ech { - panic(fmt.Sprintf("freepages: failed to get all reachable pages (%v)", e)) - } + defer close(ech) + tx.recursivelyCheckBucket(&tx.root, reachable, nofreed, HexKVStringer(), ech) }() - tx.recursivelyCheckBucket(&tx.root, reachable, nofreed, HexKVStringer(), ech) - close(ech) + // following for loop will exit once channel is closed in the above goroutine. + // we don't need to wait explictly with a waitgroup + for e := range ech { + panic(fmt.Sprintf("freepages: failed to get all reachable pages (%v)", e)) + } // TODO: If check bucket reported any corruptions (ech) we shouldn't proceed to freeing the pages. @@ -1320,6 +1369,9 @@ type Options struct { // PageSize overrides the default OS page size. PageSize int + // MaxSize sets the maximum size of the data file. A value <= 0 means no limit. + MaxSize int + // NoSync sets the initial value of DB.NoSync. Normally this can just be // set directly on the DB itself when returned from Open(), but this option // is useful in APIs which expose Options but not the underlying DB. @@ -1336,6 +1388,11 @@ type Options struct { // Logger is the logger used for bbolt. Logger Logger + + // NoStatistics turns off statistics collection, Stats method will + // return empty structure in this case. This can be beneficial for + // performance under high-concurrency read-only transactions. + NoStatistics bool } func (o *Options) String() string { @@ -1343,8 +1400,8 @@ func (o *Options) String() string { return "{}" } - return fmt.Sprintf("{Timeout: %s, NoGrowSync: %t, NoFreelistSync: %t, PreLoadFreelist: %t, FreelistType: %s, ReadOnly: %t, MmapFlags: %x, InitialMmapSize: %d, PageSize: %d, NoSync: %t, OpenFile: %p, Mlock: %t, Logger: %p}", - o.Timeout, o.NoGrowSync, o.NoFreelistSync, o.PreLoadFreelist, o.FreelistType, o.ReadOnly, o.MmapFlags, o.InitialMmapSize, o.PageSize, o.NoSync, o.OpenFile, o.Mlock, o.Logger) + return fmt.Sprintf("{Timeout: %s, NoGrowSync: %t, NoFreelistSync: %t, PreLoadFreelist: %t, FreelistType: %s, ReadOnly: %t, MmapFlags: %x, InitialMmapSize: %d, PageSize: %d, MaxSize: %d, NoSync: %t, OpenFile: %p, Mlock: %t, Logger: %p, NoStatistics: %t}", + o.Timeout, o.NoGrowSync, o.NoFreelistSync, o.PreLoadFreelist, o.FreelistType, o.ReadOnly, o.MmapFlags, o.InitialMmapSize, o.PageSize, o.MaxSize, o.NoSync, o.OpenFile, o.Mlock, o.Logger, o.NoStatistics) } diff --git a/vendor/go.etcd.io/bbolt/errors/errors.go b/vendor/go.etcd.io/bbolt/errors/errors.go index c115289e56..dbebd6330e 100644 --- a/vendor/go.etcd.io/bbolt/errors/errors.go +++ b/vendor/go.etcd.io/bbolt/errors/errors.go @@ -69,6 +69,9 @@ var ( // ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize. ErrValueTooLarge = errors.New("value too large") + // ErrMaxSizeReached is returned when the configured maximum size of the data file is reached. + ErrMaxSizeReached = errors.New("database reached maximum size") + // ErrIncompatibleValue is returned when trying to create or delete a bucket // on an existing non-bucket key or when trying to create or delete a // non-bucket key on an existing bucket key. diff --git a/vendor/go.etcd.io/bbolt/internal/common/page.go b/vendor/go.etcd.io/bbolt/internal/common/page.go index ee808967c5..747588aee7 100644 --- a/vendor/go.etcd.io/bbolt/internal/common/page.go +++ b/vendor/go.etcd.io/bbolt/internal/common/page.go @@ -74,19 +74,27 @@ func (p *Page) IsFreelistPage() bool { return p.flags == FreelistPageFlag } +// IsValidPage checks Page flags correctness, only a single proper flag can be used. +func (p *Page) IsValidPage() bool { + return p.IsBranchPage() || + p.IsLeafPage() || + p.IsMetaPage() || + p.IsFreelistPage() +} + // Meta returns a pointer to the metadata section of the page. func (p *Page) Meta() *Meta { return (*Meta)(UnsafeAdd(unsafe.Pointer(p), unsafe.Sizeof(*p))) } func (p *Page) FastCheck(id Pgid) { - Assert(p.id == id, "Page expected to be: %v, but self identifies as %v", id, p.id) + if p.id != id { + panic(fmt.Sprintf("assertion failed: Page expected to be: %v, but self identifies as %v", id, p.id)) + } // Only one flag of page-type can be set. - Assert(p.IsBranchPage() || - p.IsLeafPage() || - p.IsMetaPage() || - p.IsFreelistPage(), - "page %v: has unexpected type/flags: %x", p.id, p.flags) + if !p.IsValidPage() { + panic(fmt.Sprintf("assertion failed: page %v: has unexpected type/flags: %x", p.id, p.flags)) + } } // LeafPageElement retrieves the leaf node by index @@ -122,7 +130,9 @@ func (p *Page) BranchPageElements() []branchPageElement { } func (p *Page) FreelistPageCount() (int, int) { - Assert(p.IsFreelistPage(), fmt.Sprintf("can't get freelist page count from a non-freelist page: %2x", p.flags)) + if !p.IsFreelistPage() { + panic(fmt.Sprintf("assertion failed: can't get freelist page count from a non-freelist page: %2x", p.flags)) + } // If the page.count is at the max uint16 value (64k) then it's considered // an overflow and the size of the freelist is stored as the first element. @@ -140,7 +150,9 @@ func (p *Page) FreelistPageCount() (int, int) { } func (p *Page) FreelistPageIds() []Pgid { - Assert(p.IsFreelistPage(), fmt.Sprintf("can't get freelist page IDs from a non-freelist page: %2x", p.flags)) + if !p.IsFreelistPage() { + panic(fmt.Sprintf("assertion failed: can't get freelist page IDs from a non-freelist page: %2x", p.flags)) + } idx, count := p.FreelistPageCount() @@ -335,16 +347,16 @@ func (s Pgids) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s Pgids) Less(i, j int) bool { return s[i] < s[j] } // Merge returns the sorted union of a and b. -func (a Pgids) Merge(b Pgids) Pgids { +func (s Pgids) Merge(b Pgids) Pgids { // Return the opposite slice if one is nil. - if len(a) == 0 { + if len(s) == 0 { return b } if len(b) == 0 { - return a + return s } - merged := make(Pgids, len(a)+len(b)) - Mergepgids(merged, a, b) + merged := make(Pgids, len(s)+len(b)) + Mergepgids(merged, s, b) return merged } diff --git a/vendor/go.etcd.io/bbolt/internal/freelist/hashmap.go b/vendor/go.etcd.io/bbolt/internal/freelist/hashmap.go index 8d471f4b5b..720496be81 100644 --- a/vendor/go.etcd.io/bbolt/internal/freelist/hashmap.go +++ b/vendor/go.etcd.io/bbolt/internal/freelist/hashmap.go @@ -108,8 +108,10 @@ func (f *hashMap) Allocate(txid common.Txid, n int) common.Pgid { func (f *hashMap) FreeCount() int { common.Verify(func() { expectedFreePageCount := f.hashmapFreeCountSlow() - common.Assert(int(f.freePagesCount) == expectedFreePageCount, - "freePagesCount (%d) is out of sync with free pages map (%d)", f.freePagesCount, expectedFreePageCount) + if int(f.freePagesCount) != expectedFreePageCount { + panic(fmt.Sprintf("assertion failed: freePagesCount (%d) is out of sync with free pages map (%d)", + f.freePagesCount, expectedFreePageCount)) + } }) return int(f.freePagesCount) } @@ -169,6 +171,11 @@ func (f *hashMap) delSpan(start common.Pgid, size uint64) { } func (f *hashMap) mergeSpans(ids common.Pgids) { + if len(ids) == 0 { + return + } + sort.Sort(ids) + common.Verify(func() { ids1Freemap := f.idsFromFreemaps() ids2Forward := f.idsFromForwardMap() @@ -181,7 +188,6 @@ func (f *hashMap) mergeSpans(ids common.Pgids) { panic(fmt.Sprintf("Detected mismatch, f.freemaps: %v, f.backwardMap: %v", f.freemaps, f.backwardMap)) } - sort.Sort(ids) prev := common.Pgid(0) for _, id := range ids { // The ids shouldn't have duplicated free ID. @@ -196,26 +202,36 @@ func (f *hashMap) mergeSpans(ids common.Pgids) { } } }) - for _, id := range ids { - // try to see if we can merge and update - f.mergeWithExistingSpan(id) + + start := ids[0] + end := ids[0] + for i := 1; i < len(ids); i++ { + id := ids[i] + if id == end+1 { + end = id + continue + } + + f.mergeWithExistingSpan(start, end) + start, end = id, id } + f.mergeWithExistingSpan(start, end) } -// mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward -func (f *hashMap) mergeWithExistingSpan(pid common.Pgid) { - prev := pid - 1 - next := pid + 1 +// mergeWithExistingSpan merges free span [start, end] with adjacent existing free spans (both backward and forward). +func (f *hashMap) mergeWithExistingSpan(start, end common.Pgid) { + prev := start - 1 + next := end + 1 preSize, mergeWithPrev := f.backwardMap[prev] nextSize, mergeWithNext := f.forwardMap[next] - newStart := pid - newSize := uint64(1) + newStart := start + newSize := uint64(end - start + 1) if mergeWithPrev { - //merge with previous span - start := prev + 1 - common.Pgid(preSize) - f.delSpan(start, preSize) + // merge with previous span + prevStart := prev + 1 - common.Pgid(preSize) + f.delSpan(prevStart, preSize) newStart -= common.Pgid(preSize) newSize += preSize diff --git a/vendor/go.etcd.io/bbolt/internal/freelist/shared.go b/vendor/go.etcd.io/bbolt/internal/freelist/shared.go index f2d1130083..f30a69f107 100644 --- a/vendor/go.etcd.io/bbolt/internal/freelist/shared.go +++ b/vendor/go.etcd.io/bbolt/internal/freelist/shared.go @@ -220,10 +220,10 @@ func (t *shared) Reload(p *common.Page) { func (t *shared) NoSyncReload(pgIds common.Pgids) { // Build a cache of only pending pages. - pcache := make(map[common.Pgid]bool) + pcache := make(map[common.Pgid]struct{}) for _, txp := range t.pending { for _, pendingID := range txp.ids { - pcache[pendingID] = true + pcache[pendingID] = struct{}{} } } @@ -231,7 +231,7 @@ func (t *shared) NoSyncReload(pgIds common.Pgids) { // with any pages not in the pending lists. a := []common.Pgid{} for _, id := range pgIds { - if !pcache[id] { + if _, ok := pcache[id]; !ok { a = append(a, id) } } diff --git a/vendor/go.etcd.io/bbolt/tx.go b/vendor/go.etcd.io/bbolt/tx.go index 1669fb16a2..aa0066bd35 100644 --- a/vendor/go.etcd.io/bbolt/tx.go +++ b/vendor/go.etcd.io/bbolt/tx.go @@ -357,13 +357,15 @@ func (tx *Tx) close() { tx.db.rwlock.Unlock() // Merge statistics. - tx.db.statlock.Lock() - tx.db.stats.FreePageN = freelistFreeN - tx.db.stats.PendingPageN = freelistPendingN - tx.db.stats.FreeAlloc = (freelistFreeN + freelistPendingN) * tx.db.pageSize - tx.db.stats.FreelistInuse = freelistAlloc - tx.db.stats.TxStats.add(&tx.stats) - tx.db.statlock.Unlock() + if tx.db.stats != nil { + tx.db.statlock.Lock() + tx.db.stats.FreePageN = freelistFreeN + tx.db.stats.PendingPageN = freelistPendingN + tx.db.stats.FreeAlloc = (freelistFreeN + freelistPendingN) * tx.db.pageSize + tx.db.stats.FreelistInuse = freelistAlloc + tx.db.stats.TxStats.add(&tx.stats) + tx.db.statlock.Unlock() + } } else { tx.db.removeTx(tx) } @@ -396,7 +398,7 @@ func (tx *Tx) WriteTo(w io.Writer) (n int64, err error) { // the transaction was based on. // // To overcome this, we reuse the already opened file handle when - // WritFlag not set. When the WriteFlag is set, we reopen the file + // WriteFlag not set. When the WriteFlag is set, we reopen the file // but verify that it still refers to the same underlying file // (by device and inode). If it does not, we fall back to // reusing the existing already opened file handle. diff --git a/vendor/go.etcd.io/bbolt/tx_check.go b/vendor/go.etcd.io/bbolt/tx_check.go index c3ecbb9750..a1e9b33cf0 100644 --- a/vendor/go.etcd.io/bbolt/tx_check.go +++ b/vendor/go.etcd.io/bbolt/tx_check.go @@ -36,6 +36,11 @@ func (tx *Tx) Check(options ...CheckOption) <-chan error { } func (tx *Tx) check(cfg checkConfig, ch chan error) { + defer func() { + if r := recover(); r != nil { + ch <- panicked{r} + } + }() // Force loading free list if opened in ReadOnly mode. tx.db.loadFreelist() @@ -281,10 +286,10 @@ func HexKVStringer() KVStringer { type hexKvStringer struct{} -func (_ hexKvStringer) KeyToString(key []byte) string { +func (hexKvStringer) KeyToString(key []byte) string { return hex.EncodeToString(key) } -func (_ hexKvStringer) ValueToString(value []byte) string { +func (hexKvStringer) ValueToString(value []byte) string { return hex.EncodeToString(value) } diff --git a/vendor/golang.org/x/mod/modfile/read.go b/vendor/golang.org/x/mod/modfile/read.go index 5b528c718e..9e35e1ac51 100644 --- a/vendor/golang.org/x/mod/modfile/read.go +++ b/vendor/golang.org/x/mod/modfile/read.go @@ -924,7 +924,7 @@ var ( moduleStr = []byte("module") ) -// ModulePath returns the module path from the gomod file text. +// ModulePath returns the module path from the go.mod file text. // If it cannot find a module path, it returns an empty string. // It is tolerant of unrelated problems in the go.mod file. func ModulePath(mod []byte) string { diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go index 9ab203b562..20ba825d29 100644 --- a/vendor/golang.org/x/mod/modfile/rule.go +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -1477,7 +1477,7 @@ func (f *File) setRequireSeparateIndirect(req []*Require, simplify bool) { // Delete requirements we don't want anymore. // Update versions and indirect comments on requirements we want to keep. // If a requirement is in last{Direct,Indirect}Block with the wrong - // indirect marking after this, or if the requirement is in an single + // indirect marking after this, or if the requirement is in a single // uncommented mixed block (oneFlatUncommentedBlock), move it to the // correct block. // @@ -1537,7 +1537,7 @@ func (f *File) DropRequire(path string) error { return nil } -// AddExclude adds a exclude statement to the mod file. Errors if the provided +// AddExclude adds an exclude statement to the mod file. Errors if the provided // version is not a canonical version string func (f *File) AddExclude(path, vers string) error { if err := checkCanonicalVersion(path, vers); err != nil { @@ -1708,7 +1708,7 @@ func (f *File) AddIgnore(path string) error { return nil } -// DropIgnore removes a ignore directive with the given path. +// DropIgnore removes an ignore directive with the given path. // It does nothing if no such ignore directive exists. func (f *File) DropIgnore(path string) error { for _, t := range f.Ignore { diff --git a/vendor/modules.txt b/vendor/modules.txt index 287fba4ab7..94a2748501 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -76,8 +76,8 @@ github.com/cespare/xxhash/v2 # github.com/containerd/cgroups/v3 v3.1.2 ## explicit; go 1.22.0 github.com/containerd/cgroups/v3/cgroup1/stats -# github.com/containerd/containerd v1.7.33 -## explicit; go 1.24.0 +# github.com/containerd/containerd v1.7.34 +## explicit; go 1.25.0 github.com/containerd/containerd/archive github.com/containerd/containerd/archive/compression github.com/containerd/containerd/archive/tarheader @@ -163,7 +163,7 @@ github.com/davecgh/go-spew/spew # github.com/distribution/reference v0.6.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v29.5.3+incompatible +# github.com/docker/cli v29.6.2+incompatible ## explicit github.com/docker/cli/cli/config github.com/docker/cli/cli/config/configfile @@ -317,7 +317,7 @@ github.com/golang/groupcache/lru # github.com/golang/protobuf v1.5.4 ## explicit; go 1.17 github.com/golang/protobuf/proto -# github.com/google/cel-go v0.28.1 +# github.com/google/cel-go v0.29.2 ## explicit; go 1.23.0 github.com/google/cel-go/cel github.com/google/cel-go/checker @@ -398,8 +398,8 @@ github.com/itchyny/timefmt-go # github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 ## explicit github.com/jbenet/go-context/io -# github.com/joelanford/ignore v0.1.1 -## explicit; go 1.21 +# github.com/joelanford/ignore v0.1.2 +## explicit; go 1.25.0 github.com/joelanford/ignore # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 @@ -423,7 +423,7 @@ github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.20 ## explicit; go 1.15 github.com/mattn/go-isatty -# github.com/mattn/go-sqlite3 v1.14.45 +# github.com/mattn/go-sqlite3 v1.14.48 ## explicit; go 1.21 github.com/mattn/go-sqlite3 # github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 @@ -438,7 +438,7 @@ github.com/mitchellh/hashstructure/v2 # github.com/moby/locker v1.0.1 ## explicit; go 1.13 github.com/moby/locker -# github.com/moby/moby/client v0.4.1 +# github.com/moby/moby/client v0.5.0 ## explicit; go 1.24 github.com/moby/moby/client/pkg/versions # github.com/moby/sys/capability v0.4.0 @@ -547,7 +547,7 @@ github.com/openshift/client-go/config/listers/config/v1alpha2 # github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a ## explicit; go 1.24.0 github.com/openshift/library-go/pkg/crypto -# github.com/operator-framework/api v0.44.0 +# github.com/operator-framework/api v0.45.0 ## explicit; go 1.26.3 github.com/operator-framework/api/crds github.com/operator-framework/api/pkg/constraints @@ -566,7 +566,7 @@ github.com/operator-framework/api/pkg/validation github.com/operator-framework/api/pkg/validation/errors github.com/operator-framework/api/pkg/validation/interfaces github.com/operator-framework/api/pkg/validation/internal -# github.com/operator-framework/operator-registry v1.72.0 +# github.com/operator-framework/operator-registry v1.73.0 ## explicit; go 1.26.3 github.com/operator-framework/operator-registry/alpha/declcfg github.com/operator-framework/operator-registry/alpha/model @@ -647,8 +647,8 @@ github.com/tidwall/btree # github.com/x448/float16 v0.8.4 ## explicit; go 1.11 github.com/x448/float16 -# go.etcd.io/bbolt v1.4.3 -## explicit; go 1.23 +# go.etcd.io/bbolt v1.5.0 +## explicit; go 1.25.0 go.etcd.io/bbolt go.etcd.io/bbolt/errors go.etcd.io/bbolt/internal/common @@ -760,7 +760,7 @@ go.opentelemetry.io/proto/otlp/collector/trace/v1 go.opentelemetry.io/proto/otlp/common/v1 go.opentelemetry.io/proto/otlp/resource/v1 go.opentelemetry.io/proto/otlp/trace/v1 -# go.podman.io/common v0.68.0 +# go.podman.io/common v0.68.1 ## explicit; go 1.25.6 go.podman.io/common/pkg/auth go.podman.io/common/pkg/capabilities @@ -856,7 +856,7 @@ golang.org/x/crypto/salsa20/salsa ## explicit; go 1.25.0 golang.org/x/exp/maps golang.org/x/exp/slices -# golang.org/x/mod v0.37.0 +# golang.org/x/mod v0.38.0 ## explicit; go 1.25.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile