From 459eaca8660b9f2e28005e602dc8b91fd0a63010 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 15 Jan 2026 22:53:05 -0500 Subject: [PATCH 01/10] poc for sql null types --- cmd/main.go | 40 ++++++++++++++++++++++++++++++++++++++++ lazy.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 cmd/main.go diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..e14516b --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + + "code.jakeyoungdev.com/go/lazy" +) + +func main() { + + // res, err := lazy.RandomStruct(lazy.StructConfig{ + // Query: "insert into test (one, two) values (?, ?), (?, ?)", + // Example: test{}, + // RowCount: 2, + // }) + + x, y := lazy.RandomGenerate(lazy.Config{ + Query: "select * from table", + Example: lazy.Test{}, + RowCount: 1, + }) + if y != nil { + panic(y) + } + fmt.Printf("%+v", x) + + // if err != nil { + // panic(err) + // } + + // fmt.Println(res) + + // t, ok := res.MockStructs[0].(test) + // if !ok { + // fmt.Println("errrrrrr") + // } + + // fmt.Println(t.One) + // fmt.Println(t.Two) +} diff --git a/lazy.go b/lazy.go index 4a7bcc4..7fe7707 100644 --- a/lazy.go +++ b/lazy.go @@ -1,6 +1,7 @@ package lazy import ( + "database/sql" "database/sql/driver" "errors" "fmt" @@ -11,6 +12,10 @@ import ( "github.com/jmoiron/sqlx" ) +type Test struct { + Timer sql.NullTime `db:"timer"` +} + const ( DB_TAG = "db" //tag used to parse sql fields in example struct LAZY_TAG = "lazy" //tag label for KEY_VALUE @@ -34,6 +39,7 @@ type Config struct { // generates mock data based on configuration to be used for sqlmock func RandomGenerate(m Config) (*Mock, error) { + fmt.Println("huh") //example struct cannot be nil and must be a struct if m.Example == nil { return nil, errors.New("example value cannot be nil") @@ -81,9 +87,33 @@ func RandomGenerate(m Config) (*Mock, error) { continue } + //attempt other types //generate random values nv := kindToRandom(field) if nv == nil { + //this will catch the sql.Null* types, although not slices of them. In these statements it should be + //50/50 chance of null vs value and then psuedo random from there + //1. Write simple 50/50 function + //2. move this logic to nullKindToRandom function + //3. add bind type option to allow for more than just AT now that I'm using postgres + switch field.Type { + case reflect.TypeOf(sql.NullTime{}): + // + case reflect.TypeOf(sql.NullInt16{}): + // + case reflect.TypeOf(sql.NullInt32{}): + // + case reflect.TypeOf(sql.NullInt64{}): + // + case reflect.TypeOf(sql.NullBool{}): + // + case reflect.TypeOf(sql.NullFloat64{}): + // + case reflect.TypeOf(sql.NullString{}): + // + case reflect.TypeOf(sql.NullByte{}): + // + } return nil, fmt.Errorf("could not match type: %s", retType.Name()) } From 4ec738b130bed5e65625031e590069fcc818d348 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 16 Jan 2026 10:18:24 -0500 Subject: [PATCH 02/10] type and bind updates - using sql nullTypes - adding additional bind type option --- lazy.go | 87 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/lazy.go b/lazy.go index 7fe7707..a0d54e0 100644 --- a/lazy.go +++ b/lazy.go @@ -8,6 +8,7 @@ import ( "math/rand/v2" "reflect" "regexp" + "time" "github.com/jmoiron/sqlx" ) @@ -20,8 +21,15 @@ const ( DB_TAG = "db" //tag used to parse sql fields in example struct LAZY_TAG = "lazy" //tag label for KEY_VALUE KEY_VALUE = "key" //tag value for LAZY_TAG + //bind values + BindQuestion Binds = 1 + BindDollar Binds = 2 + BindNamed Binds = 3 + BindAt Binds = 4 ) +type Binds int + // mock data generated based on config type Mock struct { Query string @@ -35,11 +43,11 @@ type Config struct { Example any Keys []any //length of keys must = RowCount, if set RowCount int + BindType Binds } // generates mock data based on configuration to be used for sqlmock func RandomGenerate(m Config) (*Mock, error) { - fmt.Println("huh") //example struct cannot be nil and must be a struct if m.Example == nil { return nil, errors.New("example value cannot be nil") @@ -91,30 +99,62 @@ func RandomGenerate(m Config) (*Mock, error) { //generate random values nv := kindToRandom(field) if nv == nil { - //this will catch the sql.Null* types, although not slices of them. In these statements it should be - //50/50 chance of null vs value and then psuedo random from there - //1. Write simple 50/50 function - //2. move this logic to nullKindToRandom function - //3. add bind type option to allow for more than just AT now that I'm using postgres + //move this logic to nullKindToRandom function + nullType := false switch field.Type { case reflect.TypeOf(sql.NullTime{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], time.Now()) + } + nullType = true case reflect.TypeOf(sql.NullInt16{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], rand.Int()) + } + nullType = true case reflect.TypeOf(sql.NullInt32{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], rand.Int32()) + } + nullType = true case reflect.TypeOf(sql.NullInt64{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], rand.Int64()) + } + nullType = true case reflect.TypeOf(sql.NullBool{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], rand.Int()%2 == 0) + } + nullType = true case reflect.TypeOf(sql.NullFloat64{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], rand.Float64()) + } + nullType = true case reflect.TypeOf(sql.NullString{}): - // - case reflect.TypeOf(sql.NullByte{}): - // + if isNull() { + rows[y] = append(rows[y], nil) + } else { + rows[y] = append(rows[y], fmt.Sprintf("random %d", rand.Int())) + } + nullType = true + } + if !nullType { + return nil, fmt.Errorf("could not match type: %s", retType.Name()) } - return nil, fmt.Errorf("could not match type: %s", retType.Name()) } rows[y] = append(rows[y], nv) @@ -123,7 +163,7 @@ func RandomGenerate(m Config) (*Mock, error) { return &Mock{ //sql is rebound and escaped for sqlmock.ExpectQuery - Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(m.Query)), + Query: sqlx.Rebind(int(m.BindType), regexp.QuoteMeta(m.Query)), Columns: columns, Rows: rows, }, nil @@ -144,9 +184,11 @@ func kindToRandom(field reflect.StructField) any { case reflect.Float64: return rand.Float64() case reflect.String: - return fmt.Sprintf("%d", rand.Int()) + return fmt.Sprintf("random %d", rand.Int()) case reflect.Bool: return rand.Int()%2 == 0 + case reflect.TypeOf(time.Time{}).Kind(): + return time.Now() case reflect.Array: underlying := field.Type.Elem().Kind() count := reflect.ValueOf(field).Len() //fill entire length @@ -245,3 +287,12 @@ func kindToRandom(field reflect.StructField) any { return nil } + +func isNull() bool { + x := rand.IntN(2) + if x%2 == 0 { + return true + } else { + return false + } +} From 7ce271bdb731c7d78292b100c70e9f9d16d40ae5 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 16 Jan 2026 10:27:57 -0500 Subject: [PATCH 03/10] [test] Adding test case for nulls - ensuring nulls work - will be undone --- lazy.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lazy.go b/lazy.go index a0d54e0..b3d2a37 100644 --- a/lazy.go +++ b/lazy.go @@ -103,11 +103,11 @@ func RandomGenerate(m Config) (*Mock, error) { nullType := false switch field.Type { case reflect.TypeOf(sql.NullTime{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], time.Now()) - } + // if isNull() { + rows[y] = append(rows[y], nil) + // } else { + // rows[y] = append(rows[y], time.Now()) + // } nullType = true case reflect.TypeOf(sql.NullInt16{}): if isNull() { From b4428e397e756be77bf5e946bba91ed99f0090e6 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 16 Jan 2026 10:31:40 -0500 Subject: [PATCH 04/10] using proper struct for nulltypes --- lazy.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lazy.go b/lazy.go index b3d2a37..7fe33ad 100644 --- a/lazy.go +++ b/lazy.go @@ -103,11 +103,14 @@ func RandomGenerate(m Config) (*Mock, error) { nullType := false switch field.Type { case reflect.TypeOf(sql.NullTime{}): - // if isNull() { - rows[y] = append(rows[y], nil) - // } else { - // rows[y] = append(rows[y], time.Now()) - // } + var temp sql.NullTime + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Time = time.Now() + } + rows[y] = append(rows[y], temp) nullType = true case reflect.TypeOf(sql.NullInt16{}): if isNull() { From 7fd2f60168caac0792c4af4d0e82c4dab3a0b077 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 16 Jan 2026 10:34:05 -0500 Subject: [PATCH 05/10] simplifying logic --- lazy.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lazy.go b/lazy.go index 7fe33ad..6677270 100644 --- a/lazy.go +++ b/lazy.go @@ -104,9 +104,7 @@ func RandomGenerate(m Config) (*Mock, error) { switch field.Type { case reflect.TypeOf(sql.NullTime{}): var temp sql.NullTime - if isNull() { - temp.Valid = false - } else { + if !isNull() { temp.Valid = true temp.Time = time.Now() } From e8ceac6f88ca6c8be2207cc4c96f0b6d0292af9a Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 16 Jan 2026 15:59:44 -0500 Subject: [PATCH 06/10] added checks for sql null types --- cmd/main.go | 8 +++- lazy.go | 132 +++++++++++++++++++++++++++------------------------- 2 files changed, 76 insertions(+), 64 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index e14516b..3988d8c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,13 +1,19 @@ package main import ( + "database/sql" "fmt" + "time" "code.jakeyoungdev.com/go/lazy" ) func main() { + type Test struct { + Timer sql.NullTime `db:"timer"` + TestTime time.Time `db:"times"` + } // res, err := lazy.RandomStruct(lazy.StructConfig{ // Query: "insert into test (one, two) values (?, ?), (?, ?)", // Example: test{}, @@ -16,7 +22,7 @@ func main() { x, y := lazy.RandomGenerate(lazy.Config{ Query: "select * from table", - Example: lazy.Test{}, + Example: Test{}, RowCount: 1, }) if y != nil { diff --git a/lazy.go b/lazy.go index 6677270..ac0c17a 100644 --- a/lazy.go +++ b/lazy.go @@ -13,10 +13,6 @@ import ( "github.com/jmoiron/sqlx" ) -type Test struct { - Timer sql.NullTime `db:"timer"` -} - const ( DB_TAG = "db" //tag used to parse sql fields in example struct LAZY_TAG = "lazy" //tag label for KEY_VALUE @@ -95,67 +91,9 @@ func RandomGenerate(m Config) (*Mock, error) { continue } - //attempt other types - //generate random values nv := kindToRandom(field) if nv == nil { - //move this logic to nullKindToRandom function - nullType := false - switch field.Type { - case reflect.TypeOf(sql.NullTime{}): - var temp sql.NullTime - if !isNull() { - temp.Valid = true - temp.Time = time.Now() - } - rows[y] = append(rows[y], temp) - nullType = true - case reflect.TypeOf(sql.NullInt16{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], rand.Int()) - } - nullType = true - case reflect.TypeOf(sql.NullInt32{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], rand.Int32()) - } - nullType = true - case reflect.TypeOf(sql.NullInt64{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], rand.Int64()) - } - nullType = true - case reflect.TypeOf(sql.NullBool{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], rand.Int()%2 == 0) - } - nullType = true - case reflect.TypeOf(sql.NullFloat64{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], rand.Float64()) - } - nullType = true - case reflect.TypeOf(sql.NullString{}): - if isNull() { - rows[y] = append(rows[y], nil) - } else { - rows[y] = append(rows[y], fmt.Sprintf("random %d", rand.Int())) - } - nullType = true - } - if !nullType { - return nil, fmt.Errorf("could not match type: %s", retType.Name()) - } + return nil, fmt.Errorf("could not match type: %s", retType.Name()) } rows[y] = append(rows[y], nv) @@ -172,6 +110,72 @@ func RandomGenerate(m Config) (*Mock, error) { // converts basic reflect Kind's to psuedo-random data, slices are given a random amount of entries func kindToRandom(field reflect.StructField) any { + //this isn't ideal, but since the sql types are structs they were being filled in a weird + //manner. For now we check those types first, clean this up later. + switch field.Type { + case reflect.TypeOf(sql.NullTime{}): + fmt.Println("found nulltime") + var temp sql.NullTime + if !isNull() { + temp.Valid = true + temp.Time = time.Now() + } + return temp + case reflect.TypeOf(sql.NullInt16{}): + var temp sql.NullInt16 + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Int16 = int16(rand.Int()) + } + return temp + case reflect.TypeOf(sql.NullInt32{}): + var temp sql.NullInt32 + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Int32 = rand.Int32() + } + return temp + case reflect.TypeOf(sql.NullInt64{}): + var temp sql.NullInt64 + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Int64 = rand.Int64() + } + return temp + case reflect.TypeOf(sql.NullBool{}): + var temp sql.NullBool + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Bool = (rand.Int()%2 == 0) + } + return temp + case reflect.TypeOf(sql.NullFloat64{}): + var temp sql.NullFloat64 + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.Float64 = rand.Float64() + } + return temp + case reflect.TypeOf(sql.NullString{}): + var temp sql.NullString + if isNull() { + temp.Valid = false + } else { + temp.Valid = true + temp.String = fmt.Sprintf("random %d", rand.Int()) + } + return temp + } kind := field.Type.Kind() switch kind { case reflect.Int: @@ -187,8 +191,10 @@ func kindToRandom(field reflect.StructField) any { case reflect.String: return fmt.Sprintf("random %d", rand.Int()) case reflect.Bool: + fmt.Println("THIS TWO") return rand.Int()%2 == 0 case reflect.TypeOf(time.Time{}).Kind(): + fmt.Println("THIS?") return time.Now() case reflect.Array: underlying := field.Type.Elem().Kind() From 8f950e38b8d9d0807738319aabcd3538bce750ce Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 23 Jan 2026 14:52:29 -0500 Subject: [PATCH 07/10] comment updated --- lazy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lazy.go b/lazy.go index ac0c17a..3750850 100644 --- a/lazy.go +++ b/lazy.go @@ -111,7 +111,7 @@ func RandomGenerate(m Config) (*Mock, error) { // converts basic reflect Kind's to psuedo-random data, slices are given a random amount of entries func kindToRandom(field reflect.StructField) any { //this isn't ideal, but since the sql types are structs they were being filled in a weird - //manner. For now we check those types first, clean this up later. + //manner. For now we check those types first, and will clean this up later. switch field.Type { case reflect.TypeOf(sql.NullTime{}): fmt.Println("found nulltime") From 9719cde88b8ea7c1dccd1d2d220ea2b004cf8ea0 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 23 Jan 2026 14:54:28 -0500 Subject: [PATCH 08/10] cleanup - removing test main - adding files to gitignore to prevent future pushes --- .gitignore | 8 +------- cmd/main.go | 46 ---------------------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) delete mode 100644 cmd/main.go diff --git a/.gitignore b/.gitignore index 5b90e79..f3b4020 100644 --- a/.gitignore +++ b/.gitignore @@ -15,13 +15,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Dependency directories (remove the comment below to include it) -# vendor/ - -# Go workspace file -go.work -go.work.sum - # env file .env +cmd/* \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 3988d8c..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - "time" - - "code.jakeyoungdev.com/go/lazy" -) - -func main() { - - type Test struct { - Timer sql.NullTime `db:"timer"` - TestTime time.Time `db:"times"` - } - // res, err := lazy.RandomStruct(lazy.StructConfig{ - // Query: "insert into test (one, two) values (?, ?), (?, ?)", - // Example: test{}, - // RowCount: 2, - // }) - - x, y := lazy.RandomGenerate(lazy.Config{ - Query: "select * from table", - Example: Test{}, - RowCount: 1, - }) - if y != nil { - panic(y) - } - fmt.Printf("%+v", x) - - // if err != nil { - // panic(err) - // } - - // fmt.Println(res) - - // t, ok := res.MockStructs[0].(test) - // if !ok { - // fmt.Println("errrrrrr") - // } - - // fmt.Println(t.One) - // fmt.Println(t.Two) -} From 88e67d69fcc4f10c841d01cffddc5da70fb67ef7 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 23 Jan 2026 14:56:28 -0500 Subject: [PATCH 09/10] removing debug prints --- lazy.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lazy.go b/lazy.go index 3750850..40006f8 100644 --- a/lazy.go +++ b/lazy.go @@ -114,7 +114,6 @@ func kindToRandom(field reflect.StructField) any { //manner. For now we check those types first, and will clean this up later. switch field.Type { case reflect.TypeOf(sql.NullTime{}): - fmt.Println("found nulltime") var temp sql.NullTime if !isNull() { temp.Valid = true @@ -191,10 +190,8 @@ func kindToRandom(field reflect.StructField) any { case reflect.String: return fmt.Sprintf("random %d", rand.Int()) case reflect.Bool: - fmt.Println("THIS TWO") return rand.Int()%2 == 0 case reflect.TypeOf(time.Time{}).Kind(): - fmt.Println("THIS?") return time.Now() case reflect.Array: underlying := field.Type.Elem().Kind() @@ -280,7 +277,7 @@ func kindToRandom(field reflect.StructField) any { case reflect.String: var strslice []string for x := 0; x < count; x++ { - strslice = append(strslice, fmt.Sprintf("%d", rand.Int())) + strslice = append(strslice, fmt.Sprintf("random %d", rand.Int())) } return strslice case reflect.Bool: From f67a02a9fe51f83b2b7051430f86abc4a3021eb1 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 23 Jan 2026 14:58:51 -0500 Subject: [PATCH 10/10] adding comments --- lazy.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lazy.go b/lazy.go index 40006f8..db5bc7e 100644 --- a/lazy.go +++ b/lazy.go @@ -292,6 +292,8 @@ func kindToRandom(field reflect.StructField) any { return nil } +// a quick and dirty rng function to determine if nulltypes are null or +// if we will generate a random value func isNull() bool { x := rand.IntN(2) if x%2 == 0 {