github.com/aacfactory/fns-contrib/databases/sql@v1.2.84/dac/specifications/view.go (about) 1 package specifications 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/aacfactory/errors" 7 "reflect" 8 "strings" 9 ) 10 11 type ViewInfo struct { 12 pure bool 13 name string 14 schema string 15 base any 16 } 17 18 func MaybeView(e any) (ok bool) { 19 rv := reflect.Indirect(reflect.ValueOf(e)) 20 rt := rv.Type() 21 _, ok = rt.MethodByName("ViewInfo") 22 return 23 } 24 25 func GetViewInfo(e any) (info ViewInfo, err error) { 26 rv := reflect.Indirect(reflect.ValueOf(e)) 27 rt := rv.Type() 28 // info 29 _, hasInfoFunc := rt.MethodByName("ViewInfo") 30 if !hasInfoFunc { 31 err = errors.Warning(fmt.Sprintf("sql: %s.%s has not ViewInfo func", rt.PkgPath(), rt.Name())) 32 return 33 } 34 infoFunc := rv.MethodByName("ViewInfo") 35 results := infoFunc.Call(nil) 36 if len(results) != 1 { 37 err = errors.Warning(fmt.Sprintf("sql: %s.%s has invalid ViewInfo func", rt.PkgPath(), rt.Name())) 38 return 39 } 40 result := results[0] 41 // pure 42 _, hasPureFunc := result.Type().MethodByName("Pure") 43 if !hasPureFunc { 44 err = errors.Warning(fmt.Sprintf("sql: %s.%s has not ViewInfo func", rt.PkgPath(), rt.Name())) 45 return 46 } 47 nameResults := result.MethodByName("Pure").Call(nil) 48 if len(nameResults) != 3 && nameResults[0].Type().Kind() != reflect.String && nameResults[1].Type().Kind() != reflect.String && nameResults[2].Type().Kind() != reflect.Bool { 49 err = errors.Warning(fmt.Sprintf("sql: %s.%s has invalid ViewInfo func", rt.PkgPath(), rt.Name())) 50 return 51 } 52 schema := nameResults[0].String() 53 name := nameResults[1].String() 54 pure := nameResults[2].Bool() 55 if pure { 56 info = ViewInfo{ 57 pure: pure, 58 name: strings.TrimSpace(name), 59 schema: strings.TrimSpace(schema), 60 base: nil, 61 } 62 return 63 } 64 // base 65 _, hasBaseFunc := result.Type().MethodByName("Base") 66 if !hasBaseFunc { 67 err = errors.Warning(fmt.Sprintf("sql: %s.%s has not ViewInfo func", rt.PkgPath(), rt.Name())) 68 return 69 } 70 baseResults := result.MethodByName("Base").Call(nil) 71 if len(baseResults) != 1 && nameResults[0].Type().Kind() != reflect.Interface { 72 err = errors.Warning(fmt.Sprintf("sql: %s.%s has invalid ViewInfo func", rt.PkgPath(), rt.Name())) 73 return 74 } 75 base := baseResults[0].Interface() 76 info = ViewInfo{ 77 pure: false, 78 name: "", 79 schema: "", 80 base: base, 81 } 82 return 83 } 84 85 func ScanView(ctx context.Context, view any) (spec *Specification, err error) { 86 rv := reflect.Indirect(reflect.ValueOf(view)) 87 rt := rv.Type() 88 key := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) 89 info, infoErr := GetViewInfo(view) 90 if infoErr != nil { 91 err = errors.Warning("sql: scan view failed"). 92 WithCause(infoErr). 93 WithMeta("struct", key) 94 return 95 } 96 if info.pure { 97 name := info.name 98 if name == "" { 99 err = errors.Warning("sql: scan view failed"). 100 WithCause(fmt.Errorf("table name is required")). 101 WithMeta("struct", rt.String()) 102 return 103 } 104 schema := info.schema 105 columns, columnsErr := scanTableFields(ctx, fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()), rt) 106 if columnsErr != nil { 107 err = errors.Warning("sql: scan view failed"). 108 WithCause(columnsErr). 109 WithMeta("struct", reflect.TypeOf(view).String()) 110 return 111 } 112 spec = &Specification{ 113 Key: key, 114 Schema: schema, 115 Name: name, 116 View: true, 117 Type: rt, 118 Columns: columns, 119 } 120 tableNames := make([]string, 0, 1) 121 if schema != "" { 122 tableNames = append(tableNames, schema) 123 } 124 tableNames = append(tableNames, name) 125 dict.Set(fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()), tableNames...) 126 } else { 127 base, baseErr := GetSpecification(ctx, info.base) 128 if baseErr != nil { 129 err = errors.Warning("sql: scan view failed"). 130 WithCause(baseErr). 131 WithMeta("struct", reflect.TypeOf(view).String()) 132 return 133 } 134 columns, columnsErr := scanTableFields(ctx, fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()), rt) 135 if columnsErr != nil { 136 err = errors.Warning("sql: scan view failed"). 137 WithCause(columnsErr). 138 WithMeta("struct", reflect.TypeOf(view).String()) 139 return 140 } 141 spec = &Specification{ 142 Key: key, 143 Schema: base.Schema, 144 Name: base.Name, 145 View: true, 146 ViewBase: base, 147 Type: rt, 148 Columns: columns, 149 } 150 tableNames := make([]string, 0, 1) 151 if base.Schema != "" { 152 tableNames = append(tableNames, base.Schema) 153 } 154 tableNames = append(tableNames, base.Name) 155 dict.Set(fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()), tableNames...) 156 } 157 return 158 }