github.com/gogf/gf@v1.16.9/database/gdb/gdb_z_mysql_internal_test.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gdb
     8  
     9  import (
    10  	"fmt"
    11  	"github.com/gogf/gf/container/gvar"
    12  	"github.com/gogf/gf/os/gcmd"
    13  	"github.com/gogf/gf/os/gtime"
    14  	"github.com/gogf/gf/test/gtest"
    15  	"testing"
    16  )
    17  
    18  const (
    19  	SCHEMA     = "test_internal"
    20  	TestDbUser = "root"
    21  	TestDbPass = "12345678"
    22  )
    23  
    24  var (
    25  	db         DB
    26  	configNode ConfigNode
    27  )
    28  
    29  func init() {
    30  	parser, err := gcmd.Parse(map[string]bool{
    31  		"name": true,
    32  		"type": true,
    33  	}, false)
    34  	gtest.AssertNil(err)
    35  	configNode = ConfigNode{
    36  		Host:             "127.0.0.1",
    37  		Port:             "3306",
    38  		User:             TestDbUser,
    39  		Pass:             TestDbPass,
    40  		Name:             parser.GetOpt("name", ""),
    41  		Type:             parser.GetOpt("type", "mysql"),
    42  		Role:             "master",
    43  		Charset:          "utf8",
    44  		Weight:           1,
    45  		MaxIdleConnCount: 10,
    46  		MaxOpenConnCount: 10,
    47  		MaxConnLifeTime:  600,
    48  	}
    49  	AddConfigNode(DefaultGroupName, configNode)
    50  	// Default db.
    51  	if r, err := New(); err != nil {
    52  		gtest.Error(err)
    53  	} else {
    54  		db = r
    55  	}
    56  	schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8"
    57  	if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA)); err != nil {
    58  		gtest.Error(err)
    59  	}
    60  	db.SetSchema(SCHEMA)
    61  }
    62  
    63  func dropTable(table string) {
    64  	if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
    65  		gtest.Error(err)
    66  	}
    67  }
    68  
    69  func Test_Func_FormatSqlWithArgs(t *testing.T) {
    70  	// mysql
    71  	gtest.C(t, func(t *gtest.T) {
    72  		var s string
    73  		s = FormatSqlWithArgs("select * from table where id>=? and sex=?", []interface{}{100, 1})
    74  		t.Assert(s, "select * from table where id>=100 and sex=1")
    75  	})
    76  	// mssql
    77  	gtest.C(t, func(t *gtest.T) {
    78  		var s string
    79  		s = FormatSqlWithArgs("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1})
    80  		t.Assert(s, "select * from table where id>=100 and sex=1")
    81  	})
    82  	// pgsql
    83  	gtest.C(t, func(t *gtest.T) {
    84  		var s string
    85  		s = FormatSqlWithArgs("select * from table where id>=$1 and sex=$2", []interface{}{100, 1})
    86  		t.Assert(s, "select * from table where id>=100 and sex=1")
    87  	})
    88  	// oracle
    89  	gtest.C(t, func(t *gtest.T) {
    90  		var s string
    91  		s = FormatSqlWithArgs("select * from table where id>=:v1 and sex=:v2", []interface{}{100, 1})
    92  		t.Assert(s, "select * from table where id>=100 and sex=1")
    93  	})
    94  }
    95  
    96  func Test_Func_doQuoteWord(t *testing.T) {
    97  	gtest.C(t, func(t *gtest.T) {
    98  		array := map[string]string{
    99  			"user":                   "`user`",
   100  			"user u":                 "user u",
   101  			"user_detail":            "`user_detail`",
   102  			"user,user_detail":       "user,user_detail",
   103  			"user u, user_detail ut": "user u, user_detail ut",
   104  			"u.id asc":               "u.id asc",
   105  			"u.id asc, ut.uid desc":  "u.id asc, ut.uid desc",
   106  		}
   107  		for k, v := range array {
   108  			t.Assert(doQuoteWord(k, "`", "`"), v)
   109  		}
   110  	})
   111  }
   112  
   113  func Test_Func_doQuoteString(t *testing.T) {
   114  	gtest.C(t, func(t *gtest.T) {
   115  		array := map[string]string{
   116  			"user":                             "`user`",
   117  			"user u":                           "`user` u",
   118  			"user,user_detail":                 "`user`,`user_detail`",
   119  			"user u, user_detail ut":           "`user` u,`user_detail` ut",
   120  			"u.id, u.name, u.age":              "`u`.`id`,`u`.`name`,`u`.`age`",
   121  			"u.id asc":                         "`u`.`id` asc",
   122  			"u.id asc, ut.uid desc":            "`u`.`id` asc,`ut`.`uid` desc",
   123  			"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
   124  			// mssql global schema access with double dots.
   125  			"user..user u, user.user_detail ut": "`user`..`user` u,`user`.`user_detail` ut",
   126  		}
   127  		for k, v := range array {
   128  			t.Assert(doQuoteString(k, "`", "`"), v)
   129  		}
   130  	})
   131  }
   132  
   133  func Test_Func_addTablePrefix(t *testing.T) {
   134  	gtest.C(t, func(t *gtest.T) {
   135  		prefix := ""
   136  		array := map[string]string{
   137  			"user":                         "`user`",
   138  			"user u":                       "`user` u",
   139  			"user as u":                    "`user` as u",
   140  			"user,user_detail":             "`user`,`user_detail`",
   141  			"user u, user_detail ut":       "`user` u,`user_detail` ut",
   142  			"`user`.user_detail":           "`user`.`user_detail`",
   143  			"`user`.`user_detail`":         "`user`.`user_detail`",
   144  			"user as u, user_detail as ut": "`user` as u,`user_detail` as ut",
   145  			"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`user` as u,`UserCenter`.`user_detail` as ut",
   146  			// mssql global schema access with double dots.
   147  			"UserCenter..user as u, user_detail as ut": "`UserCenter`..`user` as u,`user_detail` as ut",
   148  		}
   149  		for k, v := range array {
   150  			t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
   151  		}
   152  	})
   153  	gtest.C(t, func(t *gtest.T) {
   154  		prefix := "gf_"
   155  		array := map[string]string{
   156  			"user":                         "`gf_user`",
   157  			"user u":                       "`gf_user` u",
   158  			"user as u":                    "`gf_user` as u",
   159  			"user,user_detail":             "`gf_user`,`gf_user_detail`",
   160  			"user u, user_detail ut":       "`gf_user` u,`gf_user_detail` ut",
   161  			"`user`.user_detail":           "`user`.`gf_user_detail`",
   162  			"`user`.`user_detail`":         "`user`.`gf_user_detail`",
   163  			"user as u, user_detail as ut": "`gf_user` as u,`gf_user_detail` as ut",
   164  			"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`gf_user` as u,`UserCenter`.`gf_user_detail` as ut",
   165  			// mssql global schema access with double dots.
   166  			"UserCenter..user as u, user_detail as ut": "`UserCenter`..`gf_user` as u,`gf_user_detail` as ut",
   167  		}
   168  		for k, v := range array {
   169  			t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
   170  		}
   171  	})
   172  }
   173  
   174  func Test_Model_getSoftFieldName(t *testing.T) {
   175  	table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
   176  	if _, err := db.Exec(fmt.Sprintf(`
   177  CREATE TABLE %s (
   178    id        int(11) NOT NULL,
   179    name      varchar(45) DEFAULT NULL,
   180    create_at datetime DEFAULT NULL,
   181    update_at datetime DEFAULT NULL,
   182    delete_at datetime DEFAULT NULL,
   183    PRIMARY KEY (id)
   184  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
   185      `, table1)); err != nil {
   186  		gtest.Error(err)
   187  	}
   188  	defer dropTable(table1)
   189  
   190  	table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
   191  	if _, err := db.Exec(fmt.Sprintf(`
   192  CREATE TABLE %s (
   193    id        int(11) NOT NULL,
   194    name      varchar(45) DEFAULT NULL,
   195    createat datetime DEFAULT NULL,
   196    updateat datetime DEFAULT NULL,
   197    deleteat datetime DEFAULT NULL,
   198    PRIMARY KEY (id)
   199  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
   200      `, table2)); err != nil {
   201  		gtest.Error(err)
   202  	}
   203  	defer dropTable(table2)
   204  
   205  	gtest.C(t, func(t *gtest.T) {
   206  		model := db.Model(table1)
   207  		gtest.Assert(model.getSoftFieldNameCreated(table2), "createat")
   208  		gtest.Assert(model.getSoftFieldNameUpdated(table2), "updateat")
   209  		gtest.Assert(model.getSoftFieldNameDeleted(table2), "deleteat")
   210  	})
   211  }
   212  
   213  func Test_Model_getConditionForSoftDeleting(t *testing.T) {
   214  	table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
   215  	if _, err := db.Exec(fmt.Sprintf(`
   216  CREATE TABLE %s (
   217    id1        int(11) NOT NULL,
   218    name1      varchar(45) DEFAULT NULL,
   219    create_at datetime DEFAULT NULL,
   220    update_at datetime DEFAULT NULL,
   221    delete_at datetime DEFAULT NULL,
   222    PRIMARY KEY (id1)
   223  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
   224      `, table1)); err != nil {
   225  		gtest.Error(err)
   226  	}
   227  	defer dropTable(table1)
   228  
   229  	table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
   230  	if _, err := db.Exec(fmt.Sprintf(`
   231  CREATE TABLE %s (
   232    id2        int(11) NOT NULL,
   233    name2      varchar(45) DEFAULT NULL,
   234    createat datetime DEFAULT NULL,
   235    updateat datetime DEFAULT NULL,
   236    deleteat datetime DEFAULT NULL,
   237    PRIMARY KEY (id2)
   238  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
   239      `, table2)); err != nil {
   240  		gtest.Error(err)
   241  	}
   242  	defer dropTable(table2)
   243  
   244  	gtest.C(t, func(t *gtest.T) {
   245  		model := db.Model(table1)
   246  		t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
   247  	})
   248  	gtest.C(t, func(t *gtest.T) {
   249  		model := db.Model(fmt.Sprintf(`%s as t`, table1))
   250  		t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
   251  	})
   252  	gtest.C(t, func(t *gtest.T) {
   253  		model := db.Model(fmt.Sprintf(`%s, %s`, table1, table2))
   254  		t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
   255  			"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
   256  			table1, table2,
   257  		))
   258  	})
   259  	gtest.C(t, func(t *gtest.T) {
   260  		model := db.Model(fmt.Sprintf(`%s t1, %s as t2`, table1, table2))
   261  		t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
   262  	})
   263  	gtest.C(t, func(t *gtest.T) {
   264  		model := db.Model(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2))
   265  		t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
   266  	})
   267  	gtest.C(t, func(t *gtest.T) {
   268  		model := db.Model(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1")
   269  		t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
   270  	})
   271  	gtest.C(t, func(t *gtest.T) {
   272  		model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1")
   273  		t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
   274  			"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
   275  			table1, table2,
   276  		))
   277  	})
   278  	gtest.C(t, func(t *gtest.T) {
   279  		model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1")
   280  		t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
   281  			"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL AND `%s`.`deleteat` IS NULL",
   282  			table1, table2, table2,
   283  		))
   284  	})
   285  	gtest.C(t, func(t *gtest.T) {
   286  		model := db.Model(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1")
   287  		t.Assert(
   288  			model.getConditionForSoftDeleting(),
   289  			"`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL AND `t3`.`deleteat` IS NULL",
   290  		)
   291  	})
   292  }
   293  
   294  func Test_isSubQuery(t *testing.T) {
   295  	gtest.C(t, func(t *gtest.T) {
   296  		t.Assert(isSubQuery("user"), false)
   297  		t.Assert(isSubQuery("user.uid"), false)
   298  		t.Assert(isSubQuery("u, user.uid"), false)
   299  		t.Assert(isSubQuery("select 1"), true)
   300  	})
   301  }
   302  
   303  func TestResult_Structs1(t *testing.T) {
   304  	type A struct {
   305  		Id int `orm:"id"`
   306  	}
   307  	type B struct {
   308  		*A
   309  		Name string
   310  	}
   311  	gtest.C(t, func(t *gtest.T) {
   312  		r := Result{
   313  			Record{"id": gvar.New(nil), "name": gvar.New("john")},
   314  			Record{"id": gvar.New(nil), "name": gvar.New("smith")},
   315  		}
   316  		array := make([]*B, 2)
   317  		err := r.Structs(&array)
   318  		t.AssertNil(err)
   319  		t.Assert(array[0].Id, 0)
   320  		t.Assert(array[1].Id, 0)
   321  		t.Assert(array[0].Name, "john")
   322  		t.Assert(array[1].Name, "smith")
   323  	})
   324  }
   325  
   326  // https://github.com/gogf/gf/issues/1159
   327  func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
   328  	gtest.C(t, func(t *gtest.T) {
   329  		type S1 struct {
   330  			Id    int
   331  			Name  string
   332  			Age   int
   333  			Score int
   334  		}
   335  		type S3 struct {
   336  			One *S1
   337  		}
   338  		var (
   339  			s   []*S3
   340  			err error
   341  		)
   342  		r1 := Result{
   343  			Record{
   344  				"id":   gvar.New(1),
   345  				"name": gvar.New("john"),
   346  				"age":  gvar.New(16),
   347  			},
   348  			Record{
   349  				"id":   gvar.New(2),
   350  				"name": gvar.New("smith"),
   351  				"age":  gvar.New(18),
   352  			},
   353  		}
   354  		err = r1.ScanList(&s, "One")
   355  		t.AssertNil(err)
   356  		t.Assert(len(s), 2)
   357  		t.Assert(s[0].One.Name, "john")
   358  		t.Assert(s[0].One.Age, 16)
   359  		t.Assert(s[1].One.Name, "smith")
   360  		t.Assert(s[1].One.Age, 18)
   361  
   362  		r2 := Result{
   363  			Record{
   364  				"id":  gvar.New(1),
   365  				"age": gvar.New(20),
   366  			},
   367  			Record{
   368  				"id":  gvar.New(2),
   369  				"age": gvar.New(21),
   370  			},
   371  		}
   372  		err = r2.ScanList(&s, "One", "One", "id:Id")
   373  		t.AssertNil(err)
   374  		t.Assert(len(s), 2)
   375  		t.Assert(s[0].One.Name, "john")
   376  		t.Assert(s[0].One.Age, 20)
   377  		t.Assert(s[1].One.Name, "smith")
   378  		t.Assert(s[1].One.Age, 21)
   379  	})
   380  }
   381  
   382  // https://github.com/gogf/gf/issues/1159
   383  func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
   384  	gtest.C(t, func(t *gtest.T) {
   385  		type S1 struct {
   386  			Id    int
   387  			Name  string
   388  			Age   int
   389  			Score int
   390  		}
   391  		type S3 struct {
   392  			One S1
   393  		}
   394  		var (
   395  			s   []*S3
   396  			err error
   397  		)
   398  		r1 := Result{
   399  			Record{
   400  				"id":   gvar.New(1),
   401  				"name": gvar.New("john"),
   402  				"age":  gvar.New(16),
   403  			},
   404  			Record{
   405  				"id":   gvar.New(2),
   406  				"name": gvar.New("smith"),
   407  				"age":  gvar.New(18),
   408  			},
   409  		}
   410  		err = r1.ScanList(&s, "One")
   411  		t.AssertNil(err)
   412  		t.Assert(len(s), 2)
   413  		t.Assert(s[0].One.Name, "john")
   414  		t.Assert(s[0].One.Age, 16)
   415  		t.Assert(s[1].One.Name, "smith")
   416  		t.Assert(s[1].One.Age, 18)
   417  
   418  		r2 := Result{
   419  			Record{
   420  				"id":  gvar.New(1),
   421  				"age": gvar.New(20),
   422  			},
   423  			Record{
   424  				"id":  gvar.New(2),
   425  				"age": gvar.New(21),
   426  			},
   427  		}
   428  		err = r2.ScanList(&s, "One", "One", "id:Id")
   429  		t.AssertNil(err)
   430  		t.Assert(len(s), 2)
   431  		t.Assert(s[0].One.Name, "john")
   432  		t.Assert(s[0].One.Age, 20)
   433  		t.Assert(s[1].One.Name, "smith")
   434  		t.Assert(s[1].One.Age, 21)
   435  	})
   436  }
   437  
   438  // https://github.com/gogf/gf/issues/1159
   439  func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
   440  	gtest.C(t, func(t *gtest.T) {
   441  		type S1 struct {
   442  			Id    int
   443  			Name  string
   444  			Age   int
   445  			Score int
   446  		}
   447  		type S2 struct {
   448  			Id    int
   449  			Pid   int
   450  			Name  string
   451  			Age   int
   452  			Score int
   453  		}
   454  		type S3 struct {
   455  			One  *S1
   456  			Many []*S2
   457  		}
   458  		var (
   459  			s   []*S3
   460  			err error
   461  		)
   462  		r1 := Result{
   463  			Record{
   464  				"id":   gvar.New(1),
   465  				"name": gvar.New("john"),
   466  				"age":  gvar.New(16),
   467  			},
   468  			Record{
   469  				"id":   gvar.New(2),
   470  				"name": gvar.New("smith"),
   471  				"age":  gvar.New(18),
   472  			},
   473  		}
   474  		err = r1.ScanList(&s, "One")
   475  		t.AssertNil(err)
   476  		t.Assert(len(s), 2)
   477  		t.Assert(s[0].One.Name, "john")
   478  		t.Assert(s[0].One.Age, 16)
   479  		t.Assert(s[1].One.Name, "smith")
   480  		t.Assert(s[1].One.Age, 18)
   481  
   482  		r2 := Result{
   483  			Record{
   484  				"id":   gvar.New(100),
   485  				"pid":  gvar.New(1),
   486  				"age":  gvar.New(30),
   487  				"name": gvar.New("john"),
   488  			},
   489  			Record{
   490  				"id":   gvar.New(200),
   491  				"pid":  gvar.New(1),
   492  				"age":  gvar.New(31),
   493  				"name": gvar.New("smith"),
   494  			},
   495  		}
   496  		err = r2.ScanList(&s, "Many", "One", "pid:Id")
   497  		//fmt.Printf("%+v", err)
   498  		t.AssertNil(err)
   499  		t.Assert(len(s), 2)
   500  		t.Assert(s[0].One.Name, "john")
   501  		t.Assert(s[0].One.Age, 16)
   502  		t.Assert(len(s[0].Many), 2)
   503  		t.Assert(s[0].Many[0].Name, "john")
   504  		t.Assert(s[0].Many[0].Age, 30)
   505  		t.Assert(s[0].Many[1].Name, "smith")
   506  		t.Assert(s[0].Many[1].Age, 31)
   507  
   508  		t.Assert(s[1].One.Name, "smith")
   509  		t.Assert(s[1].One.Age, 18)
   510  		t.Assert(len(s[1].Many), 0)
   511  
   512  		r3 := Result{
   513  			Record{
   514  				"id":  gvar.New(100),
   515  				"pid": gvar.New(1),
   516  				"age": gvar.New(40),
   517  			},
   518  			Record{
   519  				"id":  gvar.New(200),
   520  				"pid": gvar.New(1),
   521  				"age": gvar.New(41),
   522  			},
   523  		}
   524  		err = r3.ScanList(&s, "Many", "One", "pid:Id")
   525  		//fmt.Printf("%+v", err)
   526  		t.AssertNil(err)
   527  		t.Assert(len(s), 2)
   528  		t.Assert(s[0].One.Name, "john")
   529  		t.Assert(s[0].One.Age, 16)
   530  		t.Assert(len(s[0].Many), 2)
   531  		t.Assert(s[0].Many[0].Name, "john")
   532  		t.Assert(s[0].Many[0].Age, 40)
   533  		t.Assert(s[0].Many[1].Name, "smith")
   534  		t.Assert(s[0].Many[1].Age, 41)
   535  
   536  		t.Assert(s[1].One.Name, "smith")
   537  		t.Assert(s[1].One.Age, 18)
   538  		t.Assert(len(s[1].Many), 0)
   539  	})
   540  }
   541  
   542  // https://github.com/gogf/gf/issues/1159
   543  func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
   544  	gtest.C(t, func(t *gtest.T) {
   545  		type S1 struct {
   546  			Id    int
   547  			Name  string
   548  			Age   int
   549  			Score int
   550  		}
   551  		type S2 struct {
   552  			Id    int
   553  			Pid   int
   554  			Name  string
   555  			Age   int
   556  			Score int
   557  		}
   558  		type S3 struct {
   559  			One  S1
   560  			Many []S2
   561  		}
   562  		var (
   563  			s   []S3
   564  			err error
   565  		)
   566  		r1 := Result{
   567  			Record{
   568  				"id":   gvar.New(1),
   569  				"name": gvar.New("john"),
   570  				"age":  gvar.New(16),
   571  			},
   572  			Record{
   573  				"id":   gvar.New(2),
   574  				"name": gvar.New("smith"),
   575  				"age":  gvar.New(18),
   576  			},
   577  		}
   578  		err = r1.ScanList(&s, "One")
   579  		t.AssertNil(err)
   580  		t.Assert(len(s), 2)
   581  		t.Assert(s[0].One.Name, "john")
   582  		t.Assert(s[0].One.Age, 16)
   583  		t.Assert(s[1].One.Name, "smith")
   584  		t.Assert(s[1].One.Age, 18)
   585  
   586  		r2 := Result{
   587  			Record{
   588  				"id":   gvar.New(100),
   589  				"pid":  gvar.New(1),
   590  				"age":  gvar.New(30),
   591  				"name": gvar.New("john"),
   592  			},
   593  			Record{
   594  				"id":   gvar.New(200),
   595  				"pid":  gvar.New(1),
   596  				"age":  gvar.New(31),
   597  				"name": gvar.New("smith"),
   598  			},
   599  		}
   600  		err = r2.ScanList(&s, "Many", "One", "pid:Id")
   601  		//fmt.Printf("%+v", err)
   602  		t.AssertNil(err)
   603  		t.Assert(len(s), 2)
   604  		t.Assert(s[0].One.Name, "john")
   605  		t.Assert(s[0].One.Age, 16)
   606  		t.Assert(len(s[0].Many), 2)
   607  		t.Assert(s[0].Many[0].Name, "john")
   608  		t.Assert(s[0].Many[0].Age, 30)
   609  		t.Assert(s[0].Many[1].Name, "smith")
   610  		t.Assert(s[0].Many[1].Age, 31)
   611  
   612  		t.Assert(s[1].One.Name, "smith")
   613  		t.Assert(s[1].One.Age, 18)
   614  		t.Assert(len(s[1].Many), 0)
   615  
   616  		r3 := Result{
   617  			Record{
   618  				"id":  gvar.New(100),
   619  				"pid": gvar.New(1),
   620  				"age": gvar.New(40),
   621  			},
   622  			Record{
   623  				"id":  gvar.New(200),
   624  				"pid": gvar.New(1),
   625  				"age": gvar.New(41),
   626  			},
   627  		}
   628  		err = r3.ScanList(&s, "Many", "One", "pid:Id")
   629  		//fmt.Printf("%+v", err)
   630  		t.AssertNil(err)
   631  		t.Assert(len(s), 2)
   632  		t.Assert(s[0].One.Name, "john")
   633  		t.Assert(s[0].One.Age, 16)
   634  		t.Assert(len(s[0].Many), 2)
   635  		t.Assert(s[0].Many[0].Name, "john")
   636  		t.Assert(s[0].Many[0].Age, 40)
   637  		t.Assert(s[0].Many[1].Name, "smith")
   638  		t.Assert(s[0].Many[1].Age, 41)
   639  
   640  		t.Assert(s[1].One.Name, "smith")
   641  		t.Assert(s[1].One.Age, 18)
   642  		t.Assert(len(s[1].Many), 0)
   643  	})
   644  }