From c9135b9823ce529872c0d9d0c566aec5e827ed00 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 28 Jan 2025 11:51:14 +0000 Subject: [PATCH] UUID version and timestamp. --- ext/uuid/uuid.go | 32 +++++++++++++++++++++++++++++++- ext/uuid/uuid_test.go | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/ext/uuid/uuid.go b/ext/uuid/uuid.go index 4111cbda..9db1304a 100644 --- a/ext/uuid/uuid.go +++ b/ext/uuid/uuid.go @@ -7,6 +7,7 @@ import ( "bytes" "errors" "fmt" + "time" "github.com/google/uuid" @@ -35,7 +36,9 @@ func Register(db *sqlite3.Conn) error { db.CreateFunction("uuid", 2, sqlite3.INNOCUOUS, generate), db.CreateFunction("uuid", 3, sqlite3.INNOCUOUS, generate), db.CreateFunction("uuid_str", 1, flags, toString), - db.CreateFunction("uuid_blob", 1, flags, toBlob)) + db.CreateFunction("uuid_blob", 1, flags, toBlob), + db.CreateFunction("uuid_extract_version", 1, flags, version), + db.CreateFunction("uuid_extract_timestamp", 1, flags, timestamp)) } func generate(ctx sqlite3.Context, arg ...sqlite3.Value) { @@ -167,3 +170,30 @@ func toString(ctx sqlite3.Context, arg ...sqlite3.Value) { ctx.ResultText(u.String()) } } + +func version(ctx sqlite3.Context, arg ...sqlite3.Value) { + u, err := fromValue(arg[0]) + if err != nil { + ctx.ResultError(err) + return // notest + } + if u.Variant() == uuid.RFC4122 { + ctx.ResultInt64(int64(u.Version())) + } +} + +func timestamp(ctx sqlite3.Context, arg ...sqlite3.Value) { + u, err := fromValue(arg[0]) + if err != nil { + ctx.ResultError(err) + return // notest + } + if u.Variant() == uuid.RFC4122 { + switch u.Version() { + case 1, 2, 6, 7: + ctx.ResultTime( + time.Unix(u.Time().UnixTime()), + sqlite3.TimeFormatDefault) + } + } +} diff --git a/ext/uuid/uuid_test.go b/ext/uuid/uuid_test.go index acb04999..ae5a5798 100644 --- a/ext/uuid/uuid_test.go +++ b/ext/uuid/uuid_test.go @@ -2,6 +2,7 @@ package uuid import ( "testing" + "time" "github.com/google/uuid" @@ -106,7 +107,26 @@ func Test_generate(t *testing.T) { t.Error("want error") } - hash := []struct { + var tstamp time.Time + var version uuid.Version + err = db.QueryRow(` + SELECT + column1, + uuid_extract_version(column1), + uuid_extract_timestamp(column1) + FROM (VALUES (uuid(7))) + `).Scan(&u, &version, &tstamp) + if err != nil { + t.Fatal(err) + } + if got := u.Version(); got != version { + t.Errorf("got %d, want %d", got, version) + } + if got := time.Unix(u.Time().UnixTime()); !got.Equal(tstamp) { + t.Errorf("got %v, want %v", got, tstamp) + } + + tests := []struct { ver uuid.Version ns any data string @@ -120,7 +140,7 @@ func Test_generate(t *testing.T) { {3, "url", "https://www.php.net", uuid.MustParse("3f703955-aaba-3e70-a3cb-baff6aa3b28f")}, {5, "url", "https://www.php.net", uuid.MustParse("a8f6ae40-d8a7-58f0-be05-a22f94eca9ec")}, } - for _, tt := range hash { + for _, tt := range tests { err = db.QueryRow(`SELECT uuid(?, ?, ?)`, tt.ver, tt.ns, tt.data).Scan(&u) if err != nil { t.Fatal(err) @@ -142,14 +162,14 @@ func Test_convert(t *testing.T) { defer db.Close() var u uuid.UUID - lits := []string{ + tests := []string{ "'6ba7b8119dad11d180b400c04fd430c8'", "'6ba7b811-9dad-11d1-80b4-00c04fd430c8'", "'{6ba7b811-9dad-11d1-80b4-00c04fd430c8}'", "X'6ba7b8119dad11d180b400c04fd430c8'", } - for _, tt := range lits { + for _, tt := range tests { err = db.QueryRow(`SELECT uuid_str(` + tt + `)`).Scan(&u) if err != nil { t.Fatal(err) @@ -159,7 +179,7 @@ func Test_convert(t *testing.T) { } } - for _, tt := range lits { + for _, tt := range tests { err = db.QueryRow(`SELECT uuid_blob(` + tt + `)`).Scan(&u) if err != nil { t.Fatal(err) @@ -178,4 +198,14 @@ func Test_convert(t *testing.T) { if err == nil { t.Fatal("want error") } + + err = db.QueryRow(`SELECT uuid_extract_version(X'cafe')`).Scan(&u) + if err == nil { + t.Fatal("want error") + } + + err = db.QueryRow(`SELECT uuid_extract_timestamp(X'cafe')`).Scan(&u) + if err == nil { + t.Fatal("want error") + } }