github.com/voedger/voedger@v0.0.0-20240520144910-273e84102129/pkg/parser/impl_test.go (about) 1 /* 2 * Copyright (c) 2023-present unTill Pro, Ltd. 3 * @author Michael Saigachenko 4 */ 5 package parser 6 7 import ( 8 "embed" 9 "fmt" 10 "strings" 11 "testing" 12 13 "github.com/stretchr/testify/require" 14 15 "github.com/voedger/voedger/pkg/appdef" 16 "github.com/voedger/voedger/pkg/istructs" 17 ) 18 19 //go:embed sql_example_app/pmain/*.vsql 20 var fsMain embed.FS 21 22 //go:embed sql_example_app/airsbp/*.vsql 23 var fsAir embed.FS 24 25 //go:embed sql_example_app/untill/*.vsql 26 var fsUntill embed.FS 27 28 //go:embed sql_example_syspkg/*.vsql 29 var sfs embed.FS 30 31 //go:embed sql_example_app/vrestaurant/*.vsql 32 var fsvRestaurant embed.FS 33 34 //_go:embed example_app/expectedParsed.schema 35 // var expectedParsedExampledSchemaStr string 36 37 func getSysPackageAST() *PackageSchemaAST { 38 pkgSys, err := ParsePackageDir(appdef.SysPackage, sfs, "sql_example_syspkg") 39 if err != nil { 40 panic(err) 41 } 42 return pkgSys 43 } 44 45 func Test_BasicUsage(t *testing.T) { 46 47 require := require.New(t) 48 mainPkgAST, err := ParsePackageDir("github.com/untillpro/main", fsMain, "sql_example_app/pmain") 49 require.NoError(err) 50 51 airPkgAST, err := ParsePackageDir("github.com/untillpro/airsbp", fsAir, "sql_example_app/airsbp") 52 require.NoError(err) 53 54 untillPkgAST, err := ParsePackageDir("github.com/untillpro/untill", fsUntill, "sql_example_app/untill") 55 require.NoError(err) 56 57 appSchema, err := BuildAppSchema([]*PackageSchemaAST{ 58 getSysPackageAST(), 59 mainPkgAST, 60 airPkgAST, 61 untillPkgAST, 62 }) 63 require.NoError(err) 64 65 builder := appdef.New() 66 err = BuildAppDefs(appSchema, builder) 67 require.NoError(err) 68 69 app, err := builder.Build() 70 require.NoError(err) 71 72 // table 73 cdoc := app.CDoc(appdef.NewQName("main", "TablePlan")) 74 require.NotNil(cdoc) 75 require.Equal(appdef.TypeKind_CDoc, cdoc.Kind()) 76 require.Equal(appdef.DataKind_int32, cdoc.Field("FState").DataKind()) 77 require.Equal("Backoffice Table", cdoc.Comment()) 78 79 // TODO: sf := cdoc.Field("CheckedField").(appdef.IStringField) 80 // TODO: require.Equal(uint16(8), sf.Restricts().MaxLen()) 81 // TODO: require.NotNil(sf.Restricts().Pattern()) 82 83 // container of the table 84 container := cdoc.Container("TableItems") 85 require.Equal("TableItems", container.Name()) 86 require.Equal(appdef.NewQName("main", "TablePlanItem"), container.QName()) 87 require.Equal(appdef.Occurs(0), container.MinOccurs()) 88 require.Equal(appdef.Occurs(maxNestedTableContainerOccurrences), container.MaxOccurs()) 89 require.Equal(appdef.TypeKind_CRecord, container.Type().Kind()) 90 require.Equal(2+5 /* +5 system fields*/, container.Type().(appdef.IFields).FieldCount()) 91 require.Equal(appdef.DataKind_int32, container.Type().(appdef.IFields).Field("TableNo").DataKind()) 92 require.Equal(appdef.DataKind_int32, container.Type().(appdef.IFields).Field("Chairs").DataKind()) 93 94 // constraint 95 uniques := cdoc.Uniques() 96 require.Len(uniques, 2) 97 98 t.Run("first unique, automatically named", func(t *testing.T) { 99 u := uniques[appdef.MustParseQName("main.TablePlan$uniques$01")] 100 require.NotNil(u) 101 cnt := 0 102 for _, f := range u.Fields() { 103 cnt++ 104 switch n := f.Name(); n { 105 case "FState": 106 require.Equal(appdef.DataKind_int32, f.DataKind()) 107 case "Name": 108 require.Equal(appdef.DataKind_string, f.DataKind()) 109 default: 110 require.Fail("unexpected field name", n) 111 } 112 } 113 require.Equal(2, cnt) 114 }) 115 116 t.Run("second unique, named by user", func(t *testing.T) { 117 u := uniques[appdef.MustParseQName("main.TablePlan$uniques$UniqueTable")] 118 require.NotNil(u) 119 cnt := 0 120 for _, f := range u.Fields() { 121 cnt++ 122 switch n := f.Name(); n { 123 case "TableNumber": 124 require.Equal(appdef.DataKind_int32, f.DataKind()) 125 default: 126 require.Fail("unexpected field name", n) 127 } 128 } 129 require.Equal(1, cnt) 130 }) 131 132 // child table 133 crec := app.CRecord(appdef.NewQName("main", "TablePlanItem")) 134 require.NotNil(crec) 135 require.Equal(appdef.TypeKind_CRecord, crec.Kind()) 136 require.Equal(appdef.DataKind_int32, crec.Field("TableNo").DataKind()) 137 138 crec = app.CRecord(appdef.NewQName("main", "NestedWithName")) 139 require.NotNil(crec) 140 require.True(crec.Abstract()) 141 field := crec.Field("ItemName") 142 require.NotNil(field) 143 require.Equal("Field is added to any table inherited from NestedWithName\nThe current comment is also added to scheme for this field", field.Comment()) 144 145 csingleton := app.CDoc(appdef.NewQName("main", "SubscriptionProfile")) 146 require.True(csingleton.Singleton()) 147 require.Equal("CSingletones is a configration singleton.\nThese comments are included in the statement definition, but may be overridden with `WITH Comment=...`", csingleton.Comment()) 148 149 wsingletone := app.WDoc(appdef.NewQName("main", "Transaction")) 150 require.True(wsingletone.Singleton()) 151 152 cmd := app.Command(appdef.NewQName("main", "NewOrder")) 153 require.Equal("Commands can only be declared in workspaces\nCommand can have optional argument and/or unlogged argument\nCommand can return TYPE", cmd.Comment()) 154 155 // type 156 obj := app.Object(appdef.NewQName("main", "SubscriptionEvent")) 157 require.Equal(appdef.TypeKind_Object, obj.Kind()) 158 require.Equal(appdef.DataKind_string, obj.Field("Origin").DataKind()) 159 160 // view 161 view := app.View(appdef.NewQName("main", "XZReports")) 162 require.NotNil(view) 163 require.Equal(appdef.TypeKind_ViewRecord, view.Kind()) 164 require.Equal("VIEWs generated by the PROJECTOR.\nPrimary Key must be declared in View.", view.Comment()) 165 166 require.Equal(2, view.Value().UserFieldCount()) 167 require.Equal(1, view.Key().PartKey().FieldCount()) 168 require.Equal(4, view.Key().ClustCols().FieldCount()) 169 170 // workspace descriptor 171 descr := app.CDoc(appdef.NewQName("main", "MyWorkspaceDescriptor")) 172 require.NotNil(descr) 173 require.Equal(appdef.TypeKind_CDoc, descr.Kind()) 174 require.Equal(appdef.DataKind_string, descr.Field("Name").DataKind()) 175 require.Equal(appdef.DataKind_string, descr.Field("Country").DataKind()) 176 177 // fieldsets 178 cdoc = app.CDoc(appdef.NewQName("main", "WsTable")) 179 require.Equal(appdef.DataKind_string, cdoc.Field("Name").DataKind()) 180 181 crec = app.CRecord(appdef.NewQName("main", "Child")) 182 require.Equal(appdef.DataKind_int32, crec.Field("Kind").DataKind()) 183 184 // QUERY 185 q1 := app.Query(appdef.NewQName("main", "Query11")) 186 require.NotNil(q1) 187 require.Equal(appdef.TypeKind_Query, q1.Kind()) 188 189 // CUD Projector 190 proj := app.Projector(appdef.NewQName("main", "RecordsRegistryProjector")) 191 require.NotNil(proj) 192 eventsCount := 0 193 proj.Events().Enum(func(ie appdef.IProjectorEvent) { 194 eventsCount++ 195 k, on := ie.Kind(), ie.On().QName() 196 require.Len(k, 3) 197 require.Contains(k, appdef.ProjectorEventKind_Insert) 198 require.Contains(k, appdef.ProjectorEventKind_Activate) 199 require.Contains(k, appdef.ProjectorEventKind_Deactivate) 200 switch eventsCount { 201 case 1: 202 require.Equal(istructs.QNameCRecord, on) 203 case 2: 204 require.Equal(istructs.QNameWRecord, on) 205 } 206 }) 207 require.Equal(2, eventsCount) 208 require.Equal(eventsCount, proj.Events().Len()) 209 210 // Execute Projector 211 proj = app.Projector(appdef.NewQName("main", "UpdateDashboard")) 212 require.NotNil(proj) 213 eventsCount = 0 214 proj.Events().Enum(func(ie appdef.IProjectorEvent) { 215 eventsCount++ 216 if eventsCount == 1 { 217 require.Len(ie.Kind(), 1) 218 require.Equal(appdef.ProjectorEventKind_Execute, ie.Kind()[0]) 219 require.Equal(appdef.NewQName("main", "NewOrder"), ie.On().QName()) 220 } else if eventsCount == 2 { 221 require.Len(ie.Kind(), 1) 222 require.Equal(appdef.ProjectorEventKind_Execute, ie.Kind()[0]) 223 require.Equal(appdef.NewQName("main", "NewOrder2"), ie.On().QName()) 224 } 225 }) 226 require.Equal(2, eventsCount) 227 require.Equal(eventsCount, proj.Events().Len()) 228 229 stateCount := 0 230 proj.States().Enum(func(storage appdef.IStorage) { 231 stateCount++ 232 if stateCount == 1 { 233 require.Equal(appdef.NewQName("sys", "AppSecret"), storage.Name()) 234 require.Empty(storage.Names()) 235 } else if stateCount == 2 { 236 require.Equal(appdef.NewQName("sys", "Http"), storage.Name()) 237 require.Empty(storage.Names()) 238 } 239 }) 240 require.Equal(2, stateCount) 241 require.Equal(stateCount, proj.States().Len()) 242 243 intentsCount := 0 244 proj.Intents().Enum(func(storage appdef.IStorage) { 245 intentsCount++ 246 if intentsCount == 1 { 247 require.Equal(appdef.NewQName("sys", "View"), storage.Name()) 248 require.Len(storage.Names(), 4) 249 require.Equal(appdef.NewQName("main", "ActiveTablePlansView"), storage.Names()[0]) 250 require.Equal(appdef.NewQName("main", "DashboardView"), storage.Names()[1]) 251 require.Equal(appdef.NewQName("main", "NotificationsHistory"), storage.Names()[2]) 252 require.Equal(appdef.NewQName("main", "XZReports"), storage.Names()[3]) 253 } 254 }) 255 require.Equal(1, intentsCount) 256 require.Equal(intentsCount, proj.Intents().Len()) 257 258 cmd = app.Command(appdef.NewQName("main", "NewOrder2")) 259 require.Equal(1, cmd.States().Len()) 260 require.NotNil(cmd.States().Storage(appdef.NewQName("sys", "AppSecret"))) 261 require.Equal(1, cmd.States().Len()) 262 intent := cmd.Intents().Storage(appdef.NewQName("sys", "Record")) 263 require.NotNil(intent) 264 require.True(intent.Names().Contains(appdef.NewQName("main", "Transaction"))) 265 266 localNames := app.PackageLocalNames() 267 require.Len(localNames, 4) 268 require.Contains(localNames, appdef.SysPackage) 269 require.Contains(localNames, "main") 270 require.Contains(localNames, "air") 271 require.Contains(localNames, "untill") 272 273 require.Equal(appdef.SysPackagePath, app.PackageFullPath(appdef.SysPackage)) 274 require.Equal("github.com/untillpro/main", app.PackageFullPath("main")) 275 require.Equal("github.com/untillpro/airsbp", app.PackageFullPath("air")) 276 require.Equal("github.com/untillpro/untill", app.PackageFullPath("untill")) 277 278 require.Equal("main", app.PackageLocalName("github.com/untillpro/main")) 279 require.Equal("air", app.PackageLocalName("github.com/untillpro/airsbp")) 280 require.Equal("untill", app.PackageLocalName("github.com/untillpro/untill")) 281 } 282 283 type ParserAssertions struct { 284 *require.Assertions 285 } 286 287 func (require *ParserAssertions) AppSchemaError(sql string, expectErrors ...string) { 288 _, err := require.AppSchema(sql) 289 require.EqualError(err, strings.Join(expectErrors, "\n")) 290 } 291 292 func (require *ParserAssertions) NoAppSchemaError(sql string) { 293 _, err := require.AppSchema(sql) 294 require.NoError(err) 295 } 296 297 func (require *ParserAssertions) AppSchema(sql string) (*AppSchemaAST, error) { 298 ast, err := ParseFile("file.vsql", sql) 299 require.NoError(err) 300 301 pkg, err := BuildPackageSchema("github.com/company/pkg", []*FileSchemaAST{ast}) 302 require.NoError(err) 303 304 schema, err := BuildAppSchema([]*PackageSchemaAST{ 305 getSysPackageAST(), 306 pkg, 307 }) 308 309 return schema, err 310 } 311 312 func assertions(t *testing.T) *ParserAssertions { 313 return &ParserAssertions{require.New(t)} 314 } 315 316 func Test_Refs_NestedTables(t *testing.T) { 317 318 require := require.New(t) 319 320 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 321 TABLE table1 INHERITS CDoc ( 322 items TABLE inner1 ( 323 table1 ref, 324 ref1 ref(table3), 325 urg_number int32 326 ) 327 ); 328 TABLE table2 INHERITS CRecord ( 329 ); 330 TABLE table3 INHERITS CDoc ( 331 items table2 332 ); 333 `) 334 require.NoError(err) 335 pkg, err := BuildPackageSchema("test/pkg1", []*FileSchemaAST{fs}) 336 require.NoError(err) 337 338 packages, err := BuildAppSchema([]*PackageSchemaAST{ 339 getSysPackageAST(), 340 pkg, 341 }) 342 require.NoError(err) 343 344 adb := appdef.New() 345 require.NoError(BuildAppDefs(packages, adb)) 346 347 app, err := adb.Build() 348 require.NoError(err) 349 350 inner1 := app.Type(appdef.NewQName("pkg1", "inner1")) 351 ref1 := inner1.(appdef.IFields).RefField("ref1") 352 require.EqualValues(appdef.QNames{appdef.NewQName("pkg1", "table3")}, ref1.Refs()) 353 } 354 355 func Test_CircularReferencesTables(t *testing.T) { 356 require := require.New(t) 357 // Tables 358 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 359 ABSTRACT TABLE table2 INHERITS table2 (); 360 ABSTRACT TABLE table3 INHERITS table3 (); 361 ABSTRACT TABLE table4 INHERITS table5 (); 362 ABSTRACT TABLE table5 INHERITS table6 (); 363 ABSTRACT TABLE table6 INHERITS table4 (); 364 `) 365 require.NoError(err) 366 pkg, err := BuildPackageSchema("pkg/test", []*FileSchemaAST{fs}) 367 require.NoError(err) 368 369 _, err = BuildAppSchema([]*PackageSchemaAST{ 370 getSysPackageAST(), 371 pkg, 372 }) 373 require.EqualError(err, strings.Join([]string{ 374 "file1.vsql:2:2: circular reference in INHERITS", 375 "file1.vsql:3:2: circular reference in INHERITS", 376 "file1.vsql:4:2: circular reference in INHERITS", 377 "file1.vsql:5:2: circular reference in INHERITS", 378 "file1.vsql:6:2: circular reference in INHERITS", 379 }, "\n")) 380 } 381 382 func Test_CircularReferencesWorkspaces(t *testing.T) { 383 require := require.New(t) 384 // Workspaces 385 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 386 ABSTRACT WORKSPACE w1(); 387 ABSTRACT WORKSPACE w2 INHERITS w1,w2( 388 TABLE table4 INHERITS CDoc(); 389 ); 390 ABSTRACT WORKSPACE w3 INHERITS w4(); 391 ABSTRACT WORKSPACE w4 INHERITS w5(); 392 ABSTRACT WORKSPACE w5 INHERITS w3(); 393 `) 394 require.NoError(err) 395 pkg, err := BuildPackageSchema("pkg/test", []*FileSchemaAST{fs}) 396 require.NoError(err) 397 398 _, err = BuildAppSchema([]*PackageSchemaAST{ 399 getSysPackageAST(), 400 pkg, 401 }) 402 require.EqualError(err, strings.Join([]string{ 403 "file1.vsql:3:37: circular reference in INHERITS", 404 "file1.vsql:6:33: circular reference in INHERITS", 405 "file1.vsql:7:33: circular reference in INHERITS", 406 "file1.vsql:8:33: circular reference in INHERITS", 407 }, "\n")) 408 } 409 410 func Test_Workspace_Defs(t *testing.T) { 411 412 require := require.New(t) 413 414 fs1, err := ParseFile("file1.vsql", `APPLICATION test(); 415 ABSTRACT WORKSPACE AWorkspace( 416 TABLE table1 INHERITS CDoc ( 417 a ref, 418 items TABLE inner1 ( 419 b ref 420 ) 421 ); 422 ); 423 `) 424 require.NoError(err) 425 fs2, err := ParseFile("file2.vsql", ` 426 ALTER WORKSPACE AWorkspace( 427 TABLE table2 INHERITS CDoc ( 428 a ref, 429 items TABLE inner2 ( 430 b ref 431 ) 432 ); 433 ); 434 WORKSPACE MyWorkspace INHERITS AWorkspace(); 435 WORKSPACE MyWorkspace2 INHERITS AWorkspace(); 436 ALTER WORKSPACE sys.Profile( 437 USE WORKSPACE MyWorkspace; 438 ); 439 `) 440 require.NoError(err) 441 pkg, err := BuildPackageSchema("test/pkg1", []*FileSchemaAST{fs1, fs2}) 442 require.NoError(err) 443 444 packages, err := BuildAppSchema([]*PackageSchemaAST{ 445 getSysPackageAST(), 446 pkg, 447 }) 448 require.NoError(err) 449 450 builder := appdef.New() 451 require.NoError(BuildAppDefs(packages, builder)) 452 453 app, err := builder.Build() 454 require.NoError(err) 455 456 ws := app.Workspace(appdef.NewQName("pkg1", "MyWorkspace")) 457 458 require.Equal(appdef.TypeKind_CDoc, ws.Type(appdef.NewQName("pkg1", "table1")).Kind()) 459 require.Equal(appdef.TypeKind_CDoc, ws.Type(appdef.NewQName("pkg1", "table2")).Kind()) 460 require.Equal(appdef.TypeKind_CRecord, ws.Type(appdef.NewQName("pkg1", "inner1")).Kind()) 461 require.Equal(appdef.TypeKind_CRecord, ws.Type(appdef.NewQName("pkg1", "inner2")).Kind()) 462 require.Equal(appdef.TypeKind_Command, ws.Type(appdef.NewQName("sys", "CreateLogin")).Kind()) 463 464 wsProfile := app.Workspace(appdef.NewQName("sys", "Profile")) 465 466 require.Equal(appdef.TypeKind_Workspace, wsProfile.Type(appdef.NewQName("pkg1", "MyWorkspace")).Kind()) 467 require.Nil(wsProfile.TypeByName(appdef.NewQName("pkg1", "MyWorkspace2"))) 468 } 469 470 func Test_Workspace_Defs2(t *testing.T) { 471 require := require.New(t) 472 fs, err := ParseFile("file1.vsql", `IMPORT SCHEMA 'test/pkg2'; 473 APPLICATION test( 474 USE pkg2; 475 ); 476 WORKSPACE w( 477 USE TABLE pkg2.*; 478 ); 479 `) 480 require.NoError(err) 481 pkg, err := BuildPackageSchema("test/pkg1", []*FileSchemaAST{fs}) 482 require.NoError(err) 483 484 fs2, err := ParseFile("file2.vsql", ` 485 TABLE order INHERITS ODoc ( 486 order_item TABLE order_item ( 487 b ref 488 ) 489 ); 490 `) 491 require.NoError(err) 492 pkg2, err := BuildPackageSchema("test/pkg2", []*FileSchemaAST{fs2}) 493 require.NoError(err) 494 495 packages, err := BuildAppSchema([]*PackageSchemaAST{ 496 getSysPackageAST(), 497 pkg, 498 pkg2, 499 }) 500 require.NoError(err) 501 502 builder := appdef.New() 503 require.NoError(BuildAppDefs(packages, builder)) 504 505 app, err := builder.Build() 506 require.NoError(err) 507 508 ws := app.Workspace(appdef.NewQName("pkg1", "w")) 509 510 require.Equal(appdef.TypeKind_ODoc, ws.Type(appdef.NewQName("pkg2", "order")).Kind()) 511 require.Equal(appdef.TypeKind_ORecord, ws.Type(appdef.NewQName("pkg2", "order_item")).Kind()) 512 } 513 514 func Test_Alter_Workspace(t *testing.T) { 515 516 require := require.New(t) 517 518 fs0, err := ParseFile("file0.vsql", ` 519 IMPORT SCHEMA 'org/pkg1'; 520 IMPORT SCHEMA 'org/pkg2'; 521 APPLICATION test( 522 USE pkg1; 523 USE pkg2; 524 ); 525 `) 526 require.NoError(err) 527 pkg0, err := BuildPackageSchema("org/main", []*FileSchemaAST{fs0}) 528 require.NoError(err) 529 530 fs1, err := ParseFile("file1.vsql", ` 531 ABSTRACT WORKSPACE AWorkspace( 532 TABLE table1 INHERITS CDoc (a ref); 533 ); 534 `) 535 require.NoError(err) 536 pkg1, err := BuildPackageSchema("org/pkg1", []*FileSchemaAST{fs1}) 537 require.NoError(err) 538 539 fs2, err := ParseFile("file2.vsql", ` 540 IMPORT SCHEMA 'org/pkg1' 541 ALTER WORKSPACE pkg1.AWorkspace( 542 TABLE table2 INHERITS CDoc (a ref); 543 ); 544 `) 545 require.NoError(err) 546 pkg2, err := BuildPackageSchema("org/pkg2", []*FileSchemaAST{fs2}) 547 require.NoError(err) 548 549 _, err = BuildAppSchema([]*PackageSchemaAST{ 550 getSysPackageAST(), 551 pkg0, 552 pkg1, 553 pkg2, 554 }) 555 require.EqualError(err, strings.Join([]string{ 556 "file2.vsql:3:19: workspace pkg1.AWorkspace is not alterable", 557 }, "\n")) 558 } 559 560 func Test_DupFieldsInTypes(t *testing.T) { 561 require := require.New(t) 562 563 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 564 TYPE RootType ( 565 Id int32 566 ); 567 TYPE BaseType( 568 RootType, 569 baseField int 570 ); 571 TYPE BaseType2 ( 572 someField int 573 ); 574 TYPE MyType( 575 BaseType, 576 BaseType2, 577 field varchar, 578 field varchar, 579 baseField varchar, 580 someField int, 581 Id varchar 582 ) 583 `) 584 require.NoError(err) 585 pkg, err := BuildPackageSchema("pkg/test", []*FileSchemaAST{fs}) 586 require.NoError(err) 587 588 packages, err := BuildAppSchema([]*PackageSchemaAST{ 589 getSysPackageAST(), 590 pkg, 591 }) 592 require.NoError(err) 593 594 err = BuildAppDefs(packages, appdef.New()) 595 require.EqualError(err, strings.Join([]string{ 596 "file1.vsql:16:3: redefinition of field", 597 "file1.vsql:17:3: redefinition of baseField", 598 "file1.vsql:18:3: redefinition of someField", 599 "file1.vsql:19:3: redefinition of Id", 600 }, "\n")) 601 602 } 603 604 func Test_Varchar(t *testing.T) { 605 require := require.New(t) 606 607 fs, err := ParseFile("file1.vsql", fmt.Sprintf(`APPLICATION test(); 608 TYPE RootType ( 609 Oversize varchar(%d) 610 ); 611 TYPE CDoc1 ( 612 Oversize varchar(%d) 613 ); 614 `, uint32(appdef.MaxFieldLength)+1, uint32(appdef.MaxFieldLength)+1)) 615 require.NoError(err) 616 pkg, err := BuildPackageSchema("pkg/test", []*FileSchemaAST{fs}) 617 require.NoError(err) 618 619 _, err = BuildAppSchema([]*PackageSchemaAST{ 620 getSysPackageAST(), 621 pkg, 622 }) 623 require.EqualError(err, strings.Join([]string{ 624 fmt.Sprintf("file1.vsql:3:12: maximum field length is %d", appdef.MaxFieldLength), 625 fmt.Sprintf("file1.vsql:6:12: maximum field length is %d", appdef.MaxFieldLength), 626 }, "\n")) 627 628 } 629 630 func Test_DupFieldsInTables(t *testing.T) { 631 require := require.New(t) 632 633 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 634 TYPE RootType ( 635 Kind int32 636 ); 637 TYPE BaseType( 638 RootType, 639 baseField int 640 ); 641 TYPE BaseType2 ( 642 someField int 643 ); 644 ABSTRACT TABLE ByBaseTable INHERITS CDoc ( 645 Name varchar, 646 Code varchar 647 ); 648 TABLE MyTable INHERITS ByBaseTable( 649 BaseType, 650 BaseType2, 651 newField varchar, 652 field varchar, 653 field varchar, -- duplicated in the this table 654 baseField varchar, -- duplicated in the first OF 655 someField int, -- duplicated in the second OF 656 Kind int, -- duplicated in the first OF (2nd level) 657 Name int, -- duplicated in the inherited table 658 ID varchar 659 ) 660 `) 661 require.NoError(err) 662 pkg, err := BuildPackageSchema("pkg/test", []*FileSchemaAST{fs}) 663 require.NoError(err) 664 665 packages, err := BuildAppSchema([]*PackageSchemaAST{ 666 getSysPackageAST(), 667 pkg, 668 }) 669 require.NoError(err) 670 671 err = BuildAppDefs(packages, appdef.New()) 672 require.EqualError(err, strings.Join([]string{ 673 "file1.vsql:21:3: redefinition of field", 674 "file1.vsql:22:3: redefinition of baseField", 675 "file1.vsql:23:3: redefinition of someField", 676 "file1.vsql:24:3: redefinition of Kind", 677 "file1.vsql:25:3: redefinition of Name", 678 }, "\n")) 679 680 } 681 682 func Test_AbstractTables(t *testing.T) { 683 require := require.New(t) 684 685 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 686 TABLE ByBaseTable INHERITS CDoc ( 687 Name varchar 688 ); 689 TABLE MyTable INHERITS ByBaseTable( -- NOT ALLOWED (base table must be abstract) 690 ); 691 692 TABLE My1 INHERITS CRecord( 693 f1 ref(AbstractTable) -- NOT ALLOWED (reference to abstract table) 694 ); 695 696 ABSTRACT TABLE AbstractTable INHERITS CDoc( 697 ); 698 699 WORKSPACE MyWorkspace1( 700 EXTENSION ENGINE BUILTIN ( 701 702 PROJECTOR proj1 703 AFTER INSERT ON AbstractTable -- NOT ALLOWED (projector refers to abstract table) 704 INTENTS(SendMail); 705 706 SYNC PROJECTOR proj2 707 AFTER INSERT ON My1 708 INTENTS(Record(AbstractTable)); -- NOT ALLOWED (projector refers to abstract table) 709 710 PROJECTOR proj3 711 AFTER INSERT ON My1 712 STATE(Record(AbstractTable)) -- NOT ALLOWED (projector refers to abstract table) 713 INTENTS(SendMail); 714 ); 715 TABLE My2 INHERITS CRecord( 716 nested AbstractTable -- NOT ALLOWED 717 ); 718 USE TABLE AbstractTable; -- NOT ALLOWED 719 TABLE My3 INHERITS CRecord( 720 f int, 721 items ABSTRACT TABLE Nested() -- NOT ALLOWED 722 ); 723 ) 724 `) 725 require.NoError(err) 726 pkg, err := BuildPackageSchema("test/pkg1", []*FileSchemaAST{fs}) 727 require.NoError(err) 728 729 _, err = BuildAppSchema([]*PackageSchemaAST{ 730 getSysPackageAST(), 731 pkg, 732 }) 733 require.EqualError(err, strings.Join([]string{ 734 "file1.vsql:5:25: base table must be abstract", 735 "file1.vsql:19:29: projector refers to abstract table AbstractTable", 736 "file1.vsql:24:21: projector refers to abstract table AbstractTable", 737 "file1.vsql:28:10: projector refers to abstract table AbstractTable", 738 "file1.vsql:32:11: nested abstract table AbstractTable", 739 "file1.vsql:34:13: use of abstract table AbstractTable", 740 "file1.vsql:37:4: nested abstract table Nested", 741 "file1.vsql:9:10: reference to abstract table AbstractTable", 742 }, "\n")) 743 744 } 745 746 func Test_AbstractTables2(t *testing.T) { 747 require := require.New(t) 748 749 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 750 ABSTRACT TABLE AbstractTable INHERITS CDoc( 751 ); 752 753 WORKSPACE MyWorkspace1( 754 TABLE My2 INHERITS CRecord( 755 nested AbstractTable -- NOT ALLOWED 756 ); 757 ); 758 `) 759 require.NoError(err) 760 pkg, err := BuildPackageSchema("test/pkg", []*FileSchemaAST{fs}) 761 require.NoError(err) 762 763 _, err = BuildAppSchema([]*PackageSchemaAST{ 764 getSysPackageAST(), 765 pkg, 766 }) 767 require.EqualError(err, strings.Join([]string{ 768 "file1.vsql:7:11: nested abstract table AbstractTable", 769 }, "\n")) 770 771 } 772 773 func Test_WorkspaceDescriptors(t *testing.T) { 774 require := require.New(t) 775 776 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 777 ROLE R1; 778 WORKSPACE W1( 779 DESCRIPTOR(); -- gets name W1Descriptor 780 ); 781 WORKSPACE W2( 782 DESCRIPTOR W2D(); -- gets name W2D 783 ); 784 WORKSPACE W3( 785 DESCRIPTOR R1(); -- duplicated name 786 ); 787 ROLE W2D; -- duplicated name 788 `) 789 require.NoError(err) 790 pkg, err := BuildPackageSchema("test/pkg", []*FileSchemaAST{fs}) 791 require.EqualError(err, strings.Join([]string{ 792 "file1.vsql:10:14: redefinition of R1", 793 "file1.vsql:12:2: redefinition of W2D", 794 }, "\n")) 795 796 require.Equal(Ident("W1Descriptor"), pkg.Ast.Statements[2].Workspace.Descriptor.Name) 797 require.Equal(Ident("W2D"), pkg.Ast.Statements[3].Workspace.Descriptor.Name) 798 } 799 func Test_PanicUnknownFieldType(t *testing.T) { 800 require := require.New(t) 801 802 fs, err := ParseFile("file1.vsql", `APPLICATION test(); 803 TABLE MyTable INHERITS CDoc ( 804 Name asdasd, 805 Code varchar 806 ); 807 `) 808 require.NoError(err) 809 pkg, err := BuildPackageSchema("test/pkg", []*FileSchemaAST{fs}) 810 require.NoError(err) 811 812 _, err = BuildAppSchema([]*PackageSchemaAST{ 813 getSysPackageAST(), 814 pkg, 815 }) 816 require.EqualError(err, strings.Join([]string{ 817 "file1.vsql:3:8: undefined data type or table: asdasd", 818 }, "\n")) 819 820 } 821 822 func Test_Expressions(t *testing.T) { 823 require := require.New(t) 824 825 _, err := ParseFile("file1.vsql", ` 826 TABLE MyTable( 827 Int1 varchar DEFAULT 1 CHECK(Int1 > Int2), 828 Int1 int DEFAULT 1 CHECK(Text != 'asd'), 829 Int1 int DEFAULT 1 CHECK(Int2 > -5), 830 Int1 int DEFAULT 1 CHECK(TextField > 'asd' AND (SomeFloat/3.2)*4 != 5.003), 831 Int1 int DEFAULT 1 CHECK(SomeFunc('a', TextField) AND BoolField=FALSE), 832 833 CHECK(MyRowValidator(this)) 834 ) 835 `) 836 require.NoError(err) 837 838 } 839 840 func Test_Duplicates(t *testing.T) { 841 require := require.New(t) 842 843 ast1, err := ParseFile("file1.vsql", `APPLICATION test(); 844 EXTENSION ENGINE BUILTIN ( 845 FUNCTION MyTableValidator() RETURNS void; 846 FUNCTION MyTableValidator(TableRow) RETURNS string; 847 FUNCTION MyFunc2() RETURNS void; 848 ); 849 TABLE Rec1 INHERITS CRecord(); 850 `) 851 require.NoError(err) 852 853 ast2, err := ParseFile("file2.vsql", ` 854 WORKSPACE ChildWorkspace ( 855 TAG MyFunc2; -- redeclared 856 EXTENSION ENGINE BUILTIN ( 857 FUNCTION MyFunc3() RETURNS void; 858 FUNCTION MyFunc4() RETURNS void; 859 ); 860 WORKSPACE InnerWorkspace ( 861 ROLE MyFunc4; -- redeclared 862 ); 863 TABLE Doc1 INHERITS ODoc( 864 nested1 Rec1, 865 nested2 TABLE Rec1() -- redeclared 866 ) 867 ) 868 `) 869 require.NoError(err) 870 871 _, err = BuildPackageSchema("test/pkg", []*FileSchemaAST{ast1, ast2}) 872 873 require.EqualError(err, strings.Join([]string{ 874 "file1.vsql:4:3: redefinition of MyTableValidator", 875 "file2.vsql:3:3: redefinition of MyFunc2", 876 "file2.vsql:9:4: redefinition of MyFunc4", 877 "file2.vsql:13:12: redefinition of Rec1", 878 }, "\n")) 879 880 } 881 882 func Test_DuplicatesInViews(t *testing.T) { 883 require := require.New(t) 884 885 ast, err := ParseFile("file2.vsql", `APPLICATION test(); 886 WORKSPACE Workspace ( 887 VIEW test( 888 field1 int, 889 field2 int, 890 field1 varchar, 891 PRIMARY KEY(field1), 892 PRIMARY KEY(field2) 893 ) AS RESULT OF Proj1; 894 895 EXTENSION ENGINE BUILTIN ( 896 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 897 COMMAND Orders() 898 ); 899 ) 900 `) 901 require.NoError(err) 902 903 pkg, err := BuildPackageSchema("test/pkg", []*FileSchemaAST{ast}) 904 require.NoError(err) 905 906 _, err = BuildAppSchema([]*PackageSchemaAST{ 907 pkg, 908 getSysPackageAST(), 909 }) 910 911 require.EqualError(err, strings.Join([]string{ 912 "file2.vsql:6:4: redefinition of field1", 913 "file2.vsql:8:16: redefinition of primary key", 914 }, "\n")) 915 916 } 917 func Test_Views(t *testing.T) { 918 require := assertions(t) 919 920 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 921 VIEW test( 922 field1 int, 923 PRIMARY KEY(field2) 924 ) AS RESULT OF Proj1; 925 EXTENSION ENGINE BUILTIN ( 926 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 927 COMMAND Orders() 928 ); 929 ) 930 `, "file.vsql:4:17: undefined field field2") 931 932 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 933 VIEW test( 934 field1 varchar, 935 PRIMARY KEY((field1)) 936 ) AS RESULT OF Proj1; 937 EXTENSION ENGINE BUILTIN ( 938 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 939 COMMAND Orders() 940 ); 941 ) 942 `, "file.vsql:4:18: varchar field field1 not supported in partition key") 943 944 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 945 VIEW test( 946 field1 bytes, 947 PRIMARY KEY((field1)) 948 ) AS RESULT OF Proj1; 949 EXTENSION ENGINE BUILTIN ( 950 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 951 COMMAND Orders() 952 ); 953 ) 954 `, "file.vsql:4:17: bytes field field1 not supported in partition key") 955 956 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 957 VIEW test( 958 field1 varchar, 959 field2 int, 960 PRIMARY KEY(field1, field2) 961 ) AS RESULT OF Proj1; 962 EXTENSION ENGINE BUILTIN ( 963 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 964 COMMAND Orders() 965 ); 966 ) 967 `, "file.vsql:5:16: varchar field field1 can only be the last one in clustering key") 968 969 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 970 VIEW test( 971 field1 bytes, 972 field2 int, 973 PRIMARY KEY(field1, field2) 974 ) AS RESULT OF Proj1; 975 EXTENSION ENGINE BUILTIN ( 976 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 977 COMMAND Orders() 978 ); 979 ) 980 `, "file.vsql:5:16: bytes field field1 can only be the last one in clustering key") 981 982 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 983 ABSTRACT TABLE abc INHERITS CDoc(); 984 VIEW test( 985 field1 ref(abc), 986 field2 ref(unexisting), 987 PRIMARY KEY(field1, field2) 988 ) AS RESULT OF Proj1; 989 EXTENSION ENGINE BUILTIN ( 990 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 991 COMMAND Orders() 992 ); 993 ) 994 `, "file.vsql:4:15: reference to abstract table abc", "file.vsql:5:15: undefined table: unexisting") 995 996 require.AppSchemaError(`APPLICATION test(); WORKSPACE Workspace ( 997 VIEW test( 998 fld1 int32 999 ) AS RESULT OF Proj1; 1000 EXTENSION ENGINE BUILTIN ( 1001 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 1002 COMMAND Orders() 1003 ); 1004 ) 1005 `, "file.vsql:2:3: primary key not defined") 1006 } 1007 1008 func Test_Views2(t *testing.T) { 1009 require := require.New(t) 1010 1011 { 1012 ast, err := ParseFile("file2.vsql", `APPLICATION test(); WORKSPACE Workspace ( 1013 VIEW test( 1014 -- comment1 1015 field1 int, 1016 -- comment2 1017 field2 varchar(20), 1018 -- comment3 1019 field3 bytes(20), 1020 -- comment4 1021 field4 ref, 1022 PRIMARY KEY((field1,field4),field2) 1023 ) AS RESULT OF Proj1; 1024 EXTENSION ENGINE BUILTIN ( 1025 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 1026 COMMAND Orders() 1027 ); 1028 ) 1029 `) 1030 require.NoError(err) 1031 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{ast}) 1032 require.NoError(err) 1033 1034 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1035 getSysPackageAST(), 1036 pkg, 1037 }) 1038 require.NoError(err) 1039 1040 appBld := appdef.New() 1041 err = BuildAppDefs(packages, appBld) 1042 require.NoError(err) 1043 1044 app, err := appBld.Build() 1045 require.NoError(err) 1046 1047 v := app.View(appdef.NewQName("test", "test")) 1048 require.NotNil(v) 1049 } 1050 { 1051 ast, err := ParseFile("file2.vsql", `APPLICATION test(); WORKSPACE Workspace ( 1052 VIEW test( 1053 -- comment1 1054 field1 int, 1055 -- comment2 1056 field3 bytes(20), 1057 -- comment4 1058 field4 ref, 1059 PRIMARY KEY((field1),field4,field3) 1060 ) AS RESULT OF Proj1; 1061 EXTENSION ENGINE BUILTIN ( 1062 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 1063 COMMAND Orders() 1064 ); 1065 ) 1066 `) 1067 require.NoError(err) 1068 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{ast}) 1069 require.NoError(err) 1070 1071 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1072 getSysPackageAST(), 1073 pkg, 1074 }) 1075 require.NoError(err) 1076 1077 appBld := appdef.New() 1078 err = BuildAppDefs(packages, appBld) 1079 require.NoError(err) 1080 1081 app, err := appBld.Build() 1082 require.NoError(err) 1083 1084 v := app.View(appdef.NewQName("test", "test")) 1085 require.NotNil(v) 1086 } 1087 { 1088 ast, err := ParseFile("file2.vsql", `APPLICATION test(); WORKSPACE Workspace ( 1089 VIEW test( 1090 -- comment1 1091 field1 int, 1092 -- comment2 1093 field3 bytes(20), 1094 -- comment4 1095 field4 ref, 1096 PRIMARY KEY((field1),field4,field3) 1097 ) AS RESULT OF Proj1; 1098 EXTENSION ENGINE BUILTIN ( 1099 PROJECTOR Proj1 AFTER EXECUTE ON (Orders); 1100 COMMAND Orders() 1101 ); 1102 ) 1103 `) 1104 require.NoError(err) 1105 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{ast}) 1106 require.NoError(err) 1107 1108 _, err = BuildAppSchema([]*PackageSchemaAST{ 1109 getSysPackageAST(), 1110 pkg, 1111 }) 1112 require.Error(err, "file2.vsql:2:4: projector Proj1 does not declare intent for view test") 1113 1114 } 1115 1116 } 1117 func Test_Comments(t *testing.T) { 1118 require := require.New(t) 1119 1120 fs, err := ParseFile("example.vsql", ` 1121 EXTENSION ENGINE BUILTIN ( 1122 1123 -- My function 1124 -- line 2 1125 FUNCTION MyFunc() RETURNS void; 1126 1127 /* Multiline 1128 comment */ 1129 FUNCTION MyFunc1() RETURNS void; 1130 ); 1131 1132 `) 1133 require.NoError(err) 1134 1135 ps, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1136 require.NoError(err) 1137 1138 require.NotNil(ps.Ast.Statements[0].ExtEngine.Statements[0].Function.Comments) 1139 1140 comments := ps.Ast.Statements[0].ExtEngine.Statements[0].Function.GetComments() 1141 require.Len(comments, 2) 1142 require.Equal("My function", comments[0]) 1143 require.Equal("line 2", comments[1]) 1144 1145 fn := ps.Ast.Statements[0].ExtEngine.Statements[1].Function 1146 comments = fn.GetComments() 1147 require.Len(comments, 2) 1148 require.Equal("Multiline", comments[0]) 1149 require.Equal("comment", comments[1]) 1150 } 1151 1152 func Test_Undefined(t *testing.T) { 1153 require := require.New(t) 1154 1155 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1156 WORKSPACE test ( 1157 EXTENSION ENGINE WASM ( 1158 COMMAND Orders() WITH Tags=(UndefinedTag); 1159 PROJECTOR ImProjector AFTER EXECUTE ON xyz.CreateUPProfile; 1160 COMMAND CmdFakeReturn() RETURNS text; 1161 COMMAND CmdNoReturn() RETURNS void; 1162 COMMAND CmdFakeArg(text); 1163 COMMAND CmdVoidArg(void); 1164 COMMAND CmdFakeUnloggedArg(UNLOGGED text); 1165 ) 1166 ) 1167 `) 1168 require.NoError(err) 1169 1170 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1171 require.NoError(err) 1172 1173 _, err = BuildAppSchema([]*PackageSchemaAST{pkg, getSysPackageAST()}) 1174 1175 require.EqualError(err, strings.Join([]string{ 1176 "example.vsql:4:32: undefined tag: UndefinedTag", 1177 "example.vsql:5:43: xyz undefined", 1178 "example.vsql:6:36: undefined type or table: text", 1179 "example.vsql:8:23: undefined type or table: text", 1180 "example.vsql:10:40: undefined type or table: text", 1181 }, "\n")) 1182 } 1183 1184 func Test_Projectors(t *testing.T) { 1185 require := require.New(t) 1186 1187 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1188 WORKSPACE test ( 1189 TABLE Order INHERITS ODoc(); 1190 EXTENSION ENGINE WASM ( 1191 COMMAND Orders(); 1192 PROJECTOR ImProjector1 AFTER EXECUTE ON test.CreateUPProfile; -- Undefined 1193 PROJECTOR ImProjector2 AFTER EXECUTE ON Order; -- Bad: Order is not a type or command 1194 PROJECTOR ImProjector3 AFTER UPDATE ON Order; -- Bad 1195 PROJECTOR ImProjector4 AFTER ACTIVATE ON Order; -- Bad 1196 PROJECTOR ImProjector5 AFTER DEACTIVATE ON Order; -- Bad 1197 PROJECTOR ImProjector6 AFTER INSERT ON Order OR AFTER EXECUTE ON Orders; -- Good 1198 PROJECTOR ImProjector7 AFTER EXECUTE WITH PARAM ON Bill; -- Bad: Type undefined 1199 PROJECTOR ImProjector8 AFTER EXECUTE WITH PARAM ON ODoc; -- Good 1200 PROJECTOR ImProjector9 AFTER EXECUTE WITH PARAM ON ORecord; -- Bad 1201 ); 1202 ) 1203 `) 1204 require.NoError(err) 1205 1206 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1207 require.NoError(err) 1208 1209 _, err = BuildAppSchema([]*PackageSchemaAST{pkg, getSysPackageAST()}) 1210 1211 require.EqualError(err, strings.Join([]string{ 1212 "example.vsql:6:44: undefined command: test.CreateUPProfile", 1213 "example.vsql:7:44: undefined command: Order", 1214 "example.vsql:8:43: only INSERT allowed for ODoc or ORecord", 1215 "example.vsql:9:45: only INSERT allowed for ODoc or ORecord", 1216 "example.vsql:10:47: only INSERT allowed for ODoc or ORecord", 1217 "example.vsql:12:55: undefined type or ODoc: Bill", 1218 "example.vsql:14:55: undefined type or ODoc: ORecord", 1219 }, "\n")) 1220 } 1221 1222 func Test_Imports(t *testing.T) { 1223 require := require.New(t) 1224 1225 fs, err := ParseFile("example.vsql", ` 1226 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2'; 1227 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3' AS air; 1228 APPLICATION test( 1229 USE pkg2; 1230 USE pkg3; 1231 ); 1232 WORKSPACE test ( 1233 EXTENSION ENGINE WASM ( 1234 COMMAND Orders WITH Tags=(pkg2.SomeTag); 1235 QUERY Query2 RETURNS void WITH Tags=(air.SomePkg3Tag); 1236 QUERY Query3 RETURNS void WITH Tags=(air.UnknownTag); -- air.UnknownTag undefined 1237 PROJECTOR ImProjector AFTER EXECUTE ON Air.CreateUPProfil; -- Air undefined 1238 ) 1239 ) 1240 `) 1241 require.NoError(err) 1242 pkg1, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg1", []*FileSchemaAST{fs}) 1243 require.NoError(err) 1244 1245 fs, err = ParseFile("example.vsql", `TAG SomeTag;`) 1246 require.NoError(err) 1247 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1248 require.NoError(err) 1249 1250 fs, err = ParseFile("example.vsql", `TAG SomePkg3Tag;`) 1251 require.NoError(err) 1252 pkg3, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg3", []*FileSchemaAST{fs}) 1253 require.NoError(err) 1254 1255 _, err = BuildAppSchema([]*PackageSchemaAST{getSysPackageAST(), pkg1, pkg2, pkg3}) 1256 require.EqualError(err, strings.Join([]string{ 1257 "example.vsql:12:44: undefined tag: air.UnknownTag", 1258 "example.vsql:13:46: Air undefined", 1259 }, "\n")) 1260 1261 } 1262 1263 func Test_AbstractWorkspace(t *testing.T) { 1264 require := require.New(t) 1265 1266 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1267 WORKSPACE ws1 (); 1268 ABSTRACT WORKSPACE ws2( 1269 DESCRIPTOR( -- Incorrect 1270 a int 1271 ); 1272 ); 1273 WORKSPACE ws4 INHERITS ws2 (); 1274 WORKSPACE ws5 INHERITS ws1 (); -- Incorrect 1275 `) 1276 require.NoError(err) 1277 1278 ps, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1279 require.NoError(err) 1280 1281 require.False(ps.Ast.Statements[1].Workspace.Abstract) 1282 require.True(ps.Ast.Statements[2].Workspace.Abstract) 1283 require.False(ps.Ast.Statements[3].Workspace.Abstract) 1284 require.Equal("ws2", ps.Ast.Statements[3].Workspace.Inherits[0].String()) 1285 1286 _, err = BuildAppSchema([]*PackageSchemaAST{ 1287 getSysPackageAST(), 1288 ps, 1289 }) 1290 require.EqualError(err, strings.Join([]string{ 1291 "example.vsql:4:13: abstract workspace cannot have a descriptor", 1292 "example.vsql:9:25: base workspace must be abstract", 1293 }, "\n")) 1294 1295 } 1296 1297 func Test_UniqueFields(t *testing.T) { 1298 require := require.New(t) 1299 1300 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1301 TABLE MyTable INHERITS CDoc ( 1302 Int1 int32, 1303 Int2 int32 NOT NULL, 1304 UNIQUEFIELD Int1, 1305 UNIQUEFIELD Int2 1306 ) 1307 `) 1308 require.NoError(err) 1309 1310 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1311 require.NoError(err) 1312 1313 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1314 getSysPackageAST(), 1315 pkg, 1316 }) 1317 require.NoError(err) 1318 1319 appBld := appdef.New() 1320 err = BuildAppDefs(packages, appBld) 1321 require.NoError(err) 1322 1323 app, err := appBld.Build() 1324 require.NoError(err) 1325 1326 cdoc := app.CDoc(appdef.NewQName("test", "MyTable")) 1327 require.NotNil(cdoc) 1328 1329 fld := cdoc.UniqueField() 1330 require.NotNil(fld) 1331 require.Equal("Int2", fld.Name()) 1332 } 1333 1334 func Test_NestedTables(t *testing.T) { 1335 require := require.New(t) 1336 1337 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1338 TABLE NestedTable INHERITS CRecord ( 1339 ItemName varchar, 1340 DeepNested TABLE DeepNestedTable ( 1341 ItemName varchar 1342 ) 1343 ); 1344 `) 1345 require.NoError(err) 1346 1347 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1348 require.NoError(err) 1349 1350 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1351 getSysPackageAST(), 1352 pkg, 1353 }) 1354 require.NoError(err) 1355 1356 appBld := appdef.New() 1357 err = BuildAppDefs(packages, appBld) 1358 require.NoError(err) 1359 1360 app, err := appBld.Build() 1361 require.NoError(err) 1362 1363 require.NotNil(app.CRecord(appdef.NewQName("test", "NestedTable"))) 1364 require.NotNil(app.CRecord(appdef.NewQName("test", "DeepNestedTable"))) 1365 } 1366 1367 func Test_SemanticAnalysisForReferences(t *testing.T) { 1368 t.Run("Should return error because CDoc references to ODoc", func(t *testing.T) { 1369 require := require.New(t) 1370 1371 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1372 TABLE OTable INHERITS ODoc (); 1373 TABLE CTable INHERITS CDoc ( 1374 OTableRef ref(OTable) 1375 ); 1376 `) 1377 require.NoError(err) 1378 1379 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1380 require.NoError(err) 1381 1382 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1383 getSysPackageAST(), 1384 pkg, 1385 }) 1386 require.NoError(err) 1387 1388 appBld := appdef.New() 1389 err = BuildAppDefs(packages, appBld) 1390 1391 require.Contains(err.Error(), "table test.CTable can not reference to table test.OTable") 1392 }) 1393 } 1394 1395 func Test_1KStringField(t *testing.T) { 1396 require := require.New(t) 1397 1398 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1399 TABLE MyTable INHERITS CDoc ( 1400 KB varchar(1024) 1401 ) 1402 `) 1403 require.NoError(err) 1404 1405 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1406 require.NoError(err) 1407 1408 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1409 getSysPackageAST(), 1410 pkg, 1411 }) 1412 require.NoError(err) 1413 1414 appBld := appdef.New() 1415 err = BuildAppDefs(packages, appBld) 1416 require.NoError(err) 1417 1418 app, err := appBld.Build() 1419 require.NoError(err) 1420 1421 cdoc := app.CDoc(appdef.NewQName("test", "MyTable")) 1422 require.NotNil(cdoc) 1423 1424 fld := cdoc.Field("KB") 1425 require.NotNil(fld) 1426 1427 cnt := 0 1428 for _, c := range fld.Constraints() { 1429 cnt++ 1430 require.Equal(appdef.ConstraintKind_MaxLen, c.Kind()) 1431 require.EqualValues(1024, c.Value()) 1432 } 1433 require.Equal(1, cnt) 1434 } 1435 1436 func Test_ReferenceToNoTable(t *testing.T) { 1437 require := require.New(t) 1438 1439 fs, err := ParseFile("example.vsql", `APPLICATION test(); 1440 ROLE Admin; 1441 TABLE CTable INHERITS CDoc ( 1442 RefField ref(Admin) 1443 ); 1444 `) 1445 require.NoError(err) 1446 1447 pkg, err := BuildPackageSchema("test", []*FileSchemaAST{fs}) 1448 require.NoError(err) 1449 1450 _, err = BuildAppSchema([]*PackageSchemaAST{ 1451 getSysPackageAST(), 1452 pkg, 1453 }) 1454 require.Contains(err.Error(), "undefined table: Admin") 1455 1456 } 1457 1458 func Test_VRestaurantBasic(t *testing.T) { 1459 1460 require := require.New(t) 1461 1462 vRestaurantPkgAST, err := ParsePackageDir("github.com/untillpro/vrestaurant", fsvRestaurant, "sql_example_app/vrestaurant") 1463 require.NoError(err) 1464 1465 packages, err := BuildAppSchema([]*PackageSchemaAST{ 1466 getSysPackageAST(), 1467 vRestaurantPkgAST, 1468 }) 1469 require.NoError(err) 1470 1471 builder := appdef.New() 1472 err = BuildAppDefs(packages, builder) 1473 require.NoError(err) 1474 1475 app, err := builder.Build() 1476 require.NoError(err) 1477 1478 // table 1479 cdoc := app.Type(appdef.NewQName("vrestaurant", "TablePlan")) 1480 require.NotNil(cdoc) 1481 require.Equal(appdef.TypeKind_CDoc, cdoc.Kind()) 1482 require.Equal(appdef.DataKind_RecordID, cdoc.(appdef.IFields).Field("Picture").DataKind()) 1483 1484 cdoc = app.Type(appdef.NewQName("vrestaurant", "Client")) 1485 require.NotNil(cdoc) 1486 1487 cdoc = app.Type(appdef.NewQName("vrestaurant", "POSUser")) 1488 require.NotNil(cdoc) 1489 1490 cdoc = app.Type(appdef.NewQName("vrestaurant", "Department")) 1491 require.NotNil(cdoc) 1492 1493 cdoc = app.Type(appdef.NewQName("vrestaurant", "Article")) 1494 require.NotNil(cdoc) 1495 1496 // child table 1497 crec := app.Type(appdef.NewQName("vrestaurant", "TableItem")) 1498 require.NotNil(crec) 1499 require.Equal(appdef.TypeKind_CRecord, crec.Kind()) 1500 require.Equal(appdef.DataKind_int32, crec.(appdef.IFields).Field("Tableno").DataKind()) 1501 1502 // view 1503 view := app.View(appdef.NewQName("vrestaurant", "SalesPerDay")) 1504 require.NotNil(view) 1505 require.Equal(appdef.TypeKind_ViewRecord, view.Kind()) 1506 } 1507 1508 func Test_AppSchema(t *testing.T) { 1509 require := require.New(t) 1510 1511 fs, err := ParseFile("example1.vsql", ` 1512 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2' AS air1; 1513 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3' AS air2; 1514 APPLICATION test( 1515 USE air1; 1516 USE air2; 1517 ); 1518 TABLE MyTable INHERITS CDoc (); 1519 `) 1520 require.NoError(err) 1521 pkg1, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg1", []*FileSchemaAST{fs}) 1522 require.NoError(err) 1523 1524 fs, err = ParseFile("example2.vsql", ` 1525 TABLE MyTable INHERITS CDoc (); 1526 `) 1527 require.NoError(err) 1528 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1529 require.NoError(err) 1530 1531 fs, err = ParseFile("example3.vsql", ` 1532 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2' AS air1; 1533 WORKSPACE myWorkspace ( 1534 USE TABLE air1.MyTable; 1535 ); 1536 `) 1537 require.NoError(err) 1538 pkg3, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg3", []*FileSchemaAST{fs}) 1539 require.NoError(err) 1540 1541 appSchema, err := BuildAppSchema([]*PackageSchemaAST{getSysPackageAST(), pkg1, pkg2, pkg3}) 1542 require.NoError(err) 1543 1544 builder := appdef.New() 1545 err = BuildAppDefs(appSchema, builder) 1546 require.NoError(err) 1547 1548 app, err := builder.Build() 1549 require.NoError(err) 1550 1551 cdoc := app.CDoc(appdef.NewQName("pkg1", "MyTable")) 1552 require.NotNil(cdoc) 1553 1554 cdoc = app.CDoc(appdef.NewQName("air1", "MyTable")) 1555 require.NotNil(cdoc) 1556 1557 ws := app.Workspace(appdef.NewQName("air2", "myWorkspace")) 1558 require.NotNil(ws) 1559 require.NotNil(ws.Type(appdef.NewQName("air1", "MyTable"))) 1560 } 1561 1562 func Test_AppSchemaErrors(t *testing.T) { 1563 require := require.New(t) 1564 fs, err := ParseFile("example2.vsql", ``) 1565 require.NoError(err) 1566 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1567 require.NoError(err) 1568 1569 fs, err = ParseFile("example3.vsql", ``) 1570 require.NoError(err) 1571 pkg3, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg3", []*FileSchemaAST{fs}) 1572 require.NoError(err) 1573 1574 f := func(sql string, expectErrors ...string) { 1575 ast, err := ParseFile("file2.vsql", sql) 1576 require.NoError(err) 1577 pkg, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg4", []*FileSchemaAST{ast}) 1578 require.NoError(err) 1579 1580 _, err = BuildAppSchema([]*PackageSchemaAST{ 1581 pkg, pkg2, pkg3, 1582 }) 1583 require.EqualError(err, strings.Join(expectErrors, "\n")) 1584 } 1585 1586 f(`IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3'; 1587 APPLICATION test( 1588 USE air1; 1589 USE pkg3; 1590 )`, "file2.vsql:3:3: air1 undefined", 1591 "application does not define use of package github.com/untillpro/airsbp3/pkg2. Check if the package is defined in IMPORT SCHEMA and parsed under the same name") 1592 1593 f(`IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2' AS air1; 1594 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3'; 1595 APPLICATION test( 1596 USE air1; 1597 USE pkg3; 1598 USE pkg3; 1599 )`, "file2.vsql:6:4: package with the same name already included in application") 1600 1601 f(`IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2' AS air1; 1602 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3'; 1603 APPLICATION test( 1604 USE air1; 1605 USE pkg3; 1606 ); 1607 APPLICATION test( 1608 USE air1; 1609 USE pkg3; 1610 )`, "file2.vsql:7:3: redefinition of application") 1611 1612 f(`IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2' AS air1; 1613 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3'; 1614 `, "application not defined") 1615 1616 f(`IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkgX' AS air1; 1617 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg3'; 1618 APPLICATION test( 1619 USE pkg3; 1620 USE air1; 1621 ) 1622 `, "file2.vsql:5:4: could not import github.com/untillpro/airsbp3/pkgX. Check if the package is parsed under exactly this name") 1623 } 1624 1625 func Test_AppIn2Schemas(t *testing.T) { 1626 require := require.New(t) 1627 fs, err := ParseFile("example2.vsql", `APPLICATION test1();`) 1628 require.NoError(err) 1629 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1630 require.NoError(err) 1631 1632 fs, err = ParseFile("example3.vsql", `APPLICATION test2();`) 1633 require.NoError(err) 1634 pkg3, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg3", []*FileSchemaAST{fs}) 1635 require.NoError(err) 1636 1637 _, err = BuildAppSchema([]*PackageSchemaAST{ 1638 pkg2, pkg3, 1639 }) 1640 require.ErrorContains(err, "redefinition of application") 1641 } 1642 1643 func Test_Scope(t *testing.T) { 1644 require := require.New(t) 1645 1646 // ***** main 1647 fs, err := ParseFile("example1.vsql", ` 1648 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg1' AS p1; 1649 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg2'; 1650 APPLICATION test( 1651 USE pkg1; 1652 USE pkg2; 1653 ); 1654 `) 1655 require.NoError(err) 1656 main, err := BuildPackageSchema("github.com/untillpro/airsbp3/main", []*FileSchemaAST{fs}) 1657 require.NoError(err) 1658 1659 // ***** pkg1 1660 fs, err = ParseFile("example2.vsql", ` 1661 WORKSPACE myWorkspace1 ( 1662 TABLE MyTable INHERITS CDoc (); 1663 ); 1664 `) 1665 require.NoError(err) 1666 pkg1, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg1", []*FileSchemaAST{fs}) 1667 require.NoError(err) 1668 1669 // ***** pkg2 1670 fs, err = ParseFile("example3.vsql", ` 1671 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg1' AS p1; 1672 WORKSPACE myWorkspace2 ( 1673 USE TABLE p1.MyTable; 1674 ); 1675 `) 1676 require.NoError(err) 1677 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1678 require.NoError(err) 1679 1680 _, err = BuildAppSchema([]*PackageSchemaAST{getSysPackageAST(), main, pkg1, pkg2}) 1681 require.EqualError(err, "example3.vsql:4:16: undefined table: p1.MyTable") 1682 1683 } 1684 1685 func Test_Scope_TableRefs(t *testing.T) { 1686 require := require.New(t) 1687 1688 // ***** main 1689 fs, err := ParseFile("example1.vsql", ` 1690 IMPORT SCHEMA 'github.com/untillpro/airsbp3/pkg1'; 1691 APPLICATION test( 1692 USE pkg1; 1693 ); 1694 `) 1695 require.NoError(err) 1696 main, err := BuildPackageSchema("github.com/untillpro/airsbp3/main", []*FileSchemaAST{fs}) 1697 require.NoError(err) 1698 1699 // ***** pkg1 1700 fs, err = ParseFile("example2.vsql", ` 1701 TABLE PkgTable INHERITS CRecord(); 1702 WORKSPACE myWorkspace1 ( 1703 TABLE MyTable INHERITS CDoc ( 1704 Items TABLE MyInnerTable() 1705 ); 1706 TABLE MyTable2 INHERITS CDoc ( 1707 r1 ref(MyTable), 1708 r2 ref(MyTable2), 1709 r3 ref(PkgTable), 1710 r4 ref(MyInnerTable) 1711 ); 1712 ); 1713 WORKSPACE myWorkspace2 ( 1714 TABLE MyTable3 INHERITS CDoc ( 1715 r1 ref(MyTable), 1716 r2 ref(MyTable2), 1717 r3 ref(PkgTable), 1718 r4 ref(MyInnerTable) 1719 ); 1720 ); 1721 `) 1722 require.NoError(err) 1723 pkg1, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg1", []*FileSchemaAST{fs}) 1724 require.NoError(err) 1725 _, err = BuildAppSchema([]*PackageSchemaAST{getSysPackageAST(), main, pkg1}) 1726 require.EqualError(err, strings.Join([]string{ 1727 "example2.vsql:10:11: table PkgTable not included into workspace", 1728 "example2.vsql:16:11: undefined table: MyTable", 1729 "example2.vsql:17:11: undefined table: MyTable2", 1730 "example2.vsql:18:11: table PkgTable not included into workspace", 1731 "example2.vsql:19:11: undefined table: MyInnerTable", 1732 }, "\n")) 1733 1734 } 1735 1736 func Test_Alter_Workspace_In_Package(t *testing.T) { 1737 1738 require := require.New(t) 1739 1740 fs0, err := ParseFile("file0.vsql", ` 1741 IMPORT SCHEMA 'org/pkg1'; 1742 IMPORT SCHEMA 'org/pkg2'; 1743 APPLICATION test( 1744 USE pkg1; 1745 ); 1746 `) 1747 require.NoError(err) 1748 pkg0, err := BuildPackageSchema("org/main", []*FileSchemaAST{fs0}) 1749 require.NoError(err) 1750 1751 fs1, err := ParseFile("file1.vsql", ` 1752 ALTERABLE WORKSPACE Ws0( 1753 TABLE wst01 INHERITS CDoc(); 1754 ); 1755 ABSTRACT WORKSPACE AWs( 1756 TABLE awst1 INHERITS CDoc(); 1757 ); 1758 WORKSPACE Ws( 1759 TABLE wst1 INHERITS CDoc(); 1760 ); 1761 `) 1762 require.NoError(err) 1763 fs2, err := ParseFile("file2.vsql", ` 1764 ALTER WORKSPACE Ws0( 1765 TABLE wst02 INHERITS CDoc(); 1766 ); 1767 ALTER WORKSPACE AWs( 1768 TABLE awst2 INHERITS CDoc(); 1769 ); 1770 ALTER WORKSPACE Ws( 1771 TABLE wst2 INHERITS CDoc(); 1772 ); 1773 `) 1774 require.NoError(err) 1775 pkg1, err := BuildPackageSchema("org/pkg1", []*FileSchemaAST{fs1, fs2}) 1776 require.NoError(err) 1777 1778 _, err = BuildAppSchema([]*PackageSchemaAST{ 1779 getSysPackageAST(), 1780 pkg0, 1781 pkg1, 1782 }) 1783 require.NoError(err) 1784 } 1785 1786 func Test_UseTableErrors(t *testing.T) { 1787 require := require.New(t) 1788 1789 fs, err := ParseFile("main.vsql", ` 1790 IMPORT SCHEMA 'org/pkg1'; 1791 APPLICATION test( 1792 USE pkg1; 1793 ); 1794 WORKSPACE Ws( 1795 USE TABLE pkg1.Pkg1Table3; -- bad, declared in workspace 1796 USE TABLE pkg2.*; -- bad, package not found 1797 USE WORKSPACE ws1; -- bad, workspace not found 1798 ) 1799 `) 1800 require.NoError(err) 1801 pkg, err := BuildPackageSchema("test/main", []*FileSchemaAST{fs}) 1802 require.NoError(err) 1803 1804 // pkg1 1805 fs1, err := ParseFile("file1.vsql", ` 1806 WORKSPACE Ws( 1807 TABLE Pkg1Table3 INHERITS CDoc(); 1808 ) 1809 `) 1810 require.NoError(err) 1811 pkg1, err := BuildPackageSchema("org/pkg1", []*FileSchemaAST{fs1}) 1812 require.NoError(err) 1813 1814 _, err = BuildAppSchema([]*PackageSchemaAST{ 1815 getSysPackageAST(), 1816 pkg, 1817 pkg1, 1818 }) 1819 1820 require.EqualError(err, strings.Join([]string{ 1821 "main.vsql:7:18: undefined table: pkg1.Pkg1Table3", 1822 "main.vsql:8:13: pkg2 undefined", 1823 "main.vsql:9:17: undefined workspace: main.ws1", 1824 }, "\n")) 1825 } 1826 1827 func Test_UseTables(t *testing.T) { 1828 require := require.New(t) 1829 1830 fs, err := ParseFile("main.vsql", ` 1831 IMPORT SCHEMA 'org/pkg1'; 1832 IMPORT SCHEMA 'org/pkg2'; 1833 APPLICATION test( 1834 USE pkg1; 1835 USE pkg2; 1836 ); 1837 TABLE TestTable1 INHERITS CDoc(); 1838 TABLE TestTable2 INHERITS CDoc(); 1839 1840 WORKSPACE Ws( 1841 USE TABLE *; -- good, import all tables declared on current package level 1842 USE TABLE pkg1.*; -- good, import all tables from specified package 1843 USE TABLE pkg2.Pkg2Table1; -- good, import specified table 1844 ) 1845 `) 1846 require.NoError(err) 1847 pkg, err := BuildPackageSchema("test/main", []*FileSchemaAST{fs}) 1848 require.NoError(err) 1849 1850 // pkg1 1851 fs1, err := ParseFile("file1.vsql", ` 1852 TABLE Pkg1Table1 INHERITS CDoc(); 1853 TABLE Pkg1Table2 INHERITS CDoc(); 1854 1855 WORKSPACE Ws( 1856 TABLE Pkg1Table3 INHERITS CDoc(); 1857 ) 1858 `) 1859 require.NoError(err) 1860 pkg1, err := BuildPackageSchema("org/pkg1", []*FileSchemaAST{fs1}) 1861 require.NoError(err) 1862 1863 // pkg2 1864 fs2, err := ParseFile("file2.vsql", ` 1865 TABLE Pkg2Table1 INHERITS CDoc(); 1866 TABLE Pkg2Table2 INHERITS CDoc(); 1867 1868 WORKSPACE Ws( 1869 TABLE Pkg2Table3 INHERITS CDoc(); 1870 ) 1871 `) 1872 require.NoError(err) 1873 pkg2, err := BuildPackageSchema("org/pkg2", []*FileSchemaAST{fs2}) 1874 require.NoError(err) 1875 1876 schema, err := BuildAppSchema([]*PackageSchemaAST{ 1877 getSysPackageAST(), 1878 pkg, 1879 pkg1, 1880 pkg2, 1881 }) 1882 1883 require.NoError(err) 1884 1885 builder := appdef.New() 1886 err = BuildAppDefs(schema, builder) 1887 require.NoError(err) 1888 1889 app, err := builder.Build() 1890 require.NoError(err) 1891 1892 ws := app.Workspace(appdef.NewQName("main", "Ws")) 1893 require.NotNil(ws) 1894 1895 require.NotEqual(appdef.TypeKind_null, ws.Type(appdef.NewQName("main", "TestTable1")).Kind()) 1896 require.NotEqual(appdef.TypeKind_null, ws.Type(appdef.NewQName("main", "TestTable2")).Kind()) 1897 require.NotEqual(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg1", "Pkg1Table1")).Kind()) 1898 require.NotEqual(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg1", "Pkg1Table2")).Kind()) 1899 require.Equal(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg1", "Pkg1Table3")).Kind()) 1900 require.NotEqual(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg2", "Pkg2Table1")).Kind()) 1901 require.Equal(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg2", "Pkg2Table2")).Kind()) 1902 require.Equal(appdef.TypeKind_null, ws.Type(appdef.NewQName("pkg2", "Pkg2Table3")).Kind()) 1903 1904 _, err = builder.Build() 1905 require.NoError(err) 1906 1907 } 1908 1909 func Test_Storages(t *testing.T) { 1910 require := require.New(t) 1911 fs, err := ParseFile("example2.vsql", `APPLICATION test1(); 1912 EXTENSION ENGINE BUILTIN ( 1913 STORAGE MyStorage( 1914 INSERT SCOPE(PROJECTORS) 1915 ); 1916 ) 1917 `) 1918 require.NoError(err) 1919 pkg2, err := BuildPackageSchema("github.com/untillpro/airsbp3/pkg2", []*FileSchemaAST{fs}) 1920 require.NoError(err) 1921 1922 schema, err := BuildAppSchema([]*PackageSchemaAST{ 1923 pkg2, 1924 }) 1925 require.ErrorContains(err, "storages are only declared in sys package") 1926 require.Nil(schema) 1927 } 1928 1929 func buildPackage(sql string) *PackageSchemaAST { 1930 fs, err := ParseFile("source.vsql", sql) 1931 if err != nil { 1932 panic(err) 1933 } 1934 pkg, err := BuildPackageSchema("github.com/voedger/voedger/app1", []*FileSchemaAST{fs}) 1935 if err != nil { 1936 panic(err) 1937 } 1938 return pkg 1939 } 1940 1941 func Test_OdocCmdArgs(t *testing.T) { 1942 require := require.New(t) 1943 pkgApp1 := buildPackage(` 1944 1945 APPLICATION registry( 1946 ); 1947 1948 TABLE TableODoc INHERITS ODoc ( 1949 orecord1 TABLE orecord1( 1950 orecord2 TABLE orecord2() 1951 ) 1952 ); 1953 1954 WORKSPACE Workspace1 ( 1955 EXTENSION ENGINE BUILTIN ( 1956 COMMAND CmdODoc1(TableODoc) RETURNS TableODoc; 1957 ) 1958 ); 1959 1960 `) 1961 1962 schema, err := BuildAppSchema([]*PackageSchemaAST{pkgApp1, getSysPackageAST()}) 1963 require.NoError(err) 1964 1965 builder := appdef.New() 1966 err = BuildAppDefs(schema, builder) 1967 require.NoError(err) 1968 1969 app, err := builder.Build() 1970 require.NoError(err) 1971 1972 cmdOdoc := app.Command(appdef.NewQName("app1", "CmdODoc1")) 1973 require.NotNil(cmdOdoc) 1974 require.NotNil(cmdOdoc.Param()) 1975 1976 odoc := cmdOdoc.Param().(appdef.IContainers) 1977 require.Equal(1, odoc.ContainerCount()) 1978 require.Equal("orecord1", odoc.Container("orecord1").Name()) 1979 container := odoc.Container("orecord1") 1980 require.Equal(appdef.Occurs(0), container.MinOccurs()) 1981 require.Equal(appdef.Occurs(100), container.MaxOccurs()) 1982 1983 orec := app.ORecord(appdef.NewQName("app1", "orecord1")) 1984 require.NotNil(orec) 1985 require.Equal(1, orec.ContainerCount()) 1986 require.Equal("orecord2", orec.Container("orecord2").Name()) 1987 1988 } 1989 1990 func Test_TypeContainers(t *testing.T) { 1991 require := require.New(t) 1992 pkgApp1 := buildPackage(` 1993 1994 APPLICATION registry( 1995 ); 1996 1997 TYPE Person ( 1998 Name varchar, 1999 Age int32 2000 ); 2001 2002 TYPE Item ( 2003 Name varchar, 2004 Price currency 2005 ); 2006 2007 TYPE Deal ( 2008 side1 Person NOT NULL, -- collection 1..1 2009 side2 Person -- collection 0..1 2010 -- items Item[] NOT NULL, -- (not yet supported by kernel) collection 1..* (up to maxNestedTableContainerOccurrences = 100) 2011 -- discounts Item[3] -- (not yet supported by kernel) collection 0..3 (one-based numbering convention for arrays, similarly to PostgreSQL) 2012 ); 2013 2014 WORKSPACE Workspace1 ( 2015 EXTENSION ENGINE BUILTIN ( 2016 COMMAND CmdDeal(Deal) RETURNS Deal; 2017 ) 2018 ); 2019 `) 2020 2021 schema, err := BuildAppSchema([]*PackageSchemaAST{pkgApp1, getSysPackageAST()}) 2022 require.NoError(err) 2023 2024 builder := appdef.New() 2025 err = BuildAppDefs(schema, builder) 2026 require.NoError(err) 2027 2028 validate := func(par appdef.IType) { 2029 o, ok := par.(appdef.IObject) 2030 require.True(ok, "expected %v supports IObject", par) 2031 require.Equal(2, o.ContainerCount()) 2032 require.Equal(appdef.Occurs(1), o.Container("side1").MinOccurs()) 2033 require.Equal(appdef.Occurs(1), o.Container("side1").MaxOccurs()) 2034 require.Equal(appdef.Occurs(0), o.Container("side2").MinOccurs()) 2035 require.Equal(appdef.Occurs(1), o.Container("side2").MaxOccurs()) 2036 2037 /* TODO: uncomment when kernel supports it 2038 require.Equal(appdef.Occurs(1), o.Container("items").MinOccurs()) 2039 require.Equal(appdef.Occurs(100), o.Container("items").MaxOccurs()) 2040 require.Equal(appdef.Occurs(0), o.Container("discounts").MinOccurs()) 2041 require.Equal(appdef.Occurs(3), o.Container("discounts").MaxOccurs()) 2042 */ 2043 } 2044 2045 app, err := builder.Build() 2046 require.NoError(err) 2047 2048 cmd := app.Command(appdef.NewQName("app1", "CmdDeal")) 2049 validate(cmd.Param()) 2050 validate(cmd.Result()) 2051 } 2052 2053 func Test_EmptyType(t *testing.T) { 2054 require := require.New(t) 2055 pkgApp1 := buildPackage(` 2056 2057 APPLICATION registry( 2058 ); 2059 2060 TYPE EmptyType ( 2061 ); 2062 `) 2063 2064 schema, err := BuildAppSchema([]*PackageSchemaAST{pkgApp1, getSysPackageAST()}) 2065 require.NoError(err) 2066 2067 builder := appdef.New() 2068 err = BuildAppDefs(schema, builder) 2069 require.NoError(err) 2070 2071 app, err := builder.Build() 2072 require.NoError(err) 2073 2074 cdoc := app.Object(appdef.NewQName("app1", "EmptyType")) 2075 require.NotNil(cdoc) 2076 } 2077 2078 func Test_EmptyType1(t *testing.T) { 2079 require := require.New(t) 2080 pkgApp1 := buildPackage(` 2081 2082 APPLICATION registry( 2083 ); 2084 2085 TYPE SomeType ( 2086 t int321 2087 ); 2088 2089 TABLE SomeTable INHERITS CDoc ( 2090 t int321 2091 ) 2092 `) 2093 2094 _, err := BuildAppSchema([]*PackageSchemaAST{pkgApp1, getSysPackageAST()}) 2095 require.EqualError(err, strings.Join([]string{ 2096 "source.vsql:7:4: undefined type: int321", 2097 "source.vsql:11:4: undefined data type or table: int321", 2098 }, "\n")) 2099 2100 } 2101 2102 func Test_ODocUnknown(t *testing.T) { 2103 require := require.New(t) 2104 pkgApp1 := buildPackage(`APPLICATION registry(); 2105 TABLE MyTable1 INHERITS ODocUnknown ( MyField ref(registry.Login) NOT NULL ); 2106 `) 2107 2108 _, err := BuildAppSchema([]*PackageSchemaAST{pkgApp1, getSysPackageAST()}) 2109 require.EqualError(err, strings.Join([]string{ 2110 "source.vsql:2:1: undefined table kind", 2111 "source.vsql:2:25: undefined table: ODocUnknown", 2112 "source.vsql:2:51: registry undefined", 2113 }, "\n")) 2114 2115 } 2116 2117 //go:embed package.vsql 2118 var pkgSqlFS embed.FS 2119 2120 func TestParseFilesFromFSRoot(t *testing.T) { 2121 t.Run("dot", func(t *testing.T) { 2122 _, err := ParsePackageDir("github.com/untillpro/main", pkgSqlFS, ".") 2123 require.NoError(t, err) 2124 }) 2125 } 2126 2127 func Test_Constraints(t *testing.T) { 2128 require := assertions(t) 2129 2130 require.AppSchemaError(` 2131 APPLICATION app1(); 2132 TABLE SomeTable INHERITS CDoc ( 2133 t1 int32, 2134 t2 int32, 2135 CONSTRAINT c1 UNIQUE(t1), 2136 CONSTRAINT c1 UNIQUE(t2) 2137 )`, "file.vsql:7:3: redefinition of c1") 2138 2139 require.AppSchemaError(` 2140 APPLICATION app1(); 2141 TABLE SomeTable INHERITS CDoc ( 2142 UNIQUEFIELD UnknownField 2143 )`, "file.vsql:4:3: undefined field UnknownField") 2144 2145 require.AppSchemaError(` 2146 APPLICATION app1(); 2147 TABLE SomeTable INHERITS CDoc ( 2148 t1 int32, 2149 t2 int32, 2150 CONSTRAINT c1 UNIQUE(t1), 2151 CONSTRAINT c2 UNIQUE(t2, t1) 2152 )`, "file.vsql:7:3: field t1 already in unique constraint") 2153 2154 } 2155 2156 func Test_Grants(t *testing.T) { 2157 require := assertions(t) 2158 2159 require.AppSchemaError(` 2160 APPLICATION app1(); 2161 ROLE role1; 2162 WORKSPACE ws1 ( 2163 GRANT ALL ON TABLE Fake TO app1; 2164 GRANT INSERT ON COMMAND Fake TO role1; 2165 GRANT SELECT ON QUERY Fake TO role1; 2166 GRANT INSERT ON WORKSPACE Fake TO role1; 2167 TABLE Tbl INHERITS CDoc(); 2168 GRANT ALL(FakeCol) ON TABLE Tbl TO role1; 2169 GRANT INSERT,UPDATE(FakeCol) ON TABLE Tbl TO role1; 2170 GRANT INSERT ON ALL COMMANDS WITH TAG x TO role1; 2171 TABLE Nested1 INHERITS CRecord(); 2172 TABLE Tbl2 INHERITS CDoc( 2173 ref1 ref(Tbl), 2174 items TABLE Nested(), 2175 items2 Nested1 2176 ); 2177 GRANT ALL(ref1) ON TABLE Tbl2 TO role1; 2178 GRANT ALL(items) ON TABLE Tbl2 TO role1; 2179 GRANT ALL(items2) ON TABLE Tbl2 TO role1; 2180 ); 2181 `, "file.vsql:5:30: undefined role: app1", 2182 "file.vsql:5:22: undefined table: Fake", 2183 "file.vsql:6:27: undefined command: Fake", 2184 "file.vsql:7:25: undefined query: Fake", 2185 "file.vsql:8:29: undefined workspace: Fake", 2186 "file.vsql:10:13: undefined field FakeCol", 2187 "file.vsql:11:23: undefined field FakeCol", 2188 "file.vsql:12:41: undefined tag: x", 2189 ) 2190 } 2191 2192 func Test_UndefinedType(t *testing.T) { 2193 require := assertions(t) 2194 2195 require.AppSchemaError(`APPLICATION app1(); 2196 TABLE MyTable2 INHERITS ODoc ( 2197 MyField int23 NOT NULL 2198 ); 2199 `, "file.vsql:3:9: undefined data type or table: int23", 2200 ) 2201 } 2202 2203 func Test_DescriptorInProjector(t *testing.T) { 2204 require := assertions(t) 2205 2206 require.AppSchemaError(`APPLICATION app1(); 2207 WORKSPACE w ( 2208 EXTENSION ENGINE BUILTIN ( 2209 PROJECTOR x AFTER INSERT ON (unknown.z) STATE(Http); 2210 ); 2211 ); 2212 `, 2213 "file.vsql:4:34: unknown undefined") 2214 2215 require.NoAppSchemaError(`APPLICATION app1(); 2216 WORKSPACE RestaurantWS ( 2217 DESCRIPTOR Restaurant (); 2218 EXTENSION ENGINE BUILTIN ( 2219 PROJECTOR NewRestaurantVat AFTER INSERT OR UPDATE ON (Restaurant) STATE(AppSecret, Http) INTENTS(SendMail); 2220 ); 2221 ); 2222 `) 2223 } 2224 2225 type testVarResolver struct { 2226 resolved map[appdef.QName]bool 2227 } 2228 2229 func (t testVarResolver) AsInt32(name appdef.QName) (int32, bool) { 2230 t.resolved[name] = true 2231 return 1, true 2232 } 2233 2234 func Test_Variables(t *testing.T) { 2235 require := assertions(t) 2236 2237 require.AppSchemaError(`APPLICATION app1(); RATE AppDefaultRate variable PER HOUR;`, "file.vsql:1:41: variable undefined") 2238 2239 schema, err := require.AppSchema(`APPLICATION app1(); 2240 DECLARE variable int32 DEFAULT 100; 2241 RATE AppDefaultRate variable PER HOUR; 2242 `) 2243 require.NoError(err) 2244 2245 resolver := testVarResolver{resolved: make(map[appdef.QName]bool)} 2246 2247 BuildAppDefs(schema, appdef.New(), WithVariableResolver(&resolver)) 2248 require.True(resolver.resolved[appdef.NewQName("pkg", "variable")]) 2249 } 2250 2251 func Test_RatesAndLimits(t *testing.T) { 2252 require := assertions(t) 2253 2254 require.AppSchemaError(`APPLICATION app1(); 2255 WORKSPACE w ( 2256 RATE r 1 PER HOUR; 2257 LIMIT l1 ON EVERYTHING WITH RATE x; 2258 LIMIT l2 ON COMMAND x WITH RATE r; 2259 LIMIT l3 ON QUERY y WITH RATE r; 2260 LIMIT l4 ON TAG z WITH RATE r; 2261 LIMIT l5 ON TABLE t WITH RATE r; 2262 );`, 2263 "file.vsql:4:36: undefined rate: x", 2264 "file.vsql:5:23: undefined command: x", 2265 "file.vsql:6:21: undefined query: y", 2266 "file.vsql:7:19: undefined tag: z", 2267 "file.vsql:8:21: undefined table: t") 2268 } 2269 2270 func Test_RefsFromInheritedWs(t *testing.T) { 2271 require := assertions(t) 2272 2273 require.NoAppSchemaError(`APPLICATION test(); 2274 ABSTRACT WORKSPACE base ( 2275 TABLE tab1 INHERITS WDoc ( 2276 Fld1 int32 2277 ); 2278 ); 2279 WORKSPACE work INHERITS base ( 2280 TABLE tab2 INHERITS WDoc ( 2281 Fld2 ref(tab1) 2282 ); 2283 );`) 2284 } 2285 2286 //go:embed test/pkg1.vsql 2287 var pkg1FS embed.FS 2288 2289 func Test_Panic1(t *testing.T) { 2290 ast, errs := ParsePackageDir(appdef.SysPackage, pkg1FS, "test") 2291 require.ErrorContains(t, errs, "no valid schema files") 2292 require.Nil(t, ast) 2293 } 2294 2295 func Test_Identifiers(t *testing.T) { 2296 require := assertions(t) 2297 2298 _, err := ParseFile("file1.vsql", `APPLICATION app1(); 2299 WORKSPACE w ( 2300 ROLE _role; 2301 );`) 2302 require.ErrorContains(err, "file1.vsql:3:8: invalid input text") 2303 2304 _, err = ParseFile("file1.vsql", `APPLICATION app1(); 2305 WORKSPACE w ( 2306 ROLE r234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890r23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456; 2307 );`) 2308 require.ErrorContains(err, "file1.vsql:3:263: unexpected token") 2309 2310 _, err = ParseFile("file1.vsql", `APPLICATION app1(); 2311 WORKSPACE w ( 2312 ROLE r世界; 2313 );`) 2314 require.ErrorContains(err, "file1.vsql:3:9: invalid input text") 2315 } 2316 2317 func Test_RefsWorkspaces(t *testing.T) { 2318 require := assertions(t) 2319 2320 require.AppSchemaError(`APPLICATION test(); 2321 TABLE t1 INHERITS WDoc(); 2322 WORKSPACE w2 ( 2323 TABLE tab2 INHERITS WDoc( 2324 f ref(t1) -- error, t1 is not in the workspace 2325 ); 2326 TYPE typ2( 2327 f ref(t1) -- error, t1 is not in the workspace 2328 ); 2329 VIEW test( 2330 f ref(t1), -- error, t1 is not in the workspace 2331 PRIMARY KEY(f) 2332 ) AS RESULT OF Proj1; 2333 2334 EXTENSION ENGINE BUILTIN ( 2335 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 2336 COMMAND Orders() 2337 ); 2338 2339 );`, 2340 "file.vsql:5:10: table t1 not included into workspace", 2341 "file.vsql:8:10: table t1 not included into workspace", 2342 "file.vsql:11:10: table t1 not included into workspace") 2343 2344 require.NoAppSchemaError(`APPLICATION test(); 2345 WORKSPACE w2 ( 2346 TABLE t1 INHERITS WDoc( 2347 items TABLE t2( 2348 items TABLE t3() 2349 ) 2350 ); 2351 TABLE tab2 INHERITS WDoc( 2352 f1 ref(t2), 2353 f2 ref(t3) 2354 ); 2355 TYPE typ2( 2356 f1 ref(t2), 2357 f2 ref(t3) 2358 ); 2359 VIEW test( 2360 f1 ref(t2), 2361 f2 ref(t3), 2362 PRIMARY KEY(f1) 2363 ) AS RESULT OF Proj1; 2364 2365 EXTENSION ENGINE BUILTIN ( 2366 PROJECTOR Proj1 AFTER EXECUTE ON (Orders) INTENTS (View(test)); 2367 COMMAND Orders() 2368 ); 2369 2370 );`) 2371 } 2372 2373 func Test_ScheduledProjectors(t *testing.T) { 2374 2375 t.Run("bad workspace", func(t *testing.T) { 2376 require := assertions(t) 2377 require.AppSchemaError(`APPLICATION test(); 2378 WORKSPACE w2 ( 2379 EXTENSION ENGINE BUILTIN ( 2380 PROJECTOR Proj1 CRON '1 0 * * *'; 2381 ); 2382 );`, "file.vsql:4:6: scheduled projector must be in app workspace") 2383 }) 2384 2385 t.Run("bad cron and intents", func(t *testing.T) { 2386 require := assertions(t) 2387 require.AppSchemaError(`APPLICATION test(); 2388 ALTER WORKSPACE AppWorkspaceWS ( 2389 VIEW test( 2390 i int32, 2391 PRIMARY KEY(i) 2392 ) AS RESULT OF Proj1; 2393 2394 EXTENSION ENGINE BUILTIN ( 2395 PROJECTOR Proj1 CRON 'blah' INTENTS (View(test)); 2396 ); 2397 );`, "file.vsql:9:6: invalid cron schedule: blah", "file.vsql:9:6: scheduled projector cannot have intents") 2398 }) 2399 2400 t.Run("good cron", func(t *testing.T) { 2401 require := assertions(t) 2402 require.NoAppSchemaError(`APPLICATION test(); 2403 ALTER WORKSPACE sys.AppWorkspaceWS ( 2404 EXTENSION ENGINE BUILTIN ( 2405 PROJECTOR ScheduledProjector CRON '1 0 * * *'; 2406 ); 2407 );`) 2408 }) 2409 } 2410 2411 func Test_DataTypes(t *testing.T) { 2412 2413 require := assertions(t) 2414 require.NoAppSchemaError(`APPLICATION test(); 2415 ALTER WORKSPACE sys.AppWorkspaceWS ( 2416 TABLE t1 INHERITS WDoc( 2417 s1_1_1 character varying(10), 2418 s1_1_1 character varying, 2419 s1_2_1 varchar(10), 2420 s1_2_2 varchar, 2421 s1_3_1 text(10), 2422 s1_3_1 text, 2423 2424 s2_1_1 binary varying(10), 2425 s2_1_1 binary varying, 2426 s2_2_1 varbinary(10), 2427 s2_2_2 varbinary, 2428 s2_3_1 bytes(10), 2429 s2_3_1 bytes, 2430 2431 s3_1 bigint, 2432 s3_2 int64, 2433 2434 s4_1 integer, 2435 s4_2 int32, 2436 s4_3 int, 2437 2438 s5_1 real, 2439 s5_2 float, 2440 s5_3 float32, 2441 2442 s6_1 double precision, 2443 s6_2 float64, 2444 2445 s7_2 money, 2446 s7_3 currency, 2447 2448 s8_1 boolean, 2449 s8_2 bool, 2450 2451 s9_1 binary large object, 2452 s9_2 blob, 2453 2454 s10_1 qualified name, 2455 s10_2 qname, 2456 2457 s11_1 record 2458 2459 ); 2460 );`) 2461 } 2462 2463 func Test_UniquesFromFieldsets(t *testing.T) { 2464 require := assertions(t) 2465 schema, err := require.AppSchema(`APPLICATION test(); 2466 TYPE fieldset ( 2467 f1 int32 2468 ); 2469 TABLE t1 INHERITS WDoc( 2470 fieldset, 2471 f2 int32, 2472 UNIQUE(f1) 2473 ); 2474 `) 2475 require.NoError(err) 2476 require.NoError(BuildAppDefs(schema, appdef.New())) 2477 } 2478 2479 func Test_CRecordInDescriptor(t *testing.T) { 2480 require := assertions(t) 2481 schema, err := require.AppSchema(`APPLICATION test(); 2482 WORKSPACE w ( 2483 DESCRIPTOR wd( 2484 items x 2485 ); 2486 TABLE x INHERITS CRecord( 2487 f1 int32 2488 ); 2489 ); 2490 `) 2491 require.NoError(err) 2492 require.NoError(BuildAppDefs(schema, appdef.New())) 2493 }