@@ -775,3 +775,107 @@ func TestCreateAndManageChangeStream(t *testing.T) {
775775 t .Errorf ("Generated SQL statement incorrect.\n got %v\n want %v" , got , want )
776776 }
777777}
778+
779+ func TestUUIDColumn (t * testing.T ) {
780+ uuidTable := & spansql.CreateTable {
781+ Name : "Tasks" ,
782+ Columns : []spansql.ColumnDef {
783+ {Name : "TaskId" , Type : spansql.Type {Base : spansql .UUID }, NotNull : true },
784+ {Name : "Data" , Type : spansql.Type {Base : spansql .String }},
785+ },
786+ PrimaryKey : []spansql.KeyPart {{Column : "TaskId" }},
787+ }
788+
789+ var db database
790+ if st := db .ApplyDDL (uuidTable ); st .Code () != codes .OK {
791+ t .Fatalf ("Creating table: %v" , st .Err ())
792+ }
793+
794+ tx := db .NewTransaction ()
795+ tx .Start ()
796+ uuidVal := "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"
797+ upperUUIDVal := "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A22"
798+ normalizedUpperUUIDVal := "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22"
799+
800+ err := db .Insert (tx , "Tasks" , []spansql.ID {"TaskId" , "Data" }, []* structpb.ListValue {
801+ listV (stringV (uuidVal ), stringV ("some-data" )),
802+ listV (stringV (upperUUIDVal ), stringV ("upper-data" )),
803+ })
804+ if err != nil {
805+ t .Fatalf ("Inserting data: %v" , err )
806+ }
807+ if _ , err := tx .Commit (); err != nil {
808+ t .Fatalf ("Committing changes: %v" , err )
809+ }
810+
811+ queryTests := []struct {
812+ name string
813+ sql string
814+ want [][]interface {}
815+ }{
816+ {
817+ name : "all rows" ,
818+ sql : `SELECT * FROM Tasks` ,
819+ want : [][]interface {}{
820+ {uuidVal , "some-data" },
821+ {normalizedUpperUUIDVal , "upper-data" },
822+ },
823+ },
824+ {
825+ name : "uppercase literal filter for uppercase-inserted row" ,
826+ sql : `SELECT * FROM Tasks WHERE TaskId = 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A22'` ,
827+ want : [][]interface {}{
828+ {normalizedUpperUUIDVal , "upper-data" },
829+ },
830+ },
831+ {
832+ name : "lowercase literal filter for uppercase-inserted row" ,
833+ sql : `SELECT * FROM Tasks WHERE TaskId = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a22'` ,
834+ want : [][]interface {}{
835+ {normalizedUpperUUIDVal , "upper-data" },
836+ },
837+ },
838+ {
839+ name : "uppercase literal filter for lowercase-inserted row" ,
840+ sql : `SELECT * FROM Tasks WHERE TaskId = 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'` ,
841+ want : [][]interface {}{
842+ {uuidVal , "some-data" },
843+ },
844+ },
845+ }
846+
847+ for _ , tc := range queryTests {
848+ q , err := spansql .ParseQuery (tc .sql )
849+ if err != nil {
850+ t .Fatalf ("ParseQuery(%s) [%s]: %v" , tc .sql , tc .name , err )
851+ }
852+ ri , err := db .Query (q , nil )
853+ if err != nil {
854+ t .Fatalf ("Query(%s) [%s]: %v" , tc .sql , tc .name , err )
855+ }
856+ got := slurp (t , ri )
857+ if ! reflect .DeepEqual (got , tc .want ) {
858+ t .Errorf ("[%s] Query(%s) got %v, want %v" , tc .name , tc .sql , got , tc .want )
859+ }
860+ }
861+
862+ // Test invalid UUID insertions
863+ invalidUUIDs := []string {
864+ "not-a-uuid" ,
865+ "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a1" , // too short (35 chars)
866+ "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a111" , // too long (37 chars)
867+ "a0eebc999c0b4ef8bb6d6bb9bd380a11" , // missing hyphens
868+ "g0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" , // non-hex char
869+ }
870+ for _ , badUUID := range invalidUUIDs {
871+ txBad := db .NewTransaction ()
872+ txBad .Start ()
873+ errBad := db .Insert (txBad , "Tasks" , []spansql.ID {"TaskId" , "Data" }, []* structpb.ListValue {
874+ listV (stringV (badUUID ), stringV ("bad" )),
875+ })
876+ if errBad == nil {
877+ t .Errorf ("db.Insert succeeded for invalid UUID %q, want error" , badUUID )
878+ }
879+ txBad .Rollback ()
880+ }
881+ }
0 commit comments