Repository: apache/iceberg-go
Pull request #70 (2024-03-14 06:07:05)

Add the build from source section
It will be better to add the build instruction for the newcomers
even though it might be straight for Go develop to find where's main.go file.

By the way, I'm wondering if adding the Makefile to manage the build and test instructions
is good. If yes,  I would submit a PR to resolve this.




Patch:
diff --git a/README.md b/README.md
index c942dc8..aeb249d 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,19 @@
 
 `iceberg` is a Golang implementation of the [Iceberg table spec](https://iceberg.apache.org/spec/).
 
+## Build From Source
+
+### Prerequisites
+
+* Go 1.21 or later
+
+### Build
+
+```shell
+$ git clone https://github.com/apache/iceberg-go.git
+$ cd iceberg-go/cmd/iceberg && go build .
+```
+
 ## Feature Support / Roadmap
 
 ### FileSystem Support
diff --git a/errors.go b/errors.go
index c5bd870..71f9f63 100644
--- a/errors.go
+++ b/errors.go
@@ -25,4 +25,7 @@ var (
 	ErrInvalidArgument   = errors.New("invalid argument")
 	ErrInvalidSchema     = errors.New("invalid schema")
 	ErrInvalidTransform  = errors.New("invalid transform syntax")
+	ErrType              = errors.New("type error")
+	ErrBadCast           = errors.New("could not cast value")
+	ErrBadLiteral        = errors.New("invalid literal value")
 )
diff --git a/go.mod b/go.mod
index 88bb9b5..b386bbc 100644
--- a/go.mod
+++ b/go.mod
@@ -20,18 +20,20 @@ module github.com/apache/iceberg-go
 go 1.21
 
 require (
-	github.com/aws/aws-sdk-go-v2 v1.24.1
-	github.com/aws/aws-sdk-go-v2/config v1.26.3
-	github.com/aws/aws-sdk-go-v2/credentials v1.16.14
+	github.com/apache/arrow/go/v16 v16.1.0
+	github.com/aws/aws-sdk-go-v2 v1.27.0
+	github.com/aws/aws-sdk-go-v2/config v1.27.16
+	github.com/aws/aws-sdk-go-v2/credentials v1.17.16
 	github.com/aws/aws-sdk-go-v2/service/glue v1.73.1
 	github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0
+	github.com/aws/smithy-go v1.20.2
 	github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
-	github.com/google/uuid v1.3.1
-	github.com/hamba/avro/v2 v2.16.0
-	github.com/pterm/pterm v0.12.78
-	github.com/stretchr/testify v1.8.4
-	github.com/wolfeidau/s3iofs v1.5.0
-	golang.org/x/exp v0.0.0-20231006140011-7918f672742d
+	github.com/google/uuid v1.6.0
+	github.com/hamba/avro/v2 v2.22.1
+	github.com/pterm/pterm v0.12.79
+	github.com/stretchr/testify v1.9.0
+	github.com/wolfeidau/s3iofs v1.5.2
+	golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
 )
 
 require (
@@ -39,24 +41,26 @@ require (
 	atomicgo.dev/keyboard v0.2.9 // indirect
 	atomicgo.dev/schedule v0.1.0 // indirect
 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
-	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
-	github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
 	github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect
-	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 // indirect
-	github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
-	github.com/aws/smithy-go v1.19.0 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 // indirect
 	github.com/containerd/console v1.0.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
+	github.com/google/flatbuffers v24.3.25+incompatible // indirect
 	github.com/gookit/color v1.5.4 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.17.8 // indirect
 	github.com/lithammer/fuzzysearch v1.1.8 // indirect
 	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
@@ -64,11 +68,15 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/rivo/uniseg v0.4.4 // indirect
-	github.com/stretchr/objx v0.5.0 // indirect
+	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
-	golang.org/x/net v0.18.0 // indirect
-	golang.org/x/sys v0.16.0 // indirect
-	golang.org/x/term v0.16.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/mod v0.17.0 // indirect
+	golang.org/x/net v0.25.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
+	golang.org/x/term v0.20.0 // indirect
+	golang.org/x/text v0.15.0 // indirect
+	golang.org/x/tools v0.21.0 // indirect
+	golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 62ea84a..57df29c 100644
--- a/go.sum
+++ b/go.sum
@@ -15,45 +15,47 @@ github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/
 github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
 github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
 github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
+github.com/apache/arrow/go/v16 v16.1.0 h1:dwgfOya6s03CzH9JrjCBx6bkVb4yPD4ma3haj9p7FXI=
+github.com/apache/arrow/go/v16 v16.1.0/go.mod h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA=
 github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
-github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
-github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
+github.com/aws/aws-sdk-go-v2 v1.27.0 h1:7bZWKoXhzI+mMR/HjdMx8ZCC5+6fY0lS5tr0bbgiLlo=
+github.com/aws/aws-sdk-go-v2 v1.27.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
 github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
-github.com/aws/aws-sdk-go-v2/config v1.26.3 h1:dKuc2jdp10y13dEEvPqWxqLoc0vF3Z9FC45MvuQSxOA=
-github.com/aws/aws-sdk-go-v2/config v1.26.3/go.mod h1:Bxgi+DeeswYofcYO0XyGClwlrq3DZEXli0kLf4hkGA0=
-github.com/aws/aws-sdk-go-v2/credentials v1.16.14 h1:mMDTwwYO9A0/JbOCOG7EOZHtYM+o7OfGWfu0toa23VE=
-github.com/aws/aws-sdk-go-v2/credentials v1.16.14/go.mod h1:cniAUh3ErQPHtCQGPT5ouvSAQ0od8caTO9OOuufZOAE=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
-github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
+github.com/aws/aws-sdk-go-v2/config v1.27.16 h1:knpCuH7laFVGYTNd99Ns5t+8PuRjDn4HnnZK48csipM=
+github.com/aws/aws-sdk-go-v2/config v1.27.16/go.mod h1:vutqgRhDUktwSge3hrC3nkuirzkJ4E/mLj5GvI0BQas=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.16 h1:7d2QxY83uYl0l58ceyiSpxg9bSbStqBC6BeEeHEchwo=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.16/go.mod h1:Ae6li/6Yc6eMzysRL2BXlPYvnrLLBg3D11/AmOjw50k=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 h1:dQLK4TjtnlRGb0czOht2CevZ5l6RSyRWAnKeGd7VAFE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3/go.mod h1:TL79f2P6+8Q7dTsILpiVST+AL9lkF6PPGI167Ny0Cjw=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 h1:lf/8VTF2cM+N4SLzaYJERKEWAXq8MOMpZfU6wEPWsPk=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7/go.mod h1:4SjkU7QiqK2M9oozyMzfZ/23LmUY+h3oFqhdeP5OMiI=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 h1:4OYVp0705xu8yjdyoWix0r9wPIRXnIzzOoUpQVHIJ/g=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7/go.mod h1:vd7ESTEvI76T2Na050gODNmNU7+OyKrIKroYTu4ABiI=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 h1:5oE2WzJE56/mVveuDZPJESKlg/00AaS2pY2QZcnxg4M=
 github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10/go.mod h1:FHbKWQtRBYUz4vO5WBWjzMD2by126ny5y/1EoaWoLfI=
 github.com/aws/aws-sdk-go-v2/service/glue v1.73.1 h1:z/NBYW8RygzWrDgNWib10fuLUBl0SLj0KruGoEHxnKQ=
 github.com/aws/aws-sdk-go-v2/service/glue v1.73.1/go.mod h1:F3B9DC5FsIHAxUtHZdY5KUeqN+tHoGlRPzSSYdXjC38=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 h1:L0ai8WICYHozIKK+OtPzVJBugL7culcuM4E4JOpIEm8=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10/go.mod h1:byqfyxJBshFk0fF9YmK0M0ugIO8OWjzH2T3bPG4eGuA=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS37fdKEvAsGHOr9fa/qvwxfJurR/BzE=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10/go.mod h1:jMx5INQFYFYB3lQD9W0D8Ohgq6Wnl7NYOJ2TQndbulI=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 h1:UXqEWQI0n+q0QixzU0yUUQBZXRd5037qdInTIHFTl98=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9/go.mod h1:xP6Gq6fzGZT8w/ZN+XvGMZ2RU1LeEs7b2yUP5DN8NY4=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 h1:uO5XR6QGBcmPyo2gxofYJLFkcVQ4izOoGDNenlZhTEk=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7/go.mod h1:feeeAYfAcwTReM6vbwjEyDmiGho+YgBhaFULuXDW8kc=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU=
 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU=
-github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac=
-github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6 h1:Yf2MIo9x+0tyv76GljxzqA3WtC5mw7NmazD2chwjxE4=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.6/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
-github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
-github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
-github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
-github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.9 h1:aD7AGQhvPuAxlSUfo0CWU7s6FpkbyykMhGYMvlqTjVs=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.9/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3 h1:Pav5q3cA260Zqez42T9UhIlsd9QeypszRPwC9LdSSsQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.3/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.10 h1:69tpbPED7jKPyzMcrwSvhWcJ9bPnZsZs18NT40JwM0g=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.10/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws=
+github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
+github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
 github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
 github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -61,31 +63,38 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI=
+github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
 github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
 github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
 github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
-github.com/hamba/avro/v2 v2.16.0 h1:0XhyP65Hs8iMLtdSR0v7ZrwRjsbIZdvr7KzYgmx1Mbo=
-github.com/hamba/avro/v2 v2.16.0/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo=
+github.com/hamba/avro/v2 v2.22.1 h1:q1rAbfJsrbMaZPDLQvwUQMfQzp6H+hGXvckmU/lXemk=
+github.com/hamba/avro/v2 v2.22.1/go.mod h1:HOeTrE3kvWnBAgsufqhAzDDV5gvS0QXs65Z6BHfGgbg=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
+github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
-github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
-github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
 github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -107,46 +116,49 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
 github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
 github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
 github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
-github.com/pterm/pterm v0.12.78 h1:QTWKaIAa4B32GKwqVXtu9m1DUMgWw3VRljMkMevX+b8=
-github.com/pterm/pterm v0.12.78/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo=
+github.com/pterm/pterm v0.12.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4=
+github.com/pterm/pterm v0.12.79/go.mod h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
 github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
 github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 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.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/wolfeidau/s3iofs v1.5.0 h1:l0GsqWYnHwHYkBxDnMVL0fNA2mhRnnXm4rtRG93HsOw=
-github.com/wolfeidau/s3iofs v1.5.0/go.mod h1:fPAKzdWmZ1Z2L9vnqL6d1eb7pVsUgkUstxQUkG1HIHA=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/wolfeidau/s3iofs v1.5.2 h1:2dmzSxdrSY29GsILVheJqBbURVQX3KglggSBtVWCYj4=
+github.com/wolfeidau/s3iofs v1.5.2/go.mod h1:fPAKzdWmZ1Z2L9vnqL6d1eb7pVsUgkUstxQUkG1HIHA=
 github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
+github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
-golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
+golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
-golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -157,27 +169,31 @@ golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
-golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
+golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/literals.go b/literals.go
new file mode 100644
index 0000000..9b42e59
--- /dev/null
+++ b/literals.go
@@ -0,0 +1,780 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package iceberg
+
+import (
+	"bytes"
+	"cmp"
+	"errors"
+	"fmt"
+	"math"
+	"reflect"
+	"strconv"
+	"time"
+
+	"github.com/apache/arrow/go/v16/arrow"
+	"github.com/apache/arrow/go/v16/arrow/decimal128"
+	"github.com/google/uuid"
+)
+
+// LiteralType is a generic type constraint for the explicit Go types that we allow
+// for literal values. This represents the actual primitive types that exist in Iceberg
+type LiteralType interface {
+	bool | int32 | int64 | float32 | float64 | Date |
+		Time | Timestamp | string | []byte | uuid.UUID | Decimal
+}
+
+// Comparator is a comparison function for specific literal types:
+//
+//	returns 0 if v1 == v2
+//	returns <0 if v1 < v2
+//	returns >0 if v1 > v2
+type Comparator[T LiteralType] func(v1, v2 T) int
+
+// Literal is a non-null literal value. It can be casted using To and be checked for
+// equality against other literals.
+type Literal interface {
+	fmt.Stringer
+
+	Type() Type
+	To(Type) (Literal, error)
+	Equals(Literal) bool
+}
+
+// TypedLiteral is a generic interface for Literals so that you can retrieve the value.
+// This is based on the physical representative type, which means that FixedLiteral and
+// BinaryLiteral will both return []byte, etc.
+type TypedLiteral[T LiteralType] interface {
+	Literal
+
+	Value() T
+	Comparator() Comparator[T]
+}
+
+// NewLiteral provides a literal based on the type of T
+func NewLiteral[T LiteralType](val T) Literal {
+	switch v := any(val).(type) {
+	case bool:
+		return BoolLiteral(v)
+	case int32:
+		return Int32Literal(v)
+	case int64:
+		return Int64Literal(v)
+	case float32:
+		return Float32Literal(v)
+	case float64:
+		return Float64Literal(v)
+	case Date:
+		return DateLiteral(v)
+	case Time:
+		return TimeLiteral(v)
+	case Timestamp:
+		return TimestampLiteral(v)
+	case string:
+		return StringLiteral(v)
+	case []byte:
+		return BinaryLiteral(v)
+	case uuid.UUID:
+		return UUIDLiteral(v)
+	case Decimal:
+		return DecimalLiteral(v)
+	}
+	panic("can't happen due to literal type constraint")
+}
+
+// convenience to avoid repreating this pattern for primitive types
+func literalEq[L interface {
+	comparable
+	LiteralType
+}, T TypedLiteral[L]](lhs T, other Literal) bool {
+	rhs, ok := other.(T)
+	if !ok {
+		return false
+	}
+
+	return lhs.Value() == rhs.Value()
+}
+
+// AboveMaxLiteral represents values that are above the maximum for their type
+// such as values > math.MaxInt32 for an Int32Literal
+type AboveMaxLiteral interface {
+	Literal
+
+	aboveMax()
+}
+
+// BelowMinLiteral represents values that are below the minimum for their type
+// such as values < math.MinInt32 for an Int32Literal
+type BelowMinLiteral interface {
+	Literal
+
+	belowMin()
+}
+
+type aboveMaxLiteral[T int32 | int64 | float32 | float64] struct {
+	value T
+}
+
+func (ab aboveMaxLiteral[T]) aboveMax() {}
+
+func (ab aboveMaxLiteral[T]) Type() Type {
+	var z T
+	switch any(z).(type) {
+	case int32:
+		return PrimitiveTypes.Int32
+	case int64:
+		return PrimitiveTypes.Int64
+	case float32:
+		return PrimitiveTypes.Float32
+	case float64:
+		return PrimitiveTypes.Float64
+	default:
+		panic("should never happen")
+	}
+}
+
+func (ab aboveMaxLiteral[T]) To(t Type) (Literal, error) {
+	if ab.Type().Equals(t) {
+		return ab, nil
+	}
+	return nil, fmt.Errorf("%w: cannot change type of AboveMax%sLiteral",
+		ErrBadCast, reflect.TypeOf(T(0)).String())
+}
+
+func (ab aboveMaxLiteral[T]) Value() T { return ab.value }
+
+func (ab aboveMaxLiteral[T]) String() string { return "AboveMax" }
+func (ab aboveMaxLiteral[T]) Equals(other Literal) bool {
+	// AboveMaxLiteral isn't comparable and thus isn't even equal to itself
+	return false
+}
+
+type belowMinLiteral[T int32 | int64 | float32 | float64] struct {
+	value T
+}
+
+func (bm belowMinLiteral[T]) belowMin() {}
+
+func (bm belowMinLiteral[T]) Type() Type {
+	var z T
+	switch any(z).(type) {
+	case int32:
+		return PrimitiveTypes.Int32
+	case int64:
+		return PrimitiveTypes.Int64
+	case float32:
+		return PrimitiveTypes.Float32
+	case float64:
+		return PrimitiveTypes.Float64
+	default:
+		panic("should never happen")
+	}
+}
+
+func (bm belowMinLiteral[T]) To(t Type) (Literal, error) {
+	if bm.Type().Equals(t) {
+		return bm, nil
+	}
+	return nil, fmt.Errorf("%w: cannot change type of BelowMin%sLiteral",
+		ErrBadCast, reflect.TypeOf(T(0)).String())
+}
+
+func (bm belowMinLiteral[T]) Value() T { return bm.value }
+
+func (bm belowMinLiteral[T]) String() string { return "BelowMin" }
+func (bm belowMinLiteral[T]) Equals(other Literal) bool {
+	// BelowMinLiteral isn't comparable and thus isn't even equal to itself
+	return false
+}
+
+func Int32AboveMaxLiteral() Literal {
+	return aboveMaxLiteral[int32]{value: math.MaxInt32}
+}
+
+func Int64AboveMaxLiteral() Literal {
+	return aboveMaxLiteral[int64]{value: math.MaxInt64}
+}
+
+func Float32AboveMaxLiteral() Literal {
+	return aboveMaxLiteral[float32]{value: math.MaxFloat32}
+}
+
+func Float64AboveMaxLiteral() Literal {
+	return aboveMaxLiteral[float64]{value: math.MaxFloat64}
+}
+
+func Int32BelowMinLiteral() Literal {
+	return belowMinLiteral[int32]{value: math.MinInt32}
+}
+
+func Int64BelowMinLiteral() Literal {
+	return belowMinLiteral[int64]{value: math.MinInt64}
+}
+
+func Float32BelowMinLiteral() Literal {
+	return belowMinLiteral[float32]{value: -math.MaxFloat32}
+}
+
+func Float64BelowMinLiteral() Literal {
+	return belowMinLiteral[float64]{value: -math.MaxFloat64}
+}
+
+type BoolLiteral bool
+
+func (BoolLiteral) Comparator() Comparator[bool] {
+	return func(v1, v2 bool) int {
+		if v1 {
+			if v2 {
+				return 0
+			}
+			return 1
+		}
+		return -1
+	}
+}
+
+func (b BoolLiteral) Type() Type     { return PrimitiveTypes.Bool }
+func (b BoolLiteral) Value() bool    { return bool(b) }
+func (b BoolLiteral) String() string { return strconv.FormatBool(bool(b)) }
+func (b BoolLiteral) To(t Type) (Literal, error) {
+	switch t.(type) {
+	case BooleanType:
+		return b, nil
+	}
+	return nil, fmt.Errorf("%w: BoolLiteral to %s", ErrBadCast, t)
+}
+
+func (b BoolLiteral) Equals(l Literal) bool {
+	return literalEq(b, l)
+}
+
+type Int32Literal int32
+
+func (Int32Literal) Comparator() Comparator[int32] { return cmp.Compare[int32] }
+func (i Int32Literal) Type() Type                  { return PrimitiveTypes.Int32 }
+func (i Int32Literal) Value() int32                { return int32(i) }
+func (i Int32Literal) String() string              { return strconv.FormatInt(int64(i), 10) }
+func (i Int32Literal) To(t Type) (Literal, error) {
+	switch t := t.(type) {
+	case Int32Type:
+		return i, nil
+	case Int64Type:
+		return Int64Literal(i), nil
+	case Float32Type:
+		return Float32Literal(i), nil
+	case Float64Type:
+		return Float64Literal(i), nil
+	case DateType:
+		return DateLiteral(i), nil
+	case TimeType:
+		return TimeLiteral(i), nil
+	case TimestampType:
+		return TimestampLiteral(i), nil
+	case TimestampTzType:
+		return TimestampLiteral(i), nil
+	case DecimalType:
+		unscaled := Decimal{Val: decimal128.FromI64(int64(i)), Scale: 0}
+		if t.scale == 0 {
+			return DecimalLiteral(unscaled), nil
+		}
+		out, err := unscaled.Val.Rescale(0, int32(t.scale))
+		if err != nil {
+			return nil, fmt.Errorf("%w: failed to cast to DecimalType: %s", ErrBadCast, err.Error())
+		}
+		return DecimalLiteral{Val: out, Scale: t.scale}, nil
+	}
+
+	return nil, fmt.Errorf("%w: Int32Literal to %s", ErrBadCast, t)
+}
+
+func (i Int32Literal) Equals(other Literal) bool {
+	return literalEq(i, other)
+}
+
+type Int64Literal int64
+
+func (Int64Literal) Comparator() Comparator[int64] { return cmp.Compare[int64] }
+func (i Int64Literal) Type() Type                  { return PrimitiveTypes.Int64 }
+func (i Int64Literal) Value() int64                { return int64(i) }
+func (i Int64Literal) String() string              { return strconv.FormatInt(int64(i), 10) }
+func (i Int64Literal) To(t Type) (Literal, error) {
+	switch t := t.(type) {
+	case Int32Type:
+		if math.MaxInt32 < i {
+			return Int32AboveMaxLiteral(), nil
+		} else if math.MinInt32 > i {
+			return Int32BelowMinLiteral(), nil
+		}
+		return Int32Literal(i), nil
+	case Int64Type:
+		return i, nil
+	case Float32Type:
+		return Float32Literal(i), nil
+	case Float64Type:
+		return Float64Literal(i), nil
+	case DateType:
+		return DateLiteral(i), nil
+	case TimeType:
+		return TimeLiteral(i), nil
+	case TimestampType:
+		return TimestampLiteral(i), nil
+	case TimestampTzType:
+		return TimestampLiteral(i), nil
+	case DecimalType:
+		unscaled := Decimal{Val: decimal128.FromI64(int64(i)), Scale: 0}
+		if t.scale == 0 {
+			return DecimalLiteral(unscaled), nil
+		}
+		out, err := unscaled.Val.Rescale(0, int32(t.scale))
+		if err != nil {
+			return nil, fmt.Errorf("%w: failed to cast to DecimalType: %s", ErrBadCast, err.Error())
+		}
+		return DecimalLiteral{Val: out, Scale: t.scale}, nil
+	}
+
+	return nil, fmt.Errorf("%w: Int64Literal to %s", ErrBadCast, t)
+}
+func (i Int64Literal) Equals(other Literal) bool {
+	return literalEq(i, other)
+}
+
+type Float32Literal float32
+
+func (Float32Literal) Comparator() Comparator[float32] { return cmp.Compare[float32] }
+func (f Float32Literal) Type() Type                    { return PrimitiveTypes.Float32 }
+func (f Float32Literal) Value() float32                { return float32(f) }
+func (f Float32Literal) String() string                { return strconv.FormatFloat(float64(f), 'g', -1, 32) }
+func (f Float32Literal) To(t Type) (Literal, error) {
+	switch t := t.(type) {
+	case Float32Type:
+		return f, nil
+	case Float64Type:
+		return Float64Literal(f), nil
+	case DecimalType:
+		v, err := decimal128.FromFloat32(float32(f), int32(t.precision), int32(t.scale))
+		if err != nil {
+			return nil, err
+		}
+		return DecimalLiteral{Val: v, Scale: t.scale}, nil
+	}
+
+	return nil, fmt.Errorf("%w: Float32Literal to %s", ErrBadCast, t)
+}
+func (f Float32Literal) Equals(other Literal) bool {
+	return literalEq(f, other)
+}
+
+type Float64Literal float64
+
+func (Float64Literal) Comparator() Comparator[float64] { return cmp.Compare[float64] }
+func (f Float64Literal) Type() Type                    { return PrimitiveTypes.Float64 }
+func (f Float64Literal) Value() float64                { return float64(f) }
+func (f Float64Literal) String() string                { return strconv.FormatFloat(float64(f), 'g', -1, 64) }
+func (f Float64Literal) To(t Type) (Literal, error) {
+	switch t := t.(type) {
+	case Float32Type:
+		if math.MaxFloat32 < f {
+			return Float32AboveMaxLiteral(), nil
+		} else if -math.MaxFloat32 > f {
+			return Float32BelowMinLiteral(), nil
+		}
+		return Float32Literal(f), nil
+	case Float64Type:
+		return f, nil
+	case DecimalType:
+		v, err := decimal128.FromFloat64(float64(f), int32(t.precision), int32(t.scale))
+		if err != nil {
+			return nil, err
+		}
+		return DecimalLiteral{Val: v, Scale: t.scale}, nil
+	}
+
+	return nil, fmt.Errorf("%w: Float64Literal to %s", ErrBadCast, t)
+}
+func (f Float64Literal) Equals(other Literal) bool {
+	return literalEq(f, other)
+}
+
+type DateLiteral Date
+
+func (DateLiteral) Comparator() Comparator[Date] { return cmp.Compare[Date] }
+func (d DateLiteral) Type() Type                 { return PrimitiveTypes.Date }
+func (d DateLiteral) Value() Date                { return Date(d) }
+func (d DateLiteral) String() string {
+	t := time.Unix(0, 0).UTC().AddDate(0, 0, int(d))
+	return t.Format("2006-01-02")
+}
+func (d DateLiteral) To(t Type) (Literal, error) {
+	switch t.(type) {
+	case DateType:
+		return d, nil
+	}
+	return nil, fmt.Errorf("%w: DateLiteral to %s", ErrBadCast, t)
+}
+func (d DateLiteral) Equals(other Literal) bool {
+	return literalEq(d, other)
+}
+
+type TimeLiteral Time
+
+func (TimeLiteral) Comparator() Comparator[Time] { return cmp.Compare[Time] }
+func (t TimeLiteral) Type() Type                 { return PrimitiveTypes.Time }
+func (t TimeLiteral) Value() Time                { return Time(t) }
+func (t TimeLiteral) String() string {
+	tm := time.UnixMicro(int64(t)).UTC()
+	return tm.Format("15:04:05.000000")
+}
+func (t TimeLiteral) To(typ Type) (Literal, error) {
+	switch typ.(type) {
+	case TimeType:
+		return t, nil
+	}
+	return nil, fmt.Errorf("%w: TimeLiteral to %s", ErrBadCast, typ)
+
+}
+func (t TimeLiteral) Equals(other Literal) bool {
+	return literalEq(t, other)
+}
+
+type TimestampLiteral Timestamp
+
+func (TimestampLiteral) Comparator() Comparator[Timestamp] { return cmp.Compare[Timestamp] }
+func (t TimestampLiteral) Type() Type                      { return PrimitiveTypes.Timestamp }
+func (t TimestampLiteral) Value() Timestamp                { return Timestamp(t) }
+func (t TimestampLiteral) String() string {
+	tm := time.UnixMicro(int64(t)).UTC()
+	return tm.Format("2006-01-02 15:04:05.000000")
+}
+func (t TimestampLiteral) To(typ Type) (Literal, error) {
+	switch typ.(type) {
+	case TimestampType:
+		return t, nil
+	case TimestampTzType:
+		return t, nil
+	case DateType:
+		return DateLiteral(Timestamp(t).ToDate()), nil
+	}
+	return nil, fmt.Errorf("%w: TimestampLiteral to %s", ErrBadCast, typ)
+}
+func (t TimestampLiteral) Equals(other Literal) bool {
+	return literalEq(t, other)
+}
+
+type StringLiteral string
+
+func (StringLiteral) Comparator() Comparator[string] { return cmp.Compare[string] }
+func (s StringLiteral) Type() Type                   { return PrimitiveTypes.String }
+func (s StringLiteral) Value() string                { return string(s) }
+func (s StringLiteral) String() string               { return string(s) }
+func (s StringLiteral) To(typ Type) (Literal, error) {
+	switch t := typ.(type) {
+	case StringType:
+		return s, nil
+	case Int32Type:
+		n, err := strconv.ParseInt(string(s), 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s",
+				errors.Join(ErrBadCast, err), s, typ)
+		}
+
+		if math.MaxInt32 < n {
+			return Int32AboveMaxLiteral(), nil
+		} else if math.MinInt32 > n {
+			return Int32BelowMinLiteral(), nil
+		}
+
+		return Int32Literal(n), nil
+	case Int64Type:
+		n, err := strconv.ParseInt(string(s), 10, 64)
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s",
+				errors.Join(ErrBadCast, err), s, typ)
+		}
+
+		return Int64Literal(n), nil
+	case Float32Type:
+		n, err := strconv.ParseFloat(string(s), 32)
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s",
+				errors.Join(ErrBadCast, err), s, typ)
+		}
+		return Float32Literal(n), nil
+	case Float64Type:
+		n, err := strconv.ParseFloat(string(s), 64)
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s",
+				errors.Join(ErrBadCast, err), s, typ)
+		}
+		return Float64Literal(n), nil
+	case DateType:
+		tm, err := time.Parse("2006-01-02", string(s))
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s - %s",
+				ErrBadCast, s, typ, err.Error())
+		}
+		return DateLiteral(tm.Truncate(24*time.Hour).Unix() / int64((time.Hour * 24).Seconds())), nil
+	case TimeType:
+		val, err := arrow.Time64FromString(string(s), arrow.Microsecond)
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s - %s",
+				ErrBadCast, s, typ, err.Error())
+		}
+
+		return TimeLiteral(val), nil
+	case TimestampType:
+		// requires RFC3339 with no time zone
+		tm, err := time.Parse("2006-01-02T15:04:05", string(s))
+		if err != nil {
+			return nil, fmt.Errorf("%w: invalid Timestamp format for casting from string '%s': %s",
+				ErrBadCast, s, err.Error())
+		}
+
+		return TimestampLiteral(Timestamp(tm.UTC().UnixMicro())), nil
+	case TimestampTzType:
+		// requires RFC3339 format WITH time zone
+		tm, err := time.Parse(time.RFC3339, string(s))
+		if err != nil {
+			return nil, fmt.Errorf("%w: invalid TimestampTz format for casting from string '%s': %s",
+				ErrBadCast, s, err.Error())
+		}
+
+		return TimestampLiteral(Timestamp(tm.UTC().UnixMicro())), nil
+	case UUIDType:
+		val, err := uuid.Parse(string(s))
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s - %s",
+				ErrBadCast, s, typ, err.Error())
+		}
+		return UUIDLiteral(val), nil
+	case DecimalType:
+		n, err := decimal128.FromString(string(s), int32(t.precision), int32(t.scale))
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s - %s",
+				ErrBadCast, s, typ, err.Error())
+		}
+		return DecimalLiteral{Val: n, Scale: t.scale}, nil
+	case BooleanType:
+		val, err := strconv.ParseBool(string(s))
+		if err != nil {
+			return nil, fmt.Errorf("%w: casting '%s' to %s - %s",
+				ErrBadCast, s, typ, err.Error())
+		}
+		return BoolLiteral(val), nil
+	}
+	return nil, fmt.Errorf("%w: StringLiteral to %s", ErrBadCast, typ)
+}
+
+func (s StringLiteral) Equals(other Literal) bool {
+	return literalEq(s, other)
+}
+
+type BinaryLiteral []byte
+
+func (BinaryLiteral) Comparator() Comparator[[]byte] {
+	return bytes.Compare
+}
+func (b BinaryLiteral) Type() Type     { return PrimitiveTypes.Binary }
+func (b BinaryLiteral) Value() []byte  { return []byte(b) }
+func (b BinaryLiteral) String() string { return string(b) }
+func (b BinaryLiteral) To(typ Type) (Literal, error) {
+	switch t := typ.(type) {
+	case UUIDType:
+		val, err := uuid.FromBytes(b)
+		if err != nil {
+			return nil, fmt.Errorf("%w: cannot convert BinaryLiteral to UUID",
+				errors.Join(ErrBadCast, err))
+		}
+		return UUIDLiteral(val), nil
+	case FixedType:
+		if len(b) == t.len {
+			return FixedLiteral(b), nil
+		}
+
+		return nil, fmt.Errorf("%w: cannot convert BinaryLiteral to %s, different length - %d <> %d",
+			ErrBadCast, typ, len(b), t.len)
+	case BinaryType:
+		return b, nil
+	}
+
+	return nil, fmt.Errorf("%w: BinaryLiteral to %s", ErrBadCast, typ)
+}
+func (b BinaryLiteral) Equals(other Literal) bool {
+	rhs, ok := other.(BinaryLiteral)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal([]byte(b), rhs)
+}
+
+type FixedLiteral []byte
+
+func (FixedLiteral) Comparator() Comparator[[]byte] { return bytes.Compare }
+func (f FixedLiteral) Type() Type                   { return FixedTypeOf(len(f)) }
+func (f FixedLiteral) Value() []byte                { return []byte(f) }
+func (f FixedLiteral) String() string               { return string(f) }
+func (f FixedLiteral) To(typ Type) (Literal, error) {
+	switch t := typ.(type) {
+	case UUIDType:
+		val, err := uuid.FromBytes(f)
+		if err != nil {
+			return nil, fmt.Errorf("%w: cannot convert FixedLiteral to UUID - %s",
+				ErrBadCast, err.Error())
+		}
+		return UUIDLiteral(val), nil
+	case FixedType:
+		if len(f) == t.len {
+			return FixedLiteral(f), nil
+		}
+
+		return nil, fmt.Errorf("%w: cannot convert FixedLiteral to %s, different length - %d <> %d",
+			ErrBadCast, typ, len(f), t.len)
+	case BinaryType:
+		return f, nil
+	}
+
+	return nil, fmt.Errorf("%w: FixedLiteral[%d] to %s",
+		ErrBadCast, len(f), typ)
+}
+func (f FixedLiteral) Equals(other Literal) bool {
+	rhs, ok := other.(FixedLiteral)
+	if !ok {
+		return false
+	}
+
+	return bytes.Equal([]byte(f), rhs)
+}
+
+type UUIDLiteral uuid.UUID
+
+func (UUIDLiteral) Comparator() Comparator[uuid.UUID] {
+	return func(v1, v2 uuid.UUID) int {
+		return bytes.Compare(v1[:], v2[:])
+	}
+}
+
+func (UUIDLiteral) Type() Type         { return PrimitiveTypes.UUID }
+func (u UUIDLiteral) Value() uuid.UUID { return uuid.UUID(u) }
+func (u UUIDLiteral) String() string   { return uuid.UUID(u).String() }
+func (u UUIDLiteral) To(typ Type) (Literal, error) {
+	switch t := typ.(type) {
+	case UUIDType:
+		return u, nil
+	case FixedType:
+		if len(u) == t.len {
+			v, _ := uuid.UUID(u).MarshalBinary()
+			return FixedLiteral(v), nil
+		}
+
+		return nil, fmt.Errorf("%w: cannot convert UUIDLiteral to %s, different length - %d <> %d",
+			ErrBadCast, typ, len(u), t.len)
+	case BinaryType:
+		v, _ := uuid.UUID(u).MarshalBinary()
+		return BinaryLiteral(v), nil
+	}
+
+	return nil, fmt.Errorf("%w: UUIDLiteral to %s", ErrBadCast, typ)
+}
+func (u UUIDLiteral) Equals(other Literal) bool {
+	rhs, ok := other.(UUIDLiteral)
+	if !ok {
+		return false
+	}
+
+	return uuid.UUID(u) == uuid.UUID(rhs)
+}
+
+type DecimalLiteral Decimal
+
+func (DecimalLiteral) Comparator() Comparator[Decimal] {
+	return func(v1, v2 Decimal) int {
+		if v1.Scale == v2.Scale {
+			return v1.Val.Cmp(v2.Val)
+		}
+
+		rescaled, err := v2.Val.Rescale(int32(v2.Scale), int32(v1.Scale))
+		if err != nil {
+			return -1
+		}
+
+		return v1.Val.Cmp(rescaled)
+	}
+}
+func (d DecimalLiteral) Type() Type     { return DecimalTypeOf(9, d.Scale) }
+func (d DecimalLiteral) Value() Decimal { return Decimal(d) }
+func (d DecimalLiteral) String() string {
+	return d.Val.ToString(int32(d.Scale))
+}
+
+func (d DecimalLiteral) To(t Type) (Literal, error) {
+	switch t := t.(type) {
+	case DecimalType:
+		if d.Scale == t.scale {
+			return d, nil
+		}
+
+		return nil, fmt.Errorf("%w: could not convert %v to %s",
+			ErrBadCast, d, t)
+	case Int32Type:
+		v := d.Val.BigInt().Int64()
+		if v > math.MaxInt32 {
+			return Int32AboveMaxLiteral(), nil
+		} else if v < math.MinInt32 {
+			return Int32BelowMinLiteral(), nil
+		}
+
+		return Int32Literal(int32(v)), nil
+	case Int64Type:
+		v := d.Val.BigInt()
+		if !v.IsInt64() {
+			if v.Sign() > 0 {
+				return Int64AboveMaxLiteral(), nil
+			} else if v.Sign() < 0 {
+				return Int64BelowMinLiteral(), nil
+			}
+		}
+
+		return Int64Literal(v.Int64()), nil
+	case Float32Type:
+		v := d.Val.ToFloat64(int32(d.Scale))
+		if v > math.MaxFloat32 {
+			return Float32AboveMaxLiteral(), nil
+		} else if v < -math.MaxFloat32 {
+			return Float32BelowMinLiteral(), nil
+		}
+
+		return Float32Literal(float32(v)), nil
+	case Float64Type:
+		return Float64Literal(d.Val.ToFloat64(int32(d.Scale))), nil
+	}
+
+	return nil, fmt.Errorf("%w: DecimalLiteral to %s", ErrBadCast, t)
+}
+
+func (d DecimalLiteral) Equals(other Literal) bool {
+	rhs, ok := other.(DecimalLiteral)
+	if !ok {
+		return false
+	}
+
+	rescaled, err := rhs.Val.Rescale(int32(rhs.Scale), int32(d.Scale))
+	if err != nil {
+		return false
+	}
+	return d.Val == rescaled
+}
diff --git a/types.go b/types.go
index 8ca0ad7..5aabdb4 100644
--- a/types.go
+++ b/types.go
@@ -23,7 +23,9 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
+	"time"
 
+	"github.com/apache/arrow/go/v16/arrow/decimal128"
 	"golang.org/x/exp/slices"
 )
 
@@ -407,6 +409,7 @@ func (f FixedType) Equals(other Type) bool {
 func (f FixedType) Len() int       { return f.len }
 func (f FixedType) Type() string   { return fmt.Sprintf("fixed[%d]", f.len) }
 func (f FixedType) String() string { return fmt.Sprintf("fixed[%d]", f.len) }
+func (f FixedType) primitive()     {}
 
 func DecimalTypeOf(prec, scale int) DecimalType {
 	return DecimalType{precision: prec, scale: scale}
@@ -430,6 +433,12 @@ func (d DecimalType) Type() string   { return fmt.Sprintf("decimal(%d, %d)", d.p
 func (d DecimalType) String() string { return fmt.Sprintf("decimal(%d, %d)", d.precision, d.scale) }
 func (d DecimalType) Precision() int { return d.precision }
 func (d DecimalType) Scale() int     { return d.scale }
+func (DecimalType) primitive()       {}
+
+type Decimal struct {
+	Val   decimal128.Num
+	Scale int
+}
 
 type PrimitiveType interface {
 	Type
@@ -527,6 +536,11 @@ func (TimeType) String() string { return "time" }
 
 type Timestamp int64
 
+func (t Timestamp) ToDate() Date {
+	tm := time.UnixMicro(int64(t)).UTC()
+	return Date(tm.Truncate(24*time.Hour).Unix() / int64((time.Hour * 24).Seconds()))
+}
+
 // TimestampType represents a number of microseconds since the unix epoch
 // without regard for timezone.
 type TimestampType struct{}
diff --git a/utils.go b/utils.go
index 907a35f..fd669f8 100644
--- a/utils.go
+++ b/utils.go
@@ -18,10 +18,9 @@
 package iceberg
 
 import (
+	"cmp"
 	"runtime/debug"
 	"strings"
-
-	"golang.org/x/exp/constraints"
 )
 
 var version string
@@ -40,7 +39,7 @@ func init() {
 
 func Version() string { return version }
 
-func max[T constraints.Ordered](vals ...T) T {
+func max[T cmp.Ordered](vals ...T) T {
 	if len(vals) == 0 {
 		panic("can't call max with no arguments")
 	}


Test patch:
diff --git a/literals_test.go b/literals_test.go
new file mode 100644
index 0000000..f7a483b
--- /dev/null
+++ b/literals_test.go
@@ -0,0 +1,729 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package iceberg_test
+
+import (
+	"math"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/apache/arrow/go/v16/arrow"
+	"github.com/apache/arrow/go/v16/arrow/decimal128"
+	"github.com/apache/iceberg-go"
+	"github.com/google/uuid"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestNumericLiteralCompare(t *testing.T) {
+	smallLit := iceberg.NewLiteral(int32(10)).(iceberg.Int32Literal)
+	bigLit := iceberg.NewLiteral(int32(1000)).(iceberg.Int32Literal)
+
+	assert.False(t, smallLit.Equals(bigLit))
+	assert.True(t, smallLit.Equals(iceberg.NewLiteral(int32(10))))
+
+	cmp := smallLit.Comparator()
+
+	assert.Equal(t, -1, cmp(smallLit.Value(), bigLit.Value()))
+	assert.Equal(t, 1, cmp(bigLit.Value(), smallLit.Value()))
+	assert.True(t, smallLit.Type().Equals(iceberg.PrimitiveTypes.Int32))
+}
+
+func TestIntConversion(t *testing.T) {
+	lit := iceberg.NewLiteral(int32(34))
+
+	t.Run("to int64", func(t *testing.T) {
+		longLit, err := lit.To(iceberg.PrimitiveTypes.Int64)
+		assert.NoError(t, err)
+		assert.IsType(t, iceberg.Int64Literal(0), longLit)
+		assert.EqualValues(t, 34, longLit)
+	})
+
+	t.Run("to float32", func(t *testing.T) {
+		floatLit, err := lit.To(iceberg.PrimitiveTypes.Float32)
+		assert.NoError(t, err)
+		assert.IsType(t, iceberg.Float32Literal(0), floatLit)
+		assert.EqualValues(t, 34, floatLit)
+	})
+
+	t.Run("to float64", func(t *testing.T) {
+		dblLit, err := lit.To(iceberg.PrimitiveTypes.Float64)
+		assert.NoError(t, err)
+		assert.IsType(t, iceberg.Float64Literal(0), dblLit)
+		assert.EqualValues(t, 34, dblLit)
+	})
+}
+
+func TestIntToDecimalConversion(t *testing.T) {
+	tests := []struct {
+		ty  iceberg.DecimalType
+		val iceberg.Decimal
+	}{
+		{iceberg.DecimalTypeOf(9, 0),
+			iceberg.Decimal{Val: decimal128.FromI64(34), Scale: 0}},
+		{iceberg.DecimalTypeOf(9, 2),
+			iceberg.Decimal{Val: decimal128.FromI64(3400), Scale: 2}},
+		{iceberg.DecimalTypeOf(9, 4),
+			iceberg.Decimal{Val: decimal128.FromI64(340000), Scale: 4}},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.ty.String(), func(t *testing.T) {
+			lit := iceberg.Int32Literal(34)
+
+			dec, err := lit.To(tt.ty)
+			require.NoError(t, err)
+			assert.IsType(t, iceberg.DecimalLiteral(tt.val), dec)
+			assert.EqualValues(t, tt.val, dec)
+		})
+	}
+}
+
+func TestIntToDateConversion(t *testing.T) {
+	oneDay, _ := time.Parse("2006-01-02", "2022-03-28")
+	val := int32(arrow.Date32FromTime(oneDay))
+	dateLit, err := iceberg.NewLiteral(val).To(iceberg.PrimitiveTypes.Date)
+	require.NoError(t, err)
+	assert.True(t, dateLit.Type().Equals(iceberg.PrimitiveTypes.Date))
+	assert.EqualValues(t, val, dateLit)
+
+	lit := iceberg.Int32Literal(34)
+	tm, err := lit.To(iceberg.PrimitiveTypes.Time)
+	require.NoError(t, err)
+	assert.EqualValues(t, lit, tm)
+
+	tm, err = lit.To(iceberg.PrimitiveTypes.Timestamp)
+	require.NoError(t, err)
+	assert.EqualValues(t, lit, tm)
+
+	tm, err = lit.To(iceberg.PrimitiveTypes.TimestampTz)
+	require.NoError(t, err)
+	assert.EqualValues(t, lit, tm)
+}
+
+func TestInt64Conversions(t *testing.T) {
+	tests := []struct {
+		from iceberg.Int64Literal
+		to   iceberg.Literal
+	}{
+		{iceberg.Int64Literal(34), iceberg.NewLiteral(int32(34))},
+		{iceberg.Int64Literal(34), iceberg.NewLiteral(float32(34))},
+		{iceberg.Int64Literal(34), iceberg.NewLiteral(float64(34))},
+		{iceberg.Int64Literal(19709), iceberg.NewLiteral(iceberg.Date(19709))},
+		{iceberg.Int64Literal(51661919000), iceberg.NewLiteral(iceberg.Time(51661919000))},
+		{iceberg.Int64Literal(1647305201), iceberg.NewLiteral(iceberg.Timestamp(1647305201))},
+		{iceberg.Int64Literal(34),
+			iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(34), Scale: 0})},
+		{iceberg.Int64Literal(34),
+			iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(3400), Scale: 2})},
+		{iceberg.Int64Literal(34),
+			iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(340000), Scale: 4})},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.to.Type().String(), func(t *testing.T) {
+			got, err := tt.from.To(tt.to.Type())
+			require.NoError(t, err)
+			assert.True(t, tt.to.Equals(got))
+		})
+	}
+}
+
+func TestInt64ToInt32OutsideBound(t *testing.T) {
+	bigLit := iceberg.NewLiteral(int64(math.MaxInt32 + 1))
+	aboveMax, err := bigLit.To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Implements(t, (*iceberg.AboveMaxLiteral)(nil), aboveMax)
+	assert.Equal(t, iceberg.Int32AboveMaxLiteral(), aboveMax)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int32, aboveMax.Type())
+
+	smallLit := iceberg.NewLiteral(int64(math.MinInt32 - 1))
+	belowMin, err := smallLit.To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Implements(t, (*iceberg.BelowMinLiteral)(nil), belowMin)
+	assert.Equal(t, iceberg.Int32BelowMinLiteral(), belowMin)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int32, belowMin.Type())
+}
+
+func TestFloatConversions(t *testing.T) {
+	n1, _ := decimal128.FromFloat32(34.56, 9, 1)
+	n2, _ := decimal128.FromFloat32(34.56, 9, 2)
+	n3, _ := decimal128.FromFloat32(34.56, 9, 4)
+
+	tests := []struct {
+		from iceberg.Float32Literal
+		to   iceberg.Literal
+	}{
+		{iceberg.Float32Literal(34.5), iceberg.NewLiteral(float64(34.5))},
+		{iceberg.Float32Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n1, Scale: 1})},
+		{iceberg.Float32Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n2, Scale: 2})},
+		{iceberg.Float32Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n3, Scale: 4})},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.to.Type().String(), func(t *testing.T) {
+			got, err := tt.from.To(tt.to.Type())
+			require.NoError(t, err)
+			assert.Truef(t, tt.to.Equals(got), "expected: %s, got: %s", tt.to, got)
+		})
+	}
+}
+
+func TestFloat64Conversions(t *testing.T) {
+	n1, _ := decimal128.FromFloat64(34.56, 9, 1)
+	n2, _ := decimal128.FromFloat64(34.56, 9, 2)
+	n3, _ := decimal128.FromFloat64(34.56, 9, 4)
+
+	tests := []struct {
+		from iceberg.Float64Literal
+		to   iceberg.Literal
+	}{
+		{iceberg.Float64Literal(34.5), iceberg.NewLiteral(float32(34.5))},
+		{iceberg.Float64Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n1, Scale: 1})},
+		{iceberg.Float64Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n2, Scale: 2})},
+		{iceberg.Float64Literal(34.56),
+			iceberg.NewLiteral(iceberg.Decimal{Val: n3, Scale: 4})},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.to.Type().String(), func(t *testing.T) {
+			got, err := tt.from.To(tt.to.Type())
+			require.NoError(t, err)
+			assert.Truef(t, tt.to.Equals(got), "expected: %s, got: %s", tt.to, got)
+		})
+	}
+}
+
+func TestFloat64toFloat32OutsideBounds(t *testing.T) {
+	bigLit := iceberg.NewLiteral(float64(math.MaxFloat32 + 1.0e37))
+	aboveMax, err := bigLit.To(iceberg.PrimitiveTypes.Float32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Float32AboveMaxLiteral(), aboveMax)
+
+	smallLit := iceberg.NewLiteral(float64(-math.MaxFloat32 - 1.0e37))
+	belowMin, err := smallLit.To(iceberg.PrimitiveTypes.Float32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Float32BelowMinLiteral(), belowMin)
+}
+
+func TestDecimalToDecimalConversion(t *testing.T) {
+	lit := iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(3411), Scale: 2})
+
+	v, err := lit.To(iceberg.DecimalTypeOf(9, 2))
+	require.NoError(t, err)
+	assert.Equal(t, lit, v)
+
+	v, err = lit.To(iceberg.DecimalTypeOf(11, 2))
+	require.NoError(t, err)
+	assert.Equal(t, lit, v)
+
+	_, err = lit.To(iceberg.DecimalTypeOf(9, 0))
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "could not convert 34.11 to decimal(9, 0)")
+
+	_, err = lit.To(iceberg.DecimalTypeOf(9, 1))
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "could not convert 34.11 to decimal(9, 1)")
+
+	_, err = lit.To(iceberg.DecimalTypeOf(9, 3))
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "could not convert 34.11 to decimal(9, 3)")
+}
+
+func TestDecimalLiteralConversions(t *testing.T) {
+	n1 := iceberg.Decimal{Val: decimal128.FromI64(1234), Scale: 2}
+	n2 := iceberg.Decimal{Val: decimal128.FromI64(math.MaxInt32 + 1), Scale: 0}
+	n3 := iceberg.Decimal{Val: decimal128.FromI64(math.MinInt32 - 1), Scale: 10}
+
+	tests := []struct {
+		from iceberg.DecimalLiteral
+		to   iceberg.Literal
+	}{
+		{iceberg.DecimalLiteral(n1), iceberg.NewLiteral(int32(1234))},
+		{iceberg.DecimalLiteral(n1), iceberg.NewLiteral(int64(1234))},
+		{iceberg.DecimalLiteral(n2), iceberg.NewLiteral(int64(math.MaxInt32 + 1))},
+		{iceberg.DecimalLiteral(n1), iceberg.NewLiteral(float32(12.34))},
+		{iceberg.DecimalLiteral(n1), iceberg.NewLiteral(float64(12.34))},
+		{iceberg.DecimalLiteral(n3), iceberg.NewLiteral(int64(math.MinInt32 - 1))},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.to.Type().String(), func(t *testing.T) {
+			got, err := tt.from.To(tt.to.Type())
+			require.NoError(t, err)
+			assert.Truef(t, tt.to.Equals(got), "expected: %s, got: %s", tt.to, got)
+		})
+	}
+
+	above, err := iceberg.DecimalLiteral(n2).To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int32AboveMaxLiteral(), above)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int32, above.Type())
+
+	below, err := iceberg.DecimalLiteral(n3).To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int32BelowMinLiteral(), below)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int32, below.Type())
+
+	n4 := iceberg.Decimal{Val: decimal128.FromU64(math.MaxInt64 + 1), Scale: 0}
+	n5 := iceberg.Decimal{Val: decimal128.FromU64(math.MaxUint64).Negate(), Scale: 20}
+
+	above, err = iceberg.DecimalLiteral(n4).To(iceberg.PrimitiveTypes.Int64)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int64AboveMaxLiteral(), above)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int64, above.Type())
+
+	below, err = iceberg.DecimalLiteral(n5).To(iceberg.PrimitiveTypes.Int64)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int64BelowMinLiteral(), below)
+	assert.Equal(t, iceberg.PrimitiveTypes.Int64, below.Type())
+
+	v, err := decimal128.FromFloat64(math.MaxFloat32+1e37, 38, -1)
+	require.NoError(t, err)
+	above, err = iceberg.DecimalLiteral(iceberg.Decimal{Val: v, Scale: -1}).
+		To(iceberg.PrimitiveTypes.Float32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Float32AboveMaxLiteral(), above)
+	assert.Equal(t, iceberg.PrimitiveTypes.Float32, above.Type())
+
+	below, err = iceberg.DecimalLiteral(iceberg.Decimal{Val: v.Negate(), Scale: -1}).
+		To(iceberg.PrimitiveTypes.Float32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Float32BelowMinLiteral(), below)
+	assert.Equal(t, iceberg.PrimitiveTypes.Float32, below.Type())
+}
+
+func TestLiteralTimestampToDate(t *testing.T) {
+	v, _ := arrow.TimestampFromString("1970-01-01T00:00:00.000000+00:00", arrow.Microsecond)
+	tsLit := iceberg.NewLiteral(iceberg.Timestamp(v))
+	dateLit, err := tsLit.To(iceberg.PrimitiveTypes.Date)
+	require.NoError(t, err)
+	assert.Zero(t, dateLit)
+}
+
+func TestStringLiterals(t *testing.T) {
+	sqrt2 := iceberg.NewLiteral("1.414")
+	pi := iceberg.NewLiteral("3.141")
+	piStr := iceberg.StringLiteral("3.141")
+	piDbl := iceberg.NewLiteral(float64(3.141))
+
+	v, err := pi.To(iceberg.PrimitiveTypes.Float64)
+	require.NoError(t, err)
+	assert.Equal(t, piDbl, v)
+
+	assert.False(t, sqrt2.Equals(pi))
+	assert.True(t, pi.Equals(piStr))
+	assert.False(t, pi.Equals(piDbl))
+	assert.Equal(t, "3.141", pi.String())
+
+	cmp := piStr.Comparator()
+	assert.Equal(t, -1, cmp(sqrt2.(iceberg.StringLiteral).Value(), piStr.Value()))
+	assert.Equal(t, 1, cmp(piStr.Value(), sqrt2.(iceberg.StringLiteral).Value()))
+
+	v, err = pi.To(iceberg.PrimitiveTypes.String)
+	require.NoError(t, err)
+	assert.Equal(t, pi, v)
+}
+
+func TestStringLiteralConversion(t *testing.T) {
+	tm, _ := time.Parse("2006-01-02", "2017-08-18")
+	expected := uuid.New()
+
+	tests := []struct {
+		from iceberg.StringLiteral
+		to   iceberg.Literal
+	}{
+		{iceberg.StringLiteral("2017-08-18"),
+			iceberg.NewLiteral(iceberg.Date(arrow.Date32FromTime(tm)))},
+		{iceberg.StringLiteral("14:21:01.919"),
+			iceberg.NewLiteral(iceberg.Time(51661919000))},
+		{iceberg.StringLiteral("2017-08-18T14:21:01.919234"),
+			iceberg.NewLiteral(iceberg.Timestamp(1503066061919234))},
+		{iceberg.StringLiteral(expected.String()), iceberg.NewLiteral(expected)},
+		{iceberg.StringLiteral("34.560"),
+			iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(34560), Scale: 3})},
+		{iceberg.StringLiteral("true"), iceberg.NewLiteral(true)},
+		{iceberg.StringLiteral("True"), iceberg.NewLiteral(true)},
+		{iceberg.StringLiteral("false"), iceberg.NewLiteral(false)},
+		{iceberg.StringLiteral("False"), iceberg.NewLiteral(false)},
+		{iceberg.StringLiteral("12345"), iceberg.NewLiteral(int32(12345))},
+		{iceberg.StringLiteral("12345123456"), iceberg.NewLiteral(int64(12345123456))},
+		{iceberg.StringLiteral("3.14"), iceberg.NewLiteral(float32(3.14))},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.to.Type().String(), func(t *testing.T) {
+			got, err := tt.from.To(tt.to.Type())
+			require.NoError(t, err)
+			assert.Truef(t, tt.to.Equals(got), "expected: %s, got: %s", tt.to, got)
+		})
+	}
+
+	lit := iceberg.StringLiteral("2017-08-18T14:21:01.919234-07:00")
+	casted, err := lit.To(iceberg.PrimitiveTypes.TimestampTz)
+	require.NoError(t, err)
+	expectedTimestamp := iceberg.NewLiteral(iceberg.Timestamp(1503091261919234))
+	assert.Truef(t, casted.Equals(expectedTimestamp), "expected: %s, got: %s",
+		expectedTimestamp, casted)
+
+	_, err = lit.To(iceberg.PrimitiveTypes.Timestamp)
+	require.Error(t, err)
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, `parsing time "2017-08-18T14:21:01.919234-07:00": extra text: "-07:00"`)
+	assert.ErrorContains(t, err, "invalid Timestamp format for casting from string")
+
+	_, err = iceberg.StringLiteral("2017-08-18T14:21:01.919234").To(iceberg.PrimitiveTypes.TimestampTz)
+	require.Error(t, err)
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, `cannot parse "" as "Z07:00"`)
+}
+
+func TestLiteralIdentityConversions(t *testing.T) {
+	fixedLit, _ := iceberg.NewLiteral([]byte{0x01, 0x02, 0x03}).To(iceberg.FixedTypeOf(3))
+
+	tests := []struct {
+		lit iceberg.Literal
+		typ iceberg.PrimitiveType
+	}{
+		{iceberg.NewLiteral(true), iceberg.PrimitiveTypes.Bool},
+		{iceberg.NewLiteral(int32(34)), iceberg.PrimitiveTypes.Int32},
+		{iceberg.NewLiteral(int64(340000000)), iceberg.PrimitiveTypes.Int64},
+		{iceberg.NewLiteral(float32(34.11)), iceberg.PrimitiveTypes.Float32},
+		{iceberg.NewLiteral(float64(3.5028235e38)), iceberg.PrimitiveTypes.Float64},
+		{iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(3455), Scale: 2}),
+			iceberg.DecimalTypeOf(9, 2)},
+		{iceberg.NewLiteral(iceberg.Date(19079)), iceberg.PrimitiveTypes.Date},
+		{iceberg.NewLiteral(iceberg.Timestamp(1503091261919234)),
+			iceberg.PrimitiveTypes.Timestamp},
+		{iceberg.NewLiteral("abc"), iceberg.PrimitiveTypes.String},
+		{iceberg.NewLiteral(uuid.New()), iceberg.PrimitiveTypes.UUID},
+		{fixedLit, iceberg.FixedTypeOf(3)},
+		{iceberg.NewLiteral([]byte{0x01, 0x02, 0x03}), iceberg.PrimitiveTypes.Binary},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.typ.String(), func(t *testing.T) {
+			expected, err := tt.lit.To(tt.typ)
+			require.NoError(t, err)
+			assert.Equal(t, expected, tt.lit)
+		})
+	}
+}
+
+func TestFixedLiteral(t *testing.T) {
+	fixedLit012 := iceberg.FixedLiteral{0x00, 0x01, 0x02}
+	fixedLit013 := iceberg.FixedLiteral{0x00, 0x01, 0x03}
+	assert.True(t, fixedLit012.Equals(fixedLit012))
+	assert.False(t, fixedLit012.Equals(fixedLit013))
+
+	cmp := fixedLit012.Comparator()
+	assert.Equal(t, -1, cmp(fixedLit012, fixedLit013))
+	assert.Equal(t, 1, cmp(fixedLit013, fixedLit012))
+	assert.Equal(t, 0, cmp(fixedLit013, fixedLit013))
+
+	testUuid := uuid.New()
+	lit, err := iceberg.NewLiteral(testUuid[:]).To(iceberg.FixedTypeOf(16))
+	require.NoError(t, err)
+	uuidLit, err := lit.To(iceberg.PrimitiveTypes.UUID)
+	require.NoError(t, err)
+
+	assert.EqualValues(t, uuidLit, testUuid)
+
+	fixedUuid, err := uuidLit.To(iceberg.FixedTypeOf(16))
+	require.NoError(t, err)
+	assert.EqualValues(t, testUuid[:], fixedUuid)
+
+	binUuid, err := uuidLit.To(iceberg.PrimitiveTypes.Binary)
+	require.NoError(t, err)
+	assert.EqualValues(t, testUuid[:], binUuid)
+
+	binlit, err := fixedLit012.To(iceberg.PrimitiveTypes.Binary)
+	require.NoError(t, err)
+	assert.EqualValues(t, fixedLit012, binlit)
+}
+
+func TestBinaryLiteral(t *testing.T) {
+	binLit012 := iceberg.NewLiteral([]byte{0x00, 0x01, 0x02}).(iceberg.BinaryLiteral)
+	binLit013 := iceberg.NewLiteral([]byte{0x00, 0x01, 0x03}).(iceberg.BinaryLiteral)
+	assert.True(t, binLit012.Equals(binLit012))
+	assert.False(t, binLit012.Equals(binLit013))
+
+	cmp := binLit012.Comparator()
+	assert.Equal(t, -1, cmp(binLit012, binLit013))
+	assert.Equal(t, 1, cmp(binLit013, binLit012))
+	assert.Equal(t, 0, cmp(binLit013, binLit013))
+}
+
+func TestBinaryLiteralConversions(t *testing.T) {
+	binLit012 := iceberg.NewLiteral([]byte{0x00, 0x01, 0x02})
+	fixed, err := binLit012.To(iceberg.FixedTypeOf(3))
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.FixedLiteral{0x00, 0x01, 0x02}, fixed)
+
+	_, err = binLit012.To(iceberg.FixedTypeOf(4))
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "cannot convert BinaryLiteral to fixed[4], different length - 3 <> 4")
+
+	_, err = binLit012.To(iceberg.FixedTypeOf(2))
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "cannot convert BinaryLiteral to fixed[2], different length - 3 <> 2")
+
+	testUuid := uuid.New()
+	lit := iceberg.NewLiteral(testUuid[:])
+	uuidLit, err := lit.To(iceberg.PrimitiveTypes.UUID)
+	require.NoError(t, err)
+	assert.EqualValues(t, testUuid, uuidLit)
+
+	_, err = binLit012.To(iceberg.PrimitiveTypes.UUID)
+	assert.ErrorIs(t, err, iceberg.ErrBadCast)
+	assert.ErrorContains(t, err, "cannot convert BinaryLiteral to UUID")
+}
+
+func testInvalidLiteralConversions(t *testing.T, lit iceberg.Literal, typs []iceberg.Type) {
+	t.Run(lit.Type().String(), func(t *testing.T) {
+		for _, tt := range typs {
+			t.Run(tt.String(), func(t *testing.T) {
+				_, err := lit.To(tt)
+				assert.ErrorIs(t, err, iceberg.ErrBadCast)
+			})
+		}
+	})
+}
+
+func TestInvalidBoolLiteralConversions(t *testing.T) {
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(true), []iceberg.Type{
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.PrimitiveTypes.Binary,
+		iceberg.FixedTypeOf(2),
+	})
+}
+
+func TestInvalidNumericConversions(t *testing.T) {
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(int32(34)), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(int64(34)), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(float32(34)), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(float64(34)), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(iceberg.Decimal{Val: decimal128.FromI64(3411), Scale: 2}),
+		[]iceberg.Type{
+			iceberg.PrimitiveTypes.Bool,
+			iceberg.PrimitiveTypes.Date,
+			iceberg.PrimitiveTypes.Time,
+			iceberg.PrimitiveTypes.Timestamp,
+			iceberg.PrimitiveTypes.TimestampTz,
+			iceberg.PrimitiveTypes.String,
+			iceberg.PrimitiveTypes.UUID,
+			iceberg.FixedTypeOf(1),
+			iceberg.PrimitiveTypes.Binary,
+		})
+}
+
+func TestInvalidDateTimeLiteralConversions(t *testing.T) {
+	lit, _ := iceberg.NewLiteral("2017-08-18").To(iceberg.PrimitiveTypes.Date)
+	testInvalidLiteralConversions(t, lit, []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	lit, _ = iceberg.NewLiteral("14:21:01.919").To(iceberg.PrimitiveTypes.Time)
+	testInvalidLiteralConversions(t, lit, []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+
+	lit, _ = iceberg.NewLiteral("2017-08-18T14:21:01.919").To(iceberg.PrimitiveTypes.Timestamp)
+	testInvalidLiteralConversions(t, lit, []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+		iceberg.FixedTypeOf(1),
+		iceberg.PrimitiveTypes.Binary,
+	})
+}
+
+func TestInvalidStringLiteralConversions(t *testing.T) {
+	testInvalidLiteralConversions(t, iceberg.NewLiteral("abc"), []iceberg.Type{
+		iceberg.FixedTypeOf(1), iceberg.PrimitiveTypes.Binary,
+	})
+}
+
+func TestInvalidBinaryLiteralConversions(t *testing.T) {
+	testInvalidLiteralConversions(t, iceberg.NewLiteral(uuid.New()), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.FixedTypeOf(1),
+	})
+
+	lit, _ := iceberg.NewLiteral([]byte{0x00, 0x01, 0x02}).To(iceberg.FixedTypeOf(3))
+	testInvalidLiteralConversions(t, lit, []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+	})
+
+	testInvalidLiteralConversions(t, iceberg.NewLiteral([]byte{0x00, 0x01, 0x02}), []iceberg.Type{
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.String,
+		iceberg.PrimitiveTypes.UUID,
+	})
+}
+
+func TestBadStringLiteralCasts(t *testing.T) {
+	tests := []iceberg.Type{
+		iceberg.PrimitiveTypes.Int32,
+		iceberg.PrimitiveTypes.Int64,
+		iceberg.PrimitiveTypes.Float32,
+		iceberg.PrimitiveTypes.Float64,
+		iceberg.PrimitiveTypes.Date,
+		iceberg.PrimitiveTypes.Time,
+		iceberg.PrimitiveTypes.Timestamp,
+		iceberg.PrimitiveTypes.TimestampTz,
+		iceberg.PrimitiveTypes.Bool,
+		iceberg.DecimalTypeOf(9, 2),
+		iceberg.PrimitiveTypes.UUID,
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.String(), func(t *testing.T) {
+			_, err := iceberg.NewLiteral("abc").To(tt)
+			assert.ErrorIs(t, err, iceberg.ErrBadCast)
+		})
+	}
+}
+
+func TestStringLiteralToIntMaxMinValue(t *testing.T) {
+	above, err := iceberg.NewLiteral(strconv.FormatInt(math.MaxInt32+1, 10)).
+		To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int32AboveMaxLiteral(), above)
+
+	below, err := iceberg.NewLiteral(strconv.FormatInt(math.MinInt32-1, 10)).
+		To(iceberg.PrimitiveTypes.Int32)
+	require.NoError(t, err)
+	assert.Equal(t, iceberg.Int32BelowMinLiteral(), below)
+}