github.com/runner-mei/ql@v1.1.0/introspection.go (about)

     1  // Copyright 2014 The ql Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ql
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/ast"
    11  	"reflect"
    12  	"strings"
    13  	"sync"
    14  )
    15  
    16  var (
    17  	schemaCache = map[reflect.Type]*StructInfo{}
    18  	schemaMu    sync.RWMutex
    19  )
    20  
    21  // StructInfo describes a struct type. An instance of StructInfo obtained from
    22  // StructSchema is shared and must not be mutated.  That includes the values
    23  // pointed to by the elements of Fields and Indices.
    24  type StructInfo struct {
    25  	Fields  []*StructField // Fields describe the considered fields of a struct type.
    26  	HasID   bool           // Whether the struct has a considered field named ID of type int64.
    27  	Indices []*StructIndex // Indices describe indices defined by the index or uindex ql tags.
    28  	IsPtr   bool           // Whether the StructInfo was derived from a pointer to a struct.
    29  }
    30  
    31  // StructIndex describes an index defined by the ql tag index or uindex.
    32  type StructIndex struct {
    33  	ColumnName string // Name of the column the index is on.
    34  	Name       string // Name of the index.
    35  	Unique     bool   // Whether the index is unique.
    36  }
    37  
    38  // StructField describes a considered field of a struct type.
    39  type StructField struct {
    40  	Index         int               // Index is the index of the field for reflect.Value.Field.
    41  	IsID          bool              // Whether the field corresponds to record id().
    42  	IsPtr         bool              // Whether the field is a pointer type.
    43  	MarshalType   reflect.Type      // The reflect.Type a field must be converted to when marshaling or nil when it is assignable directly. (Field->value)
    44  	Name          string            // Field name or value of the name tag (like in `ql:"name foo"`).
    45  	ReflectType   reflect.Type      // The reflect.Type of the field.
    46  	Tags          map[string]string // QL tags of this field. (`ql:"a, b c, d"` -> {"a": "", "b": "c", "d": ""})
    47  	Type          Type              // QL type of the field.
    48  	UnmarshalType reflect.Type      // The reflect.Type a value must be converted to when unmarshaling or nil when it is assignable directly. (Field<-value)
    49  	ZeroPtr       reflect.Value     // The reflect.Zero value of the field if it's a pointer type.
    50  }
    51  
    52  func (s *StructField) check(v interface{}) error {
    53  	t := reflect.TypeOf(v)
    54  	if !s.ReflectType.AssignableTo(t) {
    55  		if !s.ReflectType.ConvertibleTo(t) {
    56  			return fmt.Errorf("type %s (%v) cannot be converted to %T", s.ReflectType.Name(), s.ReflectType.Kind(), t.Name())
    57  		}
    58  
    59  		s.MarshalType = t
    60  	}
    61  
    62  	if !t.AssignableTo(s.ReflectType) {
    63  		if !t.ConvertibleTo(s.ReflectType) {
    64  			return fmt.Errorf("type %s (%v) cannot be converted to %T", t.Name(), t.Kind(), s.ReflectType.Name())
    65  		}
    66  
    67  		s.UnmarshalType = s.ReflectType
    68  	}
    69  	return nil
    70  }
    71  
    72  func parseTag(s string) map[string]string {
    73  	m := map[string]string{}
    74  	for _, v := range strings.Split(s, ",") {
    75  		v = strings.TrimSpace(v)
    76  		switch n := strings.IndexRune(v, ' '); {
    77  		case n < 0:
    78  			m[v] = ""
    79  		default:
    80  			m[v[:n]] = v[n+1:]
    81  		}
    82  	}
    83  	return m
    84  }
    85  
    86  // StructSchema returns StructInfo for v which must be a struct instance or a
    87  // pointer to a struct.  The info is computed only once for every type.
    88  // Subsequent calls to StructSchema for the same type return a cached
    89  // StructInfo.
    90  //
    91  // Note: The returned StructSchema is shared and must be not mutated, including
    92  // any other data structures it may point to.
    93  func StructSchema(v interface{}) (*StructInfo, error) {
    94  	if v == nil {
    95  		return nil, fmt.Errorf("cannot derive schema for %T(%v)", v, v)
    96  	}
    97  
    98  	typ := reflect.TypeOf(v)
    99  	schemaMu.RLock()
   100  	if r, ok := schemaCache[typ]; ok {
   101  		schemaMu.RUnlock()
   102  		return r, nil
   103  	}
   104  
   105  	schemaMu.RUnlock()
   106  	var schemaPtr bool
   107  	t := typ
   108  	if t.Kind() == reflect.Ptr {
   109  		t = t.Elem()
   110  		schemaPtr = true
   111  	}
   112  	if k := t.Kind(); k != reflect.Struct {
   113  		return nil, fmt.Errorf("cannot derive schema for type %T (%v)", v, k)
   114  	}
   115  
   116  	r := &StructInfo{IsPtr: schemaPtr}
   117  	for i := 0; i < t.NumField(); i++ {
   118  		f := t.Field(i)
   119  		fn := f.Name
   120  		if !ast.IsExported(fn) {
   121  			continue
   122  		}
   123  
   124  		tags := parseTag(f.Tag.Get("ql"))
   125  		if _, ok := tags["-"]; ok {
   126  			continue
   127  		}
   128  
   129  		if s := tags["name"]; s != "" {
   130  			fn = s
   131  		}
   132  
   133  		if fn == "ID" && f.Type.Kind() == reflect.Int64 {
   134  			r.HasID = true
   135  		}
   136  		var ix, unique bool
   137  		var xn string
   138  		xfn := fn
   139  		if s := tags["index"]; s != "" {
   140  			if _, ok := tags["uindex"]; ok {
   141  				return nil, fmt.Errorf("both index and uindex in QL struct tag")
   142  			}
   143  
   144  			ix, xn = true, s
   145  		} else if s := tags["uindex"]; s != "" {
   146  			if _, ok := tags["index"]; ok {
   147  				return nil, fmt.Errorf("both index and uindex in QL struct tag")
   148  			}
   149  
   150  			ix, unique, xn = true, true, s
   151  		}
   152  		if ix {
   153  			if fn == "ID" && r.HasID {
   154  				xfn = "id()"
   155  			}
   156  			r.Indices = append(r.Indices, &StructIndex{Name: xn, ColumnName: xfn, Unique: unique})
   157  		}
   158  
   159  		sf := &StructField{Index: i, Name: fn, Tags: tags, Type: Type(-1), ReflectType: f.Type}
   160  		fk := sf.ReflectType.Kind()
   161  		if fk == reflect.Ptr {
   162  			sf.IsPtr = true
   163  			sf.ZeroPtr = reflect.Zero(sf.ReflectType)
   164  			sf.ReflectType = sf.ReflectType.Elem()
   165  			fk = sf.ReflectType.Kind()
   166  		}
   167  
   168  		switch fk {
   169  		case reflect.Bool:
   170  			sf.Type = Bool
   171  			if err := sf.check(false); err != nil {
   172  				return nil, err
   173  			}
   174  		case reflect.Int, reflect.Uint:
   175  			return nil, fmt.Errorf("only integers of fixed size can be used to derive a schema: %v", fk)
   176  		case reflect.Int8:
   177  			sf.Type = Int8
   178  			if err := sf.check(int8(0)); err != nil {
   179  				return nil, err
   180  			}
   181  		case reflect.Int16:
   182  			if err := sf.check(int16(0)); err != nil {
   183  				return nil, err
   184  			}
   185  			sf.Type = Int16
   186  		case reflect.Int32:
   187  			if err := sf.check(int32(0)); err != nil {
   188  				return nil, err
   189  			}
   190  			sf.Type = Int32
   191  		case reflect.Int64:
   192  			if sf.ReflectType.Name() == "Duration" && sf.ReflectType.PkgPath() == "time" {
   193  				sf.Type = Duration
   194  				break
   195  			}
   196  
   197  			sf.Type = Int64
   198  			if err := sf.check(int64(0)); err != nil {
   199  				return nil, err
   200  			}
   201  		case reflect.Uint8:
   202  			sf.Type = Uint8
   203  			if err := sf.check(uint8(0)); err != nil {
   204  				return nil, err
   205  			}
   206  		case reflect.Uint16:
   207  			sf.Type = Uint16
   208  			if err := sf.check(uint16(0)); err != nil {
   209  				return nil, err
   210  			}
   211  		case reflect.Uint32:
   212  			sf.Type = Uint32
   213  			if err := sf.check(uint32(0)); err != nil {
   214  				return nil, err
   215  			}
   216  		case reflect.Uint64:
   217  			sf.Type = Uint64
   218  			if err := sf.check(uint64(0)); err != nil {
   219  				return nil, err
   220  			}
   221  		case reflect.Float32:
   222  			sf.Type = Float32
   223  			if err := sf.check(float32(0)); err != nil {
   224  				return nil, err
   225  			}
   226  		case reflect.Float64:
   227  			sf.Type = Float64
   228  			if err := sf.check(float64(0)); err != nil {
   229  				return nil, err
   230  			}
   231  		case reflect.Complex64:
   232  			sf.Type = Complex64
   233  			if err := sf.check(complex64(0)); err != nil {
   234  				return nil, err
   235  			}
   236  		case reflect.Complex128:
   237  			sf.Type = Complex128
   238  			if err := sf.check(complex128(0)); err != nil {
   239  				return nil, err
   240  			}
   241  		case reflect.Slice:
   242  			sf.Type = Blob
   243  			if err := sf.check([]byte(nil)); err != nil {
   244  				return nil, err
   245  			}
   246  		case reflect.Struct:
   247  			switch sf.ReflectType.PkgPath() {
   248  			case "math/big":
   249  				switch sf.ReflectType.Name() {
   250  				case "Int":
   251  					sf.Type = BigInt
   252  				case "Rat":
   253  					sf.Type = BigRat
   254  				}
   255  			case "time":
   256  				switch sf.ReflectType.Name() {
   257  				case "Time":
   258  					sf.Type = Time
   259  				}
   260  			}
   261  		case reflect.String:
   262  			sf.Type = String
   263  			if err := sf.check(""); err != nil {
   264  				return nil, err
   265  			}
   266  		}
   267  
   268  		if sf.Type < 0 {
   269  			return nil, fmt.Errorf("cannot derive schema for type %s (%v)", sf.ReflectType.Name(), fk)
   270  		}
   271  
   272  		sf.IsID = fn == "ID" && r.HasID
   273  		r.Fields = append(r.Fields, sf)
   274  	}
   275  
   276  	schemaMu.Lock()
   277  	schemaCache[typ] = r
   278  	if t != typ {
   279  		r2 := *r
   280  		r2.IsPtr = false
   281  		schemaCache[t] = &r2
   282  	}
   283  	schemaMu.Unlock()
   284  	return r, nil
   285  }
   286  
   287  // MustStructSchema is like StructSchema but panics on error. It simplifies
   288  // safe initialization of global variables holding StructInfo.
   289  //
   290  // MustStructSchema is safe for concurrent use by multiple goroutines.
   291  func MustStructSchema(v interface{}) *StructInfo {
   292  	s, err := StructSchema(v)
   293  	if err != nil {
   294  		panic(err)
   295  	}
   296  
   297  	return s
   298  }
   299  
   300  // SchemaOptions amend the result of Schema.
   301  type SchemaOptions struct {
   302  	// Don't wrap the CREATE statement(s) in a transaction.
   303  	NoTransaction bool
   304  
   305  	// Don't insert the IF NOT EXISTS clause in the CREATE statement(s).
   306  	NoIfNotExists bool
   307  
   308  	// Do not strip the "pkg." part from type name "pkg.Type", produce
   309  	// "pkg_Type" table name instead. Applies only when no name is passed
   310  	// to Schema().
   311  	KeepPrefix bool
   312  }
   313  
   314  var zeroSchemaOptions SchemaOptions
   315  
   316  // Schema returns a CREATE TABLE/INDEX statement(s) for a table derived from a
   317  // struct or an error, if any.  The table is named using the name parameter. If
   318  // name is an empty string then the type name of the struct is used while non
   319  // conforming characters are replaced by underscores. Value v can be also a
   320  // pointer to a struct.
   321  //
   322  // Every considered struct field type must be one of the QL types or a type
   323  // convertible to string, bool, int*, uint*, float* or complex* type or pointer
   324  // to such type. Integers with a width dependent on the architecture can not be
   325  // used. Only exported fields are considered. If an exported field QL tag
   326  // contains "-" (`ql:"-"`) then such field is not considered. A field with name
   327  // ID, having type int64, corresponds to id() - and is thus not a part of the
   328  // CREATE statement. A field QL tag containing "index name" or "uindex name"
   329  // triggers additionally creating an index or unique index on the respective
   330  // field.  Fields can be renamed using a QL tag "name newName".  Fields are
   331  // considered in the order of appearance. A QL tag is a struct tag part
   332  // prefixed by "ql:". Tags can be combined, for example:
   333  //
   334  //	type T struct {
   335  //		Foo	string	`ql:"index xFoo, name Bar"`
   336  //	}
   337  //
   338  // If opts.NoTransaction == true then the statement(s) are not wrapped in a
   339  // transaction. If opt.NoIfNotExists == true then the CREATE statement(s) omits
   340  // the IF NOT EXISTS clause. Passing nil opts is equal to passing
   341  // &SchemaOptions{}
   342  //
   343  // Schema is safe for concurrent use by multiple goroutines.
   344  func Schema(v interface{}, name string, opt *SchemaOptions) (List, error) {
   345  	if opt == nil {
   346  		opt = &zeroSchemaOptions
   347  	}
   348  	s, err := StructSchema(v)
   349  	if err != nil {
   350  		return List{}, err
   351  	}
   352  
   353  	var buf bytes.Buffer
   354  	if !opt.NoTransaction {
   355  		buf.WriteString("BEGIN TRANSACTION; ")
   356  	}
   357  	buf.WriteString("CREATE TABLE ")
   358  	if !opt.NoIfNotExists {
   359  		buf.WriteString("IF NOT EXISTS ")
   360  	}
   361  	if name == "" {
   362  		name = fmt.Sprintf("%T", v)
   363  		if !opt.KeepPrefix {
   364  			a := strings.Split(name, ".")
   365  			if l := len(a); l > 1 {
   366  				name = a[l-1]
   367  			}
   368  		}
   369  		nm := []rune{}
   370  		for _, v := range name {
   371  			switch {
   372  			case v >= '0' && v <= '9' || v == '_' || v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z':
   373  				// ok
   374  			default:
   375  				v = '_'
   376  			}
   377  			nm = append(nm, v)
   378  		}
   379  		name = string(nm)
   380  	}
   381  	buf.WriteString(name + " (")
   382  	for _, v := range s.Fields {
   383  		if v.IsID {
   384  			continue
   385  		}
   386  
   387  		buf.WriteString(fmt.Sprintf("%s %s, ", v.Name, v.Type))
   388  	}
   389  	buf.WriteString("); ")
   390  	for _, v := range s.Indices {
   391  		buf.WriteString("CREATE ")
   392  		if v.Unique {
   393  			buf.WriteString("UNIQUE ")
   394  		}
   395  		buf.WriteString("INDEX ")
   396  		if !opt.NoIfNotExists {
   397  			buf.WriteString("IF NOT EXISTS ")
   398  		}
   399  		buf.WriteString(fmt.Sprintf("%s ON %s (%s); ", v.Name, name, v.ColumnName))
   400  	}
   401  	if !opt.NoTransaction {
   402  		buf.WriteString("COMMIT; ")
   403  	}
   404  	l, err := Compile(buf.String())
   405  	if err != nil {
   406  		return List{}, fmt.Errorf("%s: %v", buf.String(), err)
   407  	}
   408  
   409  	return l, nil
   410  }
   411  
   412  // MustSchema is like Schema but panics on error. It simplifies safe
   413  // initialization of global variables holding compiled schemas.
   414  //
   415  // MustSchema is safe for concurrent use by multiple goroutines.
   416  func MustSchema(v interface{}, name string, opt *SchemaOptions) List {
   417  	l, err := Schema(v, name, opt)
   418  	if err != nil {
   419  		panic(err)
   420  	}
   421  
   422  	return l
   423  }
   424  
   425  // Marshal converts, in the order of appearance, fields of a struct instance v
   426  // to []interface{} or an error, if any. Value v can be also a pointer to a
   427  // struct.
   428  //
   429  // Every considered struct field type must be one of the QL types or a type
   430  // convertible to string, bool, int*, uint*, float* or complex* type or pointer
   431  // to such type. Integers with a width dependent on the architecture can not be
   432  // used. Only exported fields are considered. If an exported field QL tag
   433  // contains "-" then such field is not considered. A QL tag is a struct tag
   434  // part prefixed by "ql:".  Field with name ID, having type int64, corresponds
   435  // to id() - and is thus not part of the result.
   436  //
   437  // Marshal is safe for concurrent use by multiple goroutines.
   438  func Marshal(v interface{}) ([]interface{}, error) {
   439  	s, err := StructSchema(v)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  
   444  	val := reflect.ValueOf(v)
   445  	if s.IsPtr {
   446  		val = val.Elem()
   447  	}
   448  	n := len(s.Fields)
   449  	if s.HasID {
   450  		n--
   451  	}
   452  	r := make([]interface{}, n)
   453  	j := 0
   454  	for _, v := range s.Fields {
   455  		if v.IsID {
   456  			continue
   457  		}
   458  
   459  		f := val.Field(v.Index)
   460  		if v.IsPtr {
   461  			if f.IsNil() {
   462  				r[j] = nil
   463  				j++
   464  				continue
   465  			}
   466  
   467  			f = f.Elem()
   468  		}
   469  		if m := v.MarshalType; m != nil {
   470  			f = f.Convert(m)
   471  		}
   472  		r[j] = f.Interface()
   473  		j++
   474  	}
   475  	return r, nil
   476  }
   477  
   478  // MustMarshal is like Marshal but panics on error. It simplifies marshaling of
   479  // "safe" types, like eg. those which were already verified by Schema or
   480  // MustSchema.  When the underlying Marshal returns an error, MustMarshal
   481  // panics.
   482  //
   483  // MustMarshal is safe for concurrent use by multiple goroutines.
   484  func MustMarshal(v interface{}) []interface{} {
   485  	r, err := Marshal(v)
   486  	if err != nil {
   487  		panic(err)
   488  	}
   489  
   490  	return r
   491  }
   492  
   493  // Unmarshal stores data from []interface{} in the struct value pointed to by
   494  // v.
   495  //
   496  // Every considered struct field type must be one of the QL types or a type
   497  // convertible to string, bool, int*, uint*, float* or complex* type or pointer
   498  // to such type. Integers with a width dependent on the architecture can not be
   499  // used. Only exported fields are considered. If an exported field QL tag
   500  // contains "-" then such field is not considered. A QL tag is a struct tag
   501  // part prefixed by "ql:".  Fields are considered in the order of appearance.
   502  // Types of values in data must be compatible with the corresponding considered
   503  // field of v.
   504  //
   505  // If the struct has no ID field then the number of values in data must be equal
   506  // to the number of considered fields of v.
   507  //
   508  //	type T struct {
   509  //		A bool
   510  //		B string
   511  //	}
   512  //
   513  // Assuming the schema is
   514  //
   515  //	CREATE TABLE T (A bool, B string);
   516  //
   517  // Data might be a result of queries like
   518  //
   519  //	SELECT * FROM T;
   520  //	SELECT A, B FROM T;
   521  //
   522  // If the struct has a considered ID field then the number of values in data
   523  // must be equal to the number of considered fields in v - or one less. In the
   524  // later case the ID field is not set.
   525  //
   526  //	type U struct {
   527  //		ID int64
   528  //		A  bool
   529  //		B  string
   530  //	}
   531  //
   532  // Assuming the schema is
   533  //
   534  //	CREATE TABLE T (A bool, B string);
   535  //
   536  // Data might be a result of queries like
   537  //
   538  //	SELECT * FROM T;             // ID not set
   539  //	SELECT A, B FROM T;          // ID not set
   540  //	SELECT id(), A, B FROM T;    // ID is set
   541  //
   542  // To unmarshal a value from data into a pointer field of v, Unmarshal first
   543  // handles the case of the value being nil. In that case, Unmarshal sets the
   544  // pointer to nil. Otherwise, Unmarshal unmarshals the data value into value
   545  // pointed at by the pointer. If the pointer is nil, Unmarshal allocates a new
   546  // value for it to point to.
   547  //
   548  // Unmarshal is safe for concurrent use by multiple goroutines.
   549  func Unmarshal(v interface{}, data []interface{}) (err error) {
   550  	defer func() {
   551  		if r := recover(); r != nil {
   552  			var ok bool
   553  			if err, ok = r.(error); !ok {
   554  				err = fmt.Errorf("%v", r)
   555  			}
   556  			err = fmt.Errorf("unmarshal: %v", err)
   557  		}
   558  	}()
   559  
   560  	s, err := StructSchema(v)
   561  	if err != nil {
   562  		return err
   563  	}
   564  
   565  	if !s.IsPtr {
   566  		return fmt.Errorf("unmarshal: need a pointer to a struct")
   567  	}
   568  
   569  	id := false
   570  	nv, nf := len(data), len(s.Fields)
   571  	switch s.HasID {
   572  	case true:
   573  		switch {
   574  		case nv == nf:
   575  			id = true
   576  		case nv == nf-1:
   577  			// ok
   578  		default:
   579  			return fmt.Errorf("unmarshal: got %d values, need %d or %d", nv, nf-1, nf)
   580  		}
   581  	default:
   582  		switch {
   583  		case nv == nf:
   584  			// ok
   585  		default:
   586  			return fmt.Errorf("unmarshal: got %d values, need %d", nv, nf)
   587  		}
   588  	}
   589  
   590  	j := 0
   591  	vVal := reflect.ValueOf(v)
   592  	if s.IsPtr {
   593  		vVal = vVal.Elem()
   594  	}
   595  	for _, sf := range s.Fields {
   596  		if sf.IsID && !id {
   597  			continue
   598  		}
   599  
   600  		d := data[j]
   601  		val := reflect.ValueOf(d)
   602  		j++
   603  
   604  		fVal := vVal.Field(sf.Index)
   605  		if u := sf.UnmarshalType; u != nil {
   606  			val = val.Convert(u)
   607  		}
   608  		if !sf.IsPtr {
   609  			fVal.Set(val)
   610  			continue
   611  		}
   612  
   613  		if d == nil {
   614  			fVal.Set(sf.ZeroPtr)
   615  			continue
   616  		}
   617  
   618  		if fVal.IsNil() {
   619  			fVal.Set(reflect.New(sf.ReflectType))
   620  		}
   621  
   622  		fVal.Elem().Set(val)
   623  	}
   624  	return nil
   625  }