diff --git a/WORKSPACE b/WORKSPACE index 6fdc12466ca1..13b53ee14433 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -160,17 +160,10 @@ go_download_sdk( sdks = { "darwin_amd64": ("go1.17.10.darwin-amd64.tar.gz", "84979d5985c70cee6f303050a7e811440aad7f304efdf28665b200f096b01945"), "darwin_arm64": ("go1.17.10.darwin-arm64.tar.gz", "32098bea40117ea1ec23e7124cd188db6bdddd0ea41e2ec9bea3ba35a487e39c"), - "freebsd_386": ("go1.17.10.freebsd-386.tar.gz", "37f005846d70f477e89fcf7279f8869c28ba1d266cebf794d2f7a79a1e2127ec"), "freebsd_amd64": ("go1.17.10.freebsd-amd64.tar.gz", "33794d96f58608fdc023c5114ae9baeeb4111a74720c8830ff25029debe508f0"), - "linux_386": ("go1.17.10.linux-386.tar.gz", "5c45aad27c3091b07517ed57df5650dad5b3b71111cebdf83153878f140ad981"), "linux_amd64": ("go1.17.10.linux-amd64.tar.gz", "87fc728c9c731e2f74e4a999ef53cf07302d7ed3504b0839027bd9c10edaa3fd"), "linux_arm64": ("go1.17.10.linux-arm64.tar.gz", "649141201efa7195403eb1301b95dc79c5b3e65968986a391da1370521701b0c"), - "linux_armv6l": ("go1.17.10.linux-armv6l.tar.gz", "ad2d9053011c24be07c39f337f4a31987381d905874acbf2570511e050418dd3"), - "linux_ppc64le": ("go1.17.10.linux-ppc64le.tar.gz", "4e2310864fa9d5a4e2d589dbc5fcd98ce8c4cd25320b335efed04dc67765e66c"), - "linux_s390x": ("go1.17.10.linux-s390x.tar.gz", "e1df61f29fb0962b89d7bfe18b7db45eee003d5f8a1a7ff4d9e54616689076bf"), - "windows_386": ("go1.17.10.windows-386.zip", "60840a079a04c838dc44b7d48f74ac37506298d34d2a002ced48831ccce6bdae"), "windows_amd64": ("go1.17.10.windows-amd64.zip", "ba9198a29fa5c4f322212d21569e8507165c3b34e1ed1f1f9cf6dfb71ddcdeb2"), - "windows_arm64": ("go1.17.10.windows-arm64.zip", "9d06da8d3e9c3f4a5905c4ea9cb271294e2ff956926380322feb2561e84b4d68"), }, urls = ["https://storage.googleapis.com/public-bazel-artifacts/go/{}"], version = "1.17.10", diff --git a/build/bazelutil/distdir_files.bzl b/build/bazelutil/distdir_files.bzl index b241c9b69fb3..d3d750437e2b 100644 --- a/build/bazelutil/distdir_files.bzl +++ b/build/bazelutil/distdir_files.bzl @@ -975,17 +975,10 @@ DISTDIR_FILES = { "https://storage.googleapis.com/public-bazel-artifacts/c-deps/20220520-181309/libproj_foreign.windows.20220520-181309.tar.gz": "4d935601d1b989ff020c66011355a0e2985abacf36f9d952f7f1ce34d54684ad", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.darwin-amd64.tar.gz": "84979d5985c70cee6f303050a7e811440aad7f304efdf28665b200f096b01945", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.darwin-arm64.tar.gz": "32098bea40117ea1ec23e7124cd188db6bdddd0ea41e2ec9bea3ba35a487e39c", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.freebsd-386.tar.gz": "37f005846d70f477e89fcf7279f8869c28ba1d266cebf794d2f7a79a1e2127ec", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.freebsd-amd64.tar.gz": "33794d96f58608fdc023c5114ae9baeeb4111a74720c8830ff25029debe508f0", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-386.tar.gz": "5c45aad27c3091b07517ed57df5650dad5b3b71111cebdf83153878f140ad981", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-amd64.tar.gz": "87fc728c9c731e2f74e4a999ef53cf07302d7ed3504b0839027bd9c10edaa3fd", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-arm64.tar.gz": "649141201efa7195403eb1301b95dc79c5b3e65968986a391da1370521701b0c", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-armv6l.tar.gz": "ad2d9053011c24be07c39f337f4a31987381d905874acbf2570511e050418dd3", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-ppc64le.tar.gz": "4e2310864fa9d5a4e2d589dbc5fcd98ce8c4cd25320b335efed04dc67765e66c", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.linux-s390x.tar.gz": "e1df61f29fb0962b89d7bfe18b7db45eee003d5f8a1a7ff4d9e54616689076bf", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.windows-386.zip": "60840a079a04c838dc44b7d48f74ac37506298d34d2a002ced48831ccce6bdae", "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.windows-amd64.zip": "ba9198a29fa5c4f322212d21569e8507165c3b34e1ed1f1f9cf6dfb71ddcdeb2", - "https://storage.googleapis.com/public-bazel-artifacts/go/go1.17.10.windows-arm64.zip": "9d06da8d3e9c3f4a5905c4ea9cb271294e2ff956926380322feb2561e84b4d68", "https://storage.googleapis.com/public-bazel-artifacts/gomod/github.com/bazelbuild/buildtools/v0.0.0-20200718160251-b1667ff58f71/buildtools-v0.0.0-20200718160251-b1667ff58f71.tar.gz": "a9ef5103739dfb5ed2a5b47ab1654842a89695812e4af09e57d7015a5caf97e0", "https://storage.googleapis.com/public-bazel-artifacts/java/railroad/rr-1.63-java8.zip": "d2791cd7a44ea5be862f33f5a9b3d40aaad9858455828ebade7007ad7113fb41", "https://storage.googleapis.com/public-bazel-artifacts/js/node/v16.13.0/node-v16.13.0-darwin-arm64.tar.gz": "46d83fc0bd971db5050ef1b15afc44a6665dee40bd6c1cbaec23e1b40fa49e6d", diff --git a/pkg/sql/catalog/tabledesc/index.go b/pkg/sql/catalog/tabledesc/index.go index 88863192f447..f8287f8e9f37 100644 --- a/pkg/sql/catalog/tabledesc/index.go +++ b/pkg/sql/catalog/tabledesc/index.go @@ -170,7 +170,7 @@ func (w index) InvertedColumnName() string { } // InvertedColumnKeyType returns the type of the data element that is encoded -// as the inverted index key. This is currently always Bytes. +// as the inverted index key. This is currently always EncodedKey. // // Panics if the index is not inverted. func (w index) InvertedColumnKeyType() *types.T { diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-array b/pkg/sql/opt/memo/testdata/stats/inverted-array index f8e6e144cf76..840bec2d5b89 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-array +++ b/pkg/sql/opt/memo/testdata/stats/inverted-array @@ -72,7 +72,7 @@ index-join t ├── stats: [rows=1020] ├── key: (1) └── scan t@a_idx - ├── columns: k:1(int!null) a_inverted_key:5(int[]!null) + ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans: ["", ""] ├── flags: force-index=a_idx @@ -120,7 +120,7 @@ index-join t ├── stats: [rows=20] ├── key: (1) └── scan t@a_idx - ├── columns: k:1(int!null) a_inverted_key:5(int[]!null) + ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans: ["\x8a", "\x8c") ├── stats: [rows=20, distinct(1)=19.6078, null(1)=0, avgsize(1)=4, distinct(5)=2, null(5)=0, avgsize(5)=4] @@ -183,7 +183,7 @@ select │ ├── stats: [rows=20] │ ├── key: (1) │ └── scan t@a_idx - │ ├── columns: k:1(int!null) a_inverted_key:5(int[]!null) + │ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["C", "C"] @@ -222,7 +222,7 @@ select │ ├── stats: [rows=30] │ ├── key: (1) │ └── scan t@a_idx - │ ├── columns: k:1(int!null) a_inverted_key:5(int[]!null) + │ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["C", "C"] @@ -267,7 +267,7 @@ select │ ├── stats: [rows=30] │ ├── key: (1) │ └── scan t@a_idx - │ ├── columns: k:1(int!null) a_inverted_key:5(int[]!null) + │ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["C", "C"] diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-geo b/pkg/sql/opt/memo/testdata/stats/inverted-geo index c77cc75b9d22..d65b8aee74ef 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-geo +++ b/pkg/sql/opt/memo/testdata/stats/inverted-geo @@ -181,7 +181,7 @@ project │ │ ├── stats: [rows=1.4e-06] │ │ ├── key: (3) │ │ └── scan t@t_g_idx - │ │ ├── columns: rowid:3(int!null) g_inverted_key:6(geometry!null) + │ │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null) │ │ ├── inverted constraint: /6/3 │ │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=1.4e-06, distinct(3)=4e-07, null(3)=0, avgsize(3)=4, distinct(6)=1.4e-06, null(6)=0, avgsize(6)=4] @@ -401,7 +401,7 @@ project │ │ ├── stats: [rows=1.42e-06] │ │ ├── key: (3) │ │ └── scan t@t_g_idx - │ │ ├── columns: rowid:3(int!null) g_inverted_key:6(geometry!null) + │ │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null) │ │ ├── inverted constraint: /6/3 │ │ │ └── spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"] │ │ ├── stats: [rows=1.42e-06, distinct(3)=4e-07, null(3)=0, avgsize(3)=4, distinct(6)=1.42e-06, null(6)=0, avgsize(6)=4] @@ -480,7 +480,7 @@ select │ ├── stats: [rows=100] │ ├── key: (3) │ └── scan t@t_g_idx - │ ├── columns: rowid:3(int!null) g_inverted_key:6(geometry!null) + │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null) │ ├── inverted constraint: /6/3 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -522,7 +522,7 @@ project │ ├── stats: [rows=100] │ ├── key: (3) │ └── scan t@t_g_idx - │ ├── columns: rowid:3(int!null) g_inverted_key:6(geometry!null) + │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null) │ ├── inverted constraint: /6/3 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-geo-multi-column b/pkg/sql/opt/memo/testdata/stats/inverted-geo-multi-column index 74fb44c21fa1..4da3ab18a238 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-geo-multi-column +++ b/pkg/sql/opt/memo/testdata/stats/inverted-geo-multi-column @@ -99,7 +99,7 @@ project │ ├── stats: [rows=60.78475] │ ├── key: (1) │ └── scan t@m - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── constraint: /3: [/'banana' - /'banana'] │ ├── inverted constraint: /7/1 │ │ └── spans @@ -151,7 +151,7 @@ project │ ├── stats: [rows=60.78475] │ ├── key: (1) │ └── scan t@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:8(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:8(encodedkey!null) │ ├── inverted constraint: /8/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -214,7 +214,7 @@ project │ ├── stats: [rows=121.5695] │ ├── key: (1) │ └── scan t@m - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── constraint: /3 │ │ ├── [/'apple' - /'apple'] │ │ ├── [/'banana' - /'banana'] @@ -269,7 +269,7 @@ project │ ├── stats: [rows=121.5695] │ ├── key: (1) │ └── scan t@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:9(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:9(encodedkey!null) │ ├── inverted constraint: /9/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -332,7 +332,7 @@ project │ ├── stats: [rows=5.512417] │ ├── key: (1) │ └── scan t@mp,partial - │ ├── columns: k:1(int!null) g_inverted_key:10(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:10(encodedkey!null) │ ├── constraint: /4: [/400 - /400] │ ├── inverted constraint: /10/1 │ │ └── spans @@ -388,7 +388,7 @@ project │ ├── stats: [rows=14.82863] │ ├── key: (1) │ └── scan t@mp,partial - │ ├── columns: k:1(int!null) g_inverted_key:10(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:10(encodedkey!null) │ ├── constraint: /4 │ │ ├── [/200 - /200] │ │ ├── [/300 - /300] diff --git a/pkg/sql/opt/memo/testdata/stats/inverted-json b/pkg/sql/opt/memo/testdata/stats/inverted-json index f79985c68820..c9c0a45e0006 100644 --- a/pkg/sql/opt/memo/testdata/stats/inverted-json +++ b/pkg/sql/opt/memo/testdata/stats/inverted-json @@ -100,7 +100,7 @@ index-join t ├── stats: [rows=1110] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7\x00\x019", "7\x00\x019"] @@ -172,7 +172,7 @@ index-join t ├── stats: [rows=110] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7c\x00\x01\x12d\x00\x01", "7c\x00\x01\x12d\x00\x01"] @@ -203,7 +203,7 @@ index-join t ├── stats: [rows=1110] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7\x00\x018", "7\x00\x018"] @@ -255,7 +255,7 @@ index-join t ├── stats: [rows=110] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7\x00\x03\x00\x01*\x04\x00", "7\x00\x03\x00\x01*\x04\x00"] @@ -346,7 +346,7 @@ select │ ├── stats: [rows=110] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -386,7 +386,7 @@ select │ ├── stats: [rows=120] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -431,7 +431,7 @@ select │ ├── stats: [rows=120] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -473,7 +473,7 @@ select │ ├── stats: [rows=110] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x01*\x04\x00", "7\x00\x01*\x04\x00"] @@ -516,7 +516,7 @@ select │ ├── stats: [rows=120] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x01*\x04\x00", "7\x00\x01*\x04\x00"] @@ -567,7 +567,7 @@ select │ ├── stats: [rows=120] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x01*\x04\x00", "7\x00\x01*\x04\x00"] @@ -659,7 +659,7 @@ index-join t ├── stats: [rows=100] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7\x00\x019", "7\x00\x019"] @@ -691,7 +691,7 @@ index-join t ├── stats: [rows=100] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7\x00\x018", "7\x00\x018"] @@ -786,7 +786,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x02b\x00\x01\x12c\x00\x01", "7a\x00\x02b\x00\x01\x12c\x00\x01"] @@ -843,7 +843,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x02\x00\x03\x00\x01\x12b\x00\x01", "7a\x00\x02\x00\x03\x00\x01\x12b\x00\x01"] @@ -896,7 +896,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x02b\x00\x02\x00\x03\x00\x01\x12c\x00\x01", "7a\x00\x02b\x00\x02\x00\x03\x00\x01\x12c\x00\x01"] @@ -935,7 +935,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x018", "7a\x00\x018"] @@ -973,7 +973,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x019", "7a\x00\x019"] @@ -1005,7 +1005,7 @@ index-join t ├── stats: [rows=4e-07] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7a\x00\x01*\x02\x00", "7a\x00\x01*\x02\x00"] @@ -1041,7 +1041,7 @@ select │ ├── stats: [rows=100] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -1074,7 +1074,7 @@ index-join t ├── stats: [rows=4e-07] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7a\x00\x02b\x00\x01\x12c\x00\x01", "7a\x00\x02b\x00\x01\x12c\x00\x01"] @@ -1111,7 +1111,7 @@ select │ ├── stats: [rows=100] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -1171,7 +1171,7 @@ select │ ├── stats: [rows=100] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -1216,7 +1216,7 @@ select │ ├── stats: [rows=4e-07] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7a\x00\x02\x00\x03\x00\x01*\x02\x00", "7a\x00\x02\x00\x03\x00\x01*\x02\x00"] @@ -1258,7 +1258,7 @@ select │ ├── stats: [rows=100] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] @@ -1296,7 +1296,7 @@ index-join t ├── stats: [rows=4e-07] ├── key: (1) └── scan t@j_idx - ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) ├── inverted constraint: /5/1 │ └── spans │ ├── ["7a\x00\x02b\x00\x01\x12c\x00\x01", "7a\x00\x02b\x00\x01\x12c\x00\x01"] @@ -1337,7 +1337,7 @@ select │ ├── stats: [rows=100] │ ├── key: (1) │ └── scan t@j_idx - │ ├── columns: k:1(int!null) j_inverted_key:5(jsonb!null) + │ ├── columns: k:1(int!null) j_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/1 │ │ └── spans │ │ ├── ["7\x00\x019", "7\x00\x019"] diff --git a/pkg/sql/opt/memo/testdata/stats/partial-index-scan b/pkg/sql/opt/memo/testdata/stats/partial-index-scan index 277d5e2a71e2..0f16a67005d7 100644 --- a/pkg/sql/opt/memo/testdata/stats/partial-index-scan +++ b/pkg/sql/opt/memo/testdata/stats/partial-index-scan @@ -1245,7 +1245,7 @@ project │ ├── stats: [rows=16.66667] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1291,7 +1291,7 @@ project │ ├── stats: [rows=16.66667] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1358,7 +1358,7 @@ project │ ├── stats: [rows=8.547009] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1402,7 +1402,7 @@ project │ ├── stats: [rows=8.547009] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1492,7 +1492,7 @@ project │ ├── stats: [rows=118.7568] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1544,7 +1544,7 @@ project │ ├── stats: [rows=118.7568] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1632,7 +1632,7 @@ project │ ├── stats: [rows=121.5695] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] @@ -1682,7 +1682,7 @@ project │ ├── stats: [rows=121.5695] │ ├── key: (1) │ └── scan spatial@p,partial - │ ├── columns: k:1(int!null) g_inverted_key:7(geometry!null) + │ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null) │ ├── inverted constraint: /7/1 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] diff --git a/pkg/sql/opt/memo/testdata/stats/scan b/pkg/sql/opt/memo/testdata/stats/scan index ff4e93d12fc8..282344a420ea 100644 --- a/pkg/sql/opt/memo/testdata/stats/scan +++ b/pkg/sql/opt/memo/testdata/stats/scan @@ -2235,7 +2235,7 @@ select │ ├── stats: [rows=1] │ ├── key: (2) │ └── scan tab@tab_geom_idx - │ ├── columns: rowid:2(int!null) geom_inverted_key:5(geometry!null) + │ ├── columns: rowid:2(int!null) geom_inverted_key:5(encodedkey!null) │ ├── inverted constraint: /5/2 │ │ └── spans │ │ ├── ["B\xfd\x10\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x10\x00\x00\x00\x00\x00\x00\x00"] diff --git a/pkg/sql/opt/optbuilder/testdata/inverted-indexes b/pkg/sql/opt/optbuilder/testdata/inverted-indexes index 9b168c703c88..480f7815e9bd 100644 --- a/pkg/sql/opt/optbuilder/testdata/inverted-indexes +++ b/pkg/sql/opt/optbuilder/testdata/inverted-indexes @@ -26,11 +26,11 @@ TABLE kj ├── j jsonb ├── crdb_internal_mvcc_timestamp decimal [hidden] [system] ├── tableoid oid [hidden] [system] - ├── j_inverted_key jsonb not null [inverted] + ├── j_inverted_key encodedkey not null [inverted] ├── PRIMARY INDEX kj_pkey │ └── k int not null └── INVERTED INDEX kj_j_idx - ├── j_inverted_key jsonb not null [inverted] + ├── j_inverted_key encodedkey not null [inverted] └── k int not null build diff --git a/pkg/sql/opt/testutils/testcat/create_table.go b/pkg/sql/opt/testutils/testcat/create_table.go index edf04f121e50..820d7067aa6a 100644 --- a/pkg/sql/opt/testutils/testcat/create_table.go +++ b/pkg/sql/opt/testutils/testcat/create_table.go @@ -1028,18 +1028,15 @@ func (ti *Index) addColumn( } if ti.Inverted && isLastIndexCol { - // The last column of an inverted index is special: the index key does not - // contain values from the column itself, but contains inverted index - // entries derived from that column. Create a virtual column to be able to - // refer to it separately. + // The last column of an inverted index is special: the index key does + // not contain values from the column itself, but contains inverted + // index entries derived from that column. Create a virtual column to be + // able to refer to it separately with the special type EncodedKey. var col cat.Column - // TODO(radu,mjibson): update this when the corresponding type in the real - // catalog is fixed (see sql.newOptTable). - typ := tt.Columns[ordinal].DatumType() col.InitInverted( len(tt.Columns), colName+"_inverted_key", - typ, + types.EncodedKey, false, /* nullable */ ordinal, /* invertedSourceColumnOrdinal */ ) @@ -1104,10 +1101,12 @@ func (ti *Index) addColumnByOrdinal( if colType == keyCol || colType == strictKeyCol { typ := col.DatumType() if col.Kind() == cat.Inverted { - if !colinfo.ColumnTypeIsInvertedIndexable(typ) { + srcCol := tt.Column(col.InvertedSourceColumnOrdinal()) + srcColType := srcCol.DatumType() + if !colinfo.ColumnTypeIsInvertedIndexable(srcColType) { panic(fmt.Errorf( "column %s of type %s is not allowed as the last column of an inverted index", - col.ColName(), typ, + col.ColName(), srcColType, )) } } else if !colinfo.ColumnTypeIsIndexable(typ) { diff --git a/pkg/sql/opt/testutils/testcat/testdata/index b/pkg/sql/opt/testutils/testcat/testdata/index index 2aa11c5a38d5..700cbbadad8b 100644 --- a/pkg/sql/opt/testutils/testcat/testdata/index +++ b/pkg/sql/opt/testutils/testcat/testdata/index @@ -83,18 +83,18 @@ TABLE g ├── geog geography ├── crdb_internal_mvcc_timestamp decimal [hidden] [system] ├── tableoid oid [hidden] [system] - ├── geog_inverted_key geography not null [inverted] - ├── geog_inverted_key geography not null [inverted] + ├── geog_inverted_key encodedkey not null [inverted] + ├── geog_inverted_key encodedkey not null [inverted] ├── PRIMARY INDEX g_pkey │ └── k int not null ├── INVERTED INDEX g_a_geog_idx │ ├── a int - │ ├── geog_inverted_key geography not null [inverted] + │ ├── geog_inverted_key encodedkey not null [inverted] │ └── k int not null └── INVERTED INDEX g_a_b_geog_idx ├── a int ├── b int - ├── geog_inverted_key geography not null [inverted] + ├── geog_inverted_key encodedkey not null [inverted] └── k int not null # Test for expression index columns. @@ -133,10 +133,10 @@ TABLE xyz ├── crdb_internal_idx_expr string as (lower(z)) virtual [inaccessible] ├── crdb_internal_idx_expr_1 int as (y + 1) virtual [inaccessible] ├── crdb_internal_idx_expr_2 jsonb as (j->'a') virtual [inaccessible] - ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] - ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] + ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] + ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] ├── crdb_internal_idx_expr_3 int as (x + y) virtual [inaccessible] - ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] + ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] ├── PRIMARY INDEX xyz_pkey │ └── x int not null ├── INDEX idx1 @@ -151,12 +151,12 @@ TABLE xyz │ ├── crdb_internal_idx_expr string as (lower(z)) virtual [inaccessible] │ └── x int not null ├── INVERTED INDEX idx4 - │ ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] + │ ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] │ └── x int not null ├── INVERTED INDEX idx5 │ ├── y int │ ├── z string - │ ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] + │ ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] │ └── x int not null ├── INDEX idx6 │ ├── crdb_internal_idx_expr_3 int as (x + y) virtual [inaccessible] @@ -166,6 +166,6 @@ TABLE xyz │ └── WHERE v > 1 └── INVERTED INDEX idx7 ├── crdb_internal_idx_expr_3 int as (x + y) virtual [inaccessible] - ├── crdb_internal_idx_expr_2_inverted_key jsonb not null [inverted] + ├── crdb_internal_idx_expr_2_inverted_key encodedkey not null [inverted] ├── x int not null └── WHERE v > 1 diff --git a/pkg/sql/opt/testutils/testcat/testdata/table b/pkg/sql/opt/testutils/testcat/testdata/table index 0b481ac7f79b..23fa4eaad7d2 100644 --- a/pkg/sql/opt/testutils/testcat/testdata/table +++ b/pkg/sql/opt/testutils/testcat/testdata/table @@ -204,15 +204,15 @@ TABLE inv ├── g geometry ├── crdb_internal_mvcc_timestamp decimal [hidden] [system] ├── tableoid oid [hidden] [system] - ├── j_inverted_key jsonb not null [inverted] - ├── g_inverted_key geometry not null [inverted] + ├── j_inverted_key encodedkey not null [inverted] + ├── g_inverted_key encodedkey not null [inverted] ├── PRIMARY INDEX inv_pkey │ └── k int not null ├── INVERTED INDEX inv_j_idx - │ ├── j_inverted_key jsonb not null [inverted] + │ ├── j_inverted_key encodedkey not null [inverted] │ └── k int not null └── INVERTED INDEX inv_g_idx - ├── g_inverted_key geometry not null [inverted] + ├── g_inverted_key encodedkey not null [inverted] └── k int not null # Table with inverted indexes and implicit primary index. @@ -236,15 +236,15 @@ TABLE inv2 ├── rowid int not null default (unique_rowid()) [hidden] ├── crdb_internal_mvcc_timestamp decimal [hidden] [system] ├── tableoid oid [hidden] [system] - ├── j_inverted_key jsonb not null [inverted] - ├── g_inverted_key geometry not null [inverted] + ├── j_inverted_key encodedkey not null [inverted] + ├── g_inverted_key encodedkey not null [inverted] ├── PRIMARY INDEX inv2_pkey │ └── rowid int not null default (unique_rowid()) [hidden] ├── INVERTED INDEX inv2_j_idx - │ ├── j_inverted_key jsonb not null [inverted] + │ ├── j_inverted_key encodedkey not null [inverted] │ └── rowid int not null default (unique_rowid()) [hidden] └── INVERTED INDEX inv2_g_idx - ├── g_inverted_key geometry not null [inverted] + ├── g_inverted_key encodedkey not null [inverted] └── rowid int not null default (unique_rowid()) [hidden] # Table with unique constraints. diff --git a/pkg/sql/opt_catalog.go b/pkg/sql/opt_catalog.go index aedf02926313..138fd78ad971 100644 --- a/pkg/sql/opt_catalog.go +++ b/pkg/sql/opt_catalog.go @@ -892,10 +892,8 @@ func newOptTable( invertedSourceColOrdinal, _ := ot.lookupColumnOrdinal(invertedColumnID) - // Add a inverted column that refers to the inverted index key. + // Add an inverted column that refers to the inverted index key. invertedCol, invertedColOrd := newColumn() - - // All inverted columns have type bytes. invertedCol.InitInverted( invertedColOrd, tree.Name(invertedColumnName+"_inverted_key"), diff --git a/pkg/storage/BUILD.bazel b/pkg/storage/BUILD.bazel index 2a8ec647e8e6..84ac19caed44 100644 --- a/pkg/storage/BUILD.bazel +++ b/pkg/storage/BUILD.bazel @@ -29,6 +29,7 @@ go_library( "pebble_iterator.go", "pebble_merge.go", "pebble_mvcc_scanner.go", + "point_synthesizing_iter.go", "read_as_of_iterator.go", "replicas_storage.go", "resource_limiter.go", diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index 372562c6d3e2..5595bcc07918 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -69,7 +69,7 @@ import ( // get [t=] [ts=[,]] [resolve [status=]] k= [inconsistent] [tombstones] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] // scan [t=] [ts=[,]] [resolve [status=]] k= [end=] [inconsistent] [tombstones] [reverse] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] [max=] [targetbytes=] [avoidExcess] [allowEmpty] // -// iter_new [k=] [end=] [prefix] [kind=key|keyAndIntents] [types=pointsOnly|pointsWithRanges|pointsAndRanges|rangesOnly] [maskBelow=[,]] +// iter_new [k=] [end=] [prefix] [kind=key|keyAndIntents] [types=pointsOnly|pointsWithRanges|pointsAndRanges|rangesOnly] [pointSynthesis [emitOnSeekGE]] [maskBelow=[,]] // iter_seek_ge k= [ts=[,]] // iter_seek_lt k= [ts=[,]] // iter_seek_intent_ge k= txn= @@ -1027,6 +1027,9 @@ func cmdIterNew(e *evalCtx) error { MVCCIterator: r.NewMVCCIterator(kind, opts), closeReader: closeReader, } + if e.hasArg("pointSynthesis") { + e.iter = newPointSynthesizingIter(e.iter, e.hasArg("emitOnSeekGE")) + } return nil } diff --git a/pkg/storage/mvcc_key.go b/pkg/storage/mvcc_key.go index e59ffe52be4d..f128b69c77cd 100644 --- a/pkg/storage/mvcc_key.go +++ b/pkg/storage/mvcc_key.go @@ -68,6 +68,12 @@ func (k MVCCKey) Next() MVCCKey { } } +// Clone returns a copy of the key. +func (k MVCCKey) Clone() MVCCKey { + k.Key = k.Key.Clone() + return k +} + // Compare returns -1 if this key is less than the given key, 0 if they're // equal, or 1 if this is greater. Comparison is by key,timestamp, where larger // timestamps sort before smaller ones except empty ones which sort first (like diff --git a/pkg/storage/point_synthesizing_iter.go b/pkg/storage/point_synthesizing_iter.go new file mode 100644 index 000000000000..557d79765c53 --- /dev/null +++ b/pkg/storage/point_synthesizing_iter.go @@ -0,0 +1,696 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package storage + +import ( + "sort" + "sync" + + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/storage/enginepb" + "github.com/cockroachdb/cockroach/pkg/util" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" + "github.com/cockroachdb/cockroach/pkg/util/uuid" + "github.com/cockroachdb/errors" +) + +// pointSynthesizingIterPool reuses pointSynthesizingIters to avoid allocations. +var pointSynthesizingIterPool = sync.Pool{ + New: func() interface{} { + return &pointSynthesizingIter{} + }, +} + +// pointSynthesizingIter wraps an MVCCIterator, and synthesizes MVCC point keys +// for MVCC range keys above/below existing point keys, and at the start of +// range keys (truncated to iterator bounds). If emitOnSeekGE is set, it will +// also unconditionally synthesize point keys around a SeekGE seek key if it +// overlaps an MVCC range key. +// +// It does not emit MVCC range keys at all, since these would appear to conflict +// with the synthesized point keys. +// +// During iteration, any range keys overlapping the current iterator position +// are kept in rangeKeys. When atPoint is true, the iterator is positioned on a +// real point key in the underlying iterator. Otherwise, it is positioned on a +// synthetic point key given by rangeKeysPos and rangeKeys[rangeKeysIdx]. +// +// The relative positioning of pointSynthesizingIter and the underlying iterator +// is as follows in the forward direction: +// +// - atPoint=true: rangeKeysIdx points to a range key following the point key, +// or beyond the slice bounds when there are no further range keys at this +// key position. +// +// - atPoint=false: the underlying iterator is on a following key or exhausted. +// This can either be a different version of the current key or a different +// point/range key. +// +// This positioning is mirrored in the reverse direction. For example, when +// atPoint=true and rangeKeys are exhausted, rangeKeysIdx will be len(rangeKeys) +// in the forward direction and -1 in the reverse direction. Similarly, the +// underlying iterator is always >= rangeKeysPos in the forward direction and <= +// in reverse. +// +// See also assertInvariants() which asserts positioning invariants. +type pointSynthesizingIter struct { + iter MVCCIterator + + // rangeKeys contains any range keys that overlap the current key position, + // for which points will be synthesized. + rangeKeys []MVCCRangeKeyValue + + // rangeKeysPos is the current key (along the rangeKeys span) that points will + // be synthesized for. It is only set if rangeKeys is non-empty, and may + // differ from the underlying iterator position. + rangeKeysPos roachpb.Key + + // rangeKeysIdx is the rangeKeys index of the current/pending range key + // to synthesize a point for. See struct comment for details. + rangeKeysIdx int + + // rangeKeysStart contains the start key of the current rangeKeys stack. It is + // only used to memoize rangeKeys for adjacent keys. + rangeKeysStart roachpb.Key + + // atPoint is true if the synthesizing iterator is positioned on a real point + // key in the underlying iterator. See struct comment for details. + atPoint bool + + // reverse is true when the current iterator direction is in reverse, i.e. + // following a SeekLT or Prev call. + reverse bool + + // emitOnSeekGE will synthesize point keys for the SeekGE seek key if it + // overlaps with a range key even if no point key exists. The primary use-case + // is to synthesize point keys for e.g. an MVCCGet that does not match a point + // key but overlaps a range key, which is necessary for conflict checks. + // + // This is optional, because e.g. pebbleMVCCScanner often uses seeks as an + // optimization to skip over old versions of a key, and we don't want to keep + // synthesizing point keys every time it skips ahead. + // + // TODO(erikgrinaker): This could instead check for prefix iterators, or a + // separate SeekPrefixGE() method, but we don't currently have APIs for it. + emitOnSeekGE bool +} + +var _ MVCCIterator = new(pointSynthesizingIter) + +// newPointSynthesizingIter creates a new pointSynthesizingIter, or gets one +// from the pool. +func newPointSynthesizingIter(parent MVCCIterator, emitOnSeekGE bool) *pointSynthesizingIter { + iter := pointSynthesizingIterPool.Get().(*pointSynthesizingIter) + *iter = pointSynthesizingIter{ + iter: parent, + emitOnSeekGE: emitOnSeekGE, + // Reuse pooled byte slices. + rangeKeysPos: iter.rangeKeysPos, + rangeKeysStart: iter.rangeKeysStart, + } + return iter +} + +// Close implements MVCCIterator. +// +// Close will also close the underlying iterator. Use release() to release it +// back to the pool without closing the parent iterator. +func (i *pointSynthesizingIter) Close() { + i.iter.Close() + i.release() +} + +// release releases the iterator back into the pool. +func (i *pointSynthesizingIter) release() { + *i = pointSynthesizingIter{ + // Reuse byte slices. + rangeKeysPos: i.rangeKeysPos[:0], + rangeKeysStart: i.rangeKeysStart[:0], + } + pointSynthesizingIterPool.Put(i) +} + +// updateRangeKeys updates i.rangeKeys and related fields with range keys from +// the underlying iterator. rangeKeysIdx is reset to the first/last range key. +func (i *pointSynthesizingIter) updateRangeKeys() { + if _, hasRange := i.iter.HasPointAndRange(); hasRange { + i.rangeKeysPos = append(i.rangeKeysPos[:0], i.iter.UnsafeKey().Key...) + if rangeStart := i.iter.RangeBounds().Key; !rangeStart.Equal(i.rangeKeysStart) { + i.rangeKeysStart = append(i.rangeKeysStart[:0], rangeStart...) + i.rangeKeys = i.rangeKeys[:0] + for _, rk := range i.iter.RangeKeys() { + // TODO(erikgrinaker): We should optimize the clone cost. + i.rangeKeys = append(i.rangeKeys, rk.Clone()) + } + } + } else if len(i.rangeKeys) != 0 { + i.rangeKeys = i.rangeKeys[:0] + i.rangeKeysPos = i.rangeKeysPos[:0] + i.rangeKeysStart = i.rangeKeysStart[:0] + } + if !i.reverse { + i.rangeKeysIdx = 0 + } else { + i.rangeKeysIdx = len(i.rangeKeys) - 1 // NB: -1 is correct with no range keys + } +} + +// clearRangeKeys resets the iterator by clearing out all range key state. +// gcassert:inline +func (i *pointSynthesizingIter) clearRangeKeys() { + if len(i.rangeKeys) != 0 { + i.rangeKeys = i.rangeKeys[:0] + i.rangeKeysPos = i.rangeKeysPos[:0] + i.rangeKeysStart = i.rangeKeysStart[:0] + } + if !i.reverse { + i.rangeKeysIdx = 0 + } else { + i.rangeKeysIdx = -1 + } +} + +// updateAtPoint updates i.atPoint according to whether the synthesizing +// iterator is positioned on the real point key in the underlying iterator. +// Requires i.rangeKeys to have been positioned first. +func (i *pointSynthesizingIter) updateAtPoint() { + if hasPoint, _ := i.iter.HasPointAndRange(); !hasPoint { + i.atPoint = false + } else if len(i.rangeKeys) == 0 { + i.atPoint = true + } else if point := i.iter.UnsafeKey(); !point.Key.Equal(i.rangeKeysPos) { + i.atPoint = false + } else if !i.reverse { + i.atPoint = i.rangeKeysIdx >= len(i.rangeKeys) || + !point.Timestamp.IsSet() || + i.rangeKeys[i.rangeKeysIdx].RangeKey.Timestamp.LessEq(point.Timestamp) + } else { + i.atPoint = i.rangeKeysIdx < 0 || (point.Timestamp.IsSet() && + point.Timestamp.LessEq(i.rangeKeys[i.rangeKeysIdx].RangeKey.Timestamp)) + } +} + +// updatePosition updates the synthesizing iterator with the position of the +// underlying iterator. This may step the underlying iterator to position it +// correctly relative to bare range keys. +func (i *pointSynthesizingIter) updatePosition() { + if hasPoint, hasRange := i.iter.HasPointAndRange(); !hasRange { + // Fast path: no range keys, so just clear range keys and bail out. + i.atPoint = hasPoint + i.clearRangeKeys() + + } else if !i.reverse { + // If we're on a bare range key in the forward direction, we populate the + // range keys but then step iter ahead before updating the point position. + // The next position may be a point key with the same key as the current + // range key, which must be interleaved with the synthetic points. + i.updateRangeKeys() + if hasRange && !hasPoint { + i.iter.Next() + } + i.updateAtPoint() + + } else { + // If we're on a bare range key in the reverse direction, and we've already + // emitted synthetic points for this key (as evidenced by rangeKeysPos), + // then we skip over the bare range key to avoid duplicates. + if hasRange && !hasPoint && i.iter.UnsafeKey().Key.Equal(i.rangeKeysPos) { + i.iter.Prev() + } + i.updateRangeKeys() + i.updateAtPoint() + } +} + +// SeekGE implements MVCCIterator. +func (i *pointSynthesizingIter) SeekGE(seekKey MVCCKey) { + i.reverse = false + i.iter.SeekGE(seekKey) + + // Fast path: no range key, so just reset the iterator and bail out. + hasPoint, hasRange := i.iter.HasPointAndRange() + if !hasRange { + i.atPoint = hasPoint + i.clearRangeKeys() + return + } + + // If we land in the middle of a bare range key and emitOnSeekGE is disabled, + // then skip over it to the next point/range key -- we're only supposed to + // synthesize at the range key start bound and at existing points. + // + // However, if we're seeking to a specific version and don't find an older + // point key at the seek key, then we also need to peek backwards for an + // existing point key above us, which would mandate that we synthesize point + // keys here after all. + // + // TODO(erikgrinaker): It might be faster to first do an unversioned seek to + // look for previous points and then a versioned seek. We can also omit this + // if there are no range keys below the seek timestamp. + // + // TODO(erikgrinaker): We could avoid this in the SeekGE case if we only + // synthesize points above existing points, except in the emitOnSeeGE case + // where no existing point exists. That could also result in fewer synthetic + // points overall. Do we need to synthesize older points? + var positioned bool + if !i.emitOnSeekGE && hasRange && !hasPoint && + !i.iter.RangeBounds().Key.Equal(i.iter.UnsafeKey().Key) { + i.iter.Next() + + if seekKey.Timestamp.IsSet() { + ok, err := i.iter.Valid() + if err == nil && (!ok || !seekKey.Key.Equal(i.iter.UnsafeKey().Key)) { + i.iter.Prev() + if hasP, _ := i.iter.HasPointAndRange(); hasP && seekKey.Key.Equal(i.iter.UnsafeKey().Key) { + i.updateRangeKeys() + positioned = true + } + i.iter.Next() + } + } + hasPoint, hasRange = i.iter.HasPointAndRange() + } + + if !positioned { + i.updateRangeKeys() + + // If we're now at a bare range key, we must either be at the start of it, + // or in the middle with emitOnSeekGE enabled. In either case, we want to + // move the iterator ahead to look for a point key with the same key as the + // start/seek key in order to interleave it. + if hasRange && !hasPoint { + i.iter.Next() + } + } + + // If we're seeking to a specific version, skip newer range keys. + if len(i.rangeKeys) > 0 && seekKey.Timestamp.IsSet() && seekKey.Key.Equal(i.rangeKeysPos) { + i.rangeKeysIdx = sort.Search(len(i.rangeKeys), func(idx int) bool { + return i.rangeKeys[idx].RangeKey.Timestamp.LessEq(seekKey.Timestamp) + }) + } + + i.updateAtPoint() + + // It's possible that we seeked past all of the range key versions. In this + // case, we have to reposition on the next key (current iter key). + if !i.atPoint && i.rangeKeysIdx >= len(i.rangeKeys) { + i.updatePosition() + } +} + +// SeekIntentGE implements MVCCIterator. +func (i *pointSynthesizingIter) SeekIntentGE(seekKey roachpb.Key, txnUUID uuid.UUID) { + i.reverse = false + i.iter.SeekIntentGE(seekKey, txnUUID) + + // Fast path: no range key, so just reset the iterator and bail out. + hasPoint, hasRange := i.iter.HasPointAndRange() + if !hasRange { + i.atPoint = hasPoint + i.clearRangeKeys() + return + } + + // If we land in the middle of a bare range key and emitOnSeekGE is disabled, + // then skip over it to the next point/range key. + if !i.emitOnSeekGE && hasRange && !hasPoint && + !i.iter.RangeBounds().Key.Equal(i.iter.UnsafeKey().Key) { + i.iter.Next() + } + + i.updatePosition() +} + +// Next implements MVCCIterator. +func (i *pointSynthesizingIter) Next() { + // When changing direction, flip the relative positioning with iter. + if i.reverse { + i.reverse = false + if !i.atPoint && len(i.rangeKeys) == 0 { // iterator was exhausted + i.iter.Next() + i.updatePosition() + return + } else if i.atPoint { + i.rangeKeysIdx++ + } else { + i.iter.Next() + } + } + + // Step off the current point, either real or synthetic. + if i.atPoint { + i.iter.Next() + } else { + i.rangeKeysIdx++ + } + i.updateAtPoint() + + // If we've exhausted the current range keys, update with the underlying + // iterator position (which must now be at a later key). + if !i.atPoint && i.rangeKeysIdx >= len(i.rangeKeys) { + i.updatePosition() + } +} + +// NextKey implements MVCCIterator. +func (i *pointSynthesizingIter) NextKey() { + // When changing direction, flip the relative positioning with iter. + // + // NB: This isn't really supported by the MVCCIterator interface, but we have + // best-effort handling in e.g. `pebbleIterator` and it's simple enough to + // implement, so we may as well. + if i.reverse { + i.reverse = false + if !i.atPoint { + i.iter.Next() + } + } + // Don't call NextKey() if the underlying iterator is already on the next key. + if i.atPoint || i.rangeKeysPos.Equal(i.iter.UnsafeKey().Key) { + i.iter.NextKey() + } + i.updatePosition() +} + +// SeekLT implements MVCCIterator. +func (i *pointSynthesizingIter) SeekLT(seekKey MVCCKey) { + i.reverse = true + i.iter.SeekLT(seekKey) + + // Fast path: no range key, so just reset the iterator and bail out. + hasPoint, hasRange := i.iter.HasPointAndRange() + if !hasRange { + i.atPoint = hasPoint + i.clearRangeKeys() + return + } + + // If we did a versioned seek and find a range key that overlaps the seek key, + // we may have skipped over existing point key versions of the seek key. These + // would mandate that we synthesize point keys for the seek key after all, so + // we peek ahead to check for them. + // + // TODO(erikgrinaker): It might be faster to do an unversioned seek from the + // next key first to look for points. + var positioned bool + if seekKey.Timestamp.IsSet() && hasRange && + (!hasPoint || !i.iter.UnsafeKey().Key.Equal(seekKey.Key)) && + seekKey.Key.Compare(i.iter.RangeBounds().EndKey) < 0 { + i.iter.Next() + if hasP, _ := i.iter.HasPointAndRange(); hasP && i.iter.UnsafeKey().Key.Equal(seekKey.Key) { + i.updateRangeKeys() + positioned = true + } + i.iter.Prev() + } + + if !positioned { + i.updateRangeKeys() + } + + // If we're seeking to a specific version, skip over older range keys. + if seekKey.Timestamp.IsSet() && seekKey.Key.Equal(i.rangeKeysPos) { + i.rangeKeysIdx = sort.Search(len(i.rangeKeys), func(idx int) bool { + return i.rangeKeys[idx].RangeKey.Timestamp.LessEq(seekKey.Timestamp) + }) - 1 + } + + i.updateAtPoint() + + // It's possible that we seeked past all of the range key versions. In this + // case, we have to reposition on the previous key (current iter key). + if !i.atPoint && i.rangeKeysIdx < 0 { + i.updatePosition() + } +} + +// Prev implements MVCCIterator. +func (i *pointSynthesizingIter) Prev() { + // When changing direction, flip the relative positioning with iter. + if !i.reverse { + i.reverse = true + if !i.atPoint && len(i.rangeKeys) == 0 { // iterator was exhausted + i.iter.Prev() + i.updatePosition() + return + } else if i.atPoint { + i.rangeKeysIdx-- + } else { + i.iter.Prev() + } + } + + // Step off the current point key (real or synthetic). + if i.atPoint { + i.iter.Prev() + } else { + i.rangeKeysIdx-- + } + i.updateAtPoint() + + // If we've exhausted the current range keys, and we're not positioned on a + // point key at the current range key position, then update with the + // underlying iter position (which must be before the current key). + if i.rangeKeysIdx < 0 && (!i.atPoint || !i.rangeKeysPos.Equal(i.iter.UnsafeKey().Key)) { + i.updatePosition() + } +} + +// Valid implements MVCCIterator. +func (i *pointSynthesizingIter) Valid() (bool, error) { + if util.RaceEnabled { + if err := i.assertInvariants(); err != nil { + panic(err) + } + } + if !i.atPoint && i.rangeKeysIdx >= 0 && i.rangeKeysIdx < len(i.rangeKeys) { + return true, nil // on synthetic point key + } + return i.iter.Valid() +} + +// Key implements MVCCIterator. +func (i *pointSynthesizingIter) Key() MVCCKey { + return i.UnsafeKey().Clone() +} + +// UnsafeKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeKey() MVCCKey { + if i.atPoint { + return i.iter.UnsafeKey() + } + if i.rangeKeysIdx >= len(i.rangeKeys) || i.rangeKeysIdx < 0 { + return MVCCKey{} + } + return MVCCKey{ + Key: i.rangeKeysPos, + Timestamp: i.rangeKeys[i.rangeKeysIdx].RangeKey.Timestamp, + } +} + +// UnsafeRawKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeRawKey() []byte { + if i.atPoint { + return i.iter.UnsafeRawKey() + } + return EncodeMVCCKeyPrefix(i.rangeKeysPos) +} + +// UnsafeRawMVCCKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeRawMVCCKey() []byte { + if i.atPoint { + return i.iter.UnsafeRawMVCCKey() + } + return EncodeMVCCKey(i.UnsafeKey()) +} + +// Value implements MVCCIterator. +func (i *pointSynthesizingIter) Value() []byte { + if v := i.UnsafeValue(); v != nil { + return append([]byte{}, v...) + } + return nil +} + +// UnsafeValue implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeValue() []byte { + if i.atPoint { + return i.iter.UnsafeValue() + } + if i.rangeKeysIdx >= len(i.rangeKeys) || i.rangeKeysIdx < 0 { + return nil + } + return i.rangeKeys[i.rangeKeysIdx].Value +} + +// ValueProto implements MVCCIterator. +func (i *pointSynthesizingIter) ValueProto(msg protoutil.Message) error { + return protoutil.Unmarshal(i.UnsafeValue(), msg) +} + +// HasPointAndRange implements MVCCIterator. +func (i *pointSynthesizingIter) HasPointAndRange() (bool, bool) { + ok, err := i.Valid() + return ok && err == nil, false +} + +// RangeBounds implements MVCCIterator. +func (i *pointSynthesizingIter) RangeBounds() roachpb.Span { + return roachpb.Span{} +} + +// RangeKeys implements MVCCIterator. +func (i *pointSynthesizingIter) RangeKeys() []MVCCRangeKeyValue { + return []MVCCRangeKeyValue{} +} + +// ComputeStats implements MVCCIterator. +func (i *pointSynthesizingIter) ComputeStats( + start, end roachpb.Key, nowNanos int64, +) (enginepb.MVCCStats, error) { + return i.iter.ComputeStats(start, end, nowNanos) +} + +// FindSplitKey implements MVCCIterator. +func (i *pointSynthesizingIter) FindSplitKey( + start, end, minSplitKey roachpb.Key, targetSize int64, +) (MVCCKey, error) { + return i.iter.FindSplitKey(start, end, minSplitKey, targetSize) +} + +// Stats implements MVCCIterator. +func (i *pointSynthesizingIter) Stats() IteratorStats { + return i.iter.Stats() +} + +// SupportsPrev implements MVCCIterator. +func (i *pointSynthesizingIter) SupportsPrev() bool { + return i.iter.SupportsPrev() +} + +// assertInvariants asserts iterator invariants. +func (i *pointSynthesizingIter) assertInvariants() error { + // If the underlying iterator has errored, make sure we're not positioned on a + // synthetic point such that Valid() will surface the error. + if _, err := i.iter.Valid(); err != nil { + if !i.atPoint && i.rangeKeysIdx >= 0 && i.rangeKeysIdx < len(i.rangeKeys) { + return errors.NewAssertionErrorWithWrappedErrf(err, "iterator error with synthetic point %s", + i.rangeKeysPos) + } + return nil + } + + // When atPoint is true, the underlying iterator must be valid and on a point. + if i.atPoint { + if ok, _ := i.iter.Valid(); !ok { + return errors.AssertionFailedf("atPoint with invalid iter") + } + if hasPoint, _ := i.iter.HasPointAndRange(); !hasPoint { + return errors.AssertionFailedf("atPoint at non-point position %s", i.iter.UnsafeKey()) + } + } + + // rangeKeysIdx is never more than 1 outside of the slice bounds, and the + // excess depends on the direction: len(rangeKeys) in the forward direction, + // -1 in the reverse. + if i.rangeKeysIdx < 0 || i.rangeKeysIdx >= len(i.rangeKeys) { + if (!i.reverse && i.rangeKeysIdx != len(i.rangeKeys)) || (i.reverse && i.rangeKeysIdx != -1) { + return errors.AssertionFailedf("invalid rangeKeysIdx %d with length %d and reverse=%t", + i.rangeKeysIdx, len(i.rangeKeys), i.reverse) + } + } + + // If rangeKeys is empty, atPoint is true unless exhausted and other state is + // cleared. In this case, there's nothing more to check. + if len(i.rangeKeys) == 0 { + if ok, _ := i.iter.Valid(); ok && !i.atPoint { + return errors.AssertionFailedf("no rangeKeys nor atPoint") + } + if len(i.rangeKeysPos) > 0 { + return errors.AssertionFailedf("no rangeKeys but rangeKeysPos %s", i.rangeKeysPos) + } + if len(i.rangeKeysStart) > 0 { + return errors.AssertionFailedf("no rangeKeys but rangeKeysStart %s", i.rangeKeysStart) + } + return nil + } + + // rangeKeysStart must be set, and rangeKeysPos must be at or after it. This + // implies that rangeKeysPos must also be set. + if len(i.rangeKeysStart) == 0 { + return errors.AssertionFailedf("no rangeKeysStart at %s", i.iter.UnsafeKey()) + } + if i.rangeKeysPos.Compare(i.rangeKeysStart) < 0 { + return errors.AssertionFailedf("rangeKeysPos %s not after rangeKeysStart %s", + i.rangeKeysPos, i.rangeKeysStart) + } + + // rangeKeysIdx must be valid if we're not on a point. + if !i.atPoint && (i.rangeKeysIdx < 0 || i.rangeKeysIdx >= len(i.rangeKeys)) { + return errors.AssertionFailedf("not atPoint with invalid rangeKeysIdx %d at %s", + i.rangeKeysIdx, i.rangeKeysPos) + } + + // If the underlying iterator is exhausted, then there's nothing more to + // check. We must either be on a synthetic point key or exhausted iterator. + if ok, _ := i.iter.Valid(); !ok { + return nil + } + + // We now have range keys and a non-exhausted iterator. Check their relative + // positioning as minimum and maximum iter keys (in MVCC order). We can assume + // that overlapping range keys and point keys don't have the same timestamp, + // since this is enforced by MVCC mutations. + var minKey, maxKey MVCCKey + + // The iterator should never lag behind the range key position. + if !i.reverse { + minKey = MVCCKey{Key: i.rangeKeysPos} + } else { + maxKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: hlc.MinTimestamp} + } + + // If we're not at a real point, then the iterator must be ahead of the + // current synthesized point. If we are on a point, then it must lie between + // the surrounding range keys (if they exist). + minIdx, maxIdx := -1, -1 + if !i.atPoint { + if !i.reverse { + minIdx = i.rangeKeysIdx + } else { + maxIdx = i.rangeKeysIdx + } + } else if !i.reverse { + minIdx = i.rangeKeysIdx - 1 + maxIdx = i.rangeKeysIdx + } else { + minIdx = i.rangeKeysIdx + maxIdx = i.rangeKeysIdx + 1 + } + if minIdx >= 0 && minIdx < len(i.rangeKeys) { + minKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: i.rangeKeys[minIdx].RangeKey.Timestamp} + } + if maxIdx >= 0 && maxIdx < len(i.rangeKeys) { + maxKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: i.rangeKeys[maxIdx].RangeKey.Timestamp} + } + + iterKey := i.iter.Key() + if minKey.Key != nil && iterKey.Compare(minKey) < 0 { + return errors.AssertionFailedf("iter %s below minimum key %s", iterKey, minKey) + } + if maxKey.Key != nil && iterKey.Compare(maxKey) > 0 { + return errors.AssertionFailedf("iter %s above maximum key %s", iterKey, maxKey) + } + + return nil +} diff --git a/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis b/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis new file mode 100644 index 000000000000..682c4053ddd4 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis @@ -0,0 +1,1042 @@ +# Tests pointSynthesizingIter. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 7 [d7] [j7] +# 6 f6 +# 5 o---------------o k5 o-----------o +# 4 x x d4 f4 g4 +# 3 o-------o e3 o-------oh3 o---o +# 2 a2 f2 g2 +# 1 o-------------------o o-----------o +# a b c d e f g h i j k l m n o p +# +run ok +put_rangekey k=a end=f ts=1 +put_rangekey k=h end=k ts=1 +put_rangekey k=b end=d ts=3 +put_rangekey k=n end=o ts=3 +put_rangekey k=l end=o ts=5 +put k=a ts=2 v=a2 +del k=a ts=4 +del k=b ts=4 +put k=d ts=4 v=d4 +put k=e ts=3 v=e3 +put k=f ts=2 v=f2 +put k=g ts=2 v=g2 +put_rangekey k=f end=h ts=3 localTs=4 +put k=f ts=4 v=f4 +put k=f ts=6 v=f6 +put k=g ts=4 v=g4 +put_rangekey k=c end=g ts=5 +put k=h ts=3 v=h3 +put k=k ts=5 v=k5 +with t=A + txn_begin ts=7 + put k=d v=d7 + put k=j v=j7 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +rangekey: {a-b}/[1.000000000,0=/] +rangekey: {b-c}/[3.000000000,0=/ 1.000000000,0=/] +rangekey: {c-d}/[5.000000000,0=/ 3.000000000,0=/ 1.000000000,0=/] +rangekey: {d-f}/[5.000000000,0=/ 1.000000000,0=/] +rangekey: {f-g}/[5.000000000,0=/ 3.000000000,0=vheader{ localTs=4.000000000,0 } /] +rangekey: {g-h}/[3.000000000,0=vheader{ localTs=4.000000000,0 } /] +rangekey: {h-k}/[1.000000000,0=/] +rangekey: {l-n}/[5.000000000,0=/] +rangekey: {n-o}/[5.000000000,0=/ 3.000000000,0=/] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 + +# Iterate across the entire span, forward and reverse. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_scan: "a"/4.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/1.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/1.000000000,0=/ +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "d"/1.000000000,0=/ +iter_scan: "e"/5.000000000,0=/ +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "e"/1.000000000,0=/ +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "f"/5.000000000,0=/ +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "l"/5.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "h"/1.000000000,0=/ +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "f"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/5.000000000,0=/ +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "e"/1.000000000,0=/ +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "e"/5.000000000,0=/ +iter_scan: "d"/1.000000000,0=/ +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +# Iterate across the entire span using NextKey(). +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_next_key: "b"/4.000000000,0=/ +iter_next_key: "c"/5.000000000,0=/ +iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "e"/5.000000000,0=/ +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_next_key: "g"/4.000000000,0=/BYTES/g4 +iter_next_key: "h"/3.000000000,0=/BYTES/h3 +iter_next_key: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_next_key: . + +# Unversioned seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_seek_ge k=b +iter_seek_ge k=c +iter_seek_ge k=d +iter_seek_ge k=e +iter_seek_ge k=f +iter_seek_ge k=g +iter_seek_ge k=h +iter_seek_ge k=i +iter_seek_ge k=j +iter_seek_ge k=k +iter_seek_ge k=l +iter_seek_ge k=m +iter_seek_ge k=n +iter_seek_ge k=o +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=a +iter_seek_ge k=b +iter_seek_ge k=c +iter_seek_ge k=d +iter_seek_ge k=e +iter_seek_ge k=f +iter_seek_ge k=g +iter_seek_ge k=h +iter_seek_ge k=i +iter_seek_ge k=j +iter_seek_ge k=k +iter_seek_ge k=l +iter_seek_ge k=m +iter_seek_ge k=n +iter_seek_ge k=o +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "i"/1.000000000,0=/ +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=o +iter_seek_lt k=n +iter_seek_lt k=m +iter_seek_lt k=l +iter_seek_lt k=k +iter_seek_lt k=j +iter_seek_lt k=i +iter_seek_lt k=h +iter_seek_lt k=g +iter_seek_lt k=f +iter_seek_lt k=e +iter_seek_lt k=d +iter_seek_lt k=c +iter_seek_lt k=b +iter_seek_lt k=a +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "j"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "e"/1.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ +iter_seek_lt: "c"/1.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ +iter_seek_lt: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_intent_ge k=a txn=A +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=c txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=e txn=A +iter_seek_intent_ge k=f txn=A +iter_seek_intent_ge k=g txn=A +iter_seek_intent_ge k=h txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +iter_seek_intent_ge k=l txn=A +iter_seek_intent_ge k=m txn=A +iter_seek_intent_ge k=n txn=A +iter_seek_intent_ge k=o txn=A +---- +iter_seek_intent_ge: "a"/4.000000000,0=/ +iter_seek_intent_ge: "b"/4.000000000,0=/ +iter_seek_intent_ge: "c"/5.000000000,0=/ +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "e"/5.000000000,0=/ +iter_seek_intent_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_intent_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_intent_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_intent_ge: "l"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_intent_ge k=a txn=A +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=c txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=e txn=A +iter_seek_intent_ge k=f txn=A +iter_seek_intent_ge k=g txn=A +iter_seek_intent_ge k=h txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +iter_seek_intent_ge k=l txn=A +iter_seek_intent_ge k=m txn=A +iter_seek_intent_ge k=n txn=A +iter_seek_intent_ge k=o txn=A +---- +iter_seek_intent_ge: "a"/4.000000000,0=/ +iter_seek_intent_ge: "b"/4.000000000,0=/ +iter_seek_intent_ge: "c"/5.000000000,0=/ +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "e"/5.000000000,0=/ +iter_seek_intent_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_intent_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_intent_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_intent_ge: "i"/1.000000000,0=/ +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_intent_ge: "l"/5.000000000,0=/ +iter_seek_intent_ge: "m"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: . + +# Versioned seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a ts=5 +iter_seek_ge k=a ts=4 +iter_seek_ge k=a ts=3 +iter_seek_ge k=a ts=2 +iter_seek_ge k=a ts=1 +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "a"/2.000000000,0=/BYTES/a2 +iter_seek_ge: "a"/2.000000000,0=/BYTES/a2 +iter_seek_ge: "a"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=b ts=5 +iter_seek_ge k=b ts=4 +iter_seek_ge k=b ts=3 +iter_seek_ge k=b ts=2 +iter_seek_ge k=b ts=1 +---- +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "b"/3.000000000,0=/ +iter_seek_ge: "b"/1.000000000,0=/ +iter_seek_ge: "b"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=c ts=6 +iter_seek_ge k=c ts=5 +iter_seek_ge k=c ts=4 +iter_seek_ge k=c ts=3 +iter_seek_ge k=c ts=2 +iter_seek_ge k=c ts=1 +---- +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "c"/3.000000000,0=/ +iter_seek_ge: "c"/3.000000000,0=/ +iter_seek_ge: "c"/1.000000000,0=/ +iter_seek_ge: "c"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d ts=0 +iter_seek_ge k=d ts=8 +iter_seek_ge k=d ts=7 +iter_seek_ge k=d ts=6 +iter_seek_ge k=d ts=5 +iter_seek_ge k=d ts=4 +iter_seek_ge k=d ts=3 +iter_seek_ge k=d ts=2 +iter_seek_ge k=d ts=1 +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 +iter_seek_ge: "d"/5.000000000,0=/ +iter_seek_ge: "d"/5.000000000,0=/ +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 +iter_seek_ge: "d"/1.000000000,0=/ +iter_seek_ge: "d"/1.000000000,0=/ +iter_seek_ge: "d"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=6 +iter_seek_ge k=e ts=5 +iter_seek_ge k=e ts=4 +iter_seek_ge k=e ts=3 +iter_seek_ge k=e ts=2 +iter_seek_ge k=e ts=1 +---- +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_seek_ge: "e"/1.000000000,0=/ +iter_seek_ge: "e"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=f ts=7 +iter_seek_ge k=f ts=6 +iter_seek_ge k=f ts=5 +iter_seek_ge k=f ts=4 +iter_seek_ge k=f ts=3 +iter_seek_ge k=f ts=2 +iter_seek_ge k=f ts=1 +---- +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "f"/5.000000000,0=/ +iter_seek_ge: "f"/4.000000000,0=/BYTES/f4 +iter_seek_ge: "f"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_seek_ge: "f"/2.000000000,0=/BYTES/f2 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=g ts=6 +iter_seek_ge k=g ts=5 +iter_seek_ge k=g ts=4 +iter_seek_ge k=g ts=3 +iter_seek_ge k=g ts=2 +iter_seek_ge k=g ts=1 +---- +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_seek_ge: "g"/2.000000000,0=/BYTES/g2 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=h ts=4 +iter_seek_ge k=h ts=3 +iter_seek_ge k=h ts=2 +iter_seek_ge k=h ts=1 +---- +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "h"/1.000000000,0=/ +iter_seek_ge: "h"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=i ts=2 +iter_seek_ge k=i ts=1 +---- +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=j ts=8 +iter_seek_ge k=j ts=7 +iter_seek_ge k=j ts=6 +iter_seek_ge k=j ts=1 +---- +iter_seek_ge: "j"/7.000000000,0=/BYTES/j7 +iter_seek_ge: "j"/7.000000000,0=/BYTES/j7 +iter_seek_ge: "j"/1.000000000,0=/ +iter_seek_ge: "j"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=k ts=6 +iter_seek_ge k=k ts=5 +iter_seek_ge k=k ts=4 +---- +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=l ts=6 +iter_seek_ge k=l ts=5 +iter_seek_ge k=l ts=4 +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=m ts=6 +iter_seek_ge k=m ts=5 +iter_seek_ge k=m ts=4 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=n ts=6 +iter_seek_ge k=n ts=5 +iter_seek_ge k=n ts=4 +iter_seek_ge k=n ts=3 +iter_seek_ge k=n ts=2 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=o ts=6 +iter_seek_ge k=o ts=5 +iter_seek_ge k=o ts=4 +iter_seek_ge k=o ts=3 +---- +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . + +# Versioned seeks with emitOnSeekGE. +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=l ts=6 +iter_seek_ge k=l ts=5 +iter_seek_ge k=l ts=4 +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=m ts=6 +iter_seek_ge k=m ts=5 +iter_seek_ge k=m ts=4 +---- +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=n ts=6 +iter_seek_ge k=n ts=5 +iter_seek_ge k=n ts=4 +iter_seek_ge k=n ts=3 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=o ts=6 +iter_seek_ge k=o ts=5 +iter_seek_ge k=o ts=4 +---- +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . + +# Versioned reverse seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=a ts=1 +iter_seek_lt k=a ts=2 +iter_seek_lt k=a ts=3 +iter_seek_lt k=a ts=4 +iter_seek_lt k=a ts=5 +---- +iter_seek_lt: "a"/2.000000000,0=/BYTES/a2 +iter_seek_lt: "a"/4.000000000,0=/ +iter_seek_lt: "a"/4.000000000,0=/ +iter_seek_lt: . +iter_seek_lt: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=b ts=1 +iter_seek_lt k=b ts=2 +iter_seek_lt k=b ts=3 +iter_seek_lt k=b ts=4 +iter_seek_lt k=b ts=5 +---- +iter_seek_lt: "b"/3.000000000,0=/ +iter_seek_lt: "b"/3.000000000,0=/ +iter_seek_lt: "b"/4.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=c ts=1 +iter_seek_lt k=c ts=2 +iter_seek_lt k=c ts=3 +iter_seek_lt k=c ts=4 +iter_seek_lt k=c ts=5 +iter_seek_lt k=c ts=6 +---- +iter_seek_lt: "c"/3.000000000,0=/ +iter_seek_lt: "c"/3.000000000,0=/ +iter_seek_lt: "c"/5.000000000,0=/ +iter_seek_lt: "c"/5.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=d ts=1 +iter_seek_lt k=d ts=2 +iter_seek_lt k=d ts=3 +iter_seek_lt k=d ts=4 +iter_seek_lt k=d ts=5 +iter_seek_lt k=d ts=6 +iter_seek_lt k=d ts=7 +iter_seek_lt k=d ts=8 +---- +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/5.000000000,0=/ +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=e ts=1 +iter_seek_lt k=e ts=2 +iter_seek_lt k=e ts=3 +iter_seek_lt k=e ts=4 +iter_seek_lt k=e ts=5 +iter_seek_lt k=e ts=6 +---- +iter_seek_lt: "e"/3.000000000,0=/BYTES/e3 +iter_seek_lt: "e"/3.000000000,0=/BYTES/e3 +iter_seek_lt: "e"/5.000000000,0=/ +iter_seek_lt: "e"/5.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=f ts=1 +iter_seek_lt k=f ts=2 +iter_seek_lt k=f ts=3 +iter_seek_lt k=f ts=4 +iter_seek_lt k=f ts=5 +iter_seek_lt k=f ts=6 +iter_seek_lt k=f ts=7 +---- +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_seek_lt: "f"/4.000000000,0=/BYTES/f4 +iter_seek_lt: "f"/5.000000000,0=/ +iter_seek_lt: "f"/6.000000000,0=/BYTES/f6 +iter_seek_lt: "e"/1.000000000,0=/ +iter_seek_lt: "e"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g ts=1 +iter_seek_lt k=g ts=2 +iter_seek_lt k=g ts=3 +iter_seek_lt k=g ts=4 +iter_seek_lt k=g ts=5 +iter_seek_lt k=g ts=6 +---- +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_seek_lt: "g"/4.000000000,0=/BYTES/g4 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=h ts=1 +iter_seek_lt k=h ts=2 +iter_seek_lt k=h ts=3 +iter_seek_lt k=h ts=4 +---- +iter_seek_lt: "h"/3.000000000,0=/BYTES/h3 +iter_seek_lt: "h"/3.000000000,0=/BYTES/h3 +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=i ts=1 +iter_seek_lt k=i ts=2 +---- +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=j ts=1 +iter_seek_lt k=j ts=6 +iter_seek_lt k=j ts=7 +iter_seek_lt k=j ts=8 +---- +iter_seek_lt: "j"/7.000000000,0=/BYTES/j7 +iter_seek_lt: "j"/7.000000000,0=/BYTES/j7 +iter_seek_lt: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_lt: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=k ts=1 +iter_seek_lt k=k ts=4 +iter_seek_lt k=k ts=5 +iter_seek_lt k=k ts=6 +---- +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "j"/1.000000000,0=/ +iter_seek_lt: "j"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=l ts=4 +iter_seek_lt k=l ts=5 +iter_seek_lt k=l ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=l ts=4 +iter_seek_lt k=l ts=5 +iter_seek_lt k=l ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=m ts=4 +iter_seek_lt k=m ts=5 +iter_seek_lt k=m ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=n ts=2 +iter_seek_lt k=n ts=3 +iter_seek_lt k=n ts=4 +iter_seek_lt k=n ts=5 +iter_seek_lt k=n ts=6 +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_seek_lt: "n"/5.000000000,0=/ +iter_seek_lt: "n"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=o ts=1 +---- +iter_seek_lt: "n"/3.000000000,0=/ + +# Seeks with opposite scans. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d +iter_scan reverse +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d ts=5 +iter_scan reverse +---- +iter_seek_ge: "d"/5.000000000,0=/ +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g +iter_scan +---- +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g ts=2 +iter_scan +---- +iter_seek_lt: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "g"/3.000000000,0=vheader{ localTs=4.000000000,0 } / +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +# Try some direction changes. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=4 +iter_prev +iter_next +iter_next +iter_prev +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_next: "e"/1.000000000,0=/ +iter_prev: "e"/3.000000000,0=/BYTES/e3 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=e ts=4 +iter_next +iter_prev +iter_prev +iter_next +---- +iter_seek_lt: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_prev: "d"/1.000000000,0=/ +iter_next: "e"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=4 +iter_prev +iter_prev +iter_next_key +iter_next +iter_next_key +iter_prev +iter_prev +iter_next_key +iter_next +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_prev: "d"/1.000000000,0=/ +iter_next_key: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_prev: "e"/1.000000000,0=/ +iter_prev: "e"/3.000000000,0=/BYTES/e3 +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_next: "f"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=k ts=4 +iter_next_key +iter_prev +iter_next_key +iter_next +iter_prev +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_prev: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_next: "n"/3.000000000,0=/ +iter_prev: "n"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=3 +iter_prev +iter_next_key +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_next_key: "f"/6.000000000,0=/BYTES/f6 + +# Exhausting the iterator then reversing should work in both directions, +# both after a seek and after a step. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=a +iter_next +---- +iter_seek_lt: . +iter_next: "a"/4.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_lt k=a +iter_next_key +---- +iter_seek_lt: . +iter_next_key: "a"/4.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=z +iter_prev +---- +iter_seek_ge: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_next +iter_next +iter_prev +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_next: . +iter_next: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_next_key +iter_next_key +iter_prev +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_next_key: . +iter_next_key: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_prev +iter_prev +iter_next +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_prev: . +iter_prev: . +iter_next: "a"/4.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_prev +iter_prev +iter_next_key +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_prev: . +iter_prev: . +iter_next_key: "a"/4.000000000,0=/