Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 8fa10de

Browse files
[Backport 5.0] [fix] set unrestricted not working correctly (#49489)
## Description Two problems fixed here: 1. When using unified perms table, the SQL call was removing all rows where repo_id = %d. This is not correct, as we might have other rows in the DB with the same repo_id 2. When sending more items than parameter limit, the query would fail. Fixed by doing requests in chunks. ## Test plan unit tests added/modified <br> Backport 1e088ff from #49421 Co-authored-by: Milan Freml <kopancek@users.noreply.github.com>
1 parent 8bf6373 commit 8fa10de

File tree

2 files changed

+144
-44
lines changed

2 files changed

+144
-44
lines changed

enterprise/internal/database/perms_store.go

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -888,9 +888,21 @@ DO UPDATE SET
888888
unrestricted = %s;
889889
`
890890

891-
q := sqlf.Sprintf(format, pq.Array(ids), unrestricted, unrestricted)
891+
size := 65535 / 2 // 65535 is the max number of parameters in a query, 2 is the number of parameters in each row
892+
chunks, err := collections.SplitIntoChunks(ids, size)
893+
if err != nil {
894+
return err
895+
}
892896

893-
return errors.Wrap(s.Exec(ctx, q), "setting unrestricted flag")
897+
for _, chunk := range chunks {
898+
q := sqlf.Sprintf(format, pq.Array(chunk), unrestricted, unrestricted)
899+
err := s.Exec(ctx, q)
900+
if err != nil {
901+
return errors.Wrap(err, "setting unrestricted flag")
902+
}
903+
}
904+
905+
return nil
894906
}
895907

896908
func (s *permsStore) SetRepoPermissionsUnrestricted(ctx context.Context, ids []int32, unrestricted bool) error {
@@ -915,23 +927,56 @@ func (s *permsStore) SetRepoPermissionsUnrestricted(ctx context.Context, ids []i
915927
return err
916928
}
917929

930+
if !unrestricted {
931+
return txs.unsetRepoPermissionsUnrestricted(ctx, ids)
932+
}
933+
return txs.setRepoPermissionsUnrestricted(ctx, ids)
934+
}
935+
936+
func (s *permsStore) unsetRepoPermissionsUnrestricted(ctx context.Context, ids []int32) error {
937+
format := `DELETE FROM user_repo_permissions WHERE repo_id = ANY(%s) AND user_id IS NULL;`
938+
size := 65535 - 1 // for unsetting unrestricted, we have only 1 parameter per row
939+
chunks, err := collections.SplitIntoChunks(ids, size)
940+
if err != nil {
941+
return err
942+
}
943+
for _, chunk := range chunks {
944+
err := s.Exec(ctx, sqlf.Sprintf(format, pq.Array(chunk)))
945+
if err != nil {
946+
return errors.Wrap(err, "removing unrestricted flag")
947+
}
948+
}
949+
950+
return nil
951+
}
952+
953+
func (s *permsStore) setRepoPermissionsUnrestricted(ctx context.Context, ids []int32) error {
918954
currentTime := time.Now()
919955
values := make([]*sqlf.Query, 0, len(ids))
920956
for _, repoID := range ids {
921957
values = append(values, sqlf.Sprintf("(NULL, %d, %s, %s, %s)", repoID, currentTime, currentTime, authz.SourceAPI))
922958
}
923959

924-
q := sqlf.Sprintf(`
960+
format := `
925961
INSERT INTO user_repo_permissions (user_id, repo_id, created_at, updated_at, source)
926962
VALUES %s
927-
ON CONFLICT DO NOTHING`,
928-
sqlf.Join(values, ","),
929-
)
930-
if !unrestricted {
931-
q = sqlf.Sprintf(`DELETE FROM user_repo_permissions WHERE repo_id = ANY(%s)`, pq.Array(ids))
963+
ON CONFLICT DO NOTHING;
964+
`
965+
966+
size := 65535 / 4 // 65535 is the max number of parameters in a query, 4 is the number of parameters in each row
967+
chunks, err := collections.SplitIntoChunks(values, size)
968+
if err != nil {
969+
return err
970+
}
971+
972+
for _, chunk := range chunks {
973+
err = s.Exec(ctx, sqlf.Sprintf(format, sqlf.Join(chunk, ",")))
974+
if err != nil {
975+
errors.Wrapf(err, "setting repositories as unrestricted %v", chunk)
976+
}
932977
}
933978

934-
return errors.Wrapf(txs.Exec(ctx, q), "setting repositories as unrestricted %v %v", ids, unrestricted)
979+
return nil
935980
}
936981

937982
// upsertRepoPermissionsQuery upserts single row of repository permissions.

enterprise/internal/database/perms_store_test.go

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/sourcegraph/sourcegraph/internal/actor"
2727
"github.com/sourcegraph/sourcegraph/internal/api"
2828
"github.com/sourcegraph/sourcegraph/internal/authz"
29+
"github.com/sourcegraph/sourcegraph/internal/collections"
2930
"github.com/sourcegraph/sourcegraph/internal/conf"
3031
"github.com/sourcegraph/sourcegraph/internal/database"
3132
"github.com/sourcegraph/sourcegraph/internal/database/basestore"
@@ -786,17 +787,23 @@ func setupPermsRelatedEntities(t *testing.T, s *permsStore, permissions []authz.
786787
}
787788

788789
defaultErrMessage := "setup test related entities before actual test"
789-
usersQuery := sqlf.Sprintf(`INSERT INTO users(id, username) VALUES %s ON CONFLICT (id) DO NOTHING`, sqlf.Join(maps.Values(users), ","))
790-
if err := s.execute(context.Background(), usersQuery); err != nil {
791-
t.Fatal(defaultErrMessage, err)
790+
if len(users) > 0 {
791+
usersQuery := sqlf.Sprintf(`INSERT INTO users(id, username) VALUES %s ON CONFLICT (id) DO NOTHING`, sqlf.Join(maps.Values(users), ","))
792+
if err := s.execute(context.Background(), usersQuery); err != nil {
793+
t.Fatal(defaultErrMessage, err)
794+
}
792795
}
793-
externalAccountsQuery := sqlf.Sprintf(`INSERT INTO user_external_accounts(id, user_id, service_type, service_id, account_id, client_id) VALUES %s ON CONFLICT(id) DO NOTHING`, sqlf.Join(maps.Values(externalAccounts), ","))
794-
if err := s.execute(context.Background(), externalAccountsQuery); err != nil {
795-
t.Fatal(defaultErrMessage, err)
796+
if len(externalAccounts) > 0 {
797+
externalAccountsQuery := sqlf.Sprintf(`INSERT INTO user_external_accounts(id, user_id, service_type, service_id, account_id, client_id) VALUES %s ON CONFLICT(id) DO NOTHING`, sqlf.Join(maps.Values(externalAccounts), ","))
798+
if err := s.execute(context.Background(), externalAccountsQuery); err != nil {
799+
t.Fatal(defaultErrMessage, err)
800+
}
796801
}
797-
reposQuery := sqlf.Sprintf(`INSERT INTO repo(id, name) VALUES %s ON CONFLICT(id) DO NOTHING`, sqlf.Join(maps.Values(repos), ","))
798-
if err := s.execute(context.Background(), reposQuery); err != nil {
799-
t.Fatal(defaultErrMessage, err)
802+
if len(repos) > 0 {
803+
reposQuery := sqlf.Sprintf(`INSERT INTO repo(id, name) VALUES %s ON CONFLICT(id) DO NOTHING`, sqlf.Join(maps.Values(repos), ","))
804+
if err := s.execute(context.Background(), reposQuery); err != nil {
805+
t.Fatal(defaultErrMessage, err)
806+
}
800807
}
801808
}
802809

@@ -1197,14 +1204,18 @@ func TestPermsStore_SetRepoPermissionsUnrestricted(t *testing.T) {
11971204

11981205
unrestricted := (len(p) == 1 && p[0].UserID == 0)
11991206

1207+
fmt.Printf("P: %v %v\n", p, unrestricted)
1208+
12001209
if unrestricted != want {
12011210
t.Fatalf("Want %v, got %v for %d", want, unrestricted, id)
12021211
}
12031212
}
12041213

12051214
assertUnrestricted := func(t *testing.T, id int32, want bool) {
12061215
t.Helper()
1216+
fmt.Printf("before legacyUnrestricted\n")
12071217
legacyUnrestricted(t, id, want)
1218+
fmt.Printf("after legacyUnrestricted\n")
12081219

12091220
type unrestrictedResult struct {
12101221
id int32
@@ -1217,8 +1228,11 @@ func TestPermsStore_SetRepoPermissionsUnrestricted(t *testing.T) {
12171228
return r, err
12181229
})
12191230

1231+
fmt.Printf("before scanResults\n")
12201232
q := sqlf.Sprintf("SELECT repo_id, source FROM user_repo_permissions WHERE repo_id = %d AND user_id IS NULL", id)
12211233
results, err := scanResults(s.Handle().QueryContext(ctx, q.Query(sqlf.PostgresBindVar), q.Args()...))
1234+
1235+
fmt.Printf("after scanResults\n")
12221236
if err != nil {
12231237
t.Fatalf("loading user repo permissions for %d: %v", id, err)
12241238
}
@@ -1229,6 +1243,7 @@ func TestPermsStore_SetRepoPermissionsUnrestricted(t *testing.T) {
12291243
t.Fatalf("Want restricted, but found results for %d: %v", id, results)
12301244
}
12311245

1246+
fmt.Printf("Results: %v\n", results)
12321247
if want {
12331248
for _, r := range results {
12341249
require.Equal(t, authz.SourceAPI, r.source)
@@ -1243,30 +1258,46 @@ func TestPermsStore_SetRepoPermissionsUnrestricted(t *testing.T) {
12431258
VALUES (%d, %s, TRUE)`, id, fmt.Sprintf("repo-%d", id)))
12441259
}
12451260

1246-
// Add a couple of repos and a user
1247-
execQuery(t, ctx, s, sqlf.Sprintf(`INSERT INTO users (username) VALUES ('alice')`))
1248-
execQuery(t, ctx, s, sqlf.Sprintf(`INSERT INTO users (username) VALUES ('bob')`))
1249-
for i := 0; i < 2; i++ {
1250-
createRepo(t, i+1)
1251-
rp := &authz.RepoPermissions{
1252-
RepoID: int32(i + 1),
1253-
Perm: authz.Read,
1254-
UserIDs: toMapset(2),
1255-
}
1256-
if _, err := s.SetRepoPermissions(context.Background(), rp); err != nil {
1257-
t.Fatal(err)
1258-
}
1259-
if _, err := s.SetRepoPerms(context.Background(), int32(i+1), []authz.UserIDWithExternalAccountID{{UserID: 2}}, authz.SourceRepoSync); err != nil {
1260-
t.Fatal(err)
1261+
setupData := func() {
1262+
// Add a couple of repos and a user
1263+
execQuery(t, ctx, s, sqlf.Sprintf(`INSERT INTO users (username) VALUES ('alice')`))
1264+
execQuery(t, ctx, s, sqlf.Sprintf(`INSERT INTO users (username) VALUES ('bob')`))
1265+
for i := 0; i < 2; i++ {
1266+
createRepo(t, i+1)
1267+
rp := &authz.RepoPermissions{
1268+
RepoID: int32(i + 1),
1269+
Perm: authz.Read,
1270+
UserIDs: toMapset(2),
1271+
}
1272+
if _, err := s.SetRepoPermissions(context.Background(), rp); err != nil {
1273+
t.Fatal(err)
1274+
}
1275+
if _, err := s.SetRepoPerms(context.Background(), int32(i+1), []authz.UserIDWithExternalAccountID{{UserID: 2}}, authz.SourceRepoSync); err != nil {
1276+
t.Fatal(err)
1277+
}
12611278
}
12621279
}
12631280

1281+
cleanupTables := func() {
1282+
t.Helper()
1283+
1284+
cleanupPermsTables(t, s)
1285+
cleanupReposTable(t, s)
1286+
cleanupUsersTable(t, s)
1287+
}
1288+
12641289
t.Run("Both repos are restricted by default", func(t *testing.T) {
1290+
t.Cleanup(cleanupTables)
1291+
setupData()
1292+
12651293
assertUnrestricted(t, 1, false)
12661294
assertUnrestricted(t, 2, false)
12671295
})
12681296

12691297
t.Run("Set both repos to unrestricted", func(t *testing.T) {
1298+
t.Cleanup(cleanupTables)
1299+
setupData()
1300+
12701301
if err := s.SetRepoPermissionsUnrestricted(ctx, []int32{1, 2}, true); err != nil {
12711302
t.Fatal(err)
12721303
}
@@ -1275,39 +1306,63 @@ func TestPermsStore_SetRepoPermissionsUnrestricted(t *testing.T) {
12751306
})
12761307

12771308
t.Run("Set unrestricted on a repo not in permissions table", func(t *testing.T) {
1309+
t.Cleanup(cleanupTables)
1310+
setupData()
1311+
12781312
createRepo(t, 3)
1279-
if err := s.SetRepoPermissionsUnrestricted(ctx, []int32{3}, true); err != nil {
1280-
t.Fatal(err)
1281-
}
1313+
err := s.SetRepoPermissionsUnrestricted(ctx, []int32{1, 2, 3}, true)
1314+
require.NoError(t, err)
12821315

12831316
assertUnrestricted(t, 1, true)
12841317
assertUnrestricted(t, 2, true)
12851318
assertUnrestricted(t, 3, true)
12861319
})
12871320

12881321
t.Run("Unset restricted on a repo in and not in permissions table", func(t *testing.T) {
1322+
t.Cleanup(cleanupTables)
1323+
setupData()
1324+
1325+
createRepo(t, 3)
12891326
createRepo(t, 4)
1290-
if err := s.SetRepoPermissionsUnrestricted(ctx, []int32{2, 3, 4}, false); err != nil {
1291-
t.Fatal(err)
1292-
}
1327+
1328+
// set permissions on repo 4
1329+
_, err := s.SetRepoPerms(ctx, 4, []authz.UserIDWithExternalAccountID{{UserID: 2}}, authz.SourceRepoSync)
1330+
require.NoError(t, err)
1331+
err = s.SetRepoPermissionsUnrestricted(ctx, []int32{1, 2, 3, 4}, true)
1332+
require.NoError(t, err)
1333+
err = s.SetRepoPermissionsUnrestricted(ctx, []int32{2, 3, 4}, false)
1334+
require.NoError(t, err)
1335+
12931336
assertUnrestricted(t, 1, true)
12941337
assertUnrestricted(t, 2, false)
12951338
assertUnrestricted(t, 3, false)
12961339
assertUnrestricted(t, 4, false)
1340+
checkUserRepoPermissions(t, s, sqlf.Sprintf("repo_id = 4"), []authz.Permission{{UserID: 2, RepoID: 4, Source: authz.SourceRepoSync}})
12971341
})
12981342

1299-
t.Run("Set repos back to restricted again", func(t *testing.T) {
1343+
t.Run("Check parameter limit", func(t *testing.T) {
1344+
t.Cleanup(cleanupTables)
1345+
13001346
// Also checking that more than 65535 IDs can be processed without an error
13011347
var ids [66000]int32
1348+
p := make([]authz.Permission, len(ids))
13021349
for i := range ids {
13031350
ids[i] = int32(i + 1)
1351+
p[i] = authz.Permission{RepoID: ids[i], Source: authz.SourceAPI}
13041352
}
1305-
if err := s.SetRepoPermissionsUnrestricted(ctx, ids[:], false); err != nil {
1353+
1354+
chunks, err := collections.SplitIntoChunks(p, 15000)
1355+
require.NoError(t, err)
1356+
1357+
for _, chunk := range chunks {
1358+
setupPermsRelatedEntities(t, s, chunk)
1359+
}
1360+
if err := s.SetRepoPermissionsUnrestricted(ctx, ids[:], true); err != nil {
13061361
t.Fatal(err)
13071362
}
1308-
assertUnrestricted(t, 1, false)
1309-
assertUnrestricted(t, 500, false)
1310-
assertUnrestricted(t, 66000, false)
1363+
assertUnrestricted(t, 1, true)
1364+
assertUnrestricted(t, 500, true)
1365+
assertUnrestricted(t, 66000, true)
13111366
})
13121367
}
13131368

0 commit comments

Comments
 (0)