github.com/mithrandie/csvq@v1.18.1/lib/query/query_test.go (about)

     1  package query
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/mithrandie/csvq/lib/option"
    12  	"github.com/mithrandie/csvq/lib/parser"
    13  	"github.com/mithrandie/csvq/lib/value"
    14  
    15  	"github.com/mithrandie/go-text"
    16  	"github.com/mithrandie/go-text/json"
    17  )
    18  
    19  var fetchCursorTests = []struct {
    20  	Name          string
    21  	CurName       parser.Identifier
    22  	FetchPosition parser.FetchPosition
    23  	Variables     []parser.Variable
    24  	Success       bool
    25  	ResultScopes  *ReferenceScope
    26  	Error         string
    27  }{
    28  	{
    29  		Name:    "Fetch Cursor First Time",
    30  		CurName: parser.Identifier{Literal: "cur"},
    31  		Variables: []parser.Variable{
    32  			{Name: "var1"},
    33  			{Name: "var2"},
    34  		},
    35  		Success: true,
    36  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
    37  			{
    38  				scopeNameVariables: {
    39  					"var1": value.NewString("1"),
    40  					"var2": value.NewString("str1"),
    41  				},
    42  			},
    43  		}, nil, time.Time{}, nil),
    44  	},
    45  	{
    46  		Name:    "Fetch Cursor Second Time",
    47  		CurName: parser.Identifier{Literal: "cur"},
    48  		Variables: []parser.Variable{
    49  			{Name: "var1"},
    50  			{Name: "var2"},
    51  		},
    52  		Success: true,
    53  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
    54  			{
    55  				scopeNameVariables: {
    56  					"var1": value.NewString("2"),
    57  					"var2": value.NewString("str2"),
    58  				},
    59  			},
    60  		}, nil, time.Time{}, nil),
    61  	},
    62  	{
    63  		Name:    "Fetch Cursor Third Time",
    64  		CurName: parser.Identifier{Literal: "cur"},
    65  		Variables: []parser.Variable{
    66  			{Name: "var1"},
    67  			{Name: "var2"},
    68  		},
    69  		Success: true,
    70  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
    71  			{
    72  				scopeNameVariables: {
    73  					"var1": value.NewString("3"),
    74  					"var2": value.NewString("str3"),
    75  				},
    76  			},
    77  		}, nil, time.Time{}, nil),
    78  	},
    79  	{
    80  		Name:    "Fetch Cursor Forth Time",
    81  		CurName: parser.Identifier{Literal: "cur"},
    82  		Variables: []parser.Variable{
    83  			{Name: "var1"},
    84  			{Name: "var2"},
    85  		},
    86  		Success: false,
    87  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
    88  			{
    89  				scopeNameVariables: {
    90  					"var1": value.NewString("3"),
    91  					"var2": value.NewString("str3"),
    92  				},
    93  			},
    94  		}, nil, time.Time{}, nil),
    95  	},
    96  	{
    97  		Name:    "Fetch Cursor Absolute",
    98  		CurName: parser.Identifier{Literal: "cur"},
    99  		FetchPosition: parser.FetchPosition{
   100  			Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"},
   101  			Number:   parser.NewIntegerValueFromString("1"),
   102  		},
   103  		Variables: []parser.Variable{
   104  			{Name: "var1"},
   105  			{Name: "var2"},
   106  		},
   107  		Success: true,
   108  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
   109  			{
   110  				scopeNameVariables: {
   111  					"var1": value.NewString("2"),
   112  					"var2": value.NewString("str2"),
   113  				},
   114  			},
   115  		}, nil, time.Time{}, nil),
   116  	},
   117  	{
   118  		Name:    "Fetch Cursor Fetch Error",
   119  		CurName: parser.Identifier{Literal: "notexist"},
   120  		Variables: []parser.Variable{
   121  			{Name: "var1"},
   122  			{Name: "var2"},
   123  		},
   124  		Error: "cursor notexist is undeclared",
   125  	},
   126  	{
   127  		Name:    "Fetch Cursor Not Match Number Error",
   128  		CurName: parser.Identifier{Literal: "cur2"},
   129  		Variables: []parser.Variable{
   130  			{Name: "var1"},
   131  		},
   132  		Error: "fetching from cursor cur2 returns 2 values",
   133  	},
   134  	{
   135  		Name:    "Fetch Cursor Substitution Error",
   136  		CurName: parser.Identifier{Literal: "cur2"},
   137  		Variables: []parser.Variable{
   138  			{Name: "var1"},
   139  			{Name: "notexist"},
   140  		},
   141  		Error: "variable @notexist is undeclared",
   142  	},
   143  	{
   144  		Name:    "Fetch Cursor Number Value Error",
   145  		CurName: parser.Identifier{Literal: "cur"},
   146  		FetchPosition: parser.FetchPosition{
   147  			Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"},
   148  			Number:   parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
   149  		},
   150  		Variables: []parser.Variable{
   151  			{Name: "var1"},
   152  			{Name: "var2"},
   153  		},
   154  		Error: "field notexist does not exist",
   155  	},
   156  	{
   157  		Name:    "Fetch Cursor Number Not Integer Error",
   158  		CurName: parser.Identifier{Literal: "cur"},
   159  		FetchPosition: parser.FetchPosition{
   160  			Position: parser.Token{Token: parser.ABSOLUTE, Literal: "absolute"},
   161  			Number:   parser.NewNullValue(),
   162  		},
   163  		Variables: []parser.Variable{
   164  			{Name: "var1"},
   165  			{Name: "var2"},
   166  		},
   167  		Error: "fetching position NULL is not an integer value",
   168  	},
   169  }
   170  
   171  func TestFetchCursor(t *testing.T) {
   172  	defer func() {
   173  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
   174  		initFlag(TestTx.Flags)
   175  	}()
   176  
   177  	TestTx.Flags.Repository = TestDir
   178  
   179  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
   180  		{
   181  			scopeNameVariables: {
   182  				"var1": value.NewNull(),
   183  				"var2": value.NewNull(),
   184  			},
   185  			scopeNameCursors: {
   186  				"CUR": &Cursor{
   187  					query: selectQueryForCursorTest,
   188  					mtx:   &sync.Mutex{},
   189  				},
   190  				"CUR2": &Cursor{
   191  					query: selectQueryForCursorTest,
   192  					mtx:   &sync.Mutex{},
   193  				},
   194  			},
   195  		},
   196  	}, nil, time.Time{}, nil)
   197  
   198  	ctx := context.Background()
   199  	_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
   200  	_ = scope.OpenCursor(ctx, parser.Identifier{Literal: "cur"}, nil)
   201  	_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
   202  	_ = scope.OpenCursor(ctx, parser.Identifier{Literal: "cur2"}, nil)
   203  
   204  	for _, v := range fetchCursorTests {
   205  		success, err := FetchCursor(ctx, scope, v.CurName, v.FetchPosition, v.Variables)
   206  		if err != nil {
   207  			if len(v.Error) < 1 {
   208  				t.Errorf("%s: unexpected error %q", v.Name, err)
   209  			} else if err.Error() != v.Error {
   210  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
   211  			}
   212  			continue
   213  		}
   214  		if 0 < len(v.Error) {
   215  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
   216  			continue
   217  		}
   218  		if success != v.Success {
   219  			t.Errorf("%s: success = %t, want %t", v.Name, success, v.Success)
   220  		}
   221  
   222  		if !SyncMapEqual(scope.Blocks[0].Variables, v.ResultScopes.Blocks[0].Variables) {
   223  			t.Errorf("%s: variables = %v, want %v", v.Name, scope.Blocks, v.ResultScopes.Blocks)
   224  		}
   225  	}
   226  }
   227  
   228  var declareViewTests = []struct {
   229  	Name    string
   230  	ViewMap ViewMap
   231  	Expr    parser.ViewDeclaration
   232  	Result  ViewMap
   233  	Error   string
   234  }{
   235  	{
   236  		Name: "Declare View",
   237  		Expr: parser.ViewDeclaration{
   238  			View: parser.Identifier{Literal: "tbl"},
   239  			Fields: []parser.QueryExpression{
   240  				parser.Identifier{Literal: "column1"},
   241  				parser.Identifier{Literal: "column2"},
   242  			},
   243  		},
   244  		Result: GenerateViewMap([]*View{
   245  			{
   246  				FileInfo: &FileInfo{
   247  					Path:                  "tbl",
   248  					ViewType:              ViewTypeTemporaryTable,
   249  					restorePointHeader:    NewHeader("tbl", []string{"column1", "column2"}),
   250  					restorePointRecordSet: RecordSet{},
   251  				},
   252  				Header:    NewHeader("tbl", []string{"column1", "column2"}),
   253  				RecordSet: RecordSet{},
   254  			},
   255  		}),
   256  	},
   257  	{
   258  		Name: "Declare View Field Duplicate Error",
   259  		Expr: parser.ViewDeclaration{
   260  			View: parser.Identifier{Literal: "tbl"},
   261  			Fields: []parser.QueryExpression{
   262  				parser.Identifier{Literal: "column1"},
   263  				parser.Identifier{Literal: "column1"},
   264  			},
   265  		},
   266  		Error: "field name column1 is a duplicate",
   267  	},
   268  	{
   269  		Name: "Declare View From Query",
   270  		Expr: parser.ViewDeclaration{
   271  			View: parser.Identifier{Literal: "tbl"},
   272  			Fields: []parser.QueryExpression{
   273  				parser.Identifier{Literal: "column1"},
   274  				parser.Identifier{Literal: "column2"},
   275  			},
   276  			Query: parser.SelectQuery{
   277  				SelectEntity: parser.SelectEntity{
   278  					SelectClause: parser.SelectClause{
   279  						Fields: []parser.QueryExpression{
   280  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
   281  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
   282  						},
   283  					},
   284  				},
   285  			},
   286  		},
   287  		Result: GenerateViewMap([]*View{
   288  			{
   289  				FileInfo: &FileInfo{
   290  					Path:               "tbl",
   291  					ViewType:           ViewTypeTemporaryTable,
   292  					restorePointHeader: NewHeader("tbl", []string{"column1", "column2"}),
   293  					restorePointRecordSet: RecordSet{
   294  						NewRecord([]value.Primary{
   295  							value.NewInteger(1),
   296  							value.NewInteger(2),
   297  						}),
   298  					},
   299  				},
   300  				Header: NewHeader("tbl", []string{"column1", "column2"}),
   301  				RecordSet: RecordSet{
   302  					NewRecord([]value.Primary{
   303  						value.NewInteger(1),
   304  						value.NewInteger(2),
   305  					}),
   306  				},
   307  			},
   308  		}),
   309  	},
   310  	{
   311  		Name: "Declare View From Query Query Error",
   312  		Expr: parser.ViewDeclaration{
   313  			View: parser.Identifier{Literal: "tbl"},
   314  			Fields: []parser.QueryExpression{
   315  				parser.Identifier{Literal: "column1"},
   316  				parser.Identifier{Literal: "column2"},
   317  			},
   318  			Query: parser.SelectQuery{
   319  				SelectEntity: parser.SelectEntity{
   320  					SelectClause: parser.SelectClause{
   321  						Fields: []parser.QueryExpression{
   322  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
   323  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}},
   324  						},
   325  					},
   326  				},
   327  			},
   328  		},
   329  		Error: "field notexist does not exist",
   330  	},
   331  	{
   332  		Name: "Declare View From Query Field Update Error",
   333  		Expr: parser.ViewDeclaration{
   334  			View: parser.Identifier{Literal: "tbl"},
   335  			Fields: []parser.QueryExpression{
   336  				parser.Identifier{Literal: "column1"},
   337  			},
   338  			Query: parser.SelectQuery{
   339  				SelectEntity: parser.SelectEntity{
   340  					SelectClause: parser.SelectClause{
   341  						Fields: []parser.QueryExpression{
   342  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
   343  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
   344  						},
   345  					},
   346  				},
   347  			},
   348  		},
   349  		Error: "select query should return exactly 1 field for view tbl",
   350  	},
   351  	{
   352  		Name: "Declare View  From Query Field Duplicate Error",
   353  		Expr: parser.ViewDeclaration{
   354  			View: parser.Identifier{Literal: "tbl"},
   355  			Fields: []parser.QueryExpression{
   356  				parser.Identifier{Literal: "column1"},
   357  				parser.Identifier{Literal: "column1"},
   358  			},
   359  			Query: parser.SelectQuery{
   360  				SelectEntity: parser.SelectEntity{
   361  					SelectClause: parser.SelectClause{
   362  						Fields: []parser.QueryExpression{
   363  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
   364  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
   365  						},
   366  					},
   367  				},
   368  			},
   369  		},
   370  		Error: "field name column1 is a duplicate",
   371  	},
   372  	{
   373  		Name: "Declare View Redeclaration Error",
   374  		ViewMap: GenerateViewMap([]*View{
   375  			{
   376  				FileInfo: &FileInfo{
   377  					Path:     "tbl",
   378  					ViewType: ViewTypeTemporaryTable,
   379  				},
   380  			},
   381  		}),
   382  		Expr: parser.ViewDeclaration{
   383  			View: parser.Identifier{Literal: "tbl"},
   384  			Fields: []parser.QueryExpression{
   385  				parser.Identifier{Literal: "column1"},
   386  				parser.Identifier{Literal: "column2"},
   387  			},
   388  		},
   389  		Error: "view tbl is redeclared",
   390  	},
   391  }
   392  
   393  func TestDeclareView(t *testing.T) {
   394  	scope := NewReferenceScope(TestTx)
   395  	ctx := context.Background()
   396  
   397  	for _, v := range declareViewTests {
   398  		if v.ViewMap.SyncMap == nil {
   399  			scope.Blocks[0].TemporaryTables = NewViewMap()
   400  		} else {
   401  			scope.Blocks[0].TemporaryTables = v.ViewMap
   402  		}
   403  
   404  		err := DeclareView(ctx, scope, v.Expr)
   405  		if err != nil {
   406  			if len(v.Error) < 1 {
   407  				t.Errorf("%s: unexpected error %q", v.Name, err)
   408  			} else if err.Error() != v.Error {
   409  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
   410  			}
   411  			continue
   412  		}
   413  		if 0 < len(v.Error) {
   414  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
   415  			continue
   416  		}
   417  		if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.Result) {
   418  			t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.Result)
   419  		}
   420  	}
   421  }
   422  
   423  var selectTests = []struct {
   424  	Name         string
   425  	Query        parser.SelectQuery
   426  	Result       *View
   427  	SetVariables map[parser.Variable]value.Primary
   428  	Error        string
   429  }{
   430  	{
   431  		Name: "Select",
   432  		Query: parser.SelectQuery{
   433  			SelectEntity: parser.SelectEntity{
   434  				SelectClause: parser.SelectClause{
   435  					Fields: []parser.QueryExpression{
   436  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   437  						parser.Field{Object: parser.AggregateFunction{Name: "count", Args: []parser.QueryExpression{parser.AllColumns{}}}},
   438  					},
   439  				},
   440  				FromClause: parser.FromClause{
   441  					Tables: []parser.QueryExpression{
   442  						parser.Table{Object: parser.Identifier{Literal: "group_table"}},
   443  					},
   444  				},
   445  				WhereClause: parser.WhereClause{
   446  					Filter: parser.Comparison{
   447  						LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   448  						RHS:      parser.NewIntegerValueFromString("3"),
   449  						Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"},
   450  					},
   451  				},
   452  				GroupByClause: parser.GroupByClause{
   453  					Items: []parser.QueryExpression{
   454  						parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
   455  					},
   456  				},
   457  				HavingClause: parser.HavingClause{
   458  					Filter: parser.Comparison{
   459  						LHS:      parser.AggregateFunction{Name: "count", Args: []parser.QueryExpression{parser.AllColumns{}}},
   460  						RHS:      parser.NewIntegerValueFromString("1"),
   461  						Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: ">"},
   462  					},
   463  				},
   464  			},
   465  			OrderByClause: parser.OrderByClause{
   466  				Items: []parser.QueryExpression{
   467  					parser.OrderItem{Value: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   468  				},
   469  			},
   470  			LimitClause: parser.LimitClause{
   471  				Value: parser.NewIntegerValueFromString("5"),
   472  				OffsetClause: parser.OffsetClause{
   473  					Value: parser.NewIntegerValue(0),
   474  				},
   475  			},
   476  		},
   477  		Result: &View{
   478  			FileInfo: &FileInfo{
   479  				Path:      GetTestFilePath("group_table.csv"),
   480  				Delimiter: ',',
   481  				NoHeader:  false,
   482  				Encoding:  text.UTF8,
   483  				LineBreak: text.LF,
   484  			},
   485  			Header: []HeaderField{
   486  				{
   487  					View:        "group_table",
   488  					Column:      "column1",
   489  					Number:      1,
   490  					IsFromTable: true,
   491  				},
   492  				{
   493  					Column:      "COUNT(*)",
   494  					Number:      2,
   495  					IsFromTable: true,
   496  				},
   497  			},
   498  			RecordSet: []Record{
   499  				NewRecord([]value.Primary{
   500  					value.NewString("1"),
   501  					value.NewInteger(2),
   502  				}),
   503  				NewRecord([]value.Primary{
   504  					value.NewString("2"),
   505  					value.NewInteger(2),
   506  				}),
   507  			},
   508  		},
   509  	},
   510  	{
   511  		Name: "Select Replace Fields",
   512  		Query: parser.SelectQuery{
   513  			SelectEntity: parser.SelectEntity{
   514  				SelectClause: parser.SelectClause{
   515  					Fields: []parser.QueryExpression{
   516  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   517  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   518  					},
   519  				},
   520  				FromClause: parser.FromClause{
   521  					Tables: []parser.QueryExpression{
   522  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
   523  					},
   524  				},
   525  			},
   526  			LimitClause: parser.LimitClause{
   527  				Type:  parser.Token{Token: parser.FETCH},
   528  				Value: parser.NewIntegerValueFromString("1"),
   529  			},
   530  		},
   531  		Result: &View{
   532  			FileInfo: &FileInfo{
   533  				Path:      GetTestFilePath("table1.csv"),
   534  				Delimiter: ',',
   535  				NoHeader:  false,
   536  				Encoding:  text.UTF8,
   537  				LineBreak: text.LF,
   538  			},
   539  			Header: NewHeader("table1", []string{"column2", "column1"}),
   540  			RecordSet: []Record{
   541  				NewRecord([]value.Primary{
   542  					value.NewString("str1"),
   543  					value.NewString("1"),
   544  				}),
   545  			},
   546  		},
   547  	},
   548  	{
   549  		Name: "Union",
   550  		Query: parser.SelectQuery{
   551  			SelectEntity: parser.SelectSet{
   552  				LHS: parser.SelectEntity{
   553  					SelectClause: parser.SelectClause{
   554  						Fields: []parser.QueryExpression{
   555  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   556  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   557  						},
   558  					},
   559  					FromClause: parser.FromClause{
   560  						Tables: []parser.QueryExpression{
   561  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   562  						},
   563  					},
   564  				},
   565  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   566  				RHS: parser.SelectEntity{
   567  					SelectClause: parser.SelectClause{
   568  						Fields: []parser.QueryExpression{
   569  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   570  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   571  						},
   572  					},
   573  					FromClause: parser.FromClause{
   574  						Tables: []parser.QueryExpression{
   575  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   576  						},
   577  					},
   578  				},
   579  			},
   580  		},
   581  		Result: &View{
   582  			Header: NewHeader("table1", []string{"column1", "column2"}),
   583  			RecordSet: []Record{
   584  				NewRecord([]value.Primary{
   585  					value.NewString("1"),
   586  					value.NewString("str1"),
   587  				}),
   588  				NewRecord([]value.Primary{
   589  					value.NewString("2"),
   590  					value.NewString("str2"),
   591  				}),
   592  				NewRecord([]value.Primary{
   593  					value.NewString("3"),
   594  					value.NewString("str3"),
   595  				}),
   596  				NewRecord([]value.Primary{
   597  					value.NewString("4"),
   598  					value.NewString("str4"),
   599  				}),
   600  			},
   601  		},
   602  	},
   603  	{
   604  		Name: "Intersect",
   605  		Query: parser.SelectQuery{
   606  			SelectEntity: parser.SelectSet{
   607  				LHS: parser.SelectEntity{
   608  					SelectClause: parser.SelectClause{
   609  						Fields: []parser.QueryExpression{
   610  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   611  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   612  						},
   613  					},
   614  					FromClause: parser.FromClause{
   615  						Tables: []parser.QueryExpression{
   616  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   617  						},
   618  					},
   619  				},
   620  				Operator: parser.Token{Token: parser.INTERSECT, Literal: "intersect"},
   621  				RHS: parser.SelectEntity{
   622  					SelectClause: parser.SelectClause{
   623  						Fields: []parser.QueryExpression{
   624  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   625  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   626  						},
   627  					},
   628  					FromClause: parser.FromClause{
   629  						Tables: []parser.QueryExpression{
   630  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   631  						},
   632  					},
   633  				},
   634  			},
   635  		},
   636  		Result: &View{
   637  			Header: NewHeader("table1", []string{"column1", "column2"}),
   638  			RecordSet: []Record{
   639  				NewRecord([]value.Primary{
   640  					value.NewString("2"),
   641  					value.NewString("str2"),
   642  				}),
   643  				NewRecord([]value.Primary{
   644  					value.NewString("3"),
   645  					value.NewString("str3"),
   646  				}),
   647  			},
   648  		},
   649  	},
   650  	{
   651  		Name: "Except",
   652  		Query: parser.SelectQuery{
   653  			SelectEntity: parser.SelectSet{
   654  				LHS: parser.SelectEntity{
   655  					SelectClause: parser.SelectClause{
   656  						Fields: []parser.QueryExpression{
   657  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   658  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   659  						},
   660  					},
   661  					FromClause: parser.FromClause{
   662  						Tables: []parser.QueryExpression{
   663  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   664  						},
   665  					},
   666  				},
   667  				Operator: parser.Token{Token: parser.EXCEPT, Literal: "except"},
   668  				RHS: parser.SelectEntity{
   669  					SelectClause: parser.SelectClause{
   670  						Fields: []parser.QueryExpression{
   671  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   672  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   673  						},
   674  					},
   675  					FromClause: parser.FromClause{
   676  						Tables: []parser.QueryExpression{
   677  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   678  						},
   679  					},
   680  				},
   681  			},
   682  		},
   683  		Result: &View{
   684  			Header: NewHeader("table1", []string{"column1", "column2"}),
   685  			RecordSet: []Record{
   686  				NewRecord([]value.Primary{
   687  					value.NewString("1"),
   688  					value.NewString("str1"),
   689  				}),
   690  			},
   691  		},
   692  	},
   693  	{
   694  		Name: "Separate scopes on both sides of a set operator",
   695  		Query: parser.SelectQuery{
   696  			SelectEntity: parser.SelectSet{
   697  				LHS: parser.SelectEntity{
   698  					SelectClause: parser.SelectClause{
   699  						Fields: []parser.QueryExpression{
   700  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   701  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   702  						},
   703  					},
   704  					FromClause: parser.FromClause{
   705  						Tables: []parser.QueryExpression{
   706  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   707  						},
   708  					},
   709  				},
   710  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   711  				All:      parser.Token{Token: parser.ALL, Literal: "all"},
   712  				RHS: parser.SelectEntity{
   713  					SelectClause: parser.SelectClause{
   714  						Fields: []parser.QueryExpression{
   715  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   716  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   717  						},
   718  					},
   719  					FromClause: parser.FromClause{
   720  						Tables: []parser.QueryExpression{
   721  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   722  						},
   723  					},
   724  				},
   725  			},
   726  		},
   727  		Result: &View{
   728  			Header: NewHeader("table1", []string{"column1", "column2"}),
   729  			RecordSet: []Record{
   730  				NewRecord([]value.Primary{
   731  					value.NewString("1"),
   732  					value.NewString("str1"),
   733  				}),
   734  				NewRecord([]value.Primary{
   735  					value.NewString("2"),
   736  					value.NewString("str2"),
   737  				}),
   738  				NewRecord([]value.Primary{
   739  					value.NewString("3"),
   740  					value.NewString("str3"),
   741  				}),
   742  				NewRecord([]value.Primary{
   743  					value.NewString("1"),
   744  					value.NewString("str1"),
   745  				}),
   746  				NewRecord([]value.Primary{
   747  					value.NewString("2"),
   748  					value.NewString("str2"),
   749  				}),
   750  				NewRecord([]value.Primary{
   751  					value.NewString("3"),
   752  					value.NewString("str3"),
   753  				}),
   754  			},
   755  		},
   756  	},
   757  	{
   758  		Name: "Union with SubQuery",
   759  		Query: parser.SelectQuery{
   760  			SelectEntity: parser.SelectSet{
   761  				LHS: parser.Subquery{
   762  					Query: parser.SelectQuery{
   763  						SelectEntity: parser.SelectEntity{
   764  							SelectClause: parser.SelectClause{
   765  								Fields: []parser.QueryExpression{
   766  									parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   767  									parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   768  								},
   769  							},
   770  							FromClause: parser.FromClause{
   771  								Tables: []parser.QueryExpression{
   772  									parser.Table{Object: parser.Identifier{Literal: "table1"}},
   773  								},
   774  							},
   775  						},
   776  					},
   777  				},
   778  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   779  				RHS: parser.SelectEntity{
   780  					SelectClause: parser.SelectClause{
   781  						Fields: []parser.QueryExpression{
   782  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   783  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   784  						},
   785  					},
   786  					FromClause: parser.FromClause{
   787  						Tables: []parser.QueryExpression{
   788  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   789  						},
   790  					},
   791  				},
   792  			},
   793  		},
   794  		Result: &View{
   795  			Header: NewHeader("table1", []string{"column1", "column2"}),
   796  			RecordSet: []Record{
   797  				NewRecord([]value.Primary{
   798  					value.NewString("1"),
   799  					value.NewString("str1"),
   800  				}),
   801  				NewRecord([]value.Primary{
   802  					value.NewString("2"),
   803  					value.NewString("str2"),
   804  				}),
   805  				NewRecord([]value.Primary{
   806  					value.NewString("3"),
   807  					value.NewString("str3"),
   808  				}),
   809  				NewRecord([]value.Primary{
   810  					value.NewString("4"),
   811  					value.NewString("str4"),
   812  				}),
   813  			},
   814  		},
   815  	},
   816  	{
   817  		Name: "Union Field Length Error",
   818  		Query: parser.SelectQuery{
   819  			SelectEntity: parser.SelectSet{
   820  				LHS: parser.SelectEntity{
   821  					SelectClause: parser.SelectClause{
   822  						Fields: []parser.QueryExpression{
   823  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   824  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   825  						},
   826  					},
   827  					FromClause: parser.FromClause{
   828  						Tables: []parser.QueryExpression{
   829  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   830  						},
   831  					},
   832  				},
   833  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   834  				RHS: parser.SelectEntity{
   835  					SelectClause: parser.SelectClause{
   836  						Fields: []parser.QueryExpression{
   837  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   838  						},
   839  					},
   840  					FromClause: parser.FromClause{
   841  						Tables: []parser.QueryExpression{
   842  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   843  						},
   844  					},
   845  				},
   846  			},
   847  		},
   848  		Error: "result set to be combined should contain exactly 2 fields",
   849  	},
   850  	{
   851  		Name: "Union LHS Error",
   852  		Query: parser.SelectQuery{
   853  			SelectEntity: parser.SelectSet{
   854  				LHS: parser.SelectEntity{
   855  					SelectClause: parser.SelectClause{
   856  						Fields: []parser.QueryExpression{
   857  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   858  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}},
   859  						},
   860  					},
   861  					FromClause: parser.FromClause{
   862  						Tables: []parser.QueryExpression{
   863  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   864  						},
   865  					},
   866  				},
   867  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   868  				RHS: parser.SelectEntity{
   869  					SelectClause: parser.SelectClause{
   870  						Fields: []parser.QueryExpression{
   871  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   872  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   873  						},
   874  					},
   875  					FromClause: parser.FromClause{
   876  						Tables: []parser.QueryExpression{
   877  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   878  						},
   879  					},
   880  				},
   881  			},
   882  		},
   883  		Error: "field notexist does not exist",
   884  	},
   885  	{
   886  		Name: "Union RHS Error",
   887  		Query: parser.SelectQuery{
   888  			SelectEntity: parser.SelectSet{
   889  				LHS: parser.SelectEntity{
   890  					SelectClause: parser.SelectClause{
   891  						Fields: []parser.QueryExpression{
   892  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   893  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   894  						},
   895  					},
   896  					FromClause: parser.FromClause{
   897  						Tables: []parser.QueryExpression{
   898  							parser.Table{Object: parser.Identifier{Literal: "table1"}},
   899  						},
   900  					},
   901  				},
   902  				Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   903  				RHS: parser.SelectEntity{
   904  					SelectClause: parser.SelectClause{
   905  						Fields: []parser.QueryExpression{
   906  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   907  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}},
   908  						},
   909  					},
   910  					FromClause: parser.FromClause{
   911  						Tables: []parser.QueryExpression{
   912  							parser.Table{Object: parser.Identifier{Literal: "table4"}},
   913  						},
   914  					},
   915  				},
   916  			},
   917  		},
   918  		Error: "field notexist does not exist",
   919  	},
   920  	{
   921  		Name: "Inline Tables",
   922  		Query: parser.SelectQuery{
   923  			WithClause: parser.WithClause{
   924  				InlineTables: []parser.QueryExpression{
   925  					parser.InlineTable{
   926  						Name: parser.Identifier{Literal: "it"},
   927  						Fields: []parser.QueryExpression{
   928  							parser.Identifier{Literal: "c1"},
   929  						},
   930  						Query: parser.SelectQuery{
   931  							SelectEntity: parser.SelectEntity{
   932  								SelectClause: parser.SelectClause{
   933  									Fields: []parser.QueryExpression{
   934  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
   935  									},
   936  								},
   937  							},
   938  						},
   939  					},
   940  				},
   941  			},
   942  			SelectEntity: parser.SelectEntity{
   943  				SelectClause: parser.SelectClause{
   944  					Fields: []parser.QueryExpression{
   945  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}},
   946  					},
   947  				},
   948  				FromClause: parser.FromClause{
   949  					Tables: []parser.QueryExpression{
   950  						parser.Table{Object: parser.Identifier{Literal: "it"}},
   951  					},
   952  				},
   953  			},
   954  		},
   955  		Result: &View{
   956  			Header: NewHeader("it", []string{"c1"}),
   957  			RecordSet: []Record{
   958  				NewRecord([]value.Primary{
   959  					value.NewInteger(2),
   960  				}),
   961  			},
   962  		},
   963  	},
   964  	{
   965  		Name: "Inline Tables Field Length Error",
   966  		Query: parser.SelectQuery{
   967  			WithClause: parser.WithClause{
   968  				InlineTables: []parser.QueryExpression{
   969  					parser.InlineTable{
   970  						Name: parser.Identifier{Literal: "it"},
   971  						Fields: []parser.QueryExpression{
   972  							parser.Identifier{Literal: "c1"},
   973  						},
   974  						Query: parser.SelectQuery{
   975  							SelectEntity: parser.SelectSet{
   976  								LHS: parser.SelectEntity{
   977  									SelectClause: parser.SelectClause{
   978  										Fields: []parser.QueryExpression{
   979  											parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
   980  											parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
   981  										},
   982  									},
   983  									FromClause: parser.FromClause{
   984  										Tables: []parser.QueryExpression{
   985  											parser.Table{Object: parser.Identifier{Literal: "table1"}},
   986  										},
   987  									},
   988  								},
   989  								Operator: parser.Token{Token: parser.UNION, Literal: "union"},
   990  								RHS: parser.SelectEntity{
   991  									SelectClause: parser.SelectClause{
   992  										Fields: []parser.QueryExpression{
   993  											parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
   994  											parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
   995  										},
   996  									},
   997  									FromClause: parser.FromClause{
   998  										Tables: []parser.QueryExpression{
   999  											parser.Table{Object: parser.Identifier{Literal: "table4"}},
  1000  										},
  1001  									},
  1002  								},
  1003  							},
  1004  						},
  1005  					},
  1006  				},
  1007  			},
  1008  			SelectEntity: parser.SelectEntity{
  1009  				SelectClause: parser.SelectClause{
  1010  					Fields: []parser.QueryExpression{
  1011  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}}},
  1012  					},
  1013  				},
  1014  				FromClause: parser.FromClause{
  1015  					Tables: []parser.QueryExpression{
  1016  						parser.Table{Object: parser.Identifier{Literal: "it"}},
  1017  					},
  1018  				},
  1019  			},
  1020  		},
  1021  		Error: "select query should return exactly 1 field for inline table it",
  1022  	},
  1023  	{
  1024  		Name: "Inline Tables Recursion",
  1025  		Query: parser.SelectQuery{
  1026  			WithClause: parser.WithClause{
  1027  				InlineTables: []parser.QueryExpression{
  1028  					parser.InlineTable{
  1029  						Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"},
  1030  						Name:      parser.Identifier{Literal: "it"},
  1031  						Fields: []parser.QueryExpression{
  1032  							parser.Identifier{Literal: "n"},
  1033  						},
  1034  						Query: parser.SelectQuery{
  1035  							SelectEntity: parser.SelectSet{
  1036  								LHS: parser.SelectEntity{
  1037  									SelectClause: parser.SelectClause{
  1038  										Fields: []parser.QueryExpression{
  1039  											parser.Field{Object: parser.NewIntegerValueFromString("1")},
  1040  										},
  1041  									},
  1042  								},
  1043  								Operator: parser.Token{Token: parser.UNION, Literal: "union"},
  1044  								RHS: parser.SelectEntity{
  1045  									SelectClause: parser.SelectClause{
  1046  										Fields: []parser.QueryExpression{
  1047  											parser.Field{
  1048  												Object: parser.Arithmetic{
  1049  													LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1050  													RHS:      parser.NewIntegerValueFromString("1"),
  1051  													Operator: parser.Token{Token: '+', Literal: "+"},
  1052  												},
  1053  											},
  1054  										},
  1055  									},
  1056  									FromClause: parser.FromClause{
  1057  										Tables: []parser.QueryExpression{
  1058  											parser.Table{Object: parser.Identifier{Literal: "it"}},
  1059  										},
  1060  									},
  1061  									WhereClause: parser.WhereClause{
  1062  										Filter: parser.Comparison{
  1063  											LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1064  											RHS:      parser.NewIntegerValueFromString("3"),
  1065  											Operator: parser.Token{Token: parser.COMPARISON_OP, Literal: "<"},
  1066  										},
  1067  									},
  1068  								},
  1069  							},
  1070  						},
  1071  					},
  1072  				},
  1073  			},
  1074  			SelectEntity: parser.SelectEntity{
  1075  				SelectClause: parser.SelectClause{
  1076  					Fields: []parser.QueryExpression{
  1077  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}},
  1078  					},
  1079  				},
  1080  				FromClause: parser.FromClause{
  1081  					Tables: []parser.QueryExpression{
  1082  						parser.Table{Object: parser.Identifier{Literal: "it"}},
  1083  					},
  1084  				},
  1085  			},
  1086  		},
  1087  		Result: &View{
  1088  			Header: []HeaderField{
  1089  				{
  1090  					View:        "it",
  1091  					Column:      "n",
  1092  					Number:      1,
  1093  					IsFromTable: true,
  1094  				},
  1095  			},
  1096  			RecordSet: []Record{
  1097  				NewRecord([]value.Primary{
  1098  					value.NewInteger(1),
  1099  				}),
  1100  				NewRecord([]value.Primary{
  1101  					value.NewInteger(2),
  1102  				}),
  1103  				NewRecord([]value.Primary{
  1104  					value.NewInteger(3),
  1105  				}),
  1106  			},
  1107  		},
  1108  	},
  1109  	{
  1110  		Name: "Inline Tables Recursion Field Length Error",
  1111  		Query: parser.SelectQuery{
  1112  			WithClause: parser.WithClause{
  1113  				InlineTables: []parser.QueryExpression{
  1114  					parser.InlineTable{
  1115  						Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"},
  1116  						Name:      parser.Identifier{Literal: "it"},
  1117  						Fields: []parser.QueryExpression{
  1118  							parser.Identifier{Literal: "n"},
  1119  						},
  1120  						Query: parser.SelectQuery{
  1121  							SelectEntity: parser.SelectSet{
  1122  								LHS: parser.SelectEntity{
  1123  									SelectClause: parser.SelectClause{
  1124  										Fields: []parser.QueryExpression{
  1125  											parser.Field{Object: parser.NewIntegerValueFromString("1")},
  1126  										},
  1127  									},
  1128  								},
  1129  								Operator: parser.Token{Token: parser.UNION, Literal: "union"},
  1130  								RHS: parser.SelectEntity{
  1131  									SelectClause: parser.SelectClause{
  1132  										Fields: []parser.QueryExpression{
  1133  											parser.Field{
  1134  												Object: parser.Arithmetic{
  1135  													LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1136  													RHS:      parser.NewIntegerValueFromString("1"),
  1137  													Operator: parser.Token{Token: '+', Literal: "+"},
  1138  												},
  1139  											},
  1140  											parser.Field{Object: parser.NewIntegerValueFromString("2")},
  1141  										},
  1142  									},
  1143  									FromClause: parser.FromClause{
  1144  										Tables: []parser.QueryExpression{
  1145  											parser.Table{Object: parser.Identifier{Literal: "it"}},
  1146  										},
  1147  									},
  1148  									WhereClause: parser.WhereClause{
  1149  										Filter: parser.Comparison{
  1150  											LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1151  											RHS:      parser.NewIntegerValueFromString("3"),
  1152  											Operator: parser.Token{Token: '<', Literal: "<"},
  1153  										},
  1154  									},
  1155  								},
  1156  							},
  1157  						},
  1158  					},
  1159  				},
  1160  			},
  1161  			SelectEntity: parser.SelectEntity{
  1162  				SelectClause: parser.SelectClause{
  1163  					Fields: []parser.QueryExpression{
  1164  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}},
  1165  					},
  1166  				},
  1167  				FromClause: parser.FromClause{
  1168  					Tables: []parser.QueryExpression{
  1169  						parser.Table{Object: parser.Identifier{Literal: "it"}},
  1170  					},
  1171  				},
  1172  			},
  1173  		},
  1174  		Error: "result set to be combined should contain exactly 1 field",
  1175  	},
  1176  	{
  1177  		Name: "Inline Tables Recursion Recursion Limit Exceeded Error",
  1178  		Query: parser.SelectQuery{
  1179  			WithClause: parser.WithClause{
  1180  				InlineTables: []parser.QueryExpression{
  1181  					parser.InlineTable{
  1182  						Recursive: parser.Token{Token: parser.RECURSIVE, Literal: "recursive"},
  1183  						Name:      parser.Identifier{Literal: "it"},
  1184  						Fields: []parser.QueryExpression{
  1185  							parser.Identifier{Literal: "n"},
  1186  						},
  1187  						Query: parser.SelectQuery{
  1188  							SelectEntity: parser.SelectSet{
  1189  								LHS: parser.SelectEntity{
  1190  									SelectClause: parser.SelectClause{
  1191  										Fields: []parser.QueryExpression{
  1192  											parser.Field{Object: parser.NewIntegerValueFromString("1")},
  1193  										},
  1194  									},
  1195  								},
  1196  								Operator: parser.Token{Token: parser.UNION, Literal: "union"},
  1197  								RHS: parser.SelectEntity{
  1198  									SelectClause: parser.SelectClause{
  1199  										Fields: []parser.QueryExpression{
  1200  											parser.Field{
  1201  												Object: parser.Arithmetic{
  1202  													LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1203  													RHS:      parser.NewIntegerValueFromString("1"),
  1204  													Operator: parser.Token{Token: '+', Literal: "+"},
  1205  												},
  1206  											},
  1207  										},
  1208  									},
  1209  									FromClause: parser.FromClause{
  1210  										Tables: []parser.QueryExpression{
  1211  											parser.Table{Object: parser.Identifier{Literal: "it"}},
  1212  										},
  1213  									},
  1214  									WhereClause: parser.WhereClause{
  1215  										Filter: parser.Comparison{
  1216  											LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "n"}},
  1217  											RHS:      parser.NewIntegerValueFromString("10"),
  1218  											Operator: parser.Token{Token: '<', Literal: "<"},
  1219  										},
  1220  									},
  1221  								},
  1222  							},
  1223  						},
  1224  					},
  1225  				},
  1226  			},
  1227  			SelectEntity: parser.SelectEntity{
  1228  				SelectClause: parser.SelectClause{
  1229  					Fields: []parser.QueryExpression{
  1230  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "n"}}},
  1231  					},
  1232  				},
  1233  				FromClause: parser.FromClause{
  1234  					Tables: []parser.QueryExpression{
  1235  						parser.Table{Object: parser.Identifier{Literal: "it"}},
  1236  					},
  1237  				},
  1238  			},
  1239  		},
  1240  		Error: "iteration of recursive query exceeded the limit 5",
  1241  	},
  1242  	{
  1243  		Name: "Select Into Variables",
  1244  		Query: parser.SelectQuery{
  1245  			SelectEntity: parser.SelectEntity{
  1246  				SelectClause: parser.SelectClause{
  1247  					Fields: []parser.QueryExpression{
  1248  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
  1249  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
  1250  					},
  1251  				},
  1252  				IntoClause: parser.IntoClause{
  1253  					Variables: []parser.Variable{
  1254  						{Name: "var1"},
  1255  						{Name: "var2"},
  1256  					},
  1257  				},
  1258  				FromClause: parser.FromClause{
  1259  					Tables: []parser.QueryExpression{
  1260  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1261  					},
  1262  				},
  1263  			},
  1264  			LimitClause: parser.LimitClause{
  1265  				Type:  parser.Token{Token: parser.LIMIT},
  1266  				Value: parser.NewIntegerValueFromString("1"),
  1267  			},
  1268  		},
  1269  		SetVariables: map[parser.Variable]value.Primary{
  1270  			parser.Variable{Name: "var1"}: value.NewString("1"),
  1271  			parser.Variable{Name: "var2"}: value.NewString("str1"),
  1272  		},
  1273  	},
  1274  	{
  1275  		Name: "Select Into Variables Empty Result Set",
  1276  		Query: parser.SelectQuery{
  1277  			SelectEntity: parser.SelectEntity{
  1278  				SelectClause: parser.SelectClause{
  1279  					Fields: []parser.QueryExpression{
  1280  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
  1281  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
  1282  					},
  1283  				},
  1284  				IntoClause: parser.IntoClause{
  1285  					Variables: []parser.Variable{
  1286  						{Name: "var1"},
  1287  						{Name: "var2"},
  1288  					},
  1289  				},
  1290  				FromClause: parser.FromClause{
  1291  					Tables: []parser.QueryExpression{
  1292  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1293  					},
  1294  				},
  1295  				WhereClause: parser.WhereClause{
  1296  					Filter: parser.NewTernaryValueFromString("false"),
  1297  				},
  1298  			},
  1299  			LimitClause: parser.LimitClause{
  1300  				Value: parser.NewIntegerValueFromString("1"),
  1301  			},
  1302  		},
  1303  		SetVariables: map[parser.Variable]value.Primary{
  1304  			parser.Variable{Name: "var1"}: value.NewNull(),
  1305  			parser.Variable{Name: "var2"}: value.NewNull(),
  1306  		},
  1307  	},
  1308  	{
  1309  		Name: "Select Into Variables Too Many Records",
  1310  		Query: parser.SelectQuery{
  1311  			SelectEntity: parser.SelectEntity{
  1312  				SelectClause: parser.SelectClause{
  1313  					Fields: []parser.QueryExpression{
  1314  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
  1315  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
  1316  					},
  1317  				},
  1318  				IntoClause: parser.IntoClause{
  1319  					Variables: []parser.Variable{
  1320  						{Name: "var1"},
  1321  						{Name: "var2"},
  1322  					},
  1323  				},
  1324  				FromClause: parser.FromClause{
  1325  					Tables: []parser.QueryExpression{
  1326  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1327  					},
  1328  				},
  1329  			},
  1330  		},
  1331  		Error: "select into query returns too many records, should return only one record",
  1332  	},
  1333  	{
  1334  		Name: "Select Into Variables Field Length Not Match",
  1335  		Query: parser.SelectQuery{
  1336  			SelectEntity: parser.SelectEntity{
  1337  				SelectClause: parser.SelectClause{
  1338  					Fields: []parser.QueryExpression{
  1339  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
  1340  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
  1341  					},
  1342  				},
  1343  				IntoClause: parser.IntoClause{
  1344  					Variables: []parser.Variable{
  1345  						{Name: "var1"},
  1346  					},
  1347  				},
  1348  				FromClause: parser.FromClause{
  1349  					Tables: []parser.QueryExpression{
  1350  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1351  					},
  1352  				},
  1353  			},
  1354  			LimitClause: parser.LimitClause{
  1355  				Value: parser.NewIntegerValueFromString("1"),
  1356  			},
  1357  		},
  1358  		Error: "select into query should return exactly 1 field",
  1359  	},
  1360  	{
  1361  		Name: "Select Into Variables Undeclared Variable",
  1362  		Query: parser.SelectQuery{
  1363  			SelectEntity: parser.SelectEntity{
  1364  				SelectClause: parser.SelectClause{
  1365  					Fields: []parser.QueryExpression{
  1366  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}}},
  1367  						parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}}},
  1368  					},
  1369  				},
  1370  				IntoClause: parser.IntoClause{
  1371  					Variables: []parser.Variable{
  1372  						{Name: "var1"},
  1373  						{Name: "undeclared"},
  1374  					},
  1375  				},
  1376  				FromClause: parser.FromClause{
  1377  					Tables: []parser.QueryExpression{
  1378  						parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1379  					},
  1380  				},
  1381  			},
  1382  			LimitClause: parser.LimitClause{
  1383  				Value: parser.NewIntegerValueFromString("1"),
  1384  			},
  1385  		},
  1386  		Error: "variable @undeclared is undeclared",
  1387  	},
  1388  }
  1389  
  1390  func TestSelect(t *testing.T) {
  1391  	defer func() {
  1392  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  1393  		initFlag(TestTx.Flags)
  1394  	}()
  1395  
  1396  	TestTx.Flags.Repository = TestDir
  1397  
  1398  	scope := NewReferenceScope(TestTx)
  1399  	ctx := context.Background()
  1400  	_ = scope.DeclareVariable(ctx, parser.VariableDeclaration{Assignments: []parser.VariableAssignment{
  1401  		{Variable: parser.Variable{Name: "var1"}},
  1402  		{Variable: parser.Variable{Name: "var2"}, Value: parser.NewIntegerValueFromString("2")},
  1403  	}})
  1404  
  1405  	for _, v := range selectTests {
  1406  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  1407  		result, err := Select(ctx, scope, v.Query)
  1408  		if err != nil {
  1409  			if len(v.Error) < 1 {
  1410  				t.Errorf("%s: unexpected error %q", v.Name, err)
  1411  			} else if err.Error() != v.Error {
  1412  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  1413  			}
  1414  			continue
  1415  		}
  1416  		if 0 < len(v.Error) {
  1417  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  1418  			continue
  1419  		}
  1420  		if v.Result != nil {
  1421  			if !reflect.DeepEqual(result, v.Result) {
  1422  				t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result)
  1423  			}
  1424  		}
  1425  		if 0 < len(v.SetVariables) {
  1426  			for variable, expectValue := range v.SetVariables {
  1427  				val, _ := scope.GetVariable(variable)
  1428  				if !reflect.DeepEqual(val, expectValue) {
  1429  					t.Errorf("%s: variable %s = %v, want %v", v.Name, variable, val, expectValue)
  1430  				}
  1431  			}
  1432  		}
  1433  	}
  1434  }
  1435  
  1436  var insertTests = []struct {
  1437  	Name         string
  1438  	Query        parser.InsertQuery
  1439  	ResultFile   *FileInfo
  1440  	UpdateCount  int
  1441  	ViewCache    ViewMap
  1442  	ResultScopes *ReferenceScope
  1443  	Error        string
  1444  }{
  1445  	{
  1446  		Name: "Insert Query",
  1447  		Query: parser.InsertQuery{
  1448  			WithClause: parser.WithClause{
  1449  				InlineTables: []parser.QueryExpression{
  1450  					parser.InlineTable{
  1451  						Name: parser.Identifier{Literal: "it"},
  1452  						Fields: []parser.QueryExpression{
  1453  							parser.Identifier{Literal: "c1"},
  1454  						},
  1455  						Query: parser.SelectQuery{
  1456  							SelectEntity: parser.SelectEntity{
  1457  								SelectClause: parser.SelectClause{
  1458  									Fields: []parser.QueryExpression{
  1459  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  1460  									},
  1461  								},
  1462  							},
  1463  						},
  1464  					},
  1465  				},
  1466  			},
  1467  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1468  			Fields: []parser.QueryExpression{
  1469  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1470  			},
  1471  			ValuesList: []parser.QueryExpression{
  1472  				parser.RowValue{
  1473  					Value: parser.ValueList{
  1474  						Values: []parser.QueryExpression{
  1475  							parser.NewIntegerValueFromString("4"),
  1476  						},
  1477  					},
  1478  				},
  1479  				parser.RowValue{
  1480  					Value: parser.ValueList{
  1481  						Values: []parser.QueryExpression{
  1482  							parser.Subquery{
  1483  								Query: parser.SelectQuery{
  1484  									SelectEntity: parser.SelectEntity{
  1485  										SelectClause: parser.SelectClause{
  1486  											Fields: []parser.QueryExpression{
  1487  												parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  1488  											},
  1489  										},
  1490  										FromClause: parser.FromClause{
  1491  											Tables: []parser.QueryExpression{
  1492  												parser.Table{Object: parser.Identifier{Literal: "it"}},
  1493  											},
  1494  										},
  1495  									},
  1496  								},
  1497  							},
  1498  						},
  1499  					},
  1500  				},
  1501  			},
  1502  		},
  1503  		ResultFile: &FileInfo{
  1504  			Path:      GetTestFilePath("table1.csv"),
  1505  			Delimiter: ',',
  1506  			NoHeader:  false,
  1507  			Encoding:  text.UTF8,
  1508  			LineBreak: text.LF,
  1509  			ForUpdate: true,
  1510  		},
  1511  		UpdateCount: 2,
  1512  		ViewCache: GenerateViewMap([]*View{
  1513  			{
  1514  				FileInfo: &FileInfo{
  1515  					Path:      GetTestFilePath("table1.csv"),
  1516  					Delimiter: ',',
  1517  					NoHeader:  false,
  1518  					Encoding:  text.UTF8,
  1519  					LineBreak: text.LF,
  1520  					ForUpdate: true,
  1521  				},
  1522  				Header: NewHeader("table1", []string{"column1", "column2"}),
  1523  				RecordSet: []Record{
  1524  					NewRecord([]value.Primary{
  1525  						value.NewString("1"),
  1526  						value.NewString("str1"),
  1527  					}),
  1528  					NewRecord([]value.Primary{
  1529  						value.NewString("2"),
  1530  						value.NewString("str2"),
  1531  					}),
  1532  					NewRecord([]value.Primary{
  1533  						value.NewString("3"),
  1534  						value.NewString("str3"),
  1535  					}),
  1536  					NewRecord([]value.Primary{
  1537  						value.NewInteger(4),
  1538  						value.NewNull(),
  1539  					}),
  1540  					NewRecord([]value.Primary{
  1541  						value.NewInteger(2),
  1542  						value.NewNull(),
  1543  					}),
  1544  				},
  1545  			},
  1546  		}),
  1547  	},
  1548  	{
  1549  		Name: "Insert Query For Temporary View",
  1550  		Query: parser.InsertQuery{
  1551  			Table: parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t"}},
  1552  			Fields: []parser.QueryExpression{
  1553  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1554  			},
  1555  			ValuesList: []parser.QueryExpression{
  1556  				parser.RowValue{
  1557  					Value: parser.ValueList{
  1558  						Values: []parser.QueryExpression{
  1559  							parser.NewIntegerValueFromString("4"),
  1560  						},
  1561  					},
  1562  				},
  1563  				parser.RowValue{
  1564  					Value: parser.ValueList{
  1565  						Values: []parser.QueryExpression{
  1566  							parser.NewIntegerValueFromString("2"),
  1567  						},
  1568  					},
  1569  				},
  1570  			},
  1571  		},
  1572  		ResultFile: &FileInfo{
  1573  			Path:      "tmpview",
  1574  			Delimiter: ',',
  1575  			ViewType:  ViewTypeTemporaryTable,
  1576  		},
  1577  		UpdateCount: 2,
  1578  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  1579  			{
  1580  				scopeNameTempTables: {
  1581  					"TMPVIEW": &View{
  1582  						Header: NewHeader("tmpview", []string{"column1", "column2"}),
  1583  						RecordSet: []Record{
  1584  							NewRecord([]value.Primary{
  1585  								value.NewString("1"),
  1586  								value.NewString("str1"),
  1587  							}),
  1588  							NewRecord([]value.Primary{
  1589  								value.NewString("2"),
  1590  								value.NewString("str2"),
  1591  							}),
  1592  							NewRecord([]value.Primary{
  1593  								value.NewInteger(4),
  1594  								value.NewNull(),
  1595  							}),
  1596  							NewRecord([]value.Primary{
  1597  								value.NewInteger(2),
  1598  								value.NewNull(),
  1599  							}),
  1600  						},
  1601  						FileInfo: &FileInfo{
  1602  							Path:      "tmpview",
  1603  							Delimiter: ',',
  1604  							ViewType:  ViewTypeTemporaryTable,
  1605  						},
  1606  					},
  1607  				},
  1608  			},
  1609  		}, nil, time.Time{}, nil),
  1610  	},
  1611  	{
  1612  		Name: "Insert Query All Fields",
  1613  		Query: parser.InsertQuery{
  1614  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1615  			ValuesList: []parser.QueryExpression{
  1616  				parser.RowValue{
  1617  					Value: parser.ValueList{
  1618  						Values: []parser.QueryExpression{
  1619  							parser.NewIntegerValueFromString("4"),
  1620  							parser.NewStringValue("str4"),
  1621  						},
  1622  					},
  1623  				},
  1624  				parser.RowValue{
  1625  					Value: parser.ValueList{
  1626  						Values: []parser.QueryExpression{
  1627  							parser.NewIntegerValueFromString("5"),
  1628  							parser.NewStringValue("str5"),
  1629  						},
  1630  					},
  1631  				},
  1632  			},
  1633  		},
  1634  		ResultFile: &FileInfo{
  1635  			Path:      GetTestFilePath("table1.csv"),
  1636  			Delimiter: ',',
  1637  			NoHeader:  false,
  1638  			Encoding:  text.UTF8,
  1639  			LineBreak: text.LF,
  1640  			ForUpdate: true,
  1641  		},
  1642  		UpdateCount: 2,
  1643  	},
  1644  	{
  1645  		Name: "Insert Query Inline Table Cannot Be Updated Error",
  1646  		Query: parser.InsertQuery{
  1647  			WithClause: parser.WithClause{
  1648  				InlineTables: []parser.QueryExpression{
  1649  					parser.InlineTable{
  1650  						Name: parser.Identifier{Literal: "it"},
  1651  						Fields: []parser.QueryExpression{
  1652  							parser.Identifier{Literal: "c1"},
  1653  						},
  1654  						Query: parser.SelectQuery{
  1655  							SelectEntity: parser.SelectEntity{
  1656  								SelectClause: parser.SelectClause{
  1657  									Fields: []parser.QueryExpression{
  1658  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  1659  									},
  1660  								},
  1661  							},
  1662  						},
  1663  					},
  1664  				},
  1665  			},
  1666  			Table: parser.Table{Object: parser.Identifier{Literal: "it"}},
  1667  			Fields: []parser.QueryExpression{
  1668  				parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  1669  			},
  1670  			ValuesList: []parser.QueryExpression{
  1671  				parser.RowValue{
  1672  					Value: parser.ValueList{
  1673  						Values: []parser.QueryExpression{
  1674  							parser.NewIntegerValueFromString("4"),
  1675  						},
  1676  					},
  1677  				},
  1678  				parser.RowValue{
  1679  					Value: parser.ValueList{
  1680  						Values: []parser.QueryExpression{
  1681  							parser.Subquery{
  1682  								Query: parser.SelectQuery{
  1683  									SelectEntity: parser.SelectEntity{
  1684  										SelectClause: parser.SelectClause{
  1685  											Fields: []parser.QueryExpression{
  1686  												parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  1687  											},
  1688  										},
  1689  										FromClause: parser.FromClause{
  1690  											Tables: []parser.QueryExpression{
  1691  												parser.Table{Object: parser.Identifier{Literal: "it"}},
  1692  											},
  1693  										},
  1694  									},
  1695  								},
  1696  							},
  1697  						},
  1698  					},
  1699  				},
  1700  			},
  1701  		},
  1702  		Error: "inline table cannot be updated",
  1703  	},
  1704  	{
  1705  		Name: "Insert Query File Does Not Exist Error",
  1706  		Query: parser.InsertQuery{
  1707  			Table: parser.Table{Object: parser.Identifier{Literal: "notexist"}},
  1708  			Fields: []parser.QueryExpression{
  1709  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1710  			},
  1711  			ValuesList: []parser.QueryExpression{
  1712  				parser.RowValue{
  1713  					Value: parser.ValueList{
  1714  						Values: []parser.QueryExpression{
  1715  							parser.NewIntegerValueFromString("4"),
  1716  						},
  1717  					},
  1718  				},
  1719  				parser.RowValue{
  1720  					Value: parser.ValueList{
  1721  						Values: []parser.QueryExpression{
  1722  							parser.NewIntegerValueFromString("5"),
  1723  						},
  1724  					},
  1725  				},
  1726  			},
  1727  		},
  1728  		Error: "file notexist does not exist",
  1729  	},
  1730  	{
  1731  		Name: "Insert Query Field Does Not Exist Error",
  1732  		Query: parser.InsertQuery{
  1733  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1734  			Fields: []parser.QueryExpression{
  1735  				parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  1736  			},
  1737  			ValuesList: []parser.QueryExpression{
  1738  				parser.RowValue{
  1739  					Value: parser.ValueList{
  1740  						Values: []parser.QueryExpression{
  1741  							parser.NewIntegerValueFromString("4"),
  1742  						},
  1743  					},
  1744  				},
  1745  				parser.RowValue{
  1746  					Value: parser.ValueList{
  1747  						Values: []parser.QueryExpression{
  1748  							parser.NewIntegerValueFromString("5"),
  1749  						},
  1750  					},
  1751  				},
  1752  			},
  1753  		},
  1754  		Error: "field notexist does not exist",
  1755  	},
  1756  	{
  1757  		Name: "Insert Select Query",
  1758  		Query: parser.InsertQuery{
  1759  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1760  			Fields: []parser.QueryExpression{
  1761  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1762  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  1763  			},
  1764  			Query: parser.SelectQuery{
  1765  				SelectEntity: parser.SelectEntity{
  1766  					SelectClause: parser.SelectClause{
  1767  						Fields: []parser.QueryExpression{
  1768  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
  1769  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
  1770  						},
  1771  					},
  1772  					FromClause: parser.FromClause{
  1773  						Tables: []parser.QueryExpression{
  1774  							parser.Table{Object: parser.Identifier{Literal: "table2"}},
  1775  						},
  1776  					},
  1777  				},
  1778  			},
  1779  		},
  1780  		ResultFile: &FileInfo{
  1781  			Path:      GetTestFilePath("table1.csv"),
  1782  			Delimiter: ',',
  1783  			NoHeader:  false,
  1784  			Encoding:  text.UTF8,
  1785  			LineBreak: text.LF,
  1786  			ForUpdate: true,
  1787  		},
  1788  		UpdateCount: 3,
  1789  	},
  1790  	{
  1791  		Name: "Insert Select Query Field Does Not Exist Error",
  1792  		Query: parser.InsertQuery{
  1793  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1794  			Fields: []parser.QueryExpression{
  1795  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1796  			},
  1797  			Query: parser.SelectQuery{
  1798  				SelectEntity: parser.SelectEntity{
  1799  					SelectClause: parser.SelectClause{
  1800  						Fields: []parser.QueryExpression{
  1801  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
  1802  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
  1803  						},
  1804  					},
  1805  					FromClause: parser.FromClause{
  1806  						Tables: []parser.QueryExpression{
  1807  							parser.Table{Object: parser.Identifier{Literal: "table2"}},
  1808  						},
  1809  					},
  1810  				},
  1811  			},
  1812  		},
  1813  		Error: "select query should return exactly 1 field",
  1814  	},
  1815  }
  1816  
  1817  func TestInsert(t *testing.T) {
  1818  	defer func() {
  1819  		_ = TestTx.ReleaseResources()
  1820  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  1821  		initFlag(TestTx.Flags)
  1822  	}()
  1823  
  1824  	TestTx.Flags.Repository = TestDir
  1825  	TestTx.Flags.Quiet = false
  1826  
  1827  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  1828  		{
  1829  			scopeNameTempTables: {
  1830  				"TMPVIEW": &View{
  1831  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  1832  					RecordSet: []Record{
  1833  						NewRecord([]value.Primary{
  1834  							value.NewString("1"),
  1835  							value.NewString("str1"),
  1836  						}),
  1837  						NewRecord([]value.Primary{
  1838  							value.NewString("2"),
  1839  							value.NewString("str2"),
  1840  						}),
  1841  					},
  1842  					FileInfo: &FileInfo{
  1843  						Path:      "tmpview",
  1844  						Delimiter: ',',
  1845  						ViewType:  ViewTypeTemporaryTable,
  1846  					},
  1847  				},
  1848  			},
  1849  		},
  1850  	}, nil, time.Time{}, nil)
  1851  
  1852  	ctx := context.Background()
  1853  	for _, v := range insertTests {
  1854  		_ = TestTx.ReleaseResources()
  1855  		result, cnt, err := Insert(ctx, scope, v.Query)
  1856  		if err != nil {
  1857  			if len(v.Error) < 1 {
  1858  				t.Errorf("%s: unexpected error %q", v.Name, err)
  1859  			} else if err.Error() != v.Error {
  1860  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  1861  			}
  1862  			continue
  1863  		}
  1864  		if 0 < len(v.Error) {
  1865  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  1866  			continue
  1867  		}
  1868  
  1869  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  1870  			view := value.(*View)
  1871  			if view.FileInfo.Handler != nil {
  1872  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  1873  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  1874  				}
  1875  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  1876  				view.FileInfo.Handler = nil
  1877  			}
  1878  			return true
  1879  		})
  1880  
  1881  		if !reflect.DeepEqual(result, v.ResultFile) {
  1882  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile)
  1883  		}
  1884  
  1885  		if !reflect.DeepEqual(cnt, v.UpdateCount) {
  1886  			t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount)
  1887  		}
  1888  
  1889  		if v.ViewCache.SyncMap != nil {
  1890  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  1891  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  1892  			}
  1893  		}
  1894  		if v.ResultScopes != nil {
  1895  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  1896  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  1897  			}
  1898  		}
  1899  	}
  1900  }
  1901  
  1902  var updateTests = []struct {
  1903  	Name         string
  1904  	Query        parser.UpdateQuery
  1905  	ResultFiles  []*FileInfo
  1906  	UpdateCounts []int
  1907  	ViewCache    ViewMap
  1908  	ResultScopes *ReferenceScope
  1909  	Error        string
  1910  }{
  1911  	{
  1912  		Name: "Update Query",
  1913  		Query: parser.UpdateQuery{
  1914  			WithClause: parser.WithClause{
  1915  				InlineTables: []parser.QueryExpression{
  1916  					parser.InlineTable{
  1917  						Name: parser.Identifier{Literal: "it"},
  1918  						Fields: []parser.QueryExpression{
  1919  							parser.Identifier{Literal: "c1"},
  1920  						},
  1921  						Query: parser.SelectQuery{
  1922  							SelectEntity: parser.SelectEntity{
  1923  								SelectClause: parser.SelectClause{
  1924  									Fields: []parser.QueryExpression{
  1925  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  1926  									},
  1927  								},
  1928  							},
  1929  						},
  1930  					},
  1931  				},
  1932  			},
  1933  			Tables: []parser.QueryExpression{
  1934  				parser.Table{Object: parser.Identifier{Literal: "table1"}},
  1935  			},
  1936  			SetList: []parser.UpdateSet{
  1937  				{
  1938  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1939  					Value: parser.NewStringValue("update1"),
  1940  				},
  1941  				{
  1942  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  1943  					Value: parser.NewStringValue("update2"),
  1944  				},
  1945  			},
  1946  			WhereClause: parser.WhereClause{
  1947  				Filter: parser.Comparison{
  1948  					LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  1949  					RHS: parser.Subquery{
  1950  						Query: parser.SelectQuery{
  1951  							SelectEntity: parser.SelectEntity{
  1952  								SelectClause: parser.SelectClause{
  1953  									Fields: []parser.QueryExpression{
  1954  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  1955  									},
  1956  								},
  1957  								FromClause: parser.FromClause{
  1958  									Tables: []parser.QueryExpression{
  1959  										parser.Table{Object: parser.Identifier{Literal: "it"}},
  1960  									},
  1961  								},
  1962  							},
  1963  						},
  1964  					},
  1965  					Operator: parser.Token{Token: '=', Literal: "="},
  1966  				},
  1967  			},
  1968  		},
  1969  		ResultFiles: []*FileInfo{
  1970  			{
  1971  				Path:      GetTestFilePath("table1.csv"),
  1972  				Delimiter: ',',
  1973  				NoHeader:  false,
  1974  				Encoding:  text.UTF8,
  1975  				LineBreak: text.LF,
  1976  				ForUpdate: true,
  1977  			},
  1978  		},
  1979  		UpdateCounts: []int{1},
  1980  		ViewCache: GenerateViewMap([]*View{
  1981  			{
  1982  				FileInfo: &FileInfo{
  1983  					Path:      GetTestFilePath("table1.csv"),
  1984  					Delimiter: ',',
  1985  					NoHeader:  false,
  1986  					Encoding:  text.UTF8,
  1987  					LineBreak: text.LF,
  1988  					ForUpdate: true,
  1989  				},
  1990  				Header: NewHeader("table1", []string{"column1", "column2"}),
  1991  				RecordSet: []Record{
  1992  					NewRecord([]value.Primary{
  1993  						value.NewString("1"),
  1994  						value.NewString("str1"),
  1995  					}),
  1996  					NewRecord([]value.Primary{
  1997  						value.NewString("update1"),
  1998  						value.NewString("update2"),
  1999  					}),
  2000  					NewRecord([]value.Primary{
  2001  						value.NewString("3"),
  2002  						value.NewString("str3"),
  2003  					}),
  2004  				},
  2005  			},
  2006  		}),
  2007  	},
  2008  	{
  2009  		Name: "Update Query For Temporary View",
  2010  		Query: parser.UpdateQuery{
  2011  			Tables: []parser.QueryExpression{
  2012  				parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t1"}},
  2013  			},
  2014  			SetList: []parser.UpdateSet{
  2015  				{
  2016  					Field: parser.ColumnNumber{View: parser.Identifier{Literal: "t1"}, Number: value.NewInteger(2)},
  2017  					Value: parser.NewStringValue("update"),
  2018  				},
  2019  			},
  2020  		},
  2021  		ResultFiles: []*FileInfo{
  2022  			{
  2023  				Path:      "tmpview",
  2024  				Delimiter: ',',
  2025  				ViewType:  ViewTypeTemporaryTable,
  2026  			},
  2027  		},
  2028  		UpdateCounts: []int{2},
  2029  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  2030  			{
  2031  				scopeNameTempTables: {
  2032  					"TMPVIEW": &View{
  2033  						Header: NewHeader("tmpview", []string{"column1", "column2"}),
  2034  						RecordSet: []Record{
  2035  							NewRecord([]value.Primary{
  2036  								value.NewString("1"),
  2037  								value.NewString("update"),
  2038  							}),
  2039  							NewRecord([]value.Primary{
  2040  								value.NewString("2"),
  2041  								value.NewString("update"),
  2042  							}),
  2043  						},
  2044  						FileInfo: &FileInfo{
  2045  							Path:      "tmpview",
  2046  							Delimiter: ',',
  2047  							ViewType:  ViewTypeTemporaryTable,
  2048  						},
  2049  					},
  2050  				},
  2051  			},
  2052  		}, nil, time.Time{}, nil),
  2053  	},
  2054  	{
  2055  		Name: "Update Query Multiple Table",
  2056  		Query: parser.UpdateQuery{
  2057  			Tables: []parser.QueryExpression{
  2058  				parser.Table{Object: parser.Identifier{Literal: "t1"}},
  2059  			},
  2060  			SetList: []parser.UpdateSet{
  2061  				{
  2062  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2063  					Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}},
  2064  				},
  2065  			},
  2066  			FromClause: parser.FromClause{
  2067  				Tables: []parser.QueryExpression{
  2068  					parser.Table{Object: parser.Join{
  2069  						Table: parser.Table{
  2070  							Object: parser.Identifier{Literal: "table1"},
  2071  							Alias:  parser.Identifier{Literal: "t1"},
  2072  						},
  2073  						JoinTable: parser.Table{
  2074  							Object: parser.Identifier{Literal: "table2"},
  2075  							Alias:  parser.Identifier{Literal: "t2"},
  2076  						},
  2077  						Condition: parser.JoinCondition{
  2078  							On: parser.Comparison{
  2079  								LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2080  								RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  2081  								Operator: parser.Token{Token: '=', Literal: "="},
  2082  							},
  2083  						},
  2084  					}},
  2085  				},
  2086  			},
  2087  		},
  2088  		ResultFiles: []*FileInfo{
  2089  			{
  2090  				Path:      GetTestFilePath("table1.csv"),
  2091  				Delimiter: ',',
  2092  				NoHeader:  false,
  2093  				Encoding:  text.UTF8,
  2094  				LineBreak: text.LF,
  2095  				ForUpdate: true,
  2096  			},
  2097  		},
  2098  		UpdateCounts: []int{2},
  2099  	},
  2100  	{
  2101  		Name: "Update Query Alias Must Be Specified Error",
  2102  		Query: parser.UpdateQuery{
  2103  			Tables: []parser.QueryExpression{
  2104  				parser.Table{
  2105  					Object: parser.TableFunction{
  2106  						Name: "data",
  2107  						Args: []parser.QueryExpression{
  2108  							parser.NewStringValue("c1,c2\n1,a\n2,b"),
  2109  						},
  2110  					},
  2111  				},
  2112  			},
  2113  			SetList: []parser.UpdateSet{
  2114  				{
  2115  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2116  					Value: parser.NewStringValue("update1"),
  2117  				},
  2118  				{
  2119  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2120  					Value: parser.NewStringValue("update2"),
  2121  				},
  2122  			},
  2123  		},
  2124  		Error: "alias to table identification function or URL must be specified for update",
  2125  	},
  2126  	{
  2127  		Name: "Update Query Inline Table Cannot Be Updated Error",
  2128  		Query: parser.UpdateQuery{
  2129  			Tables: []parser.QueryExpression{
  2130  				parser.Table{
  2131  					Object: parser.TableFunction{
  2132  						Name: "data",
  2133  						Args: []parser.QueryExpression{
  2134  							parser.NewStringValue("c1,c2\n1,a\n2,b"),
  2135  						},
  2136  					},
  2137  					Alias: parser.Identifier{
  2138  						Literal: "t",
  2139  					},
  2140  				},
  2141  			},
  2142  			SetList: []parser.UpdateSet{
  2143  				{
  2144  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2145  					Value: parser.NewStringValue("update1"),
  2146  				},
  2147  				{
  2148  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2149  					Value: parser.NewStringValue("update2"),
  2150  				},
  2151  			},
  2152  		},
  2153  		Error: "inline table cannot be updated",
  2154  	},
  2155  	{
  2156  		Name: "Update Query File Does Not Exist Error",
  2157  		Query: parser.UpdateQuery{
  2158  			Tables: []parser.QueryExpression{
  2159  				parser.Table{Object: parser.Identifier{Literal: "notexist"}},
  2160  			},
  2161  			SetList: []parser.UpdateSet{
  2162  				{
  2163  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2164  					Value: parser.NewStringValue("update"),
  2165  				},
  2166  			},
  2167  			WhereClause: parser.WhereClause{
  2168  				Filter: parser.Comparison{
  2169  					LHS:      parser.Identifier{Literal: "column1"},
  2170  					RHS:      parser.NewIntegerValueFromString("2"),
  2171  					Operator: parser.Token{Token: '=', Literal: "="},
  2172  				},
  2173  			},
  2174  		},
  2175  		Error: "file notexist does not exist",
  2176  	},
  2177  	{
  2178  		Name: "Update Query Filter Error",
  2179  		Query: parser.UpdateQuery{
  2180  			Tables: []parser.QueryExpression{
  2181  				parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2182  			},
  2183  			SetList: []parser.UpdateSet{
  2184  				{
  2185  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2186  					Value: parser.NewStringValue("update"),
  2187  				},
  2188  			},
  2189  			WhereClause: parser.WhereClause{
  2190  				Filter: parser.Comparison{
  2191  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  2192  					RHS:      parser.NewIntegerValueFromString("2"),
  2193  					Operator: parser.Token{Token: '=', Literal: "="},
  2194  				},
  2195  			},
  2196  		},
  2197  		Error: "field notexist does not exist",
  2198  	},
  2199  	{
  2200  		Name: "Update Query File Is Not Loaded Error",
  2201  		Query: parser.UpdateQuery{
  2202  			Tables: []parser.QueryExpression{
  2203  				parser.Table{Object: parser.Identifier{Literal: "notexist"}},
  2204  			},
  2205  			SetList: []parser.UpdateSet{
  2206  				{
  2207  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2208  					Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}},
  2209  				},
  2210  			},
  2211  			FromClause: parser.FromClause{
  2212  				Tables: []parser.QueryExpression{
  2213  					parser.Table{Object: parser.Join{
  2214  						Table: parser.Table{
  2215  							Object: parser.Identifier{Literal: "table1"},
  2216  							Alias:  parser.Identifier{Literal: "t1"},
  2217  						},
  2218  						JoinTable: parser.Table{
  2219  							Object: parser.Identifier{Literal: "table2"},
  2220  							Alias:  parser.Identifier{Literal: "t2"},
  2221  						},
  2222  						Condition: parser.JoinCondition{
  2223  							On: parser.Comparison{
  2224  								LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2225  								RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  2226  								Operator: parser.Token{Token: '=', Literal: "="},
  2227  							},
  2228  						},
  2229  					}},
  2230  				},
  2231  			},
  2232  		},
  2233  		Error: "table notexist is not loaded",
  2234  	},
  2235  	{
  2236  		Name: "Update Query Update Table Is Not Specified Error",
  2237  		Query: parser.UpdateQuery{
  2238  			Tables: []parser.QueryExpression{
  2239  				parser.Table{Object: parser.Identifier{Literal: "t2"}},
  2240  			},
  2241  			SetList: []parser.UpdateSet{
  2242  				{
  2243  					Field: parser.FieldReference{View: parser.Identifier{Literal: "t1"}, Column: parser.Identifier{Literal: "column2"}},
  2244  					Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}},
  2245  				},
  2246  			},
  2247  			FromClause: parser.FromClause{
  2248  				Tables: []parser.QueryExpression{
  2249  					parser.Table{Object: parser.Join{
  2250  						Table: parser.Table{
  2251  							Object: parser.Identifier{Literal: "table1"},
  2252  							Alias:  parser.Identifier{Literal: "t1"},
  2253  						},
  2254  						JoinTable: parser.Table{
  2255  							Object: parser.Identifier{Literal: "table2"},
  2256  							Alias:  parser.Identifier{Literal: "t2"},
  2257  						},
  2258  						Condition: parser.JoinCondition{
  2259  							On: parser.Comparison{
  2260  								LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2261  								RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  2262  								Operator: parser.Token{Token: '=', Literal: "="},
  2263  							},
  2264  						},
  2265  					}},
  2266  				},
  2267  			},
  2268  		},
  2269  		Error: "field t1.column2 does not exist in the tables to update",
  2270  	},
  2271  	{
  2272  		Name: "Update Query Update Field Error",
  2273  		Query: parser.UpdateQuery{
  2274  			Tables: []parser.QueryExpression{
  2275  				parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2276  			},
  2277  			SetList: []parser.UpdateSet{
  2278  				{
  2279  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  2280  					Value: parser.NewStringValue("update"),
  2281  				},
  2282  			},
  2283  			WhereClause: parser.WhereClause{
  2284  				Filter: parser.Comparison{
  2285  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2286  					RHS:      parser.NewIntegerValueFromString("2"),
  2287  					Operator: parser.Token{Token: '=', Literal: "="},
  2288  				},
  2289  			},
  2290  		},
  2291  		Error: "field notexist does not exist",
  2292  	},
  2293  	{
  2294  		Name: "Update Query Update Value Error",
  2295  		Query: parser.UpdateQuery{
  2296  			Tables: []parser.QueryExpression{
  2297  				parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2298  			},
  2299  			SetList: []parser.UpdateSet{
  2300  				{
  2301  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2302  					Value: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  2303  				},
  2304  			},
  2305  			WhereClause: parser.WhereClause{
  2306  				Filter: parser.Comparison{
  2307  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2308  					RHS:      parser.NewIntegerValueFromString("2"),
  2309  					Operator: parser.Token{Token: '=', Literal: "="},
  2310  				},
  2311  			},
  2312  		},
  2313  		Error: "field notexist does not exist",
  2314  	},
  2315  	{
  2316  		Name: "Update Query Record Is Ambiguous Error",
  2317  		Query: parser.UpdateQuery{
  2318  			Tables: []parser.QueryExpression{
  2319  				parser.Table{Object: parser.Identifier{Literal: "t1"}},
  2320  			},
  2321  			SetList: []parser.UpdateSet{
  2322  				{
  2323  					Field: parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2324  					Value: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}},
  2325  				},
  2326  			},
  2327  			FromClause: parser.FromClause{
  2328  				Tables: []parser.QueryExpression{
  2329  					parser.Table{Object: parser.Join{
  2330  						Table: parser.Table{
  2331  							Object: parser.Identifier{Literal: "table1"},
  2332  							Alias:  parser.Identifier{Literal: "t1"},
  2333  						},
  2334  						JoinTable: parser.Table{
  2335  							Object: parser.Identifier{Literal: "table2"},
  2336  							Alias:  parser.Identifier{Literal: "t2"},
  2337  						},
  2338  						JoinType: parser.Token{Token: parser.CROSS, Literal: "cross"},
  2339  					}},
  2340  				},
  2341  			},
  2342  		},
  2343  		Error: "value column4 to set in the field column2 is ambiguous",
  2344  	},
  2345  }
  2346  
  2347  func TestUpdate(t *testing.T) {
  2348  	defer func() {
  2349  		_ = TestTx.ReleaseResources()
  2350  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  2351  		initFlag(TestTx.Flags)
  2352  	}()
  2353  
  2354  	TestTx.Flags.Repository = TestDir
  2355  	TestTx.Flags.Quiet = false
  2356  
  2357  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  2358  		{
  2359  			scopeNameTempTables: {
  2360  				"TMPVIEW": &View{
  2361  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  2362  					RecordSet: []Record{
  2363  						NewRecord([]value.Primary{
  2364  							value.NewString("1"),
  2365  							value.NewString("str1"),
  2366  						}),
  2367  						NewRecord([]value.Primary{
  2368  							value.NewString("2"),
  2369  							value.NewString("str2"),
  2370  						}),
  2371  					},
  2372  					FileInfo: &FileInfo{
  2373  						Path:      "tmpview",
  2374  						Delimiter: ',',
  2375  						ViewType:  ViewTypeTemporaryTable,
  2376  					},
  2377  				},
  2378  			},
  2379  		},
  2380  	}, nil, time.Time{}, nil)
  2381  
  2382  	ctx := context.Background()
  2383  	for _, v := range updateTests {
  2384  		_ = TestTx.ReleaseResources()
  2385  		files, cnt, err := Update(ctx, scope, v.Query)
  2386  		if err != nil {
  2387  			if len(v.Error) < 1 {
  2388  				t.Errorf("%s: unexpected error %q", v.Name, err)
  2389  			} else if err.Error() != v.Error {
  2390  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  2391  			}
  2392  			continue
  2393  		}
  2394  		if 0 < len(v.Error) {
  2395  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  2396  			continue
  2397  		}
  2398  
  2399  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  2400  			view := value.(*View)
  2401  			if view.FileInfo.Handler != nil {
  2402  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  2403  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  2404  				}
  2405  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  2406  				view.FileInfo.Handler = nil
  2407  			}
  2408  			return true
  2409  		})
  2410  
  2411  		if !reflect.DeepEqual(files, v.ResultFiles) {
  2412  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, files, v.ResultFiles)
  2413  		}
  2414  
  2415  		if !reflect.DeepEqual(cnt, v.UpdateCounts) {
  2416  			t.Errorf("%s: update count = %v, want %v", v.Name, cnt, v.UpdateCounts)
  2417  		}
  2418  
  2419  		if v.ViewCache.SyncMap != nil {
  2420  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  2421  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  2422  			}
  2423  		}
  2424  		if v.ResultScopes != nil {
  2425  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  2426  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  2427  			}
  2428  		}
  2429  	}
  2430  }
  2431  
  2432  var replaceTests = []struct {
  2433  	Name         string
  2434  	Query        parser.ReplaceQuery
  2435  	ResultFile   *FileInfo
  2436  	UpdateCount  int
  2437  	ViewCache    ViewMap
  2438  	ResultScopes *ReferenceScope
  2439  	Error        string
  2440  }{
  2441  	{
  2442  		Name: "Replace Query",
  2443  		Query: parser.ReplaceQuery{
  2444  			WithClause: parser.WithClause{
  2445  				InlineTables: []parser.QueryExpression{
  2446  					parser.InlineTable{
  2447  						Name: parser.Identifier{Literal: "it"},
  2448  						Fields: []parser.QueryExpression{
  2449  							parser.Identifier{Literal: "c1"},
  2450  							parser.Identifier{Literal: "c2"},
  2451  						},
  2452  						Query: parser.SelectQuery{
  2453  							SelectEntity: parser.SelectEntity{
  2454  								SelectClause: parser.SelectClause{
  2455  									Fields: []parser.QueryExpression{
  2456  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  2457  										parser.Field{Object: parser.NewStringValue("str3")},
  2458  									},
  2459  								},
  2460  							},
  2461  						},
  2462  					},
  2463  				},
  2464  			},
  2465  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2466  			Fields: []parser.QueryExpression{
  2467  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2468  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2469  			},
  2470  			Keys: []parser.QueryExpression{
  2471  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2472  			},
  2473  			ValuesList: []parser.QueryExpression{
  2474  				parser.RowValue{
  2475  					Value: parser.ValueList{
  2476  						Values: []parser.QueryExpression{
  2477  							parser.NewIntegerValueFromString("4"),
  2478  							parser.NewStringValue("str4"),
  2479  						},
  2480  					},
  2481  				},
  2482  				parser.RowValue{
  2483  					Value: parser.Subquery{
  2484  						Query: parser.SelectQuery{
  2485  							SelectEntity: parser.SelectEntity{
  2486  								SelectClause: parser.SelectClause{
  2487  									Fields: []parser.QueryExpression{
  2488  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  2489  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c2"}}},
  2490  									},
  2491  								},
  2492  								FromClause: parser.FromClause{
  2493  									Tables: []parser.QueryExpression{
  2494  										parser.Table{Object: parser.Identifier{Literal: "it"}},
  2495  									},
  2496  								},
  2497  							},
  2498  						},
  2499  					},
  2500  				},
  2501  			},
  2502  		},
  2503  		ResultFile: &FileInfo{
  2504  			Path:      GetTestFilePath("table1.csv"),
  2505  			Delimiter: ',',
  2506  			NoHeader:  false,
  2507  			Encoding:  text.UTF8,
  2508  			LineBreak: text.LF,
  2509  			ForUpdate: true,
  2510  		},
  2511  		UpdateCount: 2,
  2512  		ViewCache: GenerateViewMap([]*View{
  2513  			{
  2514  				FileInfo: &FileInfo{
  2515  					Path:      GetTestFilePath("table1.csv"),
  2516  					Delimiter: ',',
  2517  					NoHeader:  false,
  2518  					Encoding:  text.UTF8,
  2519  					LineBreak: text.LF,
  2520  					ForUpdate: true,
  2521  				},
  2522  				Header: NewHeader("table1", []string{"column1", "column2"}),
  2523  				RecordSet: []Record{
  2524  					NewRecord([]value.Primary{
  2525  						value.NewString("1"),
  2526  						value.NewString("str1"),
  2527  					}),
  2528  					NewRecord([]value.Primary{
  2529  						value.NewString("2"),
  2530  						value.NewString("str3"),
  2531  					}),
  2532  					NewRecord([]value.Primary{
  2533  						value.NewString("3"),
  2534  						value.NewString("str3"),
  2535  					}),
  2536  					NewRecord([]value.Primary{
  2537  						value.NewInteger(4),
  2538  						value.NewString("str4"),
  2539  					}),
  2540  				},
  2541  			},
  2542  		}),
  2543  	},
  2544  	{
  2545  		Name: "Replace Query to Empty Table",
  2546  		Query: parser.ReplaceQuery{
  2547  			Table: parser.Table{Object: parser.Identifier{Literal: "table_empty"}},
  2548  			Fields: []parser.QueryExpression{
  2549  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2550  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2551  			},
  2552  			Keys: []parser.QueryExpression{
  2553  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2554  			},
  2555  			ValuesList: []parser.QueryExpression{
  2556  				parser.RowValue{
  2557  					Value: parser.ValueList{
  2558  						Values: []parser.QueryExpression{
  2559  							parser.NewIntegerValueFromString("4"),
  2560  							parser.NewStringValue("str4"),
  2561  						},
  2562  					},
  2563  				},
  2564  			},
  2565  		},
  2566  		ResultFile: &FileInfo{
  2567  			Path:      GetTestFilePath("table_empty.csv"),
  2568  			Delimiter: ',',
  2569  			NoHeader:  false,
  2570  			Encoding:  text.UTF8,
  2571  			LineBreak: text.LF,
  2572  			ForUpdate: true,
  2573  		},
  2574  		UpdateCount: 1,
  2575  		ViewCache: GenerateViewMap([]*View{
  2576  			{
  2577  				FileInfo: &FileInfo{
  2578  					Path:      GetTestFilePath("table_empty.csv"),
  2579  					Delimiter: ',',
  2580  					NoHeader:  false,
  2581  					Encoding:  text.UTF8,
  2582  					LineBreak: text.LF,
  2583  					ForUpdate: true,
  2584  				},
  2585  				Header: NewHeader("table_empty", []string{"column1", "column2"}),
  2586  				RecordSet: []Record{
  2587  					NewRecord([]value.Primary{
  2588  						value.NewInteger(4),
  2589  						value.NewString("str4"),
  2590  					}),
  2591  				},
  2592  			},
  2593  		}),
  2594  	},
  2595  	{
  2596  		Name: "Replace Query For Temporary View",
  2597  		Query: parser.ReplaceQuery{
  2598  			Table: parser.Table{Object: parser.Identifier{Literal: "tmpview"}, Alias: parser.Identifier{Literal: "t"}},
  2599  			Fields: []parser.QueryExpression{
  2600  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2601  			},
  2602  			Keys: []parser.QueryExpression{
  2603  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2604  			},
  2605  			ValuesList: []parser.QueryExpression{
  2606  				parser.RowValue{
  2607  					Value: parser.ValueList{
  2608  						Values: []parser.QueryExpression{
  2609  							parser.NewIntegerValueFromString("4"),
  2610  						},
  2611  					},
  2612  				},
  2613  				parser.RowValue{
  2614  					Value: parser.ValueList{
  2615  						Values: []parser.QueryExpression{
  2616  							parser.NewIntegerValueFromString("2"),
  2617  						},
  2618  					},
  2619  				},
  2620  			},
  2621  		},
  2622  		ResultFile: &FileInfo{
  2623  			Path:      "tmpview",
  2624  			Delimiter: ',',
  2625  			ViewType:  ViewTypeTemporaryTable,
  2626  		},
  2627  		UpdateCount: 2,
  2628  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  2629  			{
  2630  				scopeNameTempTables: {
  2631  					"TMPVIEW": &View{
  2632  						Header: NewHeader("tmpview", []string{"column1", "column2"}),
  2633  						RecordSet: []Record{
  2634  							NewRecord([]value.Primary{
  2635  								value.NewString("1"),
  2636  								value.NewString("str1"),
  2637  							}),
  2638  							NewRecord([]value.Primary{
  2639  								value.NewString("2"),
  2640  								value.NewString("str2"),
  2641  							}),
  2642  							NewRecord([]value.Primary{
  2643  								value.NewInteger(4),
  2644  								value.NewNull(),
  2645  							}),
  2646  						},
  2647  						FileInfo: &FileInfo{
  2648  							Path:      "tmpview",
  2649  							Delimiter: ',',
  2650  							ViewType:  ViewTypeTemporaryTable,
  2651  						},
  2652  					},
  2653  				},
  2654  			},
  2655  		}, nil, time.Time{}, nil),
  2656  	},
  2657  	{
  2658  		Name: "Replace Query All Fields",
  2659  		Query: parser.ReplaceQuery{
  2660  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2661  			Keys: []parser.QueryExpression{
  2662  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2663  			},
  2664  			ValuesList: []parser.QueryExpression{
  2665  				parser.RowValue{
  2666  					Value: parser.ValueList{
  2667  						Values: []parser.QueryExpression{
  2668  							parser.NewIntegerValueFromString("4"),
  2669  							parser.NewStringValue("str4"),
  2670  						},
  2671  					},
  2672  				},
  2673  				parser.RowValue{
  2674  					Value: parser.ValueList{
  2675  						Values: []parser.QueryExpression{
  2676  							parser.NewIntegerValueFromString("5"),
  2677  							parser.NewStringValue("str5"),
  2678  						},
  2679  					},
  2680  				},
  2681  			},
  2682  		},
  2683  		ResultFile: &FileInfo{
  2684  			Path:      GetTestFilePath("table1.csv"),
  2685  			Delimiter: ',',
  2686  			NoHeader:  false,
  2687  			Encoding:  text.UTF8,
  2688  			LineBreak: text.LF,
  2689  			ForUpdate: true,
  2690  		},
  2691  		UpdateCount: 2,
  2692  	},
  2693  	{
  2694  		Name: "Replace Query Inline Table Cannot Be Updated Error",
  2695  		Query: parser.ReplaceQuery{
  2696  			WithClause: parser.WithClause{
  2697  				InlineTables: []parser.QueryExpression{
  2698  					parser.InlineTable{
  2699  						Name: parser.Identifier{Literal: "it"},
  2700  						Fields: []parser.QueryExpression{
  2701  							parser.Identifier{Literal: "c1"},
  2702  							parser.Identifier{Literal: "c2"},
  2703  						},
  2704  						Query: parser.SelectQuery{
  2705  							SelectEntity: parser.SelectEntity{
  2706  								SelectClause: parser.SelectClause{
  2707  									Fields: []parser.QueryExpression{
  2708  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  2709  										parser.Field{Object: parser.NewStringValue("str3")},
  2710  									},
  2711  								},
  2712  							},
  2713  						},
  2714  					},
  2715  				},
  2716  			},
  2717  			Table: parser.Table{Object: parser.Identifier{Literal: "it"}},
  2718  			Fields: []parser.QueryExpression{
  2719  				parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2720  				parser.FieldReference{Column: parser.Identifier{Literal: "c2"}},
  2721  			},
  2722  			Keys: []parser.QueryExpression{
  2723  				parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  2724  			},
  2725  			ValuesList: []parser.QueryExpression{
  2726  				parser.RowValue{
  2727  					Value: parser.ValueList{
  2728  						Values: []parser.QueryExpression{
  2729  							parser.NewIntegerValueFromString("4"),
  2730  							parser.NewStringValue("str4"),
  2731  						},
  2732  					},
  2733  				},
  2734  				parser.RowValue{
  2735  					Value: parser.Subquery{
  2736  						Query: parser.SelectQuery{
  2737  							SelectEntity: parser.SelectEntity{
  2738  								SelectClause: parser.SelectClause{
  2739  									Fields: []parser.QueryExpression{
  2740  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  2741  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c2"}}},
  2742  									},
  2743  								},
  2744  								FromClause: parser.FromClause{
  2745  									Tables: []parser.QueryExpression{
  2746  										parser.Table{Object: parser.Identifier{Literal: "it"}},
  2747  									},
  2748  								},
  2749  							},
  2750  						},
  2751  					},
  2752  				},
  2753  			},
  2754  		},
  2755  		Error: "inline table cannot be updated",
  2756  	},
  2757  	{
  2758  		Name: "Replace Query File Does Not Exist Error",
  2759  		Query: parser.ReplaceQuery{
  2760  			Table: parser.Table{Object: parser.Identifier{Literal: "notexist"}},
  2761  			Fields: []parser.QueryExpression{
  2762  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2763  			},
  2764  			Keys: []parser.QueryExpression{
  2765  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2766  			},
  2767  			ValuesList: []parser.QueryExpression{
  2768  				parser.RowValue{
  2769  					Value: parser.ValueList{
  2770  						Values: []parser.QueryExpression{
  2771  							parser.NewIntegerValueFromString("4"),
  2772  						},
  2773  					},
  2774  				},
  2775  				parser.RowValue{
  2776  					Value: parser.ValueList{
  2777  						Values: []parser.QueryExpression{
  2778  							parser.NewIntegerValueFromString("5"),
  2779  						},
  2780  					},
  2781  				},
  2782  			},
  2783  		},
  2784  		Error: "file notexist does not exist",
  2785  	},
  2786  	{
  2787  		Name: "Replace Query Field Does Not Exist Error",
  2788  		Query: parser.ReplaceQuery{
  2789  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2790  			Fields: []parser.QueryExpression{
  2791  				parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  2792  			},
  2793  			Keys: []parser.QueryExpression{
  2794  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2795  			},
  2796  			ValuesList: []parser.QueryExpression{
  2797  				parser.RowValue{
  2798  					Value: parser.ValueList{
  2799  						Values: []parser.QueryExpression{
  2800  							parser.NewIntegerValueFromString("4"),
  2801  						},
  2802  					},
  2803  				},
  2804  				parser.RowValue{
  2805  					Value: parser.ValueList{
  2806  						Values: []parser.QueryExpression{
  2807  							parser.NewIntegerValueFromString("5"),
  2808  						},
  2809  					},
  2810  				},
  2811  			},
  2812  		},
  2813  		Error: "field notexist does not exist",
  2814  	},
  2815  	{
  2816  		Name: "Replace Select Query",
  2817  		Query: parser.ReplaceQuery{
  2818  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2819  			Fields: []parser.QueryExpression{
  2820  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2821  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  2822  			},
  2823  			Keys: []parser.QueryExpression{
  2824  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2825  			},
  2826  			Query: parser.SelectQuery{
  2827  				SelectEntity: parser.SelectEntity{
  2828  					SelectClause: parser.SelectClause{
  2829  						Fields: []parser.QueryExpression{
  2830  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
  2831  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
  2832  						},
  2833  					},
  2834  					FromClause: parser.FromClause{
  2835  						Tables: []parser.QueryExpression{
  2836  							parser.Table{Object: parser.Identifier{Literal: "table2"}},
  2837  						},
  2838  					},
  2839  				},
  2840  			},
  2841  		},
  2842  		ResultFile: &FileInfo{
  2843  			Path:      GetTestFilePath("table1.csv"),
  2844  			Delimiter: ',',
  2845  			NoHeader:  false,
  2846  			Encoding:  text.UTF8,
  2847  			LineBreak: text.LF,
  2848  			ForUpdate: true,
  2849  		},
  2850  		UpdateCount: 3,
  2851  	},
  2852  	{
  2853  		Name: "Replace Select Query Field Does Not Exist Error",
  2854  		Query: parser.ReplaceQuery{
  2855  			Table: parser.Table{Object: parser.Identifier{Literal: "table1"}},
  2856  			Fields: []parser.QueryExpression{
  2857  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2858  			},
  2859  			Keys: []parser.QueryExpression{
  2860  				parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  2861  			},
  2862  			Query: parser.SelectQuery{
  2863  				SelectEntity: parser.SelectEntity{
  2864  					SelectClause: parser.SelectClause{
  2865  						Fields: []parser.QueryExpression{
  2866  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column3"}}},
  2867  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "column4"}}},
  2868  						},
  2869  					},
  2870  					FromClause: parser.FromClause{
  2871  						Tables: []parser.QueryExpression{
  2872  							parser.Table{Object: parser.Identifier{Literal: "table2"}},
  2873  						},
  2874  					},
  2875  				},
  2876  			},
  2877  		},
  2878  		Error: "select query should return exactly 1 field",
  2879  	},
  2880  }
  2881  
  2882  func TestReplace(t *testing.T) {
  2883  	defer func() {
  2884  		_ = TestTx.ReleaseResources()
  2885  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  2886  		initFlag(TestTx.Flags)
  2887  	}()
  2888  
  2889  	TestTx.Flags.Repository = TestDir
  2890  	TestTx.Flags.Quiet = false
  2891  
  2892  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  2893  		{
  2894  			scopeNameTempTables: {
  2895  				"TMPVIEW": &View{
  2896  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  2897  					RecordSet: []Record{
  2898  						NewRecord([]value.Primary{
  2899  							value.NewString("1"),
  2900  							value.NewString("str1"),
  2901  						}),
  2902  						NewRecord([]value.Primary{
  2903  							value.NewString("2"),
  2904  							value.NewString("str2"),
  2905  						}),
  2906  					},
  2907  					FileInfo: &FileInfo{
  2908  						Path:      "tmpview",
  2909  						Delimiter: ',',
  2910  						ViewType:  ViewTypeTemporaryTable,
  2911  					},
  2912  				},
  2913  			},
  2914  		},
  2915  	}, nil, time.Time{}, nil)
  2916  
  2917  	ctx := context.Background()
  2918  	for _, v := range replaceTests {
  2919  		_ = TestTx.ReleaseResources()
  2920  		result, cnt, err := Replace(ctx, scope, v.Query)
  2921  		if err != nil {
  2922  			if len(v.Error) < 1 {
  2923  				t.Errorf("%s: unexpected error %q", v.Name, err)
  2924  			} else if err.Error() != v.Error {
  2925  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  2926  			}
  2927  			continue
  2928  		}
  2929  		if 0 < len(v.Error) {
  2930  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  2931  			continue
  2932  		}
  2933  
  2934  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  2935  			view := value.(*View)
  2936  			if view.FileInfo.Handler != nil {
  2937  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  2938  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  2939  				}
  2940  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  2941  				view.FileInfo.Handler = nil
  2942  			}
  2943  			return true
  2944  		})
  2945  
  2946  		if !reflect.DeepEqual(result, v.ResultFile) {
  2947  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile)
  2948  		}
  2949  
  2950  		if !reflect.DeepEqual(cnt, v.UpdateCount) {
  2951  			t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount)
  2952  		}
  2953  
  2954  		if v.ViewCache.SyncMap != nil {
  2955  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  2956  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  2957  			}
  2958  		}
  2959  		if v.ResultScopes != nil {
  2960  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  2961  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  2962  			}
  2963  		}
  2964  	}
  2965  }
  2966  
  2967  var deleteTests = []struct {
  2968  	Name         string
  2969  	Query        parser.DeleteQuery
  2970  	ResultFiles  []*FileInfo
  2971  	UpdateCounts []int
  2972  	ViewCache    ViewMap
  2973  	ResultScopes *ReferenceScope
  2974  	Error        string
  2975  }{
  2976  	{
  2977  		Name: "Delete Query",
  2978  		Query: parser.DeleteQuery{
  2979  			WithClause: parser.WithClause{
  2980  				InlineTables: []parser.QueryExpression{
  2981  					parser.InlineTable{
  2982  						Name: parser.Identifier{Literal: "it"},
  2983  						Fields: []parser.QueryExpression{
  2984  							parser.Identifier{Literal: "c1"},
  2985  						},
  2986  						Query: parser.SelectQuery{
  2987  							SelectEntity: parser.SelectEntity{
  2988  								SelectClause: parser.SelectClause{
  2989  									Fields: []parser.QueryExpression{
  2990  										parser.Field{Object: parser.NewIntegerValueFromString("2")},
  2991  									},
  2992  								},
  2993  							},
  2994  						},
  2995  					},
  2996  				},
  2997  			},
  2998  			FromClause: parser.FromClause{
  2999  				Tables: []parser.QueryExpression{
  3000  					parser.Table{
  3001  						Object: parser.Identifier{Literal: "table1"},
  3002  					},
  3003  				},
  3004  			},
  3005  			WhereClause: parser.WhereClause{
  3006  				Filter: parser.Comparison{
  3007  					LHS: parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3008  					RHS: parser.Subquery{
  3009  						Query: parser.SelectQuery{
  3010  							SelectEntity: parser.SelectEntity{
  3011  								SelectClause: parser.SelectClause{
  3012  									Fields: []parser.QueryExpression{
  3013  										parser.Field{Object: parser.FieldReference{View: parser.Identifier{Literal: "it"}, Column: parser.Identifier{Literal: "c1"}}},
  3014  									},
  3015  								},
  3016  								FromClause: parser.FromClause{
  3017  									Tables: []parser.QueryExpression{
  3018  										parser.Table{Object: parser.Identifier{Literal: "it"}},
  3019  									},
  3020  								},
  3021  							},
  3022  						},
  3023  					},
  3024  					Operator: parser.Token{Token: '=', Literal: "="},
  3025  				},
  3026  			},
  3027  		},
  3028  		ResultFiles: []*FileInfo{
  3029  			{
  3030  				Path:      GetTestFilePath("table1.csv"),
  3031  				Delimiter: ',',
  3032  				NoHeader:  false,
  3033  				Encoding:  text.UTF8,
  3034  				LineBreak: text.LF,
  3035  				ForUpdate: true,
  3036  			},
  3037  		},
  3038  		UpdateCounts: []int{1},
  3039  		ViewCache: GenerateViewMap([]*View{
  3040  			{
  3041  				FileInfo: &FileInfo{
  3042  					Path:      GetTestFilePath("table1.csv"),
  3043  					Delimiter: ',',
  3044  					NoHeader:  false,
  3045  					Encoding:  text.UTF8,
  3046  					LineBreak: text.LF,
  3047  					ForUpdate: true,
  3048  				},
  3049  				Header: NewHeader("table1", []string{"column1", "column2"}),
  3050  				RecordSet: []Record{
  3051  					NewRecord([]value.Primary{
  3052  						value.NewString("1"),
  3053  						value.NewString("str1"),
  3054  					}),
  3055  					NewRecord([]value.Primary{
  3056  						value.NewString("3"),
  3057  						value.NewString("str3"),
  3058  					}),
  3059  				},
  3060  			},
  3061  		}),
  3062  	},
  3063  	{
  3064  		Name: "Delete Query For Temporary View",
  3065  		Query: parser.DeleteQuery{
  3066  			FromClause: parser.FromClause{
  3067  				Tables: []parser.QueryExpression{
  3068  					parser.Table{
  3069  						Object: parser.Identifier{Literal: "tmpview"},
  3070  						Alias:  parser.Identifier{Literal: "t1"},
  3071  					},
  3072  				},
  3073  			},
  3074  			WhereClause: parser.WhereClause{
  3075  				Filter: parser.Comparison{
  3076  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3077  					RHS:      parser.NewIntegerValueFromString("2"),
  3078  					Operator: parser.Token{Token: '=', Literal: "="},
  3079  				},
  3080  			},
  3081  		},
  3082  		ResultFiles: []*FileInfo{
  3083  			{
  3084  				Path:      "tmpview",
  3085  				Delimiter: ',',
  3086  				ViewType:  ViewTypeTemporaryTable,
  3087  			},
  3088  		},
  3089  		UpdateCounts: []int{1},
  3090  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  3091  			{
  3092  				scopeNameTempTables: {
  3093  					"TMPVIEW": &View{
  3094  						Header: NewHeader("tmpview", []string{"column1", "column2"}),
  3095  						RecordSet: []Record{
  3096  							NewRecord([]value.Primary{
  3097  								value.NewString("1"),
  3098  								value.NewString("str1"),
  3099  							}),
  3100  						},
  3101  						FileInfo: &FileInfo{
  3102  							Path:      "tmpview",
  3103  							Delimiter: ',',
  3104  							ViewType:  ViewTypeTemporaryTable,
  3105  						},
  3106  					},
  3107  				},
  3108  			},
  3109  		}, nil, time.Time{}, nil),
  3110  	},
  3111  	{
  3112  		Name: "Delete Query Multiple Table",
  3113  		Query: parser.DeleteQuery{
  3114  			Tables: []parser.QueryExpression{
  3115  				parser.Table{Object: parser.Identifier{Literal: "t1"}},
  3116  			},
  3117  			FromClause: parser.FromClause{
  3118  				Tables: []parser.QueryExpression{
  3119  					parser.Table{Object: parser.Join{
  3120  						Table: parser.Table{
  3121  							Object: parser.Identifier{Literal: "table1"},
  3122  							Alias:  parser.Identifier{Literal: "t1"},
  3123  						},
  3124  						JoinTable: parser.Table{
  3125  							Object: parser.Identifier{Literal: "table2"},
  3126  							Alias:  parser.Identifier{Literal: "t2"},
  3127  						},
  3128  						Condition: parser.JoinCondition{
  3129  							On: parser.Comparison{
  3130  								LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3131  								RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  3132  								Operator: parser.Token{Token: '=', Literal: "="},
  3133  							},
  3134  						},
  3135  					}},
  3136  				},
  3137  			},
  3138  		},
  3139  		ResultFiles: []*FileInfo{
  3140  			{
  3141  				Path:      GetTestFilePath("table1.csv"),
  3142  				Delimiter: ',',
  3143  				NoHeader:  false,
  3144  				Encoding:  text.UTF8,
  3145  				LineBreak: text.LF,
  3146  				ForUpdate: true,
  3147  			},
  3148  		},
  3149  		UpdateCounts: []int{2},
  3150  	},
  3151  	{
  3152  		Name: "Delete Query Alias to Table Identification Function Is Not Specified Error ",
  3153  		Query: parser.DeleteQuery{
  3154  			FromClause: parser.FromClause{
  3155  				Tables: []parser.QueryExpression{
  3156  					parser.Table{
  3157  						Object: parser.TableFunction{
  3158  							Name: "data",
  3159  							Args: []parser.QueryExpression{
  3160  								parser.NewStringValue("c1,c2\n1,a\n2,b"),
  3161  							},
  3162  						},
  3163  					},
  3164  				},
  3165  			},
  3166  			WhereClause: parser.WhereClause{
  3167  				Filter: parser.Comparison{
  3168  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  3169  					RHS:      parser.NewIntegerValueFromString("2"),
  3170  					Operator: parser.Token{Token: '=', Literal: "="},
  3171  				},
  3172  			},
  3173  		},
  3174  		Error: "alias to table identification function or URL must be specified for update",
  3175  	},
  3176  	{
  3177  		Name: "Delete Query Inline Table Cannot Be Updated Error ",
  3178  		Query: parser.DeleteQuery{
  3179  			FromClause: parser.FromClause{
  3180  				Tables: []parser.QueryExpression{
  3181  					parser.Table{
  3182  						Object: parser.TableFunction{
  3183  							Name: "data",
  3184  							Args: []parser.QueryExpression{
  3185  								parser.NewStringValue("c1,c2\n1,a\n2,b"),
  3186  							},
  3187  						},
  3188  						Alias: parser.Identifier{
  3189  							Literal: "a",
  3190  						},
  3191  					},
  3192  				},
  3193  			},
  3194  			WhereClause: parser.WhereClause{
  3195  				Filter: parser.Comparison{
  3196  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "c1"}},
  3197  					RHS:      parser.NewIntegerValueFromString("2"),
  3198  					Operator: parser.Token{Token: '=', Literal: "="},
  3199  				},
  3200  			},
  3201  		},
  3202  		Error: "inline table cannot be updated",
  3203  	},
  3204  	{
  3205  		Name: "Delete Query Tables Not Specified Error",
  3206  		Query: parser.DeleteQuery{
  3207  			FromClause: parser.FromClause{
  3208  				Tables: []parser.QueryExpression{
  3209  					parser.Table{
  3210  						Object: parser.Join{
  3211  							Table: parser.Table{
  3212  								Object: parser.Identifier{Literal: "table1"},
  3213  								Alias:  parser.Identifier{Literal: "t1"},
  3214  							},
  3215  							JoinTable: parser.Table{
  3216  								Object: parser.Identifier{Literal: "table2"},
  3217  								Alias:  parser.Identifier{Literal: "t2"},
  3218  							},
  3219  							Condition: parser.JoinCondition{
  3220  								On: parser.Comparison{
  3221  									LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3222  									RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  3223  									Operator: parser.Token{Token: '=', Literal: "="},
  3224  								},
  3225  							},
  3226  						},
  3227  					},
  3228  					parser.Table{
  3229  						Object: parser.TableFunction{
  3230  							Name: "data",
  3231  							Args: []parser.QueryExpression{
  3232  								parser.NewStringValue("c1,c2\n1,a\n2,b"),
  3233  							},
  3234  						},
  3235  					},
  3236  				},
  3237  			},
  3238  		},
  3239  		Error: "tables to delete records are not specified",
  3240  	},
  3241  	{
  3242  		Name: "Delete Query File Does Not Exist Error",
  3243  		Query: parser.DeleteQuery{
  3244  			FromClause: parser.FromClause{
  3245  				Tables: []parser.QueryExpression{
  3246  					parser.Table{
  3247  						Object: parser.Identifier{Literal: "notexist"},
  3248  					},
  3249  				},
  3250  			},
  3251  			WhereClause: parser.WhereClause{
  3252  				Filter: parser.Comparison{
  3253  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3254  					RHS:      parser.NewIntegerValueFromString("2"),
  3255  					Operator: parser.Token{Token: '=', Literal: "="},
  3256  				},
  3257  			},
  3258  		},
  3259  		Error: "file notexist does not exist",
  3260  	},
  3261  	{
  3262  		Name: "Delete Query Filter Error",
  3263  		Query: parser.DeleteQuery{
  3264  			FromClause: parser.FromClause{
  3265  				Tables: []parser.QueryExpression{
  3266  					parser.Table{
  3267  						Object: parser.Identifier{Literal: "table1"},
  3268  					},
  3269  				},
  3270  			},
  3271  			WhereClause: parser.WhereClause{
  3272  				Filter: parser.Comparison{
  3273  					LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3274  					RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  3275  					Operator: parser.Token{Token: '=', Literal: "="},
  3276  				},
  3277  			},
  3278  		},
  3279  		Error: "field notexist does not exist",
  3280  	},
  3281  	{
  3282  		Name: "Delete Query File Is Not Loaded Error",
  3283  		Query: parser.DeleteQuery{
  3284  			Tables: []parser.QueryExpression{
  3285  				parser.Table{Object: parser.Identifier{Literal: "notexist"}},
  3286  			},
  3287  			FromClause: parser.FromClause{
  3288  				Tables: []parser.QueryExpression{
  3289  					parser.Table{Object: parser.Join{
  3290  						Table: parser.Table{
  3291  							Object: parser.Identifier{Literal: "table1"},
  3292  							Alias:  parser.Identifier{Literal: "t1"},
  3293  						},
  3294  						JoinTable: parser.Table{
  3295  							Object: parser.Identifier{Literal: "table2"},
  3296  							Alias:  parser.Identifier{Literal: "t2"},
  3297  						},
  3298  						Condition: parser.JoinCondition{
  3299  							On: parser.Comparison{
  3300  								LHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3301  								RHS:      parser.FieldReference{Column: parser.Identifier{Literal: "column3"}},
  3302  								Operator: parser.Token{Token: '=', Literal: "="},
  3303  							},
  3304  						},
  3305  					}},
  3306  				},
  3307  			},
  3308  		},
  3309  		Error: "table notexist is not loaded",
  3310  	},
  3311  }
  3312  
  3313  func TestDelete(t *testing.T) {
  3314  	defer func() {
  3315  		_ = TestTx.ReleaseResources()
  3316  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  3317  		initFlag(TestTx.Flags)
  3318  	}()
  3319  
  3320  	TestTx.Flags.Repository = TestDir
  3321  	TestTx.Flags.Quiet = false
  3322  
  3323  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  3324  		{
  3325  			scopeNameTempTables: {
  3326  				"TMPVIEW": &View{
  3327  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  3328  					RecordSet: []Record{
  3329  						NewRecord([]value.Primary{
  3330  							value.NewString("1"),
  3331  							value.NewString("str1"),
  3332  						}),
  3333  						NewRecord([]value.Primary{
  3334  							value.NewString("2"),
  3335  							value.NewString("str2"),
  3336  						}),
  3337  					},
  3338  					FileInfo: &FileInfo{
  3339  						Path:      "tmpview",
  3340  						Delimiter: ',',
  3341  						ViewType:  ViewTypeTemporaryTable,
  3342  					},
  3343  				},
  3344  			},
  3345  		},
  3346  	}, nil, time.Time{}, nil)
  3347  
  3348  	ctx := context.Background()
  3349  	for _, v := range deleteTests {
  3350  		_ = TestTx.ReleaseResources()
  3351  		files, cnt, err := Delete(ctx, scope, v.Query)
  3352  		if err != nil {
  3353  			if len(v.Error) < 1 {
  3354  				t.Errorf("%s: unexpected error %q", v.Name, err)
  3355  			} else if err.Error() != v.Error {
  3356  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  3357  			}
  3358  			continue
  3359  		}
  3360  		if 0 < len(v.Error) {
  3361  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  3362  			continue
  3363  		}
  3364  
  3365  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  3366  			view := value.(*View)
  3367  			if view.FileInfo.Handler != nil {
  3368  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  3369  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  3370  				}
  3371  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  3372  				view.FileInfo.Handler = nil
  3373  			}
  3374  			return true
  3375  		})
  3376  
  3377  		if !reflect.DeepEqual(files, v.ResultFiles) {
  3378  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, files, v.ResultFiles)
  3379  		}
  3380  
  3381  		if !reflect.DeepEqual(cnt, v.UpdateCounts) {
  3382  			t.Errorf("%s: update count = %v, want %v", v.Name, cnt, v.UpdateCounts)
  3383  		}
  3384  
  3385  		if v.ViewCache.SyncMap != nil {
  3386  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  3387  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  3388  			}
  3389  		}
  3390  		if v.ResultScopes != nil {
  3391  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  3392  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  3393  			}
  3394  		}
  3395  	}
  3396  }
  3397  
  3398  var createTableTests = []struct {
  3399  	Name       string
  3400  	Query      parser.CreateTable
  3401  	ResultFile *FileInfo
  3402  	ViewCache  ViewMap
  3403  	Error      string
  3404  }{
  3405  	{
  3406  		Name: "Create Table",
  3407  		Query: parser.CreateTable{
  3408  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3409  			Fields: []parser.QueryExpression{
  3410  				parser.Identifier{Literal: "column1"},
  3411  				parser.Identifier{Literal: "column2"},
  3412  			},
  3413  		},
  3414  		ResultFile: &FileInfo{
  3415  			Path:      GetTestFilePath("create_table_1.csv"),
  3416  			Delimiter: ',',
  3417  			NoHeader:  false,
  3418  			Encoding:  text.UTF8,
  3419  			LineBreak: text.LF,
  3420  			ForUpdate: true,
  3421  		},
  3422  		ViewCache: GenerateViewMap([]*View{
  3423  			{
  3424  				FileInfo: &FileInfo{
  3425  					Path:      GetTestFilePath("create_table_1.csv"),
  3426  					Delimiter: ',',
  3427  					NoHeader:  false,
  3428  					Encoding:  text.UTF8,
  3429  					LineBreak: text.LF,
  3430  					ForUpdate: true,
  3431  				},
  3432  				Header:    NewHeader("create_table_1", []string{"column1", "column2"}),
  3433  				RecordSet: RecordSet{},
  3434  			},
  3435  		}),
  3436  	},
  3437  	{
  3438  		Name: "Create Table From Select Query",
  3439  		Query: parser.CreateTable{
  3440  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3441  			Fields: []parser.QueryExpression{
  3442  				parser.Identifier{Literal: "column1"},
  3443  				parser.Identifier{Literal: "column2"},
  3444  			},
  3445  			Query: parser.SelectQuery{
  3446  				SelectEntity: parser.SelectEntity{
  3447  					SelectClause: parser.SelectClause{
  3448  						Fields: []parser.QueryExpression{
  3449  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
  3450  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
  3451  						},
  3452  					},
  3453  				},
  3454  			},
  3455  		},
  3456  		ResultFile: &FileInfo{
  3457  			Path:      GetTestFilePath("create_table_1.csv"),
  3458  			Delimiter: ',',
  3459  			NoHeader:  false,
  3460  			Encoding:  text.UTF8,
  3461  			LineBreak: text.LF,
  3462  			ForUpdate: true,
  3463  		},
  3464  		ViewCache: GenerateViewMap([]*View{
  3465  			{
  3466  				FileInfo: &FileInfo{
  3467  					Path:      GetTestFilePath("create_table_1.csv"),
  3468  					Delimiter: ',',
  3469  					NoHeader:  false,
  3470  					Encoding:  text.UTF8,
  3471  					LineBreak: text.LF,
  3472  					ForUpdate: true,
  3473  				},
  3474  				Header: NewHeader("create_table_1", []string{"column1", "column2"}),
  3475  				RecordSet: RecordSet{
  3476  					NewRecord([]value.Primary{
  3477  						value.NewInteger(1),
  3478  						value.NewInteger(2),
  3479  					}),
  3480  				},
  3481  			},
  3482  		}),
  3483  	},
  3484  	{
  3485  		Name: "Create Table File Already Exist Error",
  3486  		Query: parser.CreateTable{
  3487  			Table: parser.Identifier{Literal: "table1.csv"},
  3488  			Fields: []parser.QueryExpression{
  3489  				parser.Identifier{Literal: "column1"},
  3490  				parser.Identifier{Literal: "column2"},
  3491  			},
  3492  		},
  3493  		Error: fmt.Sprintf("file %s already exists", GetTestFilePath("table1.csv")),
  3494  	},
  3495  	{
  3496  		Name: "Create Table Field Duplicate Error",
  3497  		Query: parser.CreateTable{
  3498  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3499  			Fields: []parser.QueryExpression{
  3500  				parser.Identifier{Literal: "column1"},
  3501  				parser.Identifier{Literal: "column1"},
  3502  			},
  3503  		},
  3504  		Error: "field name column1 is a duplicate",
  3505  	},
  3506  	{
  3507  		Name: "Create Table Select Query Execution Error",
  3508  		Query: parser.CreateTable{
  3509  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3510  			Fields: []parser.QueryExpression{
  3511  				parser.Identifier{Literal: "column1"},
  3512  				parser.Identifier{Literal: "column2"},
  3513  			},
  3514  			Query: parser.SelectQuery{
  3515  				SelectEntity: parser.SelectEntity{
  3516  					SelectClause: parser.SelectClause{
  3517  						Fields: []parser.QueryExpression{
  3518  							parser.Field{Object: parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}}},
  3519  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
  3520  						},
  3521  					},
  3522  				},
  3523  			},
  3524  		},
  3525  		Error: "field notexist does not exist",
  3526  	},
  3527  	{
  3528  		Name: "Create Table From Select Query Field Length Not Match Error",
  3529  		Query: parser.CreateTable{
  3530  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3531  			Fields: []parser.QueryExpression{
  3532  				parser.Identifier{Literal: "column1"},
  3533  			},
  3534  			Query: parser.SelectQuery{
  3535  				SelectEntity: parser.SelectEntity{
  3536  					SelectClause: parser.SelectClause{
  3537  						Fields: []parser.QueryExpression{
  3538  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
  3539  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
  3540  						},
  3541  					},
  3542  				},
  3543  			},
  3544  		},
  3545  		Error: "select query should return exactly 1 field for table create_table_1.csv",
  3546  	},
  3547  	{
  3548  		Name: "Create Table From Select Query Field Name Duplicate Error",
  3549  		Query: parser.CreateTable{
  3550  			Table: parser.Identifier{Literal: "create_table_1.csv"},
  3551  			Fields: []parser.QueryExpression{
  3552  				parser.Identifier{Literal: "column1"},
  3553  				parser.Identifier{Literal: "column1"},
  3554  			},
  3555  			Query: parser.SelectQuery{
  3556  				SelectEntity: parser.SelectEntity{
  3557  					SelectClause: parser.SelectClause{
  3558  						Fields: []parser.QueryExpression{
  3559  							parser.Field{Object: parser.NewIntegerValueFromString("1")},
  3560  							parser.Field{Object: parser.NewIntegerValueFromString("2")},
  3561  						},
  3562  					},
  3563  				},
  3564  			},
  3565  		},
  3566  		Error: "field name column1 is a duplicate",
  3567  	},
  3568  }
  3569  
  3570  func TestCreateTable(t *testing.T) {
  3571  	defer func() {
  3572  		_ = TestTx.ReleaseResources()
  3573  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  3574  		initFlag(TestTx.Flags)
  3575  	}()
  3576  
  3577  	TestTx.Flags.Repository = TestDir
  3578  	TestTx.Flags.Quiet = false
  3579  
  3580  	scope := NewReferenceScope(TestTx)
  3581  	ctx := context.Background()
  3582  	for _, v := range createTableTests {
  3583  		_ = TestTx.ReleaseResources()
  3584  
  3585  		result, err := CreateTable(ctx, scope, v.Query)
  3586  
  3587  		if result != nil {
  3588  			_ = TestTx.FileContainer.Close(result.Handler)
  3589  			result.Handler = nil
  3590  		}
  3591  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  3592  			view := value.(*View)
  3593  			if view.FileInfo != nil {
  3594  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  3595  				view.FileInfo.Handler = nil
  3596  			}
  3597  			return true
  3598  		})
  3599  
  3600  		if err != nil {
  3601  			if len(v.Error) < 1 {
  3602  				t.Errorf("%s: unexpected error %q", v.Name, err)
  3603  			} else if err.Error() != v.Error {
  3604  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  3605  			}
  3606  			continue
  3607  		}
  3608  		if 0 < len(v.Error) {
  3609  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  3610  			continue
  3611  		}
  3612  
  3613  		if !reflect.DeepEqual(result, v.ResultFile) {
  3614  			t.Errorf("%s: result = %v, want %v", v.Name, result, v.ResultFile)
  3615  		}
  3616  
  3617  		if v.ViewCache.SyncMap != nil {
  3618  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  3619  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  3620  			}
  3621  		}
  3622  	}
  3623  }
  3624  
  3625  var addColumnsTests = []struct {
  3626  	Name         string
  3627  	Query        parser.AddColumns
  3628  	ResultFile   *FileInfo
  3629  	UpdateCount  int
  3630  	ViewCache    ViewMap
  3631  	ResultScopes *ReferenceScope
  3632  	Error        string
  3633  }{
  3634  	{
  3635  		Name: "Add Fields",
  3636  		Query: parser.AddColumns{
  3637  			Table: parser.Identifier{Literal: "table1.csv"},
  3638  			Columns: []parser.ColumnDefault{
  3639  				{
  3640  					Column: parser.Identifier{Literal: "column3"},
  3641  				},
  3642  				{
  3643  					Column: parser.Identifier{Literal: "column4"},
  3644  				},
  3645  			},
  3646  		},
  3647  		ResultFile: &FileInfo{
  3648  			Path:      GetTestFilePath("table1.csv"),
  3649  			Delimiter: ',',
  3650  			NoHeader:  false,
  3651  			Encoding:  text.UTF8,
  3652  			LineBreak: text.LF,
  3653  			ForUpdate: true,
  3654  		},
  3655  		UpdateCount: 2,
  3656  		ViewCache: GenerateViewMap([]*View{
  3657  			{
  3658  				FileInfo: &FileInfo{
  3659  					Path:      GetTestFilePath("table1.csv"),
  3660  					Delimiter: ',',
  3661  					NoHeader:  false,
  3662  					Encoding:  text.UTF8,
  3663  					LineBreak: text.LF,
  3664  					ForUpdate: true,
  3665  				},
  3666  				Header: NewHeader("table1", []string{"column1", "column2", "column3", "column4"}),
  3667  				RecordSet: []Record{
  3668  					NewRecord([]value.Primary{
  3669  						value.NewString("1"),
  3670  						value.NewString("str1"),
  3671  						value.NewNull(),
  3672  						value.NewNull(),
  3673  					}),
  3674  					NewRecord([]value.Primary{
  3675  						value.NewString("2"),
  3676  						value.NewString("str2"),
  3677  						value.NewNull(),
  3678  						value.NewNull(),
  3679  					}),
  3680  					NewRecord([]value.Primary{
  3681  						value.NewString("3"),
  3682  						value.NewString("str3"),
  3683  						value.NewNull(),
  3684  						value.NewNull(),
  3685  					}),
  3686  				},
  3687  			},
  3688  		}),
  3689  	},
  3690  	{
  3691  		Name: "Add Fields For Temporary View",
  3692  		Query: parser.AddColumns{
  3693  			Table: parser.Identifier{Literal: "tmpview"},
  3694  			Columns: []parser.ColumnDefault{
  3695  				{
  3696  					Column: parser.Identifier{Literal: "column3"},
  3697  				},
  3698  				{
  3699  					Column: parser.Identifier{Literal: "column4"},
  3700  				},
  3701  			},
  3702  		},
  3703  		ResultFile: &FileInfo{
  3704  			Path:      "tmpview",
  3705  			Delimiter: ',',
  3706  			ViewType:  ViewTypeTemporaryTable,
  3707  		},
  3708  		UpdateCount: 2,
  3709  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  3710  			{
  3711  				scopeNameTempTables: {
  3712  					"TMPVIEW": &View{
  3713  						Header: NewHeader("tmpview", []string{"column1", "column2", "column3", "column4"}),
  3714  						RecordSet: []Record{
  3715  							NewRecord([]value.Primary{
  3716  								value.NewString("1"),
  3717  								value.NewString("str1"),
  3718  								value.NewNull(),
  3719  								value.NewNull(),
  3720  							}),
  3721  							NewRecord([]value.Primary{
  3722  								value.NewString("2"),
  3723  								value.NewString("str2"),
  3724  								value.NewNull(),
  3725  								value.NewNull(),
  3726  							}),
  3727  						},
  3728  						FileInfo: &FileInfo{
  3729  							Path:      "tmpview",
  3730  							Delimiter: ',',
  3731  							ViewType:  ViewTypeTemporaryTable,
  3732  						},
  3733  					},
  3734  				},
  3735  			},
  3736  		}, nil, time.Time{}, nil),
  3737  	},
  3738  	{
  3739  		Name: "Add Fields First",
  3740  		Query: parser.AddColumns{
  3741  			Table: parser.Identifier{Literal: "table1.csv"},
  3742  			Columns: []parser.ColumnDefault{
  3743  				{
  3744  					Column: parser.Identifier{Literal: "column3"},
  3745  					Value:  parser.NewIntegerValueFromString("2"),
  3746  				},
  3747  				{
  3748  					Column: parser.Identifier{Literal: "column4"},
  3749  					Value:  parser.NewIntegerValueFromString("1"),
  3750  				},
  3751  			},
  3752  			Position: parser.ColumnPosition{
  3753  				Position: parser.Token{Token: parser.FIRST},
  3754  			},
  3755  		},
  3756  		ResultFile: &FileInfo{
  3757  			Path:      GetTestFilePath("table1.csv"),
  3758  			Delimiter: ',',
  3759  			NoHeader:  false,
  3760  			Encoding:  text.UTF8,
  3761  			LineBreak: text.LF,
  3762  			ForUpdate: true,
  3763  		},
  3764  		UpdateCount: 2,
  3765  		ViewCache: GenerateViewMap([]*View{
  3766  			{
  3767  				FileInfo: &FileInfo{
  3768  					Path:      GetTestFilePath("table1.csv"),
  3769  					Delimiter: ',',
  3770  					NoHeader:  false,
  3771  					Encoding:  text.UTF8,
  3772  					LineBreak: text.LF,
  3773  					ForUpdate: true,
  3774  				},
  3775  				Header: NewHeader("table1", []string{"column3", "column4", "column1", "column2"}),
  3776  				RecordSet: []Record{
  3777  					NewRecord([]value.Primary{
  3778  						value.NewInteger(2),
  3779  						value.NewInteger(1),
  3780  						value.NewString("1"),
  3781  						value.NewString("str1"),
  3782  					}),
  3783  					NewRecord([]value.Primary{
  3784  						value.NewInteger(2),
  3785  						value.NewInteger(1),
  3786  						value.NewString("2"),
  3787  						value.NewString("str2"),
  3788  					}),
  3789  					NewRecord([]value.Primary{
  3790  						value.NewInteger(2),
  3791  						value.NewInteger(1),
  3792  						value.NewString("3"),
  3793  						value.NewString("str3"),
  3794  					}),
  3795  				},
  3796  			},
  3797  		}),
  3798  	},
  3799  	{
  3800  		Name: "Add Fields After",
  3801  		Query: parser.AddColumns{
  3802  			Table: parser.Identifier{Literal: "table1.csv"},
  3803  			Columns: []parser.ColumnDefault{
  3804  				{
  3805  					Column: parser.Identifier{Literal: "column3"},
  3806  				},
  3807  				{
  3808  					Column: parser.Identifier{Literal: "column4"},
  3809  					Value:  parser.NewIntegerValueFromString("1"),
  3810  				},
  3811  			},
  3812  			Position: parser.ColumnPosition{
  3813  				Position: parser.Token{Token: parser.AFTER},
  3814  				Column:   parser.FieldReference{Column: parser.Identifier{Literal: "column1"}},
  3815  			},
  3816  		},
  3817  		ResultFile: &FileInfo{
  3818  			Path:      GetTestFilePath("table1.csv"),
  3819  			Delimiter: ',',
  3820  			NoHeader:  false,
  3821  			Encoding:  text.UTF8,
  3822  			LineBreak: text.LF,
  3823  			ForUpdate: true,
  3824  		},
  3825  		UpdateCount: 2,
  3826  		ViewCache: GenerateViewMap([]*View{
  3827  			{
  3828  				FileInfo: &FileInfo{
  3829  					Path:      GetTestFilePath("table1.csv"),
  3830  					Delimiter: ',',
  3831  					NoHeader:  false,
  3832  					Encoding:  text.UTF8,
  3833  					LineBreak: text.LF,
  3834  					ForUpdate: true,
  3835  				},
  3836  				Header: NewHeader("table1", []string{"column1", "column3", "column4", "column2"}),
  3837  				RecordSet: []Record{
  3838  					NewRecord([]value.Primary{
  3839  						value.NewString("1"),
  3840  						value.NewNull(),
  3841  						value.NewInteger(1),
  3842  						value.NewString("str1"),
  3843  					}),
  3844  					NewRecord([]value.Primary{
  3845  						value.NewString("2"),
  3846  						value.NewNull(),
  3847  						value.NewInteger(1),
  3848  						value.NewString("str2"),
  3849  					}),
  3850  					NewRecord([]value.Primary{
  3851  						value.NewString("3"),
  3852  						value.NewNull(),
  3853  						value.NewInteger(1),
  3854  						value.NewString("str3"),
  3855  					}),
  3856  				},
  3857  			},
  3858  		}),
  3859  	},
  3860  	{
  3861  		Name: "Add Fields Before",
  3862  		Query: parser.AddColumns{
  3863  			Table: parser.Identifier{Literal: "table1.csv"},
  3864  			Columns: []parser.ColumnDefault{
  3865  				{
  3866  					Column: parser.Identifier{Literal: "column3"},
  3867  				},
  3868  				{
  3869  					Column: parser.Identifier{Literal: "column4"},
  3870  					Value:  parser.NewIntegerValueFromString("1"),
  3871  				},
  3872  			},
  3873  			Position: parser.ColumnPosition{
  3874  				Position: parser.Token{Token: parser.BEFORE},
  3875  				Column:   parser.ColumnNumber{View: parser.Identifier{Literal: "table1"}, Number: value.NewInteger(2)},
  3876  			},
  3877  		},
  3878  		ResultFile: &FileInfo{
  3879  			Path:      GetTestFilePath("table1.csv"),
  3880  			Delimiter: ',',
  3881  			NoHeader:  false,
  3882  			Encoding:  text.UTF8,
  3883  			LineBreak: text.LF,
  3884  			ForUpdate: true,
  3885  		},
  3886  		UpdateCount: 2,
  3887  		ViewCache: GenerateViewMap([]*View{
  3888  			{
  3889  				FileInfo: &FileInfo{
  3890  					Path:      GetTestFilePath("table1.csv"),
  3891  					Delimiter: ',',
  3892  					NoHeader:  false,
  3893  					Encoding:  text.UTF8,
  3894  					LineBreak: text.LF,
  3895  					ForUpdate: true,
  3896  				},
  3897  				Header: NewHeader("table1", []string{"column1", "column3", "column4", "column2"}),
  3898  				RecordSet: []Record{
  3899  					NewRecord([]value.Primary{
  3900  						value.NewString("1"),
  3901  						value.NewNull(),
  3902  						value.NewInteger(1),
  3903  						value.NewString("str1"),
  3904  					}),
  3905  					NewRecord([]value.Primary{
  3906  						value.NewString("2"),
  3907  						value.NewNull(),
  3908  						value.NewInteger(1),
  3909  						value.NewString("str2"),
  3910  					}),
  3911  					NewRecord([]value.Primary{
  3912  						value.NewString("3"),
  3913  						value.NewNull(),
  3914  						value.NewInteger(1),
  3915  						value.NewString("str3"),
  3916  					}),
  3917  				},
  3918  			},
  3919  		}),
  3920  	},
  3921  	{
  3922  		Name: "Add Fields Load Error",
  3923  		Query: parser.AddColumns{
  3924  			Table: parser.Identifier{Literal: "notexist"},
  3925  			Columns: []parser.ColumnDefault{
  3926  				{
  3927  					Column: parser.Identifier{Literal: "column3"},
  3928  				},
  3929  				{
  3930  					Column: parser.Identifier{Literal: "column4"},
  3931  				},
  3932  			},
  3933  		},
  3934  		Error: "file notexist does not exist",
  3935  	},
  3936  	{
  3937  		Name: "Add Fields Position Column Does Not Exist Error",
  3938  		Query: parser.AddColumns{
  3939  			Table: parser.Identifier{Literal: "table1.csv"},
  3940  			Columns: []parser.ColumnDefault{
  3941  				{
  3942  					Column: parser.Identifier{Literal: "column3"},
  3943  				},
  3944  				{
  3945  					Column: parser.Identifier{Literal: "column2"},
  3946  					Value:  parser.NewIntegerValueFromString("1"),
  3947  				},
  3948  			},
  3949  			Position: parser.ColumnPosition{
  3950  				Position: parser.Token{Token: parser.BEFORE},
  3951  				Column:   parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  3952  			},
  3953  		},
  3954  		Error: "field notexist does not exist",
  3955  	},
  3956  	{
  3957  		Name: "Add Fields Field Duplicate Error",
  3958  		Query: parser.AddColumns{
  3959  			Table: parser.Identifier{Literal: "table1.csv"},
  3960  			Columns: []parser.ColumnDefault{
  3961  				{
  3962  					Column: parser.Identifier{Literal: "column3"},
  3963  				},
  3964  				{
  3965  					Column: parser.Identifier{Literal: "column1"},
  3966  					Value:  parser.NewIntegerValueFromString("1"),
  3967  				},
  3968  			},
  3969  		},
  3970  		Error: "field name column1 is a duplicate",
  3971  	},
  3972  	{
  3973  		Name: "Add Fields Default Value Error",
  3974  		Query: parser.AddColumns{
  3975  			Table: parser.Identifier{Literal: "table1.csv"},
  3976  			Columns: []parser.ColumnDefault{
  3977  				{
  3978  					Column: parser.Identifier{Literal: "column3"},
  3979  				},
  3980  				{
  3981  					Column: parser.Identifier{Literal: "column4"},
  3982  					Value:  parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  3983  				},
  3984  			},
  3985  		},
  3986  		Error: "field notexist does not exist",
  3987  	},
  3988  }
  3989  
  3990  func TestAddColumns(t *testing.T) {
  3991  	defer func() {
  3992  		_ = TestTx.ReleaseResources()
  3993  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  3994  		initFlag(TestTx.Flags)
  3995  	}()
  3996  
  3997  	TestTx.Flags.Repository = TestDir
  3998  	TestTx.Flags.Quiet = false
  3999  
  4000  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  4001  		{
  4002  			scopeNameTempTables: {
  4003  				"TMPVIEW": &View{
  4004  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  4005  					RecordSet: []Record{
  4006  						NewRecord([]value.Primary{
  4007  							value.NewString("1"),
  4008  							value.NewString("str1"),
  4009  						}),
  4010  						NewRecord([]value.Primary{
  4011  							value.NewString("2"),
  4012  							value.NewString("str2"),
  4013  						}),
  4014  					},
  4015  					FileInfo: &FileInfo{
  4016  						Path:      "tmpview",
  4017  						Delimiter: ',',
  4018  						ViewType:  ViewTypeTemporaryTable,
  4019  					},
  4020  				},
  4021  			},
  4022  		},
  4023  	}, nil, time.Time{}, nil)
  4024  
  4025  	ctx := context.Background()
  4026  	for _, v := range addColumnsTests {
  4027  		_ = TestTx.ReleaseResources()
  4028  		result, cnt, err := AddColumns(ctx, scope, v.Query)
  4029  		if err != nil {
  4030  			if len(v.Error) < 1 {
  4031  				t.Errorf("%s: unexpected error %q", v.Name, err)
  4032  			} else if err.Error() != v.Error {
  4033  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  4034  			}
  4035  			continue
  4036  		}
  4037  		if 0 < len(v.Error) {
  4038  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  4039  			continue
  4040  		}
  4041  
  4042  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  4043  			view := value.(*View)
  4044  			if view.FileInfo.Handler != nil {
  4045  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  4046  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  4047  				}
  4048  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  4049  				view.FileInfo.Handler = nil
  4050  			}
  4051  			return true
  4052  		})
  4053  
  4054  		if !reflect.DeepEqual(result, v.ResultFile) {
  4055  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.ResultFile)
  4056  		}
  4057  
  4058  		if cnt != v.UpdateCount {
  4059  			t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount)
  4060  		}
  4061  
  4062  		if v.ViewCache.SyncMap != nil {
  4063  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  4064  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  4065  			}
  4066  		}
  4067  		if v.ResultScopes != nil {
  4068  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  4069  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  4070  			}
  4071  		}
  4072  	}
  4073  }
  4074  
  4075  var dropColumnsTests = []struct {
  4076  	Name         string
  4077  	Query        parser.DropColumns
  4078  	Result       *FileInfo
  4079  	UpdateCount  int
  4080  	ViewCache    ViewMap
  4081  	ResultScopes *ReferenceScope
  4082  	Error        string
  4083  }{
  4084  	{
  4085  		Name: "Drop Fields",
  4086  		Query: parser.DropColumns{
  4087  			Table: parser.Identifier{Literal: "table1"},
  4088  			Columns: []parser.QueryExpression{
  4089  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  4090  			},
  4091  		},
  4092  		Result: &FileInfo{
  4093  			Path:      GetTestFilePath("table1.csv"),
  4094  			Delimiter: ',',
  4095  			NoHeader:  false,
  4096  			Encoding:  text.UTF8,
  4097  			LineBreak: text.LF,
  4098  			ForUpdate: true,
  4099  		},
  4100  		UpdateCount: 1,
  4101  		ViewCache: GenerateViewMap([]*View{
  4102  			{
  4103  				FileInfo: &FileInfo{
  4104  					Path:      GetTestFilePath("table1.csv"),
  4105  					Delimiter: ',',
  4106  					NoHeader:  false,
  4107  					Encoding:  text.UTF8,
  4108  					LineBreak: text.LF,
  4109  					ForUpdate: true,
  4110  				},
  4111  				Header: NewHeader("table1", []string{"column1"}),
  4112  				RecordSet: []Record{
  4113  					NewRecord([]value.Primary{
  4114  						value.NewString("1"),
  4115  					}),
  4116  					NewRecord([]value.Primary{
  4117  						value.NewString("2"),
  4118  					}),
  4119  					NewRecord([]value.Primary{
  4120  						value.NewString("3"),
  4121  					}),
  4122  				},
  4123  			},
  4124  		}),
  4125  	},
  4126  	{
  4127  		Name: "Drop Fields For Temporary View",
  4128  		Query: parser.DropColumns{
  4129  			Table: parser.Identifier{Literal: "tmpview"},
  4130  			Columns: []parser.QueryExpression{
  4131  				parser.ColumnNumber{View: parser.Identifier{Literal: "tmpview"}, Number: value.NewInteger(2)},
  4132  			},
  4133  		},
  4134  		Result: &FileInfo{
  4135  			Path:      "tmpview",
  4136  			Delimiter: ',',
  4137  			ViewType:  ViewTypeTemporaryTable,
  4138  		},
  4139  		UpdateCount: 1,
  4140  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  4141  			{
  4142  				scopeNameTempTables: {
  4143  					"TMPVIEW": &View{
  4144  						Header: NewHeader("tmpview", []string{"column1"}),
  4145  						RecordSet: []Record{
  4146  							NewRecord([]value.Primary{
  4147  								value.NewString("1"),
  4148  							}),
  4149  							NewRecord([]value.Primary{
  4150  								value.NewString("2"),
  4151  							}),
  4152  						},
  4153  						FileInfo: &FileInfo{
  4154  							Path:      "tmpview",
  4155  							Delimiter: ',',
  4156  							ViewType:  ViewTypeTemporaryTable,
  4157  						},
  4158  					},
  4159  				},
  4160  			},
  4161  		}, nil, time.Time{}, nil),
  4162  	},
  4163  	{
  4164  		Name: "Drop Fields Load Error",
  4165  		Query: parser.DropColumns{
  4166  			Table: parser.Identifier{Literal: "notexist"},
  4167  			Columns: []parser.QueryExpression{
  4168  				parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  4169  			},
  4170  		},
  4171  		Error: "file notexist does not exist",
  4172  	},
  4173  	{
  4174  		Name: "Drop Fields Field Does Not Exist Error",
  4175  		Query: parser.DropColumns{
  4176  			Table: parser.Identifier{Literal: "table1"},
  4177  			Columns: []parser.QueryExpression{
  4178  				parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  4179  			},
  4180  		},
  4181  		Error: "field notexist does not exist",
  4182  	},
  4183  }
  4184  
  4185  func TestDropColumns(t *testing.T) {
  4186  	defer func() {
  4187  		_ = TestTx.ReleaseResources()
  4188  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  4189  		initFlag(TestTx.Flags)
  4190  	}()
  4191  
  4192  	TestTx.Flags.Repository = TestDir
  4193  	TestTx.Flags.Quiet = false
  4194  
  4195  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  4196  		{
  4197  			scopeNameTempTables: {
  4198  				"TMPVIEW": &View{
  4199  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  4200  					RecordSet: []Record{
  4201  						NewRecord([]value.Primary{
  4202  							value.NewString("1"),
  4203  							value.NewString("str1"),
  4204  						}),
  4205  						NewRecord([]value.Primary{
  4206  							value.NewString("2"),
  4207  							value.NewString("str2"),
  4208  						}),
  4209  					},
  4210  					FileInfo: &FileInfo{
  4211  						Path:      "tmpview",
  4212  						Delimiter: ',',
  4213  						ViewType:  ViewTypeTemporaryTable,
  4214  					},
  4215  				},
  4216  			},
  4217  		},
  4218  	}, nil, time.Time{}, nil)
  4219  
  4220  	ctx := context.Background()
  4221  	for _, v := range dropColumnsTests {
  4222  		_ = TestTx.ReleaseResources()
  4223  		result, cnt, err := DropColumns(ctx, scope, v.Query)
  4224  		if err != nil {
  4225  			if len(v.Error) < 1 {
  4226  				t.Errorf("%s: unexpected error %q", v.Name, err)
  4227  			} else if err.Error() != v.Error {
  4228  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  4229  			}
  4230  			continue
  4231  		}
  4232  		if 0 < len(v.Error) {
  4233  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  4234  			continue
  4235  		}
  4236  
  4237  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  4238  			view := value.(*View)
  4239  			if view.FileInfo.Handler != nil {
  4240  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  4241  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  4242  				}
  4243  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  4244  				view.FileInfo.Handler = nil
  4245  			}
  4246  			return true
  4247  		})
  4248  
  4249  		if !reflect.DeepEqual(result, v.Result) {
  4250  			t.Errorf("%s: fileinfo = %v, want %v", v.Name, result, v.Result)
  4251  		}
  4252  
  4253  		if cnt != v.UpdateCount {
  4254  			t.Errorf("%s: update count = %d, want %d", v.Name, cnt, v.UpdateCount)
  4255  		}
  4256  
  4257  		if v.ViewCache.SyncMap != nil {
  4258  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  4259  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  4260  			}
  4261  		}
  4262  		if v.ResultScopes != nil {
  4263  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  4264  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  4265  			}
  4266  		}
  4267  	}
  4268  }
  4269  
  4270  var renameColumnTests = []struct {
  4271  	Name         string
  4272  	Query        parser.RenameColumn
  4273  	Result       *FileInfo
  4274  	ViewCache    ViewMap
  4275  	ResultScopes *ReferenceScope
  4276  	Error        string
  4277  }{
  4278  	{
  4279  		Name: "Rename Column",
  4280  		Query: parser.RenameColumn{
  4281  			Table: parser.Identifier{Literal: "table1"},
  4282  			Old:   parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  4283  			New:   parser.Identifier{Literal: "newcolumn"},
  4284  		},
  4285  		Result: &FileInfo{
  4286  			Path:      GetTestFilePath("table1.csv"),
  4287  			Delimiter: ',',
  4288  			NoHeader:  false,
  4289  			Encoding:  text.UTF8,
  4290  			LineBreak: text.LF,
  4291  			ForUpdate: true,
  4292  		},
  4293  		ViewCache: GenerateViewMap([]*View{
  4294  			{
  4295  				FileInfo: &FileInfo{
  4296  					Path:      GetTestFilePath("table1.csv"),
  4297  					Delimiter: ',',
  4298  					NoHeader:  false,
  4299  					Encoding:  text.UTF8,
  4300  					LineBreak: text.LF,
  4301  					ForUpdate: true,
  4302  				},
  4303  				Header: NewHeader("table1", []string{"column1", "newcolumn"}),
  4304  				RecordSet: []Record{
  4305  					NewRecord([]value.Primary{
  4306  						value.NewString("1"),
  4307  						value.NewString("str1"),
  4308  					}),
  4309  					NewRecord([]value.Primary{
  4310  						value.NewString("2"),
  4311  						value.NewString("str2"),
  4312  					}),
  4313  					NewRecord([]value.Primary{
  4314  						value.NewString("3"),
  4315  						value.NewString("str3"),
  4316  					}),
  4317  				},
  4318  			},
  4319  		}),
  4320  	},
  4321  	{
  4322  		Name: "Rename Column For Temporary View",
  4323  		Query: parser.RenameColumn{
  4324  			Table: parser.Identifier{Literal: "tmpview"},
  4325  			Old:   parser.ColumnNumber{View: parser.Identifier{Literal: "tmpview"}, Number: value.NewInteger(2)},
  4326  			New:   parser.Identifier{Literal: "newcolumn"},
  4327  		},
  4328  		Result: &FileInfo{
  4329  			Path:      "tmpview",
  4330  			Delimiter: ',',
  4331  			ViewType:  ViewTypeTemporaryTable,
  4332  		},
  4333  		ResultScopes: GenerateReferenceScope([]map[string]map[string]interface{}{
  4334  			{
  4335  				scopeNameTempTables: {
  4336  					"TMPVIEW": &View{
  4337  						Header: NewHeader("tmpview", []string{"column1", "newcolumn"}),
  4338  						RecordSet: []Record{
  4339  							NewRecord([]value.Primary{
  4340  								value.NewString("1"),
  4341  								value.NewString("str1"),
  4342  							}),
  4343  							NewRecord([]value.Primary{
  4344  								value.NewString("2"),
  4345  								value.NewString("str2"),
  4346  							}),
  4347  						},
  4348  						FileInfo: &FileInfo{
  4349  							Path:      "tmpview",
  4350  							Delimiter: ',',
  4351  							ViewType:  ViewTypeTemporaryTable,
  4352  						},
  4353  					},
  4354  				},
  4355  			},
  4356  		}, nil, time.Time{}, nil),
  4357  	},
  4358  	{
  4359  		Name: "Rename Column Load Error",
  4360  		Query: parser.RenameColumn{
  4361  			Table: parser.Identifier{Literal: "notexist"},
  4362  			Old:   parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  4363  			New:   parser.Identifier{Literal: "newcolumn"},
  4364  		},
  4365  		Error: "file notexist does not exist",
  4366  	},
  4367  	{
  4368  		Name: "Rename Column Field Duplicate Error",
  4369  		Query: parser.RenameColumn{
  4370  			Table: parser.Identifier{Literal: "table1"},
  4371  			Old:   parser.FieldReference{Column: parser.Identifier{Literal: "column2"}},
  4372  			New:   parser.Identifier{Literal: "column1"},
  4373  		},
  4374  		Error: "field name column1 is a duplicate",
  4375  	},
  4376  	{
  4377  		Name: "Rename Column Field Does Not Exist Error",
  4378  		Query: parser.RenameColumn{
  4379  			Table: parser.Identifier{Literal: "table1"},
  4380  			Old:   parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  4381  			New:   parser.Identifier{Literal: "newcolumn"},
  4382  		},
  4383  		Error: "field notexist does not exist",
  4384  	},
  4385  }
  4386  
  4387  func TestRenameColumn(t *testing.T) {
  4388  	defer func() {
  4389  		_ = TestTx.ReleaseResources()
  4390  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  4391  		initFlag(TestTx.Flags)
  4392  	}()
  4393  
  4394  	TestTx.Flags.Repository = TestDir
  4395  	TestTx.Flags.Quiet = false
  4396  
  4397  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  4398  		{
  4399  			scopeNameTempTables: {
  4400  				"TMPVIEW": &View{
  4401  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  4402  					RecordSet: []Record{
  4403  						NewRecord([]value.Primary{
  4404  							value.NewString("1"),
  4405  							value.NewString("str1"),
  4406  						}),
  4407  						NewRecord([]value.Primary{
  4408  							value.NewString("2"),
  4409  							value.NewString("str2"),
  4410  						}),
  4411  					},
  4412  					FileInfo: &FileInfo{
  4413  						Path:      "tmpview",
  4414  						Delimiter: ',',
  4415  						ViewType:  ViewTypeTemporaryTable,
  4416  					},
  4417  				},
  4418  			},
  4419  		},
  4420  	}, nil, time.Time{}, nil)
  4421  
  4422  	ctx := context.Background()
  4423  	for _, v := range renameColumnTests {
  4424  		_ = TestTx.ReleaseResources()
  4425  		result, err := RenameColumn(ctx, scope, v.Query)
  4426  		if err != nil {
  4427  			if len(v.Error) < 1 {
  4428  				t.Errorf("%s: unexpected error %q", v.Name, err)
  4429  			} else if err.Error() != v.Error {
  4430  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  4431  			}
  4432  			continue
  4433  		}
  4434  		if 0 < len(v.Error) {
  4435  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  4436  			continue
  4437  		}
  4438  
  4439  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  4440  			view := value.(*View)
  4441  			if view.FileInfo.Handler != nil {
  4442  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  4443  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  4444  				}
  4445  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  4446  				view.FileInfo.Handler = nil
  4447  			}
  4448  			return true
  4449  		})
  4450  
  4451  		if !reflect.DeepEqual(result, v.Result) {
  4452  			t.Errorf("%s: result = %v, want %v", v.Name, result, v.Result)
  4453  		}
  4454  
  4455  		if v.ViewCache.SyncMap != nil {
  4456  			if !SyncMapEqual(TestTx.CachedViews, v.ViewCache) {
  4457  				t.Errorf("%s: view cache = %v, want %v", v.Name, TestTx.CachedViews, v.ViewCache)
  4458  			}
  4459  		}
  4460  		if v.ResultScopes != nil {
  4461  			if !SyncMapEqual(scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables) {
  4462  				t.Errorf("%s: temporary views list = %v, want %v", v.Name, scope.Blocks[0].TemporaryTables, v.ResultScopes.Blocks[0].TemporaryTables)
  4463  			}
  4464  		}
  4465  	}
  4466  }
  4467  
  4468  var setTableAttributeTests = []struct {
  4469  	Name   string
  4470  	Query  parser.SetTableAttribute
  4471  	Expect *FileInfo
  4472  	Error  string
  4473  }{
  4474  	{
  4475  		Name: "Set Delimiter to TSV",
  4476  		Query: parser.SetTableAttribute{
  4477  			Table:     parser.Identifier{Literal: "table1.csv"},
  4478  			Attribute: parser.Identifier{Literal: "delimiter"},
  4479  			Value:     parser.NewStringValue("\t"),
  4480  		},
  4481  		Expect: &FileInfo{
  4482  			Path:      GetTestFilePath("table1.csv"),
  4483  			Delimiter: '\t',
  4484  			Format:    option.TSV,
  4485  			Encoding:  text.UTF8,
  4486  			LineBreak: text.LF,
  4487  			ForUpdate: true,
  4488  		},
  4489  	},
  4490  	{
  4491  		Name: "Set Delimiter to TSV with FormatSpecifiedFunction",
  4492  		Query: parser.SetTableAttribute{
  4493  			Table: parser.FormatSpecifiedFunction{
  4494  				Type:          parser.Token{Token: parser.CSV, Literal: "csv"},
  4495  				Path:          parser.Identifier{Literal: "table1.csv"},
  4496  				FormatElement: parser.NewStringValue(","),
  4497  			},
  4498  			Attribute: parser.Identifier{Literal: "delimiter"},
  4499  			Value:     parser.NewStringValue("\t"),
  4500  		},
  4501  		Expect: &FileInfo{
  4502  			Path:      GetTestFilePath("table1.csv"),
  4503  			Delimiter: '\t',
  4504  			Format:    option.TSV,
  4505  			Encoding:  text.UTF8,
  4506  			LineBreak: text.LF,
  4507  			ForUpdate: true,
  4508  		},
  4509  	},
  4510  	{
  4511  		Name: "Set Delimiter to CSV",
  4512  		Query: parser.SetTableAttribute{
  4513  			Table:     parser.Identifier{Literal: "table1.csv"},
  4514  			Attribute: parser.Identifier{Literal: "delimiter"},
  4515  			Value:     parser.NewStringValue(";"),
  4516  		},
  4517  		Expect: &FileInfo{
  4518  			Path:      GetTestFilePath("table1.csv"),
  4519  			Delimiter: ';',
  4520  			Format:    option.CSV,
  4521  			Encoding:  text.UTF8,
  4522  			LineBreak: text.LF,
  4523  			ForUpdate: true,
  4524  		},
  4525  	},
  4526  	{
  4527  		Name: "Set Delimiter Error",
  4528  		Query: parser.SetTableAttribute{
  4529  			Table:     parser.Identifier{Literal: "table1.csv"},
  4530  			Attribute: parser.Identifier{Literal: "delimiter"},
  4531  			Value:     parser.NewStringValue("aa"),
  4532  		},
  4533  		Error: "delimiter must be one character",
  4534  	},
  4535  	{
  4536  		Name: "Set Delimiter Not Allowed Value",
  4537  		Query: parser.SetTableAttribute{
  4538  			Table:     parser.Identifier{Literal: "table1.csv"},
  4539  			Attribute: parser.Identifier{Literal: "delimiter"},
  4540  			Value:     parser.NewNullValue(),
  4541  		},
  4542  		Error: "NULL for delimiter is not allowed",
  4543  	},
  4544  	{
  4545  		Name: "Set DelimiterPositions",
  4546  		Query: parser.SetTableAttribute{
  4547  			Table:     parser.Identifier{Literal: "table1.csv"},
  4548  			Attribute: parser.Identifier{Literal: "delimiter_positions"},
  4549  			Value:     parser.NewStringValue("S[2, 5, 10]"),
  4550  		},
  4551  		Expect: &FileInfo{
  4552  			Path:               GetTestFilePath("table1.csv"),
  4553  			Delimiter:          ',',
  4554  			DelimiterPositions: []int{2, 5, 10},
  4555  			Format:             option.FIXED,
  4556  			Encoding:           text.UTF8,
  4557  			SingleLine:         true,
  4558  			LineBreak:          text.LF,
  4559  			ForUpdate:          true,
  4560  		},
  4561  	},
  4562  	{
  4563  		Name: "Set DelimiterPositions Error",
  4564  		Query: parser.SetTableAttribute{
  4565  			Table:     parser.Identifier{Literal: "table1.csv"},
  4566  			Attribute: parser.Identifier{Literal: "delimiter_positions"},
  4567  			Value:     parser.NewStringValue("invalid"),
  4568  		},
  4569  		Error: "delimiter positions must be \"SPACES\" or a JSON array of integers",
  4570  	},
  4571  	{
  4572  		Name: "Set Format to Text",
  4573  		Query: parser.SetTableAttribute{
  4574  			Table:     parser.Identifier{Literal: "table1.csv"},
  4575  			Attribute: parser.Identifier{Literal: "format"},
  4576  			Value:     parser.NewStringValue("text"),
  4577  		},
  4578  		Expect: &FileInfo{
  4579  			Path:      GetTestFilePath("table1.csv"),
  4580  			Delimiter: ',',
  4581  			Format:    option.TEXT,
  4582  			Encoding:  text.UTF8,
  4583  			LineBreak: text.LF,
  4584  			ForUpdate: true,
  4585  		},
  4586  	},
  4587  	{
  4588  		Name: "Set Format to JSON",
  4589  		Query: parser.SetTableAttribute{
  4590  			Table:     parser.Identifier{Literal: "table1.csv"},
  4591  			Attribute: parser.Identifier{Literal: "format"},
  4592  			Value:     parser.NewStringValue("json"),
  4593  		},
  4594  		Expect: &FileInfo{
  4595  			Path:      GetTestFilePath("table1.csv"),
  4596  			Delimiter: ',',
  4597  			Format:    option.JSON,
  4598  			Encoding:  text.UTF8,
  4599  			LineBreak: text.LF,
  4600  			ForUpdate: true,
  4601  		},
  4602  	},
  4603  	{
  4604  		Name: "Set Format to TSV",
  4605  		Query: parser.SetTableAttribute{
  4606  			Table:     parser.Identifier{Literal: "table1.csv"},
  4607  			Attribute: parser.Identifier{Literal: "format"},
  4608  			Value:     parser.NewStringValue("tsv"),
  4609  		},
  4610  		Expect: &FileInfo{
  4611  			Path:      GetTestFilePath("table1.csv"),
  4612  			Delimiter: '\t',
  4613  			Format:    option.TSV,
  4614  			Encoding:  text.UTF8,
  4615  			LineBreak: text.LF,
  4616  			ForUpdate: true,
  4617  		},
  4618  	},
  4619  	{
  4620  		Name: "Set Format Error",
  4621  		Query: parser.SetTableAttribute{
  4622  			Table:     parser.Identifier{Literal: "table1.csv"},
  4623  			Attribute: parser.Identifier{Literal: "format"},
  4624  			Value:     parser.NewStringValue("invalid"),
  4625  		},
  4626  		Error: "format must be one of CSV|TSV|FIXED|JSON|JSONL|LTSV|GFM|ORG|BOX|TEXT",
  4627  	},
  4628  	{
  4629  		Name: "Set Encoding to SJIS",
  4630  		Query: parser.SetTableAttribute{
  4631  			Table:     parser.Identifier{Literal: "table1.csv"},
  4632  			Attribute: parser.Identifier{Literal: "encoding"},
  4633  			Value:     parser.NewStringValue("sjis"),
  4634  		},
  4635  		Expect: &FileInfo{
  4636  			Path:      GetTestFilePath("table1.csv"),
  4637  			Delimiter: ',',
  4638  			Format:    option.CSV,
  4639  			Encoding:  text.SJIS,
  4640  			LineBreak: text.LF,
  4641  			ForUpdate: true,
  4642  		},
  4643  	},
  4644  	{
  4645  		Name: "Set Encoding to SJIS with Identifier",
  4646  		Query: parser.SetTableAttribute{
  4647  			Table:     parser.Identifier{Literal: "table1.csv"},
  4648  			Attribute: parser.Identifier{Literal: "encoding"},
  4649  			Value:     parser.Identifier{Literal: "sjis"},
  4650  		},
  4651  		Expect: &FileInfo{
  4652  			Path:      GetTestFilePath("table1.csv"),
  4653  			Delimiter: ',',
  4654  			Format:    option.CSV,
  4655  			Encoding:  text.SJIS,
  4656  			LineBreak: text.LF,
  4657  			ForUpdate: true,
  4658  		},
  4659  	},
  4660  	{
  4661  		Name: "Set Encoding Error",
  4662  		Query: parser.SetTableAttribute{
  4663  			Table:     parser.Identifier{Literal: "table1.csv"},
  4664  			Attribute: parser.Identifier{Literal: "encoding"},
  4665  			Value:     parser.NewStringValue("invalid"),
  4666  		},
  4667  		Error: "encoding must be one of UTF8|UTF8M|UTF16|UTF16BE|UTF16LE|UTF16BEM|UTF16LEM|SJIS",
  4668  	},
  4669  	{
  4670  		Name: "Set Encoding Error in JSON Format",
  4671  		Query: parser.SetTableAttribute{
  4672  			Table:     parser.Identifier{Literal: "table.json"},
  4673  			Attribute: parser.Identifier{Literal: "encoding"},
  4674  			Value:     parser.NewStringValue("sjis"),
  4675  		},
  4676  		Error: "json format is supported only UTF8",
  4677  	},
  4678  	{
  4679  		Name: "Set LineBreak to CRLF",
  4680  		Query: parser.SetTableAttribute{
  4681  			Table:     parser.Identifier{Literal: "table1.csv"},
  4682  			Attribute: parser.Identifier{Literal: "line_break"},
  4683  			Value:     parser.NewStringValue("crlf"),
  4684  		},
  4685  		Expect: &FileInfo{
  4686  			Path:      GetTestFilePath("table1.csv"),
  4687  			Delimiter: ',',
  4688  			Format:    option.CSV,
  4689  			Encoding:  text.UTF8,
  4690  			LineBreak: text.CRLF,
  4691  			ForUpdate: true,
  4692  		},
  4693  	},
  4694  	{
  4695  		Name: "Set LineBreak Error",
  4696  		Query: parser.SetTableAttribute{
  4697  			Table:     parser.Identifier{Literal: "table1.csv"},
  4698  			Attribute: parser.Identifier{Literal: "line_break"},
  4699  			Value:     parser.NewStringValue("invalid"),
  4700  		},
  4701  		Error: "line-break must be one of CRLF|CR|LF",
  4702  	},
  4703  	{
  4704  		Name: "Set NoHeader to true",
  4705  		Query: parser.SetTableAttribute{
  4706  			Table:     parser.Identifier{Literal: "table1.csv"},
  4707  			Attribute: parser.Identifier{Literal: "header"},
  4708  			Value:     parser.NewTernaryValueFromString("false"),
  4709  		},
  4710  		Expect: &FileInfo{
  4711  			Path:      GetTestFilePath("table1.csv"),
  4712  			Delimiter: ',',
  4713  			Format:    option.CSV,
  4714  			Encoding:  text.UTF8,
  4715  			LineBreak: text.LF,
  4716  			NoHeader:  true,
  4717  			ForUpdate: true,
  4718  		},
  4719  	},
  4720  	{
  4721  		Name: "Set NoHeader Not Allowed Value",
  4722  		Query: parser.SetTableAttribute{
  4723  			Table:     parser.Identifier{Literal: "table1.csv"},
  4724  			Attribute: parser.Identifier{Literal: "header"},
  4725  			Value:     parser.NewNullValue(),
  4726  		},
  4727  		Error: "NULL for header is not allowed",
  4728  	},
  4729  	{
  4730  		Name: "Set EncloseAll to true",
  4731  		Query: parser.SetTableAttribute{
  4732  			Table:     parser.Identifier{Literal: "table1.csv"},
  4733  			Attribute: parser.Identifier{Literal: "enclose_all"},
  4734  			Value:     parser.NewStringValue("true"),
  4735  		},
  4736  		Expect: &FileInfo{
  4737  			Path:       GetTestFilePath("table1.csv"),
  4738  			Delimiter:  ',',
  4739  			Format:     option.CSV,
  4740  			Encoding:   text.UTF8,
  4741  			LineBreak:  text.LF,
  4742  			EncloseAll: true,
  4743  			ForUpdate:  true,
  4744  		},
  4745  	},
  4746  	{
  4747  		Name: "Set JsonEscape to HEX",
  4748  		Query: parser.SetTableAttribute{
  4749  			Table:     parser.Identifier{Literal: "table.json"},
  4750  			Attribute: parser.Identifier{Literal: "json_escape"},
  4751  			Value:     parser.NewStringValue("hex"),
  4752  		},
  4753  		Expect: &FileInfo{
  4754  			Path:        GetTestFilePath("table.json"),
  4755  			Delimiter:   ',',
  4756  			Format:      option.JSON,
  4757  			Encoding:    text.UTF8,
  4758  			LineBreak:   text.LF,
  4759  			JsonEscape:  json.HexDigits,
  4760  			PrettyPrint: false,
  4761  			ForUpdate:   true,
  4762  		},
  4763  	},
  4764  	{
  4765  		Name: "Set JsonEscape Error",
  4766  		Query: parser.SetTableAttribute{
  4767  			Table:     parser.Identifier{Literal: "table.json"},
  4768  			Attribute: parser.Identifier{Literal: "json_escape"},
  4769  			Value:     parser.NewStringValue("invalid"),
  4770  		},
  4771  		Error: "json escape type must be one of BACKSLASH|HEX|HEXALL",
  4772  	},
  4773  	{
  4774  		Name: "Set PrettyPring to true",
  4775  		Query: parser.SetTableAttribute{
  4776  			Table:     parser.Identifier{Literal: "table.json"},
  4777  			Attribute: parser.Identifier{Literal: "pretty_print"},
  4778  			Value:     parser.NewTernaryValueFromString("true"),
  4779  		},
  4780  		Expect: &FileInfo{
  4781  			Path:        GetTestFilePath("table.json"),
  4782  			Delimiter:   ',',
  4783  			Format:      option.JSON,
  4784  			Encoding:    text.UTF8,
  4785  			LineBreak:   text.LF,
  4786  			PrettyPrint: true,
  4787  			ForUpdate:   true,
  4788  		},
  4789  	},
  4790  	{
  4791  		Name: "Not Exist Table Error",
  4792  		Query: parser.SetTableAttribute{
  4793  			Table:     parser.Identifier{Literal: "notexist.csv"},
  4794  			Attribute: parser.Identifier{Literal: "delimiter"},
  4795  			Value:     parser.NewStringValue(","),
  4796  		},
  4797  		Error: "file notexist.csv does not exist",
  4798  	},
  4799  	{
  4800  		Name: "Temporary View Error",
  4801  		Query: parser.SetTableAttribute{
  4802  			Table:     parser.Identifier{Literal: "tmpview"},
  4803  			Attribute: parser.Identifier{Literal: "delimiter"},
  4804  			Value:     parser.NewStringValue(","),
  4805  		},
  4806  		Error: "table attributes can only be set on files",
  4807  	},
  4808  	{
  4809  		Name: "Value Evaluation Error",
  4810  		Query: parser.SetTableAttribute{
  4811  			Table:     parser.Identifier{Literal: "table1.csv"},
  4812  			Attribute: parser.Identifier{Literal: "delimiter"},
  4813  			Value:     parser.FieldReference{Column: parser.Identifier{Literal: "notexist"}},
  4814  		},
  4815  		Error: "field notexist does not exist",
  4816  	},
  4817  	{
  4818  		Name: "Not Exist Attribute Error",
  4819  		Query: parser.SetTableAttribute{
  4820  			Table:     parser.Identifier{Literal: "table1.csv"},
  4821  			Attribute: parser.Identifier{Literal: "notexist"},
  4822  			Value:     parser.NewStringValue(","),
  4823  		},
  4824  		Error: "table attribute notexist does not exist",
  4825  	},
  4826  }
  4827  
  4828  func TestSetTableAttribute(t *testing.T) {
  4829  	defer func() {
  4830  		_ = TestTx.ReleaseResources()
  4831  		_ = TestTx.CachedViews.Clean(TestTx.FileContainer)
  4832  		initFlag(TestTx.Flags)
  4833  	}()
  4834  
  4835  	TestTx.Flags.Repository = TestDir
  4836  	TestTx.Flags.Quiet = false
  4837  
  4838  	scope := GenerateReferenceScope([]map[string]map[string]interface{}{
  4839  		{
  4840  			scopeNameTempTables: {
  4841  				"TMPVIEW": &View{
  4842  					Header: NewHeader("tmpview", []string{"column1", "column2"}),
  4843  					RecordSet: []Record{
  4844  						NewRecord([]value.Primary{
  4845  							value.NewString("1"),
  4846  							value.NewString("str1"),
  4847  						}),
  4848  						NewRecord([]value.Primary{
  4849  							value.NewString("2"),
  4850  							value.NewString("str2"),
  4851  						}),
  4852  					},
  4853  					FileInfo: &FileInfo{
  4854  						Path:      "tmpview",
  4855  						Delimiter: ',',
  4856  						ViewType:  ViewTypeTemporaryTable,
  4857  					},
  4858  				},
  4859  			},
  4860  		},
  4861  	}, nil, time.Time{}, nil)
  4862  
  4863  	ctx := context.Background()
  4864  	for _, v := range setTableAttributeTests {
  4865  		_ = TestTx.ReleaseResources()
  4866  
  4867  		_, _, err := SetTableAttribute(ctx, scope, v.Query)
  4868  		if err != nil {
  4869  			if len(v.Error) < 1 {
  4870  				t.Errorf("%s: unexpected error %q", v.Name, err)
  4871  			} else if err.Error() != v.Error {
  4872  				t.Errorf("%s: error %q, want error %q", v.Name, err.Error(), v.Error)
  4873  			}
  4874  			continue
  4875  		}
  4876  		if 0 < len(v.Error) {
  4877  			t.Errorf("%s: no error, want error %q", v.Name, v.Error)
  4878  			continue
  4879  		}
  4880  
  4881  		TestTx.CachedViews.Range(func(key, value interface{}) bool {
  4882  			view := value.(*View)
  4883  			if view.FileInfo.Handler != nil {
  4884  				if view.FileInfo.Path != view.FileInfo.Handler.Path() {
  4885  					t.Errorf("file pointer = %q, want %q for %q", view.FileInfo.Handler.Path(), view.FileInfo.Path, v.Name)
  4886  				}
  4887  				_ = TestTx.FileContainer.Close(view.FileInfo.Handler)
  4888  				view.FileInfo.Handler = nil
  4889  			}
  4890  			return true
  4891  		})
  4892  
  4893  		view, _ := LoadViewFromTableIdentifier(ctx, scope.CreateNode(), v.Query.Table, false, false)
  4894  
  4895  		if !reflect.DeepEqual(view.FileInfo, v.Expect) {
  4896  			t.Errorf("%s: result = %v, want %v", v.Name, view.FileInfo, v.Expect)
  4897  		}
  4898  
  4899  		_, _, err = SetTableAttribute(ctx, scope, v.Query)
  4900  		if err == nil {
  4901  			t.Errorf("%s: no error, want TableAttributeUnchangedError for duplicate set", v.Name)
  4902  		} else if _, ok := err.(*TableAttributeUnchangedError); !ok {
  4903  			t.Errorf("%s: error = %T, want TableAttributeUnchangedError for duplicate set", v.Name, err)
  4904  		}
  4905  	}
  4906  }