github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/sys/it/impl_sqlquery_test.go (about)

     1  /*
     2   * Copyright (c) 2022-present unTill Pro, Ltd.
     3   */
     4  
     5  package sys_it
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"log"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/voedger/voedger/pkg/appdef"
    17  	"github.com/voedger/voedger/pkg/istructs"
    18  	"github.com/voedger/voedger/pkg/processors"
    19  	"github.com/voedger/voedger/pkg/sys/sqlquery"
    20  	coreutils "github.com/voedger/voedger/pkg/utils"
    21  	it "github.com/voedger/voedger/pkg/vit"
    22  )
    23  
    24  func TestBasicUsage_SqlQuery(t *testing.T) {
    25  	require := require.New(t)
    26  	vit := it.NewVIT(t, &it.SharedConfig_App1)
    27  	defer vit.TearDown()
    28  
    29  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
    30  	idUntillUsers := vit.GetAny("app1pkg.untill_users", ws)
    31  
    32  	findPLogOffsetByWLogOffset := func(wLogOffset istructs.Offset) istructs.Offset {
    33  		type row struct {
    34  			Workspace  istructs.WSID
    35  			PlogOffset istructs.Offset
    36  			WLogOffset istructs.Offset
    37  		}
    38  		body := `{"args":{"Query":"select Workspace, PlogOffset, WLogOffset from sys.plog limit -1"},"elements":[{"fields":["Result"]}]}`
    39  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
    40  		for _, element := range resp.Sections[0].Elements {
    41  			r := new(row)
    42  			require.NoError(json.Unmarshal([]byte(element[0][0][0].(string)), r))
    43  			if r.Workspace == ws.WSID && r.WLogOffset == wLogOffset {
    44  				return r.PlogOffset
    45  			}
    46  		}
    47  		panic("PlogOffset not found")
    48  	}
    49  
    50  	tableNum := vit.NextNumber()
    51  
    52  	body := `{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.category","name":"Awesome food"}}]}`
    53  	vit.PostWS(ws, "c.sys.CUD", body)
    54  	body = fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.bill","tableno":%d,"id_untill_users":%d,"table_part":"a","proforma":0,"working_day":"20230227"}}]}`, tableNum, idUntillUsers)
    55  	vit.PostWS(ws, "c.sys.CUD", body)
    56  	body = `{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.payments","name":"EFT","guid":"0a53b7c6-2c47-491c-ac00-307b8d5ba6f2"}}]}`
    57  	resp := vit.PostWS(ws, "c.sys.CUD", body)
    58  
    59  	body = fmt.Sprintf(`{"args":{"Query":"select CUDs from sys.plog where Offset>=%d"},"elements":[{"fields":["Result"]}]}`, findPLogOffsetByWLogOffset(resp.CurrentWLogOffset))
    60  	resp = vit.PostWS(ws, "q.sys.SqlQuery", body)
    61  
    62  	require.Contains(resp.SectionRow()[0], "0a53b7c6-2c47-491c-ac00-307b8d5ba6f2")
    63  }
    64  
    65  func TestSqlQuery_plog(t *testing.T) {
    66  	vit := it.NewVIT(t, &it.SharedConfig_App1)
    67  	defer vit.TearDown()
    68  
    69  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
    70  	idUntillUsers := vit.GetAny("app1pkg.untill_users", ws)
    71  
    72  	pLogSize := 0
    73  	// it is wrong to consider last resp.CurrentWLogOffset as the pLog events amount because pLog contains events from different workspaces
    74  	// currently log of partition 0 contains events from 2 workspaces: pseudo 140737488420870 and newely created 140737488486400
    75  	// following util shows the initial content on pLog of partition 0:
    76  	t.Run("print the pLog content", func(t *testing.T) {
    77  		require := require.New(t)
    78  		body := `{"args":{"Query":"select * from sys.plog limit -1"},"elements":[{"fields":["Result"]}]}`
    79  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
    80  
    81  		for _, intf := range resp.Sections[0].Elements {
    82  			m := map[string]interface{}{}
    83  			require.NoError(json.Unmarshal([]byte(intf[0][0][0].(string)), &m))
    84  			log.Println(int(m["Workspace"].(float64)), m["PlogOffset"], m["WLogOffset"])
    85  		}
    86  		pLogSize = len(resp.Sections[0].Elements)
    87  	})
    88  	// note that we have wlogOffset 7 twice, so the last resp.CurrentWLogOffset is not the amount of events in pLog
    89  	// currently events amount is 13, the last resp.CurrentWLogOffset is 12:
    90  	/*
    91  		140737488420870 1 7
    92  		140737488486400 2 1
    93  		140737488486400 3 2
    94  		140737488486400 4 3
    95  		140737488486400 5 4
    96  		140737488486400 6 5
    97  		140737488486400 7 6
    98  		140737488486400 8 7
    99  		140737488486400 9 8
   100  		140737488486400 10 9
   101  		140737488486400 11 10
   102  		140737488486400 12 11
   103  		140737488486400 13 12
   104  	*/
   105  
   106  	for i := 1; i <= 101; i++ {
   107  		tableno := vit.NextNumber()
   108  		body := fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID":%d,"sys.QName":"app1pkg.bill","tableno":%d,"id_untill_users":%d,"table_part":"a","proforma":0,"working_day":"20230227"}}]}`, i, tableno, idUntillUsers)
   109  		vit.PostWS(ws, "c.sys.CUD", body)
   110  		pLogSize++
   111  	}
   112  
   113  	time.Sleep(ProjectionAwaitTime)
   114  
   115  	t.Run("Should read events with default Offset and limit", func(t *testing.T) {
   116  		require := require.New(t)
   117  		body := `{"args":{"Query":"select * from sys.plog"},"elements":[{"fields":["Result"]}]}`
   118  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   119  
   120  		m := map[string]interface{}{}
   121  		require.NoError(json.Unmarshal([]byte(resp.SectionRow()[0].(string)), &m))
   122  		require.Equal(sqlquery.DefaultOffset, istructs.Offset(m["PlogOffset"].(float64)))
   123  		require.Len(resp.Sections[0].Elements, sqlquery.DefaultLimit)
   124  	})
   125  
   126  	lastPLogOffset := 0
   127  
   128  	t.Run("Should read all events", func(t *testing.T) {
   129  		require := require.New(t)
   130  		body := `{"args":{"Query":"select * from sys.plog limit -1"},"elements":[{"fields":["Result"]}]}`
   131  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   132  
   133  		m := map[string]interface{}{}
   134  		require.NoError(json.Unmarshal([]byte(resp.SectionRow()[0].(string)), &m))
   135  		require.Equal(sqlquery.DefaultOffset, istructs.Offset(m["PlogOffset"].(float64)))
   136  		require.GreaterOrEqual(len(resp.Sections[0].Elements), pLogSize)
   137  
   138  		m = map[string]interface{}{}
   139  		require.NoError(json.Unmarshal([]byte(resp.SectionRow(len(resp.Sections[0].Elements) - 1)[0].(string)), &m))
   140  		lastPLogOffset = int(m["PlogOffset"].(float64))
   141  
   142  	})
   143  	t.Run("Should read one event by limit", func(t *testing.T) {
   144  		require := require.New(t)
   145  		body := `{"args":{"Query":"select * from sys.plog limit 1"},"elements":[{"fields":["Result"]}]}`
   146  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   147  
   148  		require.Len(resp.Sections[0].Elements, 1)
   149  	})
   150  	t.Run("Should read one event by Offset", func(t *testing.T) {
   151  		require := require.New(t)
   152  		body := fmt.Sprintf(`{"args":{"Query":"select * from sys.plog where Offset > %d"},"elements":[{"fields":["Result"]}]}`, lastPLogOffset-1)
   153  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   154  
   155  		m := map[string]interface{}{}
   156  		require.NoError(json.Unmarshal([]byte(resp.SectionRow()[0].(string)), &m))
   157  		require.Equal(lastPLogOffset, int(m["PlogOffset"].(float64)))
   158  		require.Len(resp.Sections[0].Elements, 1)
   159  	})
   160  	t.Run("Should read two events by Offset", func(t *testing.T) {
   161  		require := require.New(t)
   162  		body := fmt.Sprintf(`{"args":{"Query":"select * from sys.plog where Offset >= %d"},"elements":[{"fields":["Result"]}]}`, lastPLogOffset-1)
   163  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   164  
   165  		require.Len(resp.Sections[0].Elements, 2)
   166  
   167  		m := map[string]interface{}{}
   168  		require.NoError(json.Unmarshal([]byte(resp.SectionRow()[0].(string)), &m))
   169  		require.Equal(lastPLogOffset-1, int(m["PlogOffset"].(float64)))
   170  		m = map[string]interface{}{}
   171  		require.NoError(json.Unmarshal([]byte(resp.SectionRow(1)[0].(string)), &m))
   172  		require.Equal(lastPLogOffset, int(m["PlogOffset"].(float64)))
   173  	})
   174  	t.Run("Should read event with specified Offset", func(t *testing.T) {
   175  		require := require.New(t)
   176  		specifiedOffset := lastPLogOffset - 52
   177  		body := fmt.Sprintf(`{"args":{"Query":"select * from sys.plog where Offset = %d"},"elements":[{"fields":["Result"]}]}`, specifiedOffset)
   178  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   179  
   180  		require.Len(resp.Sections[0].Elements, 1)
   181  		require.Contains(resp.SectionRow()[0], fmt.Sprintf(`"PlogOffset":%d`, specifiedOffset))
   182  	})
   183  	t.Run("Should return error when field not found in def", func(t *testing.T) {
   184  		body := `{"args":{"Query":"select abracadabra from sys.plog"}}`
   185  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   186  
   187  		resp.RequireError(t, "field 'abracadabra' not found in def")
   188  	})
   189  }
   190  
   191  func TestSqlQuery_wlog(t *testing.T) {
   192  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   193  	defer vit.TearDown()
   194  
   195  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   196  	idUntillUsers := vit.GetAny("app1pkg.untill_users", ws)
   197  
   198  	var lastWLogOffset istructs.Offset
   199  	for i := 1; i <= 101; i++ {
   200  		tableno := vit.NextNumber()
   201  		body := fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID":%d,"sys.QName":"app1pkg.bill","tableno":%d,"id_untill_users":%d,"table_part":"a","proforma":0,"working_day":"20230227"}}]}`, i, tableno, idUntillUsers)
   202  		resp := vit.PostWS(ws, "c.sys.CUD", body)
   203  		lastWLogOffset = resp.CurrentWLogOffset
   204  	}
   205  	wLogEventsAmount := int(lastWLogOffset)
   206  
   207  	t.Run("Should read events with default Offset and limit", func(t *testing.T) {
   208  		require := require.New(t)
   209  
   210  		body := `{"args":{"Query":"select * from sys.wlog"},"elements":[{"fields":["Result"]}]}`
   211  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   212  
   213  		require.Len(resp.Sections[0].Elements, 100)
   214  	})
   215  	t.Run("Should read all events", func(t *testing.T) {
   216  		require := require.New(t)
   217  
   218  		body := `{"args":{"Query":"select * from sys.wlog limit -1"},"elements":[{"fields":["Result"]}]}`
   219  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   220  
   221  		require.Len(resp.Sections[0].Elements, wLogEventsAmount)
   222  	})
   223  	t.Run("Should read one event by limit", func(t *testing.T) {
   224  		require := require.New(t)
   225  
   226  		body := `{"args":{"Query":"select * from sys.wlog limit 1"},"elements":[{"fields":["Result"]}]}`
   227  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   228  
   229  		require.Len(resp.Sections[0].Elements, 1)
   230  	})
   231  	t.Run("Should read one event by Offset", func(t *testing.T) {
   232  		require := require.New(t)
   233  
   234  		body := fmt.Sprintf(`{"args":{"Query":"select * from sys.wlog where Offset > %d"},"elements":[{"fields":["Result"]}]}`, lastWLogOffset-1)
   235  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   236  
   237  		require.Len(resp.Sections[0].Elements, 1)
   238  	})
   239  	t.Run("Should read two events by Offset", func(t *testing.T) {
   240  		require := require.New(t)
   241  
   242  		body := fmt.Sprintf(`{"args":{"Query":"select * from sys.wlog where Offset >= %d"},"elements":[{"fields":["Result"]}]}`, lastWLogOffset-1)
   243  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   244  
   245  		require.Len(resp.Sections[0].Elements, 2)
   246  	})
   247  	t.Run("Should return error when field not found in def", func(t *testing.T) {
   248  		body := `{"args":{"Query":"select abracadabra from sys.wlog"}}`
   249  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   250  
   251  		resp.RequireError(t, "field 'abracadabra' not found in def")
   252  	})
   253  }
   254  
   255  func TestSqlQuery_readLogParams(t *testing.T) {
   256  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   257  	defer vit.TearDown()
   258  
   259  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   260  
   261  	t.Run("Should return error when limit value not parsable", func(t *testing.T) {
   262  		body := `{"args":{"Query":"select * from sys.plog limit 7.1"}}`
   263  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   264  
   265  		resp.RequireError(t, `strconv.ParseInt: parsing "7.1": invalid syntax`)
   266  	})
   267  	t.Run("Should return error when limit value invalid", func(t *testing.T) {
   268  		body := `{"args":{"Query":"select * from sys.plog limit -3"}}`
   269  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   270  
   271  		resp.RequireError(t, "limit must be greater than -2")
   272  	})
   273  	t.Run("Should return error when Offset value not parsable", func(t *testing.T) {
   274  		body := `{"args":{"Query":"select * from sys.plog where Offset >= 2.1"}}`
   275  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   276  
   277  		resp.RequireError(t, `strconv.ParseInt: parsing "2.1": invalid syntax`)
   278  	})
   279  	t.Run("Should return error when Offset value invalid", func(t *testing.T) {
   280  		body := `{"args":{"Query":"select * from sys.plog where Offset >= 0"}}`
   281  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   282  
   283  		resp.RequireError(t, "offset must be greater than zero")
   284  	})
   285  	t.Run("Should return error when Offset operation not supported", func(t *testing.T) {
   286  		body := `{"args":{"Query":"select * from sys.plog where Offset < 2"}}`
   287  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   288  
   289  		resp.RequireError(t, "unsupported operation: <")
   290  	})
   291  	t.Run("Should return error when column name not supported", func(t *testing.T) {
   292  		body := `{"args":{"Query":"select * from sys.plog where something >= 1"}}`
   293  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   294  
   295  		resp.RequireError(t, "unsupported column name: something")
   296  	})
   297  	t.Run("Should return error when expression not supported", func(t *testing.T) {
   298  		body := `{"args":{"Query":"select * from sys.wlog where Offset >= 1 and something >= 5"}}`
   299  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   300  
   301  		resp.RequireError(t, "unsupported expression: *sqlparser.AndExpr")
   302  	})
   303  }
   304  
   305  func TestSqlQuery_records(t *testing.T) {
   306  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   307  	defer vit.TearDown()
   308  
   309  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   310  
   311  	body := `{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.payments","name":"EFT","guid":"guidEFT"}},
   312  					   {"fields":{"sys.ID":2,"sys.QName":"app1pkg.payments","name":"Cash","guid":"guidCash"}},
   313  					   {"fields":{"sys.ID":3,"sys.QName":"app1pkg.pos_emails","description":"invite"}}]}`
   314  	res := vit.PostWS(ws, "c.sys.CUD", body)
   315  
   316  	eftId := res.NewID()
   317  	cashId := res.NewIDs["2"]
   318  	emailId := res.NewIDs["3"]
   319  
   320  	t.Run("Should read record with all fields by ID", func(t *testing.T) {
   321  		require := require.New(t)
   322  		body = fmt.Sprintf(`{"args":{"Query":"select * from app1pkg.payments where id = %d"},"elements":[{"fields":["Result"]}]}`, eftId)
   323  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   324  
   325  		resStr := resp.SectionRow(len(resp.Sections[0].Elements) - 1)[0].(string)
   326  		require.Contains(resStr, `"sys.QName":"app1pkg.payments"`)
   327  		require.Contains(resStr, fmt.Sprintf(`"sys.ID":%d`, eftId))
   328  		require.Contains(resStr, `"guid":"guidEFT"`)
   329  		require.Contains(resStr, `"name":"EFT"`)
   330  		require.Contains(resStr, `"sys.IsActive":true`)
   331  	})
   332  	t.Run("Should read records with one field by IDs range", func(t *testing.T) {
   333  		require := require.New(t)
   334  		body = fmt.Sprintf(`{"args":{"Query":"select name, sys.IsActive from app1pkg.payments where id in (%d,%d)"}, "elements":[{"fields":["Result"]}]}`, eftId, cashId)
   335  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   336  
   337  		require.Equal(`{"name":"EFT","sys.IsActive":true}`, resp.SectionRow()[0])
   338  		require.Equal(`{"name":"Cash","sys.IsActive":true}`, resp.SectionRow(1)[0])
   339  	})
   340  	t.Run("Should return error when column name not supported", func(t *testing.T) {
   341  		body = `{"args":{"Query":"select * from app1pkg.payments where something = 1"}}`
   342  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   343  
   344  		resp.RequireError(t, "unsupported column name: something")
   345  	})
   346  	t.Run("Should return error when ID not parsable", func(t *testing.T) {
   347  		body = `{"args":{"Query":"select * from app1pkg.payments where id = 2.3"}}`
   348  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   349  
   350  		resp.RequireError(t, `strconv.ParseInt: parsing "2.3": invalid syntax`)
   351  	})
   352  	t.Run("Should return error when ID from IN clause not parsable", func(t *testing.T) {
   353  		body = `{"args":{"Query":"select * from app1pkg.payments where id in (1.3)"}}`
   354  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   355  
   356  		resp.RequireError(t, `strconv.ParseInt: parsing "1.3": invalid syntax`)
   357  	})
   358  	t.Run("Should return error when ID operation not supported", func(t *testing.T) {
   359  		body = `{"args":{"Query":"select * from app1pkg.payments where id >= 2"}}`
   360  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   361  
   362  		resp.RequireError(t, "unsupported operation: >=")
   363  	})
   364  	t.Run("Should return error when expression not supported", func(t *testing.T) {
   365  		body = `{"args":{"Query":"select * from app1pkg.payments where id = 2 and something = 2"}}`
   366  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   367  
   368  		resp.RequireError(t, "unsupported expression: *sqlparser.AndExpr")
   369  	})
   370  	t.Run("Should return error when ID not present", func(t *testing.T) {
   371  		body = `{"args":{"Query":"select * from app1pkg.payments"}}`
   372  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   373  
   374  		resp.RequireError(t, "unable to find singleton ID for type «app1pkg.payments»: name not found")
   375  	})
   376  	t.Run("Should return error when requested record has mismatching QName", func(t *testing.T) {
   377  		body = fmt.Sprintf(`{"args":{"Query":"select * from app1pkg.payments where id = %d"}}`, emailId)
   378  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   379  
   380  		resp.RequireError(t, fmt.Sprintf("record with ID '%d' has mismatching QName 'app1pkg.pos_emails'", emailId))
   381  	})
   382  	t.Run("Should return error when record not found", func(t *testing.T) {
   383  		body = fmt.Sprintf(`{"args":{"Query":"select * from app1pkg.payments where id = %d"}}`, istructs.NonExistingRecordID)
   384  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   385  
   386  		resp.RequireError(t, fmt.Sprintf("record with ID '%d' not found", istructs.NonExistingRecordID))
   387  	})
   388  	t.Run("Should return error when field not found in def", func(t *testing.T) {
   389  		body = fmt.Sprintf(`{"args":{"Query":"select abracadabra from app1pkg.pos_emails where id = %d"}}`, emailId)
   390  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   391  
   392  		resp.RequireError(t, "field 'abracadabra' not found in def")
   393  	})
   394  	t.Run("Should read singleton", func(t *testing.T) {
   395  		require := require.New(t)
   396  		body = `{"args":{"Query":"select sys.QName from app1pkg.test_ws"},"elements":[{"fields":["Result"]}]}`
   397  		restaurant := vit.PostWS(ws, "q.sys.SqlQuery", body).SectionRow(0)
   398  
   399  		require.Equal(`{"sys.QName":"app1pkg.test_ws"}`, restaurant[0])
   400  	})
   401  }
   402  
   403  func TestSqlQuery_view_records(t *testing.T) {
   404  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   405  	defer vit.TearDown()
   406  
   407  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   408  
   409  	body := `{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.payments","name":"EFT","guid":"guidEFT"}},
   410  					   {"fields":{"sys.ID":2,"sys.QName":"app1pkg.pos_emails","description":"invite"}}]}`
   411  	resp := vit.PostWS(ws, "c.sys.CUD", body)
   412  	paymentsID := resp.NewID()
   413  	lastWLogOffset := resp.CurrentWLogOffset
   414  
   415  	t.Run("Should read record with all fields", func(t *testing.T) {
   416  		require := require.New(t)
   417  		body = `{"args":{"Query":"select * from sys.CollectionView where PartKey = 1 and DocQName = 'app1pkg.payments'"}, "elements":[{"fields":["Result"]}]}`
   418  		resp = vit.PostWS(ws, "q.sys.SqlQuery", body)
   419  
   420  		respStr := resp.SectionRow(len(resp.Sections[0].Elements) - 1)[0].(string)
   421  		require.Contains(respStr, fmt.Sprintf(`"DocID":%d`, paymentsID))
   422  		require.Contains(respStr, `"DocQName":"app1pkg.payments"`)
   423  		require.Contains(respStr, `"ElementID":0`)
   424  		require.Contains(respStr, fmt.Sprintf(`"offs":%d`, lastWLogOffset))
   425  		require.Contains(respStr, `"PartKey":1`)
   426  		require.Contains(respStr, `"Record":{`)
   427  		require.Contains(respStr, `"sys.QName":"sys.CollectionView"`)
   428  	})
   429  	t.Run("Should return error when operator not supported", func(t *testing.T) {
   430  		body = `{"args":{"Query":"select * from sys.CollectionView where partKey > 1"}}`
   431  		resp = vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   432  
   433  		resp.RequireError(t, "unsupported operator: >")
   434  	})
   435  	t.Run("Should return error when expression not supported", func(t *testing.T) {
   436  		body = `{"args":{"Query":"select * from sys.CollectionView where partKey = 1 or docQname = 'app1pkg.payments'"}}`
   437  		resp = vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   438  
   439  		resp.RequireError(t, "unsupported expression: *sqlparser.OrExpr")
   440  	})
   441  	t.Run("Should return error when field does not exist in value def", func(t *testing.T) {
   442  		body = `{"args":{"Query":"select abracadabra from sys.CollectionView where PartKey = 1"}}`
   443  		resp = vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   444  
   445  		resp.RequireError(t, "field 'abracadabra' does not exist in 'sys.CollectionView' value def")
   446  	})
   447  	t.Run("Should return error when field does not exist in key def", func(t *testing.T) {
   448  		body = `{"args":{"Query":"select * from sys.CollectionView where partKey = 1"}}`
   449  		resp = vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   450  
   451  		resp.RequireError(t, "field 'partKey' does not exist in 'sys.CollectionView' key def")
   452  	})
   453  }
   454  
   455  func TestSqlQuery(t *testing.T) {
   456  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   457  	defer vit.TearDown()
   458  
   459  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   460  
   461  	t.Run("Should return error when script invalid", func(t *testing.T) {
   462  		body := `{"args":{"Query":" "}}`
   463  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   464  
   465  		resp.RequireContainsError(t, "syntax error")
   466  	})
   467  	t.Run("Should return error when source of data unsupported", func(t *testing.T) {
   468  		body := `{"args":{"Query":"select * from git.hub"}}`
   469  		resp := vit.PostWS(ws, "q.sys.SqlQuery", body, coreutils.Expect500())
   470  
   471  		resp.RequireError(t, "unsupported source: git.hub")
   472  	})
   473  	t.Run("Should read sys.wlog from other workspace", func(t *testing.T) {
   474  		wsOne := vit.PostWS(ws, "q.sys.SqlQuery", fmt.Sprintf(`{"args":{"Query":"select * from %d.sys.wlog"}}`, ws.Owner.ProfileWSID))
   475  		wsTwo := vit.PostWS(ws, "q.sys.SqlQuery", `{"args":{"Query":"select * from sys.wlog"}}`)
   476  
   477  		require.NotEqual(t, len(wsOne.Sections[0].Elements), len(wsTwo.Sections[0].Elements))
   478  	})
   479  
   480  	t.Run("400 bad request on read from non-inited workspace", func(t *testing.T) {
   481  		vit.PostWS(ws, "q.sys.SqlQuery", `{"args":{"Query":"select * from 555.sys.wlog"}}`, coreutils.Expect400(processors.ErrWSNotInited.Message))
   482  	})
   483  }
   484  
   485  func TestReadFromWLogWithSysRawArg(t *testing.T) {
   486  	require := require.New(t)
   487  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   488  	defer vit.TearDown()
   489  
   490  	ws := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   491  
   492  	lastOffset := vit.PostWS(ws, "c.app1pkg.TestCmdRawArg", "hello world").CurrentWLogOffset
   493  
   494  	body := fmt.Sprintf(`{"args":{"Query":"select * from sys.wlog where Offset > %d"},"elements":[{"fields":["Result"]}]}`, lastOffset-1)
   495  	resp := vit.PostWS(ws, "q.sys.SqlQuery", body)
   496  	res := resp.SectionRow()[0].(string)
   497  	m := map[string]interface{}{}
   498  	require.NoError(json.Unmarshal([]byte(res), &m))
   499  	rawArg := m["ArgumentObject"].(map[string]interface{})["Body"].(string)
   500  	require.Equal("hello world", rawArg)
   501  }
   502  
   503  func TestReadFromAnotherAppAnotherWSID(t *testing.T) {
   504  	//t.Skip("waiting for https://github.com/voedger/voedger/issues/1811")
   505  	vit := it.NewVIT(t, &it.SharedConfig_App1)
   506  	defer vit.TearDown()
   507  
   508  	oneAppWS := vit.WS(istructs.AppQName_test1_app1, "test_ws")
   509  
   510  	// create a record in one workspace of one app
   511  	categoryName := vit.NextName()
   512  	body := fmt.Sprintf(`{"cuds":[{"fields":{"sys.ID":1,"sys.QName":"app1pkg.category","name":"%s"}}]}`, categoryName)
   513  	categoryID := vit.PostWS(oneAppWS, "c.sys.CUD", body).NewID()
   514  
   515  	// create a workspace in another app
   516  	anotherAppWSOwner := vit.GetPrincipal(istructs.AppQName_test1_app2, "login")
   517  	qNameApp2_TestWSKind := appdef.NewQName("app2pkg", "test_ws")
   518  	anotherAppWS := vit.CreateWorkspace(it.WSParams{
   519  		Name:         "anotherAppWS",
   520  		Kind:         qNameApp2_TestWSKind,
   521  		ClusterID:    istructs.MainClusterID,
   522  		InitDataJSON: `{"IntFld":42}`,
   523  	}, anotherAppWSOwner)
   524  
   525  	// in the another app use sql to query the record from the first app
   526  	body = fmt.Sprintf(`{"args":{"Query":"select * from test1.app1.%d.app1pkg.category where id = %d"},"elements":[{"fields":["Result"]}]}`, oneAppWS.WSID, categoryID)
   527  	resp := vit.PostWS(anotherAppWS, "q.sys.SqlQuery", body)
   528  	resStr := resp.SectionRow(len(resp.Sections[0].Elements) - 1)[0].(string)
   529  	require.Contains(t, resStr, fmt.Sprintf(`"name":"%s"`, categoryName))
   530  }