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  }