github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/common/sql_test.go (about) 1 package common 2 3 import ( 4 "testing" 5 6 "github.com/authzed/spicedb/pkg/datastore/options" 7 8 "github.com/authzed/spicedb/pkg/tuple" 9 10 sq "github.com/Masterminds/squirrel" 11 v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" 12 "github.com/stretchr/testify/require" 13 14 "github.com/authzed/spicedb/pkg/datastore" 15 ) 16 17 func TestSchemaQueryFilterer(t *testing.T) { 18 tests := []struct { 19 name string 20 run func(filterer SchemaQueryFilterer) SchemaQueryFilterer 21 expectedSQL string 22 expectedArgs []any 23 expectedColumnCounts map[string]int 24 }{ 25 { 26 "relation filter", 27 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 28 return filterer.FilterToRelation("somerelation") 29 }, 30 "SELECT * WHERE relation = ?", 31 []any{"somerelation"}, 32 map[string]int{ 33 "relation": 1, 34 }, 35 }, 36 { 37 "resource ID filter", 38 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 39 return filterer.FilterToResourceID("someresourceid") 40 }, 41 "SELECT * WHERE object_id = ?", 42 []any{"someresourceid"}, 43 map[string]int{ 44 "object_id": 1, 45 }, 46 }, 47 { 48 "resource IDs filter", 49 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 50 return filterer.MustFilterWithResourceIDPrefix("someprefix") 51 }, 52 "SELECT * WHERE object_id LIKE ?", 53 []any{"someprefix%"}, 54 map[string]int{}, // object_id is not statically used, so not present in the map 55 }, 56 { 57 "resource IDs prefix filter", 58 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 59 return filterer.MustFilterToResourceIDs([]string{"someresourceid", "anotherresourceid"}) 60 }, 61 "SELECT * WHERE object_id IN (?, ?)", 62 []any{"someresourceid", "anotherresourceid"}, 63 map[string]int{ 64 "object_id": 2, 65 }, 66 }, 67 { 68 "resource type filter", 69 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 70 return filterer.FilterToResourceType("sometype") 71 }, 72 "SELECT * WHERE ns = ?", 73 []any{"sometype"}, 74 map[string]int{ 75 "ns": 1, 76 }, 77 }, 78 { 79 "resource filter", 80 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 81 return filterer.FilterToResourceType("sometype").FilterToResourceID("someobj").FilterToRelation("somerel") 82 }, 83 "SELECT * WHERE ns = ? AND object_id = ? AND relation = ?", 84 []any{"sometype", "someobj", "somerel"}, 85 map[string]int{ 86 "ns": 1, 87 "object_id": 1, 88 "relation": 1, 89 }, 90 }, 91 { 92 "relationships filter with no IDs or relations", 93 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 94 return filterer.MustFilterWithRelationshipsFilter(datastore.RelationshipsFilter{ 95 OptionalResourceType: "sometype", 96 }) 97 }, 98 "SELECT * WHERE ns = ?", 99 []any{"sometype"}, 100 map[string]int{ 101 "ns": 1, 102 }, 103 }, 104 { 105 "relationships filter with single ID", 106 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 107 return filterer.MustFilterWithRelationshipsFilter(datastore.RelationshipsFilter{ 108 OptionalResourceType: "sometype", 109 OptionalResourceIds: []string{"someid"}, 110 }) 111 }, 112 "SELECT * WHERE ns = ? AND object_id IN (?)", 113 []any{"sometype", "someid"}, 114 map[string]int{ 115 "ns": 1, 116 "object_id": 1, 117 }, 118 }, 119 { 120 "relationships filter with no IDs", 121 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 122 return filterer.MustFilterWithRelationshipsFilter(datastore.RelationshipsFilter{ 123 OptionalResourceType: "sometype", 124 OptionalResourceIds: []string{}, 125 }) 126 }, 127 "SELECT * WHERE ns = ?", 128 []any{"sometype"}, 129 map[string]int{ 130 "ns": 1, 131 }, 132 }, 133 { 134 "relationships filter with multiple IDs", 135 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 136 return filterer.MustFilterWithRelationshipsFilter(datastore.RelationshipsFilter{ 137 OptionalResourceType: "sometype", 138 OptionalResourceIds: []string{"someid", "anotherid"}, 139 }) 140 }, 141 "SELECT * WHERE ns = ? AND object_id IN (?, ?)", 142 []any{"sometype", "someid", "anotherid"}, 143 map[string]int{ 144 "ns": 1, 145 "object_id": 2, 146 }, 147 }, 148 { 149 "subjects filter with no IDs or relations", 150 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 151 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 152 OptionalSubjectType: "somesubjectype", 153 }) 154 }, 155 "SELECT * WHERE ((subject_ns = ?))", 156 []any{"somesubjectype"}, 157 map[string]int{ 158 "subject_ns": 1, 159 }, 160 }, 161 { 162 "multiple subjects filters with just types", 163 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 164 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 165 OptionalSubjectType: "somesubjectype", 166 }, datastore.SubjectsSelector{ 167 OptionalSubjectType: "anothersubjectype", 168 }) 169 }, 170 "SELECT * WHERE ((subject_ns = ?) OR (subject_ns = ?))", 171 []any{"somesubjectype", "anothersubjectype"}, 172 map[string]int{ 173 "subject_ns": 2, 174 }, 175 }, 176 { 177 "subjects filter with single ID", 178 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 179 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 180 OptionalSubjectType: "somesubjectype", 181 OptionalSubjectIds: []string{"somesubjectid"}, 182 }) 183 }, 184 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?)))", 185 []any{"somesubjectype", "somesubjectid"}, 186 map[string]int{ 187 "subject_ns": 1, 188 "subject_object_id": 1, 189 }, 190 }, 191 { 192 "subjects filter with single ID and no type", 193 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 194 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 195 OptionalSubjectIds: []string{"somesubjectid"}, 196 }) 197 }, 198 "SELECT * WHERE ((subject_object_id IN (?)))", 199 []any{"somesubjectid"}, 200 map[string]int{ 201 "subject_object_id": 1, 202 }, 203 }, 204 { 205 "empty subjects filter", 206 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 207 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{}) 208 }, 209 "SELECT * WHERE ((1=1))", 210 nil, 211 map[string]int{}, 212 }, 213 { 214 "subjects filter with multiple IDs", 215 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 216 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 217 OptionalSubjectType: "somesubjectype", 218 OptionalSubjectIds: []string{"somesubjectid", "anothersubjectid"}, 219 }) 220 }, 221 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?, ?)))", 222 []any{"somesubjectype", "somesubjectid", "anothersubjectid"}, 223 map[string]int{ 224 "subject_ns": 1, 225 "subject_object_id": 2, 226 }, 227 }, 228 { 229 "subjects filter with single ellipsis relation", 230 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 231 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 232 OptionalSubjectType: "somesubjectype", 233 RelationFilter: datastore.SubjectRelationFilter{}.WithEllipsisRelation(), 234 }) 235 }, 236 "SELECT * WHERE ((subject_ns = ? AND subject_relation = ?))", 237 []any{"somesubjectype", "..."}, 238 map[string]int{ 239 "subject_ns": 1, 240 "subject_relation": 1, 241 }, 242 }, 243 { 244 "subjects filter with single defined relation", 245 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 246 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 247 OptionalSubjectType: "somesubjectype", 248 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("somesubrel"), 249 }) 250 }, 251 "SELECT * WHERE ((subject_ns = ? AND subject_relation = ?))", 252 []any{"somesubjectype", "somesubrel"}, 253 map[string]int{ 254 "subject_ns": 1, 255 "subject_relation": 1, 256 }, 257 }, 258 { 259 "subjects filter with only non-ellipsis", 260 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 261 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 262 OptionalSubjectType: "somesubjectype", 263 RelationFilter: datastore.SubjectRelationFilter{}.WithOnlyNonEllipsisRelations(), 264 }) 265 }, 266 "SELECT * WHERE ((subject_ns = ? AND subject_relation <> ?))", 267 []any{"somesubjectype", "..."}, 268 map[string]int{ 269 "subject_ns": 1, 270 "subject_relation": 1, 271 }, 272 }, 273 { 274 "subjects filter with defined relation and ellipsis", 275 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 276 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 277 OptionalSubjectType: "somesubjectype", 278 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("somesubrel").WithEllipsisRelation(), 279 }) 280 }, 281 "SELECT * WHERE ((subject_ns = ? AND (subject_relation = ? OR subject_relation = ?)))", 282 []any{"somesubjectype", "...", "somesubrel"}, 283 map[string]int{ 284 "subject_ns": 1, 285 "subject_relation": 2, 286 }, 287 }, 288 { 289 "subjects filter", 290 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 291 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 292 OptionalSubjectType: "somesubjectype", 293 OptionalSubjectIds: []string{"somesubjectid", "anothersubjectid"}, 294 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("somesubrel").WithEllipsisRelation(), 295 }) 296 }, 297 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?, ?) AND (subject_relation = ? OR subject_relation = ?)))", 298 []any{"somesubjectype", "somesubjectid", "anothersubjectid", "...", "somesubrel"}, 299 map[string]int{ 300 "subject_ns": 1, 301 "subject_object_id": 2, 302 "subject_relation": 2, 303 }, 304 }, 305 { 306 "multiple subjects filter", 307 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 308 return filterer.MustFilterWithSubjectsSelectors( 309 datastore.SubjectsSelector{ 310 OptionalSubjectType: "somesubjectype", 311 OptionalSubjectIds: []string{"a", "b"}, 312 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("somesubrel").WithEllipsisRelation(), 313 }, 314 datastore.SubjectsSelector{ 315 OptionalSubjectType: "anothersubjecttype", 316 OptionalSubjectIds: []string{"b", "c"}, 317 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("anotherrel").WithEllipsisRelation(), 318 }, 319 datastore.SubjectsSelector{ 320 OptionalSubjectType: "thirdsubjectype", 321 RelationFilter: datastore.SubjectRelationFilter{}.WithOnlyNonEllipsisRelations(), 322 }, 323 ) 324 }, 325 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?, ?) AND (subject_relation = ? OR subject_relation = ?)) OR (subject_ns = ? AND subject_object_id IN (?, ?) AND (subject_relation = ? OR subject_relation = ?)) OR (subject_ns = ? AND subject_relation <> ?))", 326 []any{"somesubjectype", "a", "b", "...", "somesubrel", "anothersubjecttype", "b", "c", "...", "anotherrel", "thirdsubjectype", "..."}, 327 map[string]int{ 328 "subject_ns": 3, 329 "subject_object_id": 4, 330 "subject_relation": 5, 331 }, 332 }, 333 { 334 "v1 subject filter with namespace", 335 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 336 return filterer.FilterToSubjectFilter(&v1.SubjectFilter{ 337 SubjectType: "subns", 338 }) 339 }, 340 "SELECT * WHERE subject_ns = ?", 341 []any{"subns"}, 342 map[string]int{ 343 "subject_ns": 1, 344 }, 345 }, 346 { 347 "v1 subject filter with subject id", 348 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 349 return filterer.FilterToSubjectFilter(&v1.SubjectFilter{ 350 SubjectType: "subns", 351 OptionalSubjectId: "subid", 352 }) 353 }, 354 "SELECT * WHERE subject_ns = ? AND subject_object_id = ?", 355 []any{"subns", "subid"}, 356 map[string]int{ 357 "subject_ns": 1, 358 "subject_object_id": 1, 359 }, 360 }, 361 { 362 "v1 subject filter with relation", 363 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 364 return filterer.FilterToSubjectFilter(&v1.SubjectFilter{ 365 SubjectType: "subns", 366 OptionalRelation: &v1.SubjectFilter_RelationFilter{ 367 Relation: "subrel", 368 }, 369 }) 370 }, 371 "SELECT * WHERE subject_ns = ? AND subject_relation = ?", 372 []any{"subns", "subrel"}, 373 map[string]int{ 374 "subject_ns": 1, 375 "subject_relation": 1, 376 }, 377 }, 378 { 379 "v1 subject filter with empty relation", 380 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 381 return filterer.FilterToSubjectFilter(&v1.SubjectFilter{ 382 SubjectType: "subns", 383 OptionalRelation: &v1.SubjectFilter_RelationFilter{ 384 Relation: "", 385 }, 386 }) 387 }, 388 "SELECT * WHERE subject_ns = ? AND subject_relation = ?", 389 []any{"subns", "..."}, 390 map[string]int{ 391 "subject_ns": 1, 392 "subject_relation": 1, 393 }, 394 }, 395 { 396 "v1 subject filter", 397 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 398 return filterer.FilterToSubjectFilter(&v1.SubjectFilter{ 399 SubjectType: "subns", 400 OptionalSubjectId: "subid", 401 OptionalRelation: &v1.SubjectFilter_RelationFilter{ 402 Relation: "somerel", 403 }, 404 }) 405 }, 406 "SELECT * WHERE subject_ns = ? AND subject_object_id = ? AND subject_relation = ?", 407 []any{"subns", "subid", "somerel"}, 408 map[string]int{ 409 "subject_ns": 1, 410 "subject_object_id": 1, 411 "subject_relation": 1, 412 }, 413 }, 414 { 415 "limit", 416 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 417 return filterer.limit(100) 418 }, 419 "SELECT * LIMIT 100", 420 nil, 421 map[string]int{}, 422 }, 423 { 424 "full resources filter", 425 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 426 return filterer.MustFilterWithRelationshipsFilter( 427 datastore.RelationshipsFilter{ 428 OptionalResourceType: "someresourcetype", 429 OptionalResourceIds: []string{"someid", "anotherid"}, 430 OptionalResourceRelation: "somerelation", 431 OptionalSubjectsSelectors: []datastore.SubjectsSelector{ 432 { 433 OptionalSubjectType: "somesubjectype", 434 OptionalSubjectIds: []string{"somesubjectid", "anothersubjectid"}, 435 RelationFilter: datastore.SubjectRelationFilter{}.WithNonEllipsisRelation("somesubrel").WithEllipsisRelation(), 436 }, 437 }, 438 }, 439 ) 440 }, 441 "SELECT * WHERE ns = ? AND relation = ? AND object_id IN (?, ?) AND ((subject_ns = ? AND subject_object_id IN (?, ?) AND (subject_relation = ? OR subject_relation = ?)))", 442 []any{"someresourcetype", "somerelation", "someid", "anotherid", "somesubjectype", "somesubjectid", "anothersubjectid", "...", "somesubrel"}, 443 map[string]int{ 444 "ns": 1, 445 "object_id": 2, 446 "relation": 1, 447 "subject_ns": 1, 448 "subject_object_id": 2, 449 "subject_relation": 2, 450 }, 451 }, 452 { 453 "order by", 454 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 455 return filterer.MustFilterWithRelationshipsFilter( 456 datastore.RelationshipsFilter{ 457 OptionalResourceType: "someresourcetype", 458 }, 459 ).TupleOrder(options.ByResource) 460 }, 461 "SELECT * WHERE ns = ? ORDER BY ns, object_id, relation, subject_ns, subject_object_id, subject_relation", 462 []any{"someresourcetype"}, 463 map[string]int{ 464 "ns": 1, 465 }, 466 }, 467 { 468 "after with just namespace", 469 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 470 return filterer.MustFilterWithRelationshipsFilter( 471 datastore.RelationshipsFilter{ 472 OptionalResourceType: "someresourcetype", 473 }, 474 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 475 }, 476 "SELECT * WHERE ns = ? AND (object_id,relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?)", 477 []any{"someresourcetype", "foo", "viewer", "user", "bar", "..."}, 478 map[string]int{ 479 "ns": 1, 480 }, 481 }, 482 { 483 "after with just relation", 484 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 485 return filterer.MustFilterWithRelationshipsFilter( 486 datastore.RelationshipsFilter{ 487 OptionalResourceRelation: "somerelation", 488 }, 489 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 490 }, 491 "SELECT * WHERE relation = ? AND (ns,object_id,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?)", 492 []any{"somerelation", "someresourcetype", "foo", "user", "bar", "..."}, 493 map[string]int{ 494 "relation": 1, 495 }, 496 }, 497 { 498 "after with namespace and single resource id", 499 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 500 return filterer.MustFilterWithRelationshipsFilter( 501 datastore.RelationshipsFilter{ 502 OptionalResourceType: "someresourcetype", 503 OptionalResourceIds: []string{"one"}, 504 }, 505 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 506 }, 507 "SELECT * WHERE ns = ? AND object_id IN (?) AND (relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?)", 508 []any{"someresourcetype", "one", "viewer", "user", "bar", "..."}, 509 map[string]int{ 510 "ns": 1, 511 "object_id": 1, 512 }, 513 }, 514 { 515 "after with single resource id", 516 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 517 return filterer.MustFilterWithRelationshipsFilter( 518 datastore.RelationshipsFilter{ 519 OptionalResourceIds: []string{"one"}, 520 }, 521 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 522 }, 523 "SELECT * WHERE object_id IN (?) AND (ns,relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?)", 524 []any{"one", "someresourcetype", "viewer", "user", "bar", "..."}, 525 map[string]int{ 526 "object_id": 1, 527 }, 528 }, 529 { 530 "after with namespace and resource ids", 531 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 532 return filterer.MustFilterWithRelationshipsFilter( 533 datastore.RelationshipsFilter{ 534 OptionalResourceType: "someresourcetype", 535 OptionalResourceIds: []string{"one", "two"}, 536 }, 537 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 538 }, 539 "SELECT * WHERE ns = ? AND object_id IN (?, ?) AND (object_id,relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?)", 540 []any{"someresourcetype", "one", "two", "foo", "viewer", "user", "bar", "..."}, 541 map[string]int{ 542 "ns": 1, 543 "object_id": 2, 544 }, 545 }, 546 { 547 "after with namespace and relation", 548 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 549 return filterer.MustFilterWithRelationshipsFilter( 550 datastore.RelationshipsFilter{ 551 OptionalResourceType: "someresourcetype", 552 OptionalResourceRelation: "somerelation", 553 }, 554 ).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 555 }, 556 "SELECT * WHERE ns = ? AND relation = ? AND (object_id,subject_ns,subject_object_id,subject_relation) > (?,?,?,?)", 557 []any{"someresourcetype", "somerelation", "foo", "user", "bar", "..."}, 558 map[string]int{ 559 "ns": 1, 560 "relation": 1, 561 }, 562 }, 563 { 564 "after with subject namespace", 565 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 566 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 567 OptionalSubjectType: "somesubjectype", 568 }).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 569 }, 570 "SELECT * WHERE ((subject_ns = ?)) AND (ns,object_id,relation,subject_object_id,subject_relation) > (?,?,?,?,?)", 571 []any{"somesubjectype", "someresourcetype", "foo", "viewer", "bar", "..."}, 572 map[string]int{ 573 "subject_ns": 1, 574 }, 575 }, 576 { 577 "after with subject namespaces", 578 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 579 // NOTE: this isn't really valid (it'll return no results), but is a good test to ensure 580 // the duplicate subject type results in the subject type being in the ORDER BY. 581 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 582 OptionalSubjectType: "somesubjectype", 583 }).MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 584 OptionalSubjectType: "anothersubjectype", 585 }).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 586 }, 587 "SELECT * WHERE ((subject_ns = ?)) AND ((subject_ns = ?)) AND (ns,object_id,relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?,?)", 588 []any{"somesubjectype", "anothersubjectype", "someresourcetype", "foo", "viewer", "user", "bar", "..."}, 589 map[string]int{ 590 "subject_ns": 2, 591 }, 592 }, 593 { 594 "after with resource ID prefix", 595 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 596 return filterer.MustFilterWithResourceIDPrefix("someprefix").After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.ByResource) 597 }, 598 "SELECT * WHERE object_id LIKE ? AND (ns,object_id,relation,subject_ns,subject_object_id,subject_relation) > (?,?,?,?,?,?)", 599 []any{"someprefix%", "someresourcetype", "foo", "viewer", "user", "bar", "..."}, 600 map[string]int{}, 601 }, 602 { 603 "order by subject", 604 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 605 return filterer.MustFilterWithRelationshipsFilter( 606 datastore.RelationshipsFilter{ 607 OptionalResourceType: "someresourcetype", 608 }, 609 ).TupleOrder(options.BySubject) 610 }, 611 "SELECT * WHERE ns = ? ORDER BY subject_ns, subject_object_id, subject_relation, ns, object_id, relation", 612 []any{"someresourcetype"}, 613 map[string]int{ 614 "ns": 1, 615 }, 616 }, 617 { 618 "order by subject, after with subject namespace", 619 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 620 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 621 OptionalSubjectType: "somesubjectype", 622 }).After(tuple.MustParse("someresourcetype:foo#viewer@user:bar"), options.BySubject) 623 }, 624 "SELECT * WHERE ((subject_ns = ?)) AND (subject_object_id,ns,object_id,relation,subject_relation) > (?,?,?,?,?)", 625 []any{"somesubjectype", "bar", "someresourcetype", "foo", "viewer", "..."}, 626 map[string]int{ 627 "subject_ns": 1, 628 }, 629 }, 630 { 631 "order by subject, after with subject namespace and subject object id", 632 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 633 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 634 OptionalSubjectType: "somesubjectype", 635 OptionalSubjectIds: []string{"foo"}, 636 }).After(tuple.MustParse("someresourcetype:someresource#viewer@user:bar"), options.BySubject) 637 }, 638 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?))) AND (ns,object_id,relation,subject_relation) > (?,?,?,?)", 639 []any{"somesubjectype", "foo", "someresourcetype", "someresource", "viewer", "..."}, 640 map[string]int{"subject_ns": 1, "subject_object_id": 1}, 641 }, 642 { 643 "order by subject, after with subject namespace and multiple subject object IDs", 644 func(filterer SchemaQueryFilterer) SchemaQueryFilterer { 645 return filterer.MustFilterWithSubjectsSelectors(datastore.SubjectsSelector{ 646 OptionalSubjectType: "somesubjectype", 647 OptionalSubjectIds: []string{"foo", "bar"}, 648 }).After(tuple.MustParse("someresourcetype:someresource#viewer@user:next"), options.BySubject) 649 }, 650 "SELECT * WHERE ((subject_ns = ? AND subject_object_id IN (?, ?))) AND (subject_object_id,ns,object_id,relation,subject_relation) > (?,?,?,?,?)", 651 []any{"somesubjectype", "foo", "bar", "next", "someresourcetype", "someresource", "viewer", "..."}, 652 map[string]int{"subject_ns": 1, "subject_object_id": 2}, 653 }, 654 } 655 656 for _, test := range tests { 657 test := test 658 t.Run(test.name, func(t *testing.T) { 659 base := sq.Select("*") 660 schema := NewSchemaInformation( 661 "ns", 662 "object_id", 663 "relation", 664 "subject_ns", 665 "subject_object_id", 666 "subject_relation", 667 "caveat", 668 TupleComparison, 669 ) 670 filterer := NewSchemaQueryFilterer(schema, base) 671 672 ran := test.run(filterer) 673 require.Equal(t, test.expectedColumnCounts, ran.filteringColumnCounts) 674 675 sql, args, err := ran.queryBuilder.ToSql() 676 require.NoError(t, err) 677 require.Equal(t, test.expectedSQL, sql) 678 require.Equal(t, test.expectedArgs, args) 679 }) 680 } 681 }