If you're like me and you like to use reflect to automatically generate boilerplate code, you might want to extend your sqlc Queries instance with these two methods, which can come in handy if you want to generate files starting from the queries that you have (which is what I do in querygen and protoschema)
Last active
July 7, 2025 14:53
-
-
Save Rick-Phoenix/47cde03b600819734d36bdf9d7025f01 to your computer and use it in GitHub Desktop.
Extending sqlc queries with reflection metadata for easier file generation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| func (q *Queries) GetPkg() string { | |
| if q == nil { | |
| return "" | |
| } | |
| return reflect.TypeOf(q).Elem().PkgPath() | |
| } | |
| type QueryData struct { | |
| Name string | |
| ParamName string | |
| Params map[string]string | |
| ReturnTypes []string | |
| ReturnFields map[string]string | |
| IsErr bool | |
| SliceReturn bool | |
| IsResult bool | |
| } | |
| func (q *Queries) ExtractMethods() map[string]*QueryData { | |
| output := make(map[string]*QueryData) | |
| model := reflect.TypeOf(q) | |
| ignoredMethods := []string{"WithTx", "ExtractMethods", "GetPkg"} | |
| for i := range model.NumMethod() { | |
| method := model.Method(i) | |
| data := &QueryData{ | |
| Params: make(map[string]string), | |
| ReturnFields: make(map[string]string), | |
| } | |
| if slices.Contains(ignoredMethods, method.Name) { | |
| continue | |
| } | |
| data.Name = method.Name | |
| if method.Type.NumOut() == 1 { | |
| data.IsErr = true | |
| data.ReturnTypes = append(data.ReturnTypes, "error") | |
| } else { | |
| firstReturn := method.Type.Out(0) | |
| if firstReturn == reflect.TypeOf((*sql.Result)(nil)).Elem() { | |
| data.IsResult = true | |
| data.ReturnTypes = append(data.ReturnTypes, "sql.Result") | |
| } else { | |
| var target reflect.Type | |
| if firstReturn.Kind() == reflect.Slice { | |
| data.SliceReturn = true | |
| target = firstReturn.Elem().Elem() | |
| } else if firstReturn.Kind() == reflect.Pointer { | |
| target = firstReturn.Elem() | |
| } | |
| if target != nil && target.Kind() == reflect.Struct { | |
| for i := range target.NumField() { | |
| field := target.Field(i) | |
| data.ReturnFields[field.Name] = field.Type.Name() | |
| } | |
| } | |
| data.ReturnTypes = append(data.ReturnTypes, target.Name()) | |
| data.ReturnTypes = append(data.ReturnTypes, "error") | |
| } | |
| } | |
| if method.Type.NumIn() > 2 { | |
| queryParam := method.Type.In(2) | |
| data.ParamName = queryParam.Name() | |
| for i := range queryParam.NumField() { | |
| field := queryParam.Field(i) | |
| data.Params[field.Name] = field.Type.Name() | |
| } | |
| } | |
| output[data.Name] = data | |
| } | |
| return output | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment