Repository: genjidb/genji
Pull request #476 (2022-07-14 06:50:22)

Prepare release v0.15.1
This contains a few fixes that will be released in v0.15.1


Patch:
diff --git a/cmd/genji/go.mod b/cmd/genji/go.mod
index 8c4b5c5a..d4fd41a0 100644
--- a/cmd/genji/go.mod
+++ b/cmd/genji/go.mod
@@ -7,7 +7,7 @@ require (
 	github.com/c-bata/go-prompt v0.2.6
 	github.com/cockroachdb/errors v1.9.0
 	github.com/genjidb/genji v0.15.1
-	github.com/stretchr/testify v1.7.5
+	github.com/stretchr/testify v1.8.0
 	github.com/urfave/cli/v2 v2.3.0
 	go.uber.org/multierr v1.7.0
 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
@@ -19,7 +19,7 @@ require (
 	github.com/buger/jsonparser v1.1.1 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
-	github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b // indirect
+	github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e // indirect
 	github.com/cockroachdb/redact v1.1.3 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/cmd/genji/go.sum b/cmd/genji/go.sum
index e90d046e..780be812 100644
--- a/cmd/genji/go.sum
+++ b/cmd/genji/go.sum
@@ -45,8 +45,8 @@ github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2O
 github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=
 github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
-github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b h1:4IzbYFgF+NdvAZDMUgV1nEVszyHH3i8kgplHFfAwruk=
-github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
+github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e h1:MGFpdiedteFe4rm8UY/0FixU8RVAh4wZYUCLQys9GjI=
+github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
 github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
 github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
@@ -83,8 +83,6 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
-github.com/genjidb/genji v0.15.0 h1:gCGFEwHFo/mCa0jpuxy7nbUTlxwtkQbfi3+BDof5H8I=
-github.com/genjidb/genji v0.15.0/go.mod h1:7qk7SMAW6a+d8T5F8b3ZGPmXVvVb1crgmAi2kSCSf0g=
 github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
 github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
 github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
@@ -286,8 +284,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
-github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
diff --git a/cmd/genji/shell/command.go b/cmd/genji/shell/command.go
index bcc6a35a..288d7914 100644
--- a/cmd/genji/shell/command.go
+++ b/cmd/genji/shell/command.go
@@ -15,7 +15,7 @@ import (
 	"github.com/genjidb/genji/cmd/genji/dbutil"
 	"github.com/genjidb/genji/cmd/genji/doc"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/types"
 )
 
@@ -151,8 +151,8 @@ func runIndexesCmd(db *genji.DB, tableName string, w io.Writer) error {
 	if tableName != "" {
 		_, err := db.QueryDocument("SELECT 1 FROM __genji_catalog WHERE name = ? AND type = 'table' LIMIT 1", tableName)
 		if err != nil {
-			if errors.Is(err, errs.ErrDocumentNotFound) {
-				return fmt.Errorf("%w: %q", errs.NotFoundError{Name: tableName}, tableName)
+			if errs.IsNotFoundError(err) {
+				return errors.Wrapf(err, "table %s does not exist", tableName)
 			}
 			return err
 		}
diff --git a/db.go b/db.go
index 7bb64ec9..1a030ed1 100644
--- a/db.go
+++ b/db.go
@@ -12,11 +12,11 @@ import (
 	"github.com/cockroachdb/pebble"
 	"github.com/cockroachdb/pebble/vfs"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
 	"github.com/genjidb/genji/internal/database/catalogstore"
 	ipebble "github.com/genjidb/genji/internal/database/pebble"
 	"github.com/genjidb/genji/internal/environment"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/query"
 	"github.com/genjidb/genji/internal/query/statement"
 	"github.com/genjidb/genji/internal/sql/parser"
@@ -329,7 +329,7 @@ func scanDocument(iter document.Iterator) (types.Document, error) {
 	}
 
 	if d == nil {
-		return nil, errors.WithStack(errs.ErrDocumentNotFound)
+		return nil, errors.WithStack(errs.NewDocumentNotFoundError())
 	}
 
 	fb := document.NewFieldBuffer()
diff --git a/errors.go b/errors.go
new file mode 100644
index 00000000..83049042
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,36 @@
+package genji
+
+import (
+	"github.com/cockroachdb/errors"
+	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
+)
+
+// IsNotFoundError determines if the given error is a NotFoundError.
+// NotFoundError is returned when the requested table, index, document or sequence
+// doesn't exist.
+var IsNotFoundError = errs.IsNotFoundError
+
+// IsAlreadyExistsError determines if the error is returned as a result of
+// a conflict when attempting to create a table, an index, a document or a sequence
+// with a name that is already used by another resource.
+func IsAlreadyExistsError(err error) bool {
+	if errs.IsAlreadyExistsError(err) {
+		return true
+	}
+
+	for err != nil {
+		if cerr, ok := err.(*database.ConstraintViolationError); ok {
+			switch cerr.Constraint {
+			case "UNIQUE", "PRIMARY KEY":
+				return true
+			default:
+				return false
+			}
+		}
+
+		err = errors.Unwrap(err)
+	}
+
+	return false
+}
diff --git a/go.mod b/go.mod
index 5f066c1c..99966749 100644
--- a/go.mod
+++ b/go.mod
@@ -5,29 +5,29 @@ go 1.18
 require (
 	github.com/buger/jsonparser v1.1.1
 	github.com/cockroachdb/errors v1.9.0
-	github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b
+	github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e
 	github.com/google/go-cmp v0.5.8
-	github.com/stretchr/testify v1.7.5
+	github.com/stretchr/testify v1.8.0
 	golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
 )
 
 require (
-	github.com/DataDog/zstd v1.5.2 // indirect
+	github.com/DataDog/zstd v1.4.5 // indirect
 	github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
-	github.com/cespare/xxhash/v2 v2.1.2 // indirect
+	github.com/cespare/xxhash/v2 v2.1.1 // indirect
 	github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
 	github.com/cockroachdb/redact v1.1.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/getsentry/sentry-go v0.13.0 // indirect
+	github.com/getsentry/sentry-go v0.12.0 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang/snappy v0.0.4 // indirect
-	github.com/klauspost/compress v1.15.6 // indirect
+	github.com/golang/snappy v0.0.3 // indirect
+	github.com/klauspost/compress v1.11.7 // indirect
 	github.com/kr/pretty v0.3.0 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rogpeppe/go-internal v1.8.1 // indirect
-	golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect
-	golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
+	golang.org/x/exp v0.0.0-20200513190911-00229845015e // indirect
+	golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 0659e68e..7e98f48f 100644
--- a/go.sum
+++ b/go.sum
@@ -7,9 +7,8 @@ github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EF
 github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
 github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
 github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
 github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
-github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
-github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
 github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
 github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
@@ -22,9 +21,8 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go
 github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -39,8 +37,8 @@ github.com/cockroachdb/errors v1.9.0/go.mod h1:vaNcEYYqbIqB5JhKBhFV9CneUqeuEbB2O
 github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
 github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74=
 github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
-github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b h1:4IzbYFgF+NdvAZDMUgV1nEVszyHH3i8kgplHFfAwruk=
-github.com/cockroachdb/pebble v0.0.0-20220624201436-71d17c2a007b/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
+github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e h1:MGFpdiedteFe4rm8UY/0FixU8RVAh4wZYUCLQys9GjI=
+github.com/cockroachdb/pebble v0.0.0-20220708173837-d3484a60444e/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4=
 github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
 github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
 github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
@@ -72,9 +70,8 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
+github.com/getsentry/sentry-go v0.12.0 h1:era7g0re5iY13bHSdN/xMkyV+5zZppjRVQhZrXCaEIk=
 github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c=
-github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NWD+xWo=
-github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
 github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
 github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
 github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
@@ -111,9 +108,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -166,9 +162,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY=
-github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -252,8 +247,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
-github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -290,9 +285,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20200513190911-00229845015e h1:rMqLP+9XLy+LdbCXHjJHAmTfXCr93W7oruWA6Hq1Alc=
 golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
-golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
-golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -363,9 +357,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210909193231-528a39cd75f3/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
diff --git a/internal/database/catalog.go b/internal/database/catalog.go
index 8e911069..500e2d30 100644
--- a/internal/database/catalog.go
+++ b/internal/database/catalog.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/lock"
 	"github.com/genjidb/genji/internal/tree"
 	"github.com/genjidb/genji/lib/atomic"
@@ -422,8 +422,8 @@ func (c *Catalog) RenameTable(tx *Transaction, oldName, newName string) error {
 
 	// Delete the old table info.
 	err = c.CatalogTable.Delete(tx, oldName)
-	if errors.Is(err, errs.ErrDocumentNotFound) {
-		return errors.WithStack(errs.NotFoundError{Name: oldName})
+	if errs.IsNotFoundError(err) {
+		return errors.Wrapf(err, "table %s does not exist", oldName)
 	}
 	if err != nil {
 		return err
@@ -789,7 +789,7 @@ func (c *catalogCache) Get(tp, name string) (Relation, error) {
 
 	o, ok := m[name]
 	if !ok {
-		return nil, errors.WithStack(errs.NotFoundError{Name: name})
+		return nil, errors.WithStack(&errs.NotFoundError{Name: name})
 	}
 
 	return o, nil
diff --git a/internal/database/constraint.go b/internal/database/constraint.go
index 4a5e2b8b..703712b1 100644
--- a/internal/database/constraint.go
+++ b/internal/database/constraint.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"strings"
 
+	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/document"
 	"github.com/genjidb/genji/internal/stringutil"
 	"github.com/genjidb/genji/internal/tree"
@@ -342,15 +343,10 @@ type ConstraintViolationError struct {
 	Key        *tree.Key
 }
 
-func (c *ConstraintViolationError) Error() string {
+func (c ConstraintViolationError) Error() string {
 	return fmt.Sprintf("%s constraint error: %s", c.Constraint, c.Paths)
 }
 
 func IsConstraintViolationError(err error) bool {
-	switch err.(type) {
-	case *ConstraintViolationError:
-		return true
-	default:
-		return false
-	}
+	return errors.Is(err, (*ConstraintViolationError)(nil))
 }
diff --git a/internal/database/pebble/open.go b/internal/database/pebble/open.go
index bdac916b..0764fd47 100644
--- a/internal/database/pebble/open.go
+++ b/internal/database/pebble/open.go
@@ -23,7 +23,7 @@ var DefaultComparer = &pebble.Comparer{
 	Equal:          encoding.Equal,
 	AbbreviatedKey: encoding.AbbreviatedKey,
 	FormatKey:      pebble.DefaultComparer.FormatKey,
-	Separator:      pebble.DefaultComparer.Separator,
+	Separator:      encoding.Separator,
 	Successor:      encoding.Successor,
 	// This name is part of the C++ Level-DB implementation's default file
 	// format, and should not be changed.
diff --git a/internal/database/sequence.go b/internal/database/sequence.go
index 5fa00fd8..2f298eb4 100644
--- a/internal/database/sequence.go
+++ b/internal/database/sequence.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/tree"
 	"github.com/genjidb/genji/types"
 )
diff --git a/internal/database/table.go b/internal/database/table.go
index be6a39b0..d668ba53 100644
--- a/internal/database/table.go
+++ b/internal/database/table.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/kv"
 	"github.com/genjidb/genji/internal/tree"
 	"github.com/genjidb/genji/types"
@@ -86,7 +86,7 @@ func (t *Table) Delete(key *tree.Key) error {
 
 	err := t.Tree.Delete(key)
 	if errors.Is(err, kv.ErrKeyNotFound) {
-		return errors.WithStack(errs.ErrDocumentNotFound)
+		return errors.WithStack(errs.NewNotFoundError(key.String()))
 	}
 
 	return err
@@ -105,7 +105,7 @@ func (t *Table) Replace(key *tree.Key, d types.Document) (types.Document, error)
 		return nil, err
 	}
 	if !ok {
-		return nil, errors.Wrapf(errs.ErrDocumentNotFound, "can't replace key %q", key)
+		return nil, errors.Wrapf(errs.NewNotFoundError(key.String()), "can't replace key %q", key)
 	}
 
 	d, enc, err := t.encodeDocument(d)
@@ -151,7 +151,7 @@ func (t *Table) GetDocument(key *tree.Key) (types.Document, error) {
 	enc, err := t.Tree.Get(key)
 	if err != nil {
 		if errors.Is(err, kv.ErrKeyNotFound) {
-			return nil, errors.WithStack(errs.ErrDocumentNotFound)
+			return nil, errors.WithStack(errs.NewNotFoundError(key.String()))
 		}
 		return nil, fmt.Errorf("failed to fetch document %q: %w", key, err)
 	}
diff --git a/internal/encoding/helpers.go b/internal/encoding/helpers.go
index 591d7b2f..ec3d3bd3 100644
--- a/internal/encoding/helpers.go
+++ b/internal/encoding/helpers.go
@@ -353,5 +353,28 @@ func abbreviatedValue(key []byte) uint64 {
 	}
 
 	return 0
-	// panic(fmt.Sprintf("unsupported value type: %d, %v", key[0], key))
+}
+
+func Separator(dst, a, b []byte) (foo []byte) {
+	var n, cmp int
+	var idx int
+	var aa []byte = a
+
+	for {
+		if n == len(a) {
+			return a
+		}
+
+		a = a[n:]
+		b = b[n:]
+		idx += n
+
+		cmp, n = compareNextValue(a, b)
+		if cmp != 0 {
+			idx += n
+			dst = append(dst, aa[:idx]...)
+			dst = append(dst, 0xFF)
+			return dst
+		}
+	}
 }
diff --git a/errors/errors.go b/internal/errors/errors.go
similarity index 51%
rename from errors/errors.go
rename to internal/errors/errors.go
index def4d3a8..fcc26de9 100644
--- a/errors/errors.go
+++ b/internal/errors/errors.go
@@ -6,15 +6,6 @@ import (
 	"github.com/cockroachdb/errors"
 )
 
-var (
-	// ErrDocumentNotFound is returned when no document is associated with the provided key.
-	ErrDocumentNotFound = errors.New("document not found")
-
-	// ErrDuplicateDocument is returned when another document is already associated with a given key, primary key,
-	// or if there is a unique index violation.
-	ErrDuplicateDocument = errors.New("duplicate document")
-)
-
 // AlreadyExistsError is returned when to create a table, an index or a sequence
 // with a name that is already used by another resource.
 type AlreadyExistsError struct {
@@ -26,13 +17,15 @@ func (a AlreadyExistsError) Error() string {
 }
 
 func IsAlreadyExistsError(err error) bool {
-	err = errors.UnwrapAll(err)
-	switch err.(type) {
-	case AlreadyExistsError, *AlreadyExistsError:
-		return true
-	default:
-		return false
+	for err != nil {
+		switch err.(type) {
+		case *AlreadyExistsError, AlreadyExistsError:
+			return true
+		}
+		err = errors.Unwrap(err)
 	}
+
+	return false
 }
 
 // NotFoundError is returned when the requested table, index or sequence
@@ -41,16 +34,30 @@ type NotFoundError struct {
 	Name string
 }
 
+func NewDocumentNotFoundError() error {
+	return NewNotFoundError("document")
+}
+
+func NewNotFoundError(name string) error {
+	return &NotFoundError{Name: name}
+}
+
 func (a NotFoundError) Error() string {
+	if a.Name == "document" {
+		return "document not found"
+	}
+
 	return fmt.Sprintf("%q not found", a.Name)
 }
 
 func IsNotFoundError(err error) bool {
-	err = errors.UnwrapAll(err)
-	switch err.(type) {
-	case NotFoundError, *NotFoundError:
-		return true
-	default:
-		return false
+	for err != nil {
+		switch err.(type) {
+		case *NotFoundError, NotFoundError:
+			return true
+		}
+		err = errors.Unwrap(err)
 	}
+
+	return false
 }
diff --git a/internal/query/statement/alter.go b/internal/query/statement/alter.go
index f4c60223..35b5b402 100644
--- a/internal/query/statement/alter.go
+++ b/internal/query/statement/alter.go
@@ -2,8 +2,8 @@ package statement
 
 import (
 	"github.com/cockroachdb/errors"
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
 )
 
 // AlterStmt is a DSL that allows creating a full ALTER TABLE query.
diff --git a/internal/query/statement/create.go b/internal/query/statement/create.go
index bf7f06c0..7321c575 100644
--- a/internal/query/statement/create.go
+++ b/internal/query/statement/create.go
@@ -3,8 +3,8 @@ package statement
 import (
 	"math"
 
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/stream"
 	"github.com/genjidb/genji/internal/stream/index"
 	"github.com/genjidb/genji/internal/stream/table"
diff --git a/internal/query/statement/drop.go b/internal/query/statement/drop.go
index 6ff16dbe..c21e3c96 100644
--- a/internal/query/statement/drop.go
+++ b/internal/query/statement/drop.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 
 	"github.com/cockroachdb/errors"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 )
 
 // DropTableStmt is a DSL that allows creating a DROP TABLE query.
diff --git a/internal/query/statement/reindex.go b/internal/query/statement/reindex.go
index 05a8eea7..b49bbd3a 100644
--- a/internal/query/statement/reindex.go
+++ b/internal/query/statement/reindex.go
@@ -1,8 +1,8 @@
 package statement
 
 import (
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/stream"
 	"github.com/genjidb/genji/internal/stream/index"
 	"github.com/genjidb/genji/internal/stream/table"
diff --git a/internal/stream/docs/temp_tree_sort.go b/internal/stream/docs/temp_tree_sort.go
index 2f7276f5..9dcf61bb 100644
--- a/internal/stream/docs/temp_tree_sort.go
+++ b/internal/stream/docs/temp_tree_sort.go
@@ -3,6 +3,7 @@ package docs
 import (
 	"fmt"
 
+	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/internal/encoding"
 	"github.com/genjidb/genji/internal/environment"
 	"github.com/genjidb/genji/internal/expr"
@@ -51,6 +52,17 @@ func (op *TempTreeSortOperator) Iterate(in *environment.Environment, fn func(out
 			return err
 		}
 
+		if types.IsNull(v) {
+			// the expression might be pointing to the original document.
+			v, err = op.Expr.Eval(out.Outer)
+			if err != nil {
+				// the only valid error here is a missing field.
+				if !errors.Is(err, types.ErrFieldNotFound) {
+					return err
+				}
+			}
+		}
+
 		doc, ok := out.GetDocument()
 		if !ok {
 			panic("missing document")
diff --git a/types/value.go b/types/value.go
index f7f2ae9a..e7d7a320 100644
--- a/types/value.go
+++ b/types/value.go
@@ -124,6 +124,10 @@ func Is[T any](v Value) (T, bool) {
 	return vv.v, true
 }
 
+func IsNull(v Value) bool {
+	return v == nil || v.Type() == NullValue
+}
+
 // IsTruthy returns whether v is not equal to the zero value of its type.
 func IsTruthy(v Value) (bool, error) {
 	if v.Type() == NullValue {


Test patch:
diff --git a/db_test.go b/db_test.go
index 33532cdb..0ad7951d 100644
--- a/db_test.go
+++ b/db_test.go
@@ -11,7 +11,6 @@ import (
 
 	"github.com/genjidb/genji"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/testutil"
 	"github.com/genjidb/genji/internal/testutil/assert"
 	"github.com/genjidb/genji/types"
@@ -184,14 +183,14 @@ func TestQueryDocument(t *testing.T) {
 
 	t.Run("Should return an error if no document", func(t *testing.T) {
 		r, err := db.QueryDocument("SELECT * FROM test WHERE a > 100")
-		assert.ErrorIs(t, err, errs.ErrDocumentNotFound)
+		require.True(t, genji.IsNotFoundError(err))
 		require.Nil(t, r)
 
 		tx, err := db.Begin(false)
 		assert.NoError(t, err)
 		defer tx.Rollback()
 		r, err = tx.QueryDocument("SELECT * FROM test WHERE a > 100")
-		assert.ErrorIs(t, err, errs.ErrDocumentNotFound)
+		require.True(t, genji.IsNotFoundError(err))
 		require.Nil(t, r)
 	})
 }
diff --git a/internal/database/catalog_test.go b/internal/database/catalog_test.go
index f318d095..a700e2c2 100644
--- a/internal/database/catalog_test.go
+++ b/internal/database/catalog_test.go
@@ -8,8 +8,8 @@ import (
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/expr"
 	"github.com/genjidb/genji/internal/testutil"
 	"github.com/genjidb/genji/internal/testutil/assert"
@@ -68,7 +68,7 @@ func TestCatalogTable(t *testing.T) {
 
 			// Getting a table that doesn't exist should fail.
 			_, err = catalog.GetTable(tx, "unknown")
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "unknown"})
 			}
 
@@ -91,13 +91,13 @@ func TestCatalogTable(t *testing.T) {
 
 			// Getting a table that has been dropped should fail.
 			_, err = catalog.GetTable(tx, "test")
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "test"})
 			}
 
 			// Dropping a table that doesn't exist should fail.
 			err = catalog.DropTable(tx, "test")
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "test"})
 			}
 
@@ -153,7 +153,7 @@ func TestCatalogTable(t *testing.T) {
 
 			// Getting the old table should return an error.
 			_, err = catalog.GetTable(tx, "foo")
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "foo"})
 			}
 
@@ -179,7 +179,7 @@ func TestCatalogTable(t *testing.T) {
 
 			// Renaming a non existing table should return an error
 			err = catalog.RenameTable(tx, "foo", "")
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "foo"})
 			}
 
@@ -230,7 +230,7 @@ func TestCatalogTable(t *testing.T) {
 
 			// Renaming a non existing table should return an error
 			err = catalog.AddFieldConstraint(tx, "bar", &fieldToAdd, nil)
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "bar"})
 			}
 
@@ -351,7 +351,7 @@ func TestCatalogCreateIndex(t *testing.T) {
 			err := catalog.CreateIndex(tx, &database.IndexInfo{
 				IndexName: "idxFoo", Owner: database.Owner{TableName: "test"}, Paths: []document.Path{testutil.ParseDocumentPath(t, "foo")},
 			})
-			if !errors.Is(err, errs.NotFoundError{}) {
+			if !errs.IsNotFoundError(err) {
 				assert.ErrorIs(t, err, errs.NotFoundError{Name: "test"})
 			}
 
@@ -449,7 +449,7 @@ func TestTxDropIndex(t *testing.T) {
 
 		updateCatalog(t, db, func(tx *database.Transaction, catalog *database.Catalog) error {
 			err := catalog.DropIndex(tx, "idxFoo")
-			assert.ErrorIs(t, err, errs.NotFoundError{Name: "idxFoo"})
+			assert.ErrorIs(t, err, &errs.NotFoundError{Name: "idxFoo"})
 			return nil
 		})
 	})
diff --git a/internal/database/table_test.go b/internal/database/table_test.go
index 71daae18..9e9d3cd5 100644
--- a/internal/database/table_test.go
+++ b/internal/database/table_test.go
@@ -6,8 +6,8 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji/document"
-	errs "github.com/genjidb/genji/errors"
 	"github.com/genjidb/genji/internal/database"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/query/statement"
 	"github.com/genjidb/genji/internal/testutil"
 	"github.com/genjidb/genji/internal/testutil/assert"
@@ -93,7 +93,7 @@ func TestTableGetDocument(t *testing.T) {
 		defer cleanup()
 
 		r, err := tb.GetDocument(tree.NewEncodedKey([]byte("id")))
-		assert.ErrorIs(t, err, errs.ErrDocumentNotFound)
+		require.True(t, errs.IsNotFoundError(err))
 		require.Nil(t, r)
 	})
 
@@ -203,7 +203,7 @@ func TestTableDelete(t *testing.T) {
 
 		// try again, should fail
 		_, err = tb.GetDocument(key1)
-		assert.ErrorIs(t, err, errs.ErrDocumentNotFound)
+		require.True(t, errs.IsNotFoundError(err))
 
 		// make sure it didn't also delete the other one
 		res, err := tb.GetDocument(key2)
@@ -220,7 +220,7 @@ func TestTableReplace(t *testing.T) {
 		defer cleanup()
 
 		_, err := tb.Replace(tree.NewEncodedKey([]byte("id")), newDocument())
-		assert.ErrorIs(t, err, errs.ErrDocumentNotFound)
+		require.True(t, errs.IsNotFoundError(err))
 	})
 
 	t.Run("Should replace the right document", func(t *testing.T) {
diff --git a/internal/encoding/helpers_test.go b/internal/encoding/helpers_test.go
index d9c595f9..8fb72ebd 100644
--- a/internal/encoding/helpers_test.go
+++ b/internal/encoding/helpers_test.go
@@ -243,3 +243,26 @@ func TestAbbreviatedKey(t *testing.T) {
 		})
 	}
 }
+
+func TestSeparator(t *testing.T) {
+	tests := []struct {
+		k1, k2 string
+	}{
+		{`[1, 1]`, `[1, 2]`},
+		{`[1, 1]`, `[1, 3]`},
+	}
+
+	for _, test := range tests {
+		t.Run(fmt.Sprintf("Separator(%v, %v)", test.k1, test.k2), func(t *testing.T) {
+			v1, err := testutil.ParseExpr(t, test.k1).Eval(&environment.Environment{})
+			require.NoError(t, err)
+			v2, err := testutil.ParseExpr(t, test.k2).Eval(&environment.Environment{})
+			require.NoError(t, err)
+			k1 := mustNewKey(t, v1.V().(*document.ValueBuffer).Values...)
+			k2 := mustNewKey(t, v2.V().(*document.ValueBuffer).Values...)
+			sep := encoding.Separator(nil, k1, k2)
+			require.LessOrEqual(t, encoding.Compare(k1, sep), 0)
+			require.Less(t, encoding.Compare(sep, k2), 0)
+		})
+	}
+}
diff --git a/internal/query/statement/alter_test.go b/internal/query/statement/alter_test.go
index 91c1efce..df604edf 100644
--- a/internal/query/statement/alter_test.go
+++ b/internal/query/statement/alter_test.go
@@ -3,12 +3,11 @@ package statement_test
 import (
 	"testing"
 
-	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji"
 	"github.com/genjidb/genji/document"
 	"github.com/genjidb/genji/internal/testutil/assert"
 
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/stretchr/testify/require"
 )
 
@@ -33,7 +32,7 @@ func TestAlterTable(t *testing.T) {
 
 	// Selecting from the old name should fail.
 	err = db.Exec("SELECT * FROM foo")
-	if !errors.Is(err, errs.NotFoundError{}) {
+	if !errs.IsNotFoundError(err) {
 		assert.ErrorIs(t, err, errs.NotFoundError{Name: "foo"})
 	}
 
diff --git a/internal/query/statement/drop_test.go b/internal/query/statement/drop_test.go
index 99f0bb1c..91331ddc 100644
--- a/internal/query/statement/drop_test.go
+++ b/internal/query/statement/drop_test.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/cockroachdb/errors"
 	"github.com/genjidb/genji"
-	errs "github.com/genjidb/genji/errors"
+	errs "github.com/genjidb/genji/internal/errors"
 	"github.com/genjidb/genji/internal/testutil"
 	"github.com/genjidb/genji/internal/testutil/assert"
 	"github.com/genjidb/genji/types"
@@ -105,7 +105,7 @@ func TestDropSequence(t *testing.T) {
 
 	// Assert that the good index has been dropped.
 	_, err := db.Catalog.GetSequence("seq1")
-	require.IsType(t, errs.NotFoundError{}, errors.Unwrap(err))
+	require.IsType(t, &errs.NotFoundError{}, errors.Unwrap(err))
 	_, err = db.Catalog.GetSequence("seq2")
 	assert.NoError(t, err)
 
diff --git a/internal/testutil/assert/errors.go b/internal/testutil/assert/errors.go
index 56f010f4..9fb157b5 100644
--- a/internal/testutil/assert/errors.go
+++ b/internal/testutil/assert/errors.go
@@ -53,5 +53,5 @@ func NoErrorf(t testing.TB, err error, str string, args ...interface{}) {
 func NoError(t testing.TB, err error) {
 	t.Helper()
 
-	NoErrorf(t, err, "Expected error to be nil but got %q instead", err)
+	NoErrorf(t, err, "Expected error to be nil but got %q instead: %+v", err, err)
 }
diff --git a/sqltests/SELECT/order_by.sql b/sqltests/SELECT/order_by.sql
index c77f3378..2de5753a 100644
--- a/sqltests/SELECT/order_by.sql
+++ b/sqltests/SELECT/order_by.sql
@@ -1,6 +1,6 @@
 -- setup:
-CREATE TABLE test(a double);
-INSERT INTO test (a) VALUES (1), (2), (3);
+CREATE TABLE test(a double, b double);
+INSERT INTO test (a, b) VALUES (50, 3), (100, 4), (10, 2), (null, 1);
 
 -- suite: no index
 
@@ -8,30 +8,77 @@ INSERT INTO test (a) VALUES (1), (2), (3);
 CREATE INDEX ON test(a);
 
 -- test: asc
+SELECT b FROM test ORDER BY a;
+/* result:
+{
+    b: 1.0,
+}
+{
+    b: 2.0
+}
+{
+    b: 3.0
+}
+{
+    b: 4.0
+}
+*/
+
+
+-- test: asc / wildcard
 SELECT * FROM test ORDER BY a;
 /* result:
 {
-    a: 1.0
+    b: 1.0,
 }
 {
-    a: 2.0
+    a: 10.0,
+    b: 2.0
 }
 {
-    a: 3.0
+    a: 50.0,
+    b: 3.0
+}
+{
+    a: 100.0,
+    b: 4.0
 }
 */
 
+
 -- test: desc
-SELECT * FROM test ORDER BY a DESC;
+SELECT b FROM test ORDER BY a DESC;
 /* result:
 {
-    a: 3.0
+    b: 4.0,
+}
+{
+    b: 3.0
 }
 {
-    a: 2.0
+    b: 2.0
 }
 {
-    a: 1.0
+    b: 1.0
 }
 */
 
+-- test: desc / wildcard
+SELECT * FROM test ORDER BY a DESC;
+/* result:
+{
+    a: 100.0,
+    b: 4.0,
+}
+{
+    a: 50.0,
+    b: 3.0
+}
+{
+    a: 10.0,
+    b: 2.0
+}
+{
+    b: 1.0
+}
+*/