This is an automated email from the ASF dual-hosted git repository.

fokko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 3c37b10  feat: Adding literals (#76)
3c37b10 is described below

commit 3c37b100213c4a78d17ce443ce0978a78b724759
Author: Matt Topol <[email protected]>
AuthorDate: Tue May 28 10:57:53 2024 -0400

    feat: Adding literals (#76)
    
    * feat: Adding literals
    
    * implementing feedback
---
 errors.go        |   3 +
 go.mod           |  47 +++-
 go.sum           | 105 +++++---
 literals.go      | 780 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 literals_test.go | 729 +++++++++++++++++++++++++++++++++++++++++++++++++++
 types.go         |  14 +
 utils.go         |   5 +-
 7 files changed, 1635 insertions(+), 48 deletions(-)

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 046ee7b..5accc3f 100644
--- a/go.mod
+++ b/go.mod
@@ -20,31 +20,35 @@ module github.com/apache/iceberg-go
 go 1.21
 
 require (
+       github.com/apache/arrow/go/v16 v16.0.0
        github.com/aws/aws-sdk-go-v2 v1.27.0
-       github.com/aws/aws-sdk-go-v2/config v1.27.15
-       github.com/aws/aws-sdk-go-v2/credentials v1.17.15
-       github.com/aws/aws-sdk-go-v2/service/glue v1.81.1
-       github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3
+       github.com/aws/aws-sdk-go-v2/config v1.26.6
+       github.com/aws/aws-sdk-go-v2/credentials v1.17.12
+       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.6.0
-       github.com/hamba/avro/v2 v2.22.0
-       github.com/pterm/pterm v0.12.79
+       github.com/hamba/avro/v2 v2.21.1
+       github.com/pterm/pterm v0.12.78
        github.com/stretchr/testify v1.9.0
        github.com/wolfeidau/s3iofs v1.5.2
-       golang.org/x/exp v0.0.0-20231006140011-7918f672742d
+       golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
 )
 
 require (
        atomicgo.dev/cursor v0.2.0 // indirect
        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.6.2 // indirect
-       github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect
+       github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c // 
indirect
+       github.com/andybalholm/brotli v1.1.0 // indirect
+       github.com/apache/thrift v0.20.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.16.1 // 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.3.7 // indirect
+       github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // 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.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
@@ -54,22 +58,37 @@ require (
        github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 // 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/asmfmt v1.3.2 // indirect
        github.com/klauspost/compress v1.17.8 // indirect
+       github.com/klauspost/cpuid/v2 v2.2.7 // indirect
        github.com/lithammer/fuzzysearch v1.1.8 // indirect
        github.com/mattn/go-runewidth v0.0.15 // indirect
+       github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // 
indirect
+       github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
        github.com/mitchellh/mapstructure v1.5.0 // indirect
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 
indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
+       github.com/pierrec/lz4/v4 v4.1.21 // indirect
        github.com/pmezard/go-difflib v1.0.0 // indirect
        github.com/rivo/uniseg v0.4.4 // indirect
        github.com/stretchr/objx v0.5.2 // indirect
        github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
-       golang.org/x/net v0.23.0 // indirect
-       golang.org/x/sys v0.18.0 // indirect
-       golang.org/x/term v0.18.0 // indirect
+       github.com/zeebo/xxh3 v1.0.2 // indirect
+       golang.org/x/mod v0.17.0 // indirect
+       golang.org/x/net v0.24.0 // indirect
+       golang.org/x/sync v0.7.0 // indirect
+       golang.org/x/sys v0.20.0 // indirect
+       golang.org/x/term v0.19.0 // indirect
        golang.org/x/text v0.14.0 // indirect
+       golang.org/x/tools v0.20.0 // indirect
+       golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240227224415-6ceb2ff114de // indirect
+       google.golang.org/grpc v1.63.2 // indirect
+       google.golang.org/protobuf v1.34.1 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 289b8ed..8f16c6d 100644
--- a/go.sum
+++ b/go.sum
@@ -6,6 +6,8 @@ atomicgo.dev/keyboard v0.2.9 
h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
 atomicgo.dev/keyboard v0.2.9/go.mod 
h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
 atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
 atomicgo.dev/schedule v0.1.0/go.mod 
h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
+github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c 
h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
+github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod 
h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
 github.com/MarvinJWendt/testza v0.1.0/go.mod 
h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
 github.com/MarvinJWendt/testza v0.2.1/go.mod 
h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
 github.com/MarvinJWendt/testza v0.2.8/go.mod 
h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
@@ -15,27 +17,33 @@ 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/andybalholm/brotli v1.1.0 
h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod 
h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/apache/arrow/go/v16 v16.0.0 
h1:qRLbJRPj4zaseZrjbDHa7mUoZDDIU+4pu+mE2Lucs5g=
+github.com/apache/arrow/go/v16 v16.0.0/go.mod 
h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA=
+github.com/apache/thrift v0.20.0 
h1:631+KvYbsBZxmuJjYwhezVsrfc/TbqtZV4QcxOX1fOI=
+github.com/apache/thrift v0.20.0/go.mod 
h1:hOk1BQqcp2OLzGsyVXdfMk7YFlMxK3aoEVhjD06QhB8=
 github.com/atomicgo/cursor v0.0.1/go.mod 
h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
 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.6.2 
h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod 
h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg=
-github.com/aws/aws-sdk-go-v2/config v1.27.15 
h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc=
-github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod 
h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.15 
h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod 
h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU=
-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/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.6 
h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o=
+github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod 
h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.12 
h1:PVbKQ0KjDosI5+nEdRMU8ygEQDmkJTSHBqPjEX30lqc=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.12/go.mod 
h1:jlWtGFRtKsqc5zqerHZYmKmRkUXo3KPM14YJ13ZEjwE=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 
h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod 
h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
 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.3.7 
h1:/FUtT3xsoHO3cfh+I/kCbcMCN98QZRsiFet/V8QkWSs=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7/go.mod 
h1:MaCAgWpGooQoCWZnMur97rGn5dp350w2+CeiV5406wE=
-github.com/aws/aws-sdk-go-v2/service/glue v1.81.1 
h1:+rtgF25rrZ5O5J7k8u3xQcFWAmMQxQWvcyaz1waFj38=
-github.com/aws/aws-sdk-go-v2/service/glue v1.81.1/go.mod 
h1:NVgCBCXL2/W4WJbFhKUGATTBiWKkYuZyvuh7BsFvZzE=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 
h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod 
h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
+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.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=
@@ -44,8 +52,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url 
v1.11.9 h1:Wx0rlZoEJ
 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.54.3 
h1:57NtjG+WLims0TxIQbjTqebZUKDM03DfM11ANAekW0s=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.54.3/go.mod 
h1:739CllldowZiPPsDFcJHNF4FXrVxaSGVnZ9Ez9Iz9hc=
+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.20.8 
h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks=
 github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod 
h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA=
 github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 
h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ=
@@ -61,8 +69,14 @@ 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/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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -70,27 +84,34 @@ github.com/gookit/color v1.4.2/go.mod 
h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQ
 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.22.0 
h1:IaBMFv5xmjo38f0oaP9jZiJFXg+lmHPPg7d9YotMnPg=
-github.com/hamba/avro/v2 v2.22.0/go.mod 
h1:HOeTrE3kvWnBAgsufqhAzDDV5gvS0QXs65Z6BHfGgbg=
+github.com/hamba/avro/v2 v2.21.1 
h1:400/jTdLWQ3ib58y83VXlTJKijRouYQszY1SO0cMGt4=
+github.com/hamba/avro/v2 v2.21.1/go.mod 
h1:ouJ4PkiAEP49u0lAtQyd5Gv04MehKj+7lXwD3zpLpY0=
 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/asmfmt v1.3.2 
h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
+github.com/klauspost/asmfmt v1.3.2/go.mod 
h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
 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=
 github.com/mattn/go-runewidth v0.0.15 
h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod 
h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 
h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=
+github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod 
h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
+github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 
h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=
+github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod 
h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
 github.com/mitchellh/mapstructure v1.5.0 
h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod 
h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -98,6 +119,8 @@ github.com/modern-go/concurrent 
v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.2 
h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod 
h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pierrec/lz4/v4 v4.1.21 
h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
+github.com/pierrec/lz4/v4 v4.1.21/go.mod 
h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
 github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pterm/pterm v0.12.27/go.mod 
h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
@@ -107,8 +130,8 @@ 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.79 h1:lH3yrYMhdpeqX9y5Ep1u7DejyHy7NSQg9qrBjF9dFT4=
-github.com/pterm/pterm v0.12.79/go.mod 
h1:1v/gzOF1N0FsjbgTHZ1wVycRkKiatFvJSJC4IGaQAAo=
+github.com/pterm/pterm v0.12.78 h1:QTWKaIAa4B32GKwqVXtu9m1DUMgWw3VRljMkMevX+b8=
+github.com/pterm/pterm v0.12.78/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=
@@ -129,21 +152,29 @@ github.com/xo/terminfo 
v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1z
 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/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
+github.com/zeebo/assert v1.3.0/go.mod 
h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
+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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 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=
@@ -154,15 +185,15 @@ 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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.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.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod 
h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod 
h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
 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=
@@ -174,7 +205,19 @@ golang.org/x/tools 
v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
 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.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
+golang.org/x/tools v0.20.0/go.mod 
h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
 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=
+gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
+gonum.org/v1/gonum v0.15.0/go.mod 
h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de 
h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20240227224415-6ceb2ff114de/go.mod 
h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
+google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
+google.golang.org/grpc v1.63.2/go.mod 
h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/protobuf v1.34.1 
h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod 
h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 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/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)
+}
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")
        }

Reply via email to