Import approval right entities from Gerrit 1 into ProjectRight
The ProjectRight entity binds a single group to an ApprovalCategory, and denotes what range of values within the ApprovalCategory can be used by the members of that group. Since Gerrit 1 supported individual users to be added to projects in the approver, verifier or submitter category we create fake groups for these cases and toss the other users into those groups. The submitter category uses a position of -1, indicating that it should not appear in the review status table of a change, but it still is a valid category within the system. This hack is necesary to reuse the same ProjectRight system to handle the submitter status. Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -21,6 +21,7 @@ public class GerritConfig {
|
||||
protected String canonicalUrl;
|
||||
protected GitwebLink gitweb;
|
||||
protected List<ApprovalType> approvalTypes;
|
||||
protected List<ApprovalType> actionTypes;
|
||||
protected int sshdPort;
|
||||
|
||||
public GerritConfig() {
|
||||
@@ -43,8 +44,13 @@ public class GerritConfig {
|
||||
}
|
||||
|
||||
public void add(final ApprovalType t) {
|
||||
initApprovalTypes();
|
||||
approvalTypes.add(t);
|
||||
if (0 <= t.getCategory().getPosition()) {
|
||||
initApprovalTypes();
|
||||
approvalTypes.add(t);
|
||||
} else {
|
||||
initActionTypes();
|
||||
actionTypes.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApprovalType> getApprovalTypes() {
|
||||
@@ -58,6 +64,17 @@ public class GerritConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public List<ApprovalType> getActionTypes() {
|
||||
initActionTypes();
|
||||
return actionTypes;
|
||||
}
|
||||
|
||||
private void initActionTypes() {
|
||||
if (actionTypes == null) {
|
||||
actionTypes = new ArrayList<ApprovalType>();
|
||||
}
|
||||
}
|
||||
|
||||
public int getSshdPort() {
|
||||
return sshdPort;
|
||||
}
|
||||
|
@@ -0,0 +1,94 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.reviewdb;
|
||||
|
||||
import com.google.gwtorm.client.Column;
|
||||
import com.google.gwtorm.client.CompoundKey;
|
||||
|
||||
/** Grant to use an {@link ApprovalCategory} in the scope of a {@link Project}. */
|
||||
public final class ProjectRight {
|
||||
/** Project.Id meaning "any and all projects on this server". */
|
||||
public static final Project.Id WILD_PROJECT = new Project.Id(0);
|
||||
|
||||
public static class Key extends CompoundKey<Project.Id> {
|
||||
@Column
|
||||
protected Project.Id projectId;
|
||||
|
||||
@Column
|
||||
protected ApprovalCategory.Id categoryId;
|
||||
|
||||
@Column
|
||||
protected AccountGroup.Id groupId;
|
||||
|
||||
protected Key() {
|
||||
projectId = new Project.Id();
|
||||
categoryId = new ApprovalCategory.Id();
|
||||
groupId = new AccountGroup.Id();
|
||||
}
|
||||
|
||||
public Key(final Project.Id p, final ApprovalCategory.Id a,
|
||||
final AccountGroup.Id g) {
|
||||
projectId = p;
|
||||
categoryId = a;
|
||||
groupId = g;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project.Id getParentKey() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.google.gwtorm.client.Key<?>[] members() {
|
||||
return new com.google.gwtorm.client.Key<?>[] {categoryId, groupId};
|
||||
}
|
||||
}
|
||||
|
||||
@Column(name = Column.NONE)
|
||||
protected Key key;
|
||||
|
||||
@Column
|
||||
protected short minValue;
|
||||
|
||||
@Column
|
||||
protected short maxValue;
|
||||
|
||||
protected ProjectRight() {
|
||||
}
|
||||
|
||||
public ProjectRight(final ProjectRight.Key k) {
|
||||
key = k;
|
||||
}
|
||||
|
||||
public ProjectRight.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public short getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public void setMinValue(final short m) {
|
||||
minValue = m;
|
||||
}
|
||||
|
||||
public short getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public void setMaxValue(final short m) {
|
||||
maxValue = m;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.client.reviewdb;
|
||||
|
||||
import com.google.gwtorm.client.Access;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.client.PrimaryKey;
|
||||
import com.google.gwtorm.client.Query;
|
||||
import com.google.gwtorm.client.ResultSet;
|
||||
|
||||
public interface ProjectRightAccess extends
|
||||
Access<ProjectRight, ProjectRight.Key> {
|
||||
@PrimaryKey("key")
|
||||
ProjectRight get(ProjectRight.Key key) throws OrmException;
|
||||
|
||||
@Query("WHERE key.projectId = ?")
|
||||
ResultSet<ProjectRight> byProject(Project.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.groupId = ?")
|
||||
ResultSet<ProjectRight> byGroup(AccountGroup.Id id) throws OrmException;
|
||||
|
||||
@Query("WHERE key.categoryId = ?")
|
||||
ResultSet<ProjectRight> byApprovalCategory(ApprovalCategory.Id id)
|
||||
throws OrmException;
|
||||
}
|
@@ -59,6 +59,9 @@ public interface ReviewDb extends Schema {
|
||||
@Relation
|
||||
ProjectAccess projects();
|
||||
|
||||
@Relation
|
||||
ProjectRightAccess projectRights();
|
||||
|
||||
@Relation
|
||||
BranchAccess branches();
|
||||
|
||||
|
287
appjar/src/main/java/com/google/gerrit/pgm/ImportGerrit1.java
Normal file
287
appjar/src/main/java/com/google/gerrit/pgm/ImportGerrit1.java
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.pgm;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroup;
|
||||
import com.google.gerrit.client.reviewdb.AccountGroupMember;
|
||||
import com.google.gerrit.client.reviewdb.ApprovalCategory;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.PatchSet;
|
||||
import com.google.gerrit.client.reviewdb.Project;
|
||||
import com.google.gerrit.client.reviewdb.ProjectRight;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gerrit.client.reviewdb.SystemConfig;
|
||||
import com.google.gerrit.git.InvalidRepositoryException;
|
||||
import com.google.gerrit.git.PatchSetImporter;
|
||||
import com.google.gerrit.server.GerritServer;
|
||||
import com.google.gwtjsonrpc.server.XsrfException;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
import com.google.gwtorm.jdbc.JdbcSchema;
|
||||
|
||||
import org.spearce.jgit.lib.ObjectId;
|
||||
import org.spearce.jgit.lib.ProgressMonitor;
|
||||
import org.spearce.jgit.lib.Repository;
|
||||
import org.spearce.jgit.lib.TextProgressMonitor;
|
||||
import org.spearce.jgit.revwalk.RevCommit;
|
||||
import org.spearce.jgit.revwalk.RevWalk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Imports data from Gerrit 1 into Gerrit 2.
|
||||
* <p>
|
||||
* The tool assumes that <code>devutil/import_gerrit1.sql</code> has already
|
||||
* been executed on this schema. All existing ProjectRight entities are wiped
|
||||
* from the database and generated from scratch.
|
||||
* <p>
|
||||
* The tool requires Gerrit 1 tables (<code>gerrit1.$table_name</code>) through
|
||||
* the same database connection as the ReviewDb schema is on.
|
||||
*/
|
||||
public class ImportGerrit1 {
|
||||
private static GerritServer gs;
|
||||
private static ReviewDb db;
|
||||
private static Connection sql;
|
||||
private static ApprovalCategory verifyCategory;
|
||||
private static ApprovalCategory approveCategory;
|
||||
private static ApprovalCategory submitCategory;
|
||||
|
||||
public static void main(final String[] argv) throws OrmException,
|
||||
XsrfException, SQLException, IOException, InvalidRepositoryException {
|
||||
final ProgressMonitor pm = new TextProgressMonitor();
|
||||
gs = GerritServer.getInstance();
|
||||
db = gs.getDatabase().open();
|
||||
sql = ((JdbcSchema) db).getConnection();
|
||||
try {
|
||||
verifyCategory = db.approvalCategories().byName("Verified");
|
||||
approveCategory = db.approvalCategories().byName("Code Review");
|
||||
submitCategory = db.approvalCategories().byName("Submit");
|
||||
|
||||
final Statement query = sql.createStatement();
|
||||
java.sql.ResultSet srcs;
|
||||
|
||||
// Convert the approval right data from projects.
|
||||
//
|
||||
pm.start(1);
|
||||
pm.beginTask("Import project rights", ProgressMonitor.UNKNOWN);
|
||||
query.executeUpdate("DELETE FROM project_rights");
|
||||
insertApprovalWildCard();
|
||||
srcs =
|
||||
query.executeQuery("SELECT p.project_id, r.ar_key"
|
||||
+ " FROM gerrit1.project_code_reviews r, projects p"
|
||||
+ " WHERE p.project_id = r.project_id");
|
||||
while (srcs.next()) {
|
||||
final Project.Id projectId = new Project.Id(srcs.getInt(1));
|
||||
final String arKey = srcs.getString(2);
|
||||
doImport(projectId, arKey);
|
||||
pm.update(1);
|
||||
}
|
||||
srcs.close();
|
||||
pm.endTask();
|
||||
|
||||
// Rebuild the cached PatchSet information directly from Git.
|
||||
// There's some oddities in the Gerrit 1 data that we got from
|
||||
// Google App Engine's data store; the quickest way to fix it
|
||||
// is to just recache the data from Git.
|
||||
//
|
||||
srcs =
|
||||
query.executeQuery("SELECT change_id,patch_set_id FROM patch_sets");
|
||||
final ArrayList<PatchSet.Id> psToDo = new ArrayList<PatchSet.Id>();
|
||||
while (srcs.next()) {
|
||||
final Change.Id changeId = new Change.Id(srcs.getInt(1));
|
||||
final PatchSet.Id psId = new PatchSet.Id(changeId, srcs.getInt(2));
|
||||
psToDo.add(psId);
|
||||
}
|
||||
query.close();
|
||||
|
||||
pm.start(1);
|
||||
pm.beginTask("Import patch sets", psToDo.size());
|
||||
for (final PatchSet.Id psId : psToDo) {
|
||||
final Change c = db.changes().get(psId.getParentKey());
|
||||
final PatchSet ps = db.patchSets().get(psId);
|
||||
final String projectName = c.getDest().getParentKey().get();
|
||||
final Repository repo = gs.getRepositoryCache().get(projectName);
|
||||
final RevWalk rw = new RevWalk(repo);
|
||||
final RevCommit src =
|
||||
rw.parseCommit(ObjectId.fromString(ps.getRevision().get()));
|
||||
new PatchSetImporter(db, repo, src, ps, false).run();
|
||||
pm.update(1);
|
||||
}
|
||||
pm.endTask();
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertApprovalWildCard() throws OrmException {
|
||||
final ProjectRight.Key key =
|
||||
new ProjectRight.Key(ProjectRight.WILD_PROJECT,
|
||||
approveCategory.getId(), db.systemConfig().get(
|
||||
new SystemConfig.Key()).registeredGroupId);
|
||||
final ProjectRight pr = new ProjectRight(key);
|
||||
pr.setMinValue((short) -1);
|
||||
pr.setMaxValue((short) 1);
|
||||
db.projectRights().insert(Collections.singleton(pr));
|
||||
}
|
||||
|
||||
private static void doImport(final Project.Id projectId, final String arKey)
|
||||
throws OrmException, SQLException {
|
||||
final int arId = findId(arKey);
|
||||
if (arId < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Project proj = db.projects().get(projectId);
|
||||
final Set<AccountGroup.Id> approverg = groups(arId, "approver");
|
||||
final Set<AccountGroup.Id> verifierg = groups(arId, "verifier");
|
||||
final Set<AccountGroup.Id> submitterg = groups(arId, "submitter");
|
||||
|
||||
final Set<Account.Id> approveru = users(arId, "approver");
|
||||
final Set<Account.Id> verifieru = users(arId, "verifier");
|
||||
final Set<Account.Id> submitteru = users(arId, "submitter");
|
||||
|
||||
importCat(proj, "approvers", approveCategory, approverg, approveru);
|
||||
importCat(proj, "verifiers", verifyCategory, verifierg, verifieru);
|
||||
importCat(proj, "submitters", submitCategory, submitterg, submitteru);
|
||||
}
|
||||
|
||||
private static void importCat(final Project proj, final String type,
|
||||
final ApprovalCategory category, final Set<AccountGroup.Id> groups,
|
||||
final Set<Account.Id> users) throws OrmException {
|
||||
final HashSet<Account.Id> needGroup = new HashSet<Account.Id>(users);
|
||||
for (final AccountGroup.Id groupId : groups) {
|
||||
insertRight(proj, category, groupId);
|
||||
for (final Iterator<Account.Id> i = needGroup.iterator(); i.hasNext();) {
|
||||
if (gs.getGroupCache().isInGroup(i.next(), groupId)) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!needGroup.isEmpty()) {
|
||||
final AccountGroup.Id groupId =
|
||||
new AccountGroup.Id(db.nextAccountGroupId());
|
||||
final AccountGroup group =
|
||||
new AccountGroup(new AccountGroup.NameKey(shortName(proj) + "_"
|
||||
+ proj.getId().get() + "-" + type), groupId);
|
||||
group.setOwnerGroupId(proj.getOwnerGroupId());
|
||||
group.setDescription(proj.getName() + " " + type);
|
||||
db.accountGroups().insert(Collections.singleton(group));
|
||||
for (final Account.Id aId : needGroup) {
|
||||
db.accountGroupMembers().insert(
|
||||
Collections.singleton(new AccountGroupMember(
|
||||
new AccountGroupMember.Key(aId, groupId))));
|
||||
}
|
||||
insertRight(proj, category, groupId);
|
||||
}
|
||||
}
|
||||
|
||||
private static void insertRight(final Project proj,
|
||||
final ApprovalCategory category, final AccountGroup.Id groupId)
|
||||
throws OrmException {
|
||||
final ProjectRight.Key key =
|
||||
new ProjectRight.Key(proj.getId(), category.getId(), groupId);
|
||||
final ProjectRight pr = new ProjectRight(key);
|
||||
pr.setMinValue(Short.MIN_VALUE);
|
||||
pr.setMaxValue(Short.MAX_VALUE);
|
||||
db.projectRights().insert(Collections.singleton(pr));
|
||||
}
|
||||
|
||||
private static String shortName(final Project proj) {
|
||||
final String n = proj.getName();
|
||||
final int s = n.lastIndexOf('/');
|
||||
return 0 < s ? n.substring(s + 1) : n;
|
||||
}
|
||||
|
||||
private static Set<AccountGroup.Id> groups(final int arId, final String type)
|
||||
throws SQLException {
|
||||
final PreparedStatement ps =
|
||||
sql.prepareStatement("SELECT g.group_id FROM account_groups g,"
|
||||
+ " gerrit1.approval_right_groups s, gerrit1.account_groups o"
|
||||
+ " WHERE s.ar_id = ? AND s.type = ?"
|
||||
+ " AND o.gae_key = s.group_key AND (g.name = o.name"
|
||||
+ " OR (g.name = 'Administrators' AND o.name = 'admin'))");
|
||||
try {
|
||||
ps.setInt(1, arId);
|
||||
ps.setString(2, type);
|
||||
final java.sql.ResultSet rs = ps.executeQuery();
|
||||
try {
|
||||
final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>();
|
||||
while (rs.next()) {
|
||||
r.add(new AccountGroup.Id(rs.getInt(1)));
|
||||
}
|
||||
return r;
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<Account.Id> users(final int arId, final String type)
|
||||
throws SQLException {
|
||||
final PreparedStatement ps =
|
||||
sql.prepareStatement("SELECT a.account_id FROM accounts a,"
|
||||
+ " gerrit1.approval_right_users s"
|
||||
+ " WHERE s.ar_id = ? AND s.type = ?"
|
||||
+ " AND a.preferred_email = s.email");
|
||||
try {
|
||||
ps.setInt(1, arId);
|
||||
ps.setString(2, type);
|
||||
final java.sql.ResultSet rs = ps.executeQuery();
|
||||
try {
|
||||
final HashSet<Account.Id> r = new HashSet<Account.Id>();
|
||||
while (rs.next()) {
|
||||
r.add(new Account.Id(rs.getInt(1)));
|
||||
}
|
||||
return r;
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static int findId(final String arKey) throws SQLException {
|
||||
final PreparedStatement ps =
|
||||
sql.prepareStatement("SELECT ar_id FROM gerrit1.approval_rights"
|
||||
+ " WHERE gae_key=?");
|
||||
try {
|
||||
ps.setString(1, arKey);
|
||||
final java.sql.ResultSet rs = ps.executeQuery();
|
||||
try {
|
||||
if (rs.next()) {
|
||||
return rs.getInt(1);
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
} finally {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -216,6 +216,20 @@ public class GerritServer {
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
private void initSubmitCategory(final ReviewDb c) throws OrmException {
|
||||
final Transaction txn = c.beginTransaction();
|
||||
final ApprovalCategory cat;
|
||||
final ArrayList<ApprovalCategoryValue> vals;
|
||||
|
||||
cat = new ApprovalCategory(new ApprovalCategory.Id("SUBM"), "Submit");
|
||||
cat.setPosition((short) -1);
|
||||
vals = new ArrayList<ApprovalCategoryValue>();
|
||||
vals.add(value(cat, 1, "Submit"));
|
||||
c.approvalCategories().insert(Collections.singleton(cat), txn);
|
||||
c.approvalCategoryValues().insert(vals);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
private static ApprovalCategoryValue value(final ApprovalCategory cat,
|
||||
final int value, final String name) {
|
||||
return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(),
|
||||
@@ -241,6 +255,7 @@ public class GerritServer {
|
||||
initSystemConfig(c);
|
||||
initVerifiedCategory(c);
|
||||
initCodeReviewCategory(c);
|
||||
initSubmitCategory(c);
|
||||
sConfig = c.systemConfig().get(new SystemConfig.Key());
|
||||
}
|
||||
|
||||
|
@@ -6,20 +6,24 @@
|
||||
-- pg_dump $srcdb | psql $dstdb
|
||||
-- psql -f devutil/import_gerrit1.sql $dstdb
|
||||
--
|
||||
-- Run the ALTER commands displayed in a psql prompt.
|
||||
-- Launch Gerrit 2 once to create the database schema.
|
||||
-- Terminate it once the schema has constructed.
|
||||
--
|
||||
-- Ensure the Git repositories are where git_base_path in the
|
||||
-- system_config table says they should be.
|
||||
--
|
||||
-- Create a GerritServer.properties file for your database.
|
||||
--
|
||||
-- Run this from your shell:
|
||||
-- Ensure the GRANTs at the end of this script were run for
|
||||
-- the user listed in GerritServer.properties.
|
||||
--
|
||||
-- make release
|
||||
-- psql $dstdb -tAc 'select change_id,patch_set_id from patch_sets' \
|
||||
-- | release/bin/gerrit2.sh \
|
||||
-- Execute this from your shell:
|
||||
--
|
||||
-- cd appdist && \
|
||||
-- mvn package && \
|
||||
-- target/gerrit-2.*/gerrit-2.*/bin/gerrit2.sh \
|
||||
-- --config=GerritServer.properties \
|
||||
-- ReimportPatchSets
|
||||
-- ImportGerrit1
|
||||
--
|
||||
|
||||
DELETE FROM accounts;
|
||||
@@ -691,3 +695,11 @@ WHERE
|
||||
SELECT setval('change_id',(SELECT MAX(change_id) FROM changes));
|
||||
-- contributor_agreement_id (above)
|
||||
SELECT setval('project_id',(SELECT MAX(project_id) FROM projects));
|
||||
|
||||
-- Grant access to read tables needed for import
|
||||
--
|
||||
GRANT SELECT ON gerrit1.project_code_reviews TO gerrit2;
|
||||
GRANT SELECT ON gerrit1.approval_right_groups TO gerrit2;
|
||||
GRANT SELECT ON gerrit1.approval_right_users TO gerrit2;
|
||||
GRANT SELECT ON gerrit1.approval_rights TO gerrit2;
|
||||
GRANT SELECT ON gerrit1.account_groups TO gerrit2;
|
||||
|
Reference in New Issue
Block a user