Skip to content

Commit c79bffd

Browse files
craig[bot]edwardguo-crl
andcommitted
Merge #149869
149869: sql,externalconn: add ALTER EXTERNAL CONNECTION implmentation r=edwardguo-crl a=edwardguo-crl ### Summary Add implementation for ALTER EXTERNAL CONNECTION Fixes #98610 Release note (sql change): Users can now ALTER EXTERNAL CONNECTION to change the external connection URI when granted UPDATE privilege on EXTERNAL CONNECTION. ### Local testing ```shell root@localhost:26257/defaultdb> SHOW EXTERNAL CONNECTIONS; connection_name | connection_uri | connection_type ------------------+-------------------------+------------------ test | nodelocal://1/alterguo | STORAGE test1 | nodelocal://1/alterguo1 | STORAGE (2 rows) Time: 10ms total (execution 10ms / network 0ms) root@localhost:26257/defaultdb> ALTER EXTERNAL CONNECTION IF EXISTS test1 AS 'nodelocal://1/alterguo2'; ALTER EXTERNAL CONNECTION Time: 146ms total (execution 146ms / network 0ms) root@localhost:26257/defaultdb> SHOW EXTERNAL CONNECTIONS; connection_name | connection_uri | connection_type ------------------+-------------------------+------------------ test | nodelocal://1/alterguo | STORAGE test1 | nodelocal://1/alterguo2 | STORAGE (2 rows) Time: 3ms total (execution 3ms / network 1ms) root@localhost:26257/defaultdb> ALTER EXTERNAL CONNECTION IF EXISTS test2 AS 'nodelocal://1/alterguo2'; ALTER EXTERNAL CONNECTION Time: 2ms total (execution 2ms / network 0ms) root@localhost:26257/defaultdb> SHOW EXTERNAL CONNECTIONS; connection_name | connection_uri | connection_type ------------------+-------------------------+------------------ test | nodelocal://1/alterguo | STORAGE test1 | nodelocal://1/alterguo2 | STORAGE (2 rows) Time: 2ms total (execution 2ms / network 0ms) root@localhost:26257/defaultdb> ALTER EXTERNAL CONNECTION test2 AS 'nodelocal://1/alterguo2'; ERROR: external connection with name test2 does not exist ``` Co-authored-by: Edward Guo <[email protected]>
2 parents 026f98a + 90888fd commit c79bffd

File tree

9 files changed

+223
-17
lines changed

9 files changed

+223
-17
lines changed

pkg/cloud/externalconn/record.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ func NewExternalConnection(connDetails connectionpb.ConnectionDetails) ExternalC
7272
return ec
7373
}
7474

75-
// externalConnectionNotFoundError is returned from load when the external
75+
// ExternalConnectionNotFoundError is returned from load when the external
7676
// connection does not exist.
77-
type externalConnectionNotFoundError struct {
77+
type ExternalConnectionNotFoundError struct {
7878
connectionName string
7979
}
8080

8181
// Error makes scheduledJobNotFoundError an error.
82-
func (e *externalConnectionNotFoundError) Error() string {
82+
func (e *ExternalConnectionNotFoundError) Error() string {
8383
return fmt.Sprintf("external connection with name %s does not exist", e.connectionName)
8484
}
8585

@@ -97,10 +97,10 @@ func LoadExternalConnection(
9797
fmt.Sprintf("SELECT * FROM system.external_connections WHERE connection_name = '%s'", name))
9898

9999
if err != nil {
100-
return nil, errors.CombineErrors(err, &externalConnectionNotFoundError{connectionName: name})
100+
return nil, errors.CombineErrors(err, &ExternalConnectionNotFoundError{connectionName: name})
101101
}
102102
if row == nil {
103-
return nil, &externalConnectionNotFoundError{connectionName: name}
103+
return nil, &ExternalConnectionNotFoundError{connectionName: name}
104104
}
105105

106106
ec := NewMutableExternalConnection()
@@ -354,6 +354,26 @@ func (e *MutableExternalConnection) Create(ctx context.Context, txn isql.Txn) er
354354
return e.InitFromDatums(row, retCols)
355355
}
356356

357+
func (e *MutableExternalConnection) Update(ctx context.Context, txn isql.Txn) error {
358+
cols, qargs, err := e.marshalChanges()
359+
if err != nil {
360+
return err
361+
}
362+
363+
var setClauses []string
364+
for i, col := range cols {
365+
setClauses = append(setClauses, fmt.Sprintf("%s = $%d", col, i+1))
366+
}
367+
368+
updateQuery := fmt.Sprintf(`UPDATE system.external_connections
369+
SET %s, updated = now()
370+
WHERE connection_name = '%s'`,
371+
strings.Join(setClauses, ", "), e.ConnectionName())
372+
373+
_, err = txn.ExecEx(ctx, "ExternalConnection.Update", txn.KV(), sessiondata.NodeUserSessionDataOverride, updateQuery, qargs...)
374+
return err
375+
}
376+
357377
// marshalChanges marshals all changes in the in-memory representation and returns
358378
// the names of the columns and marshaled values.
359379
func (e *MutableExternalConnection) marshalChanges() ([]string, []interface{}, error) {

pkg/sql/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ go_library(
1010
"alter_column_type.go",
1111
"alter_database.go",
1212
"alter_default_privileges.go",
13+
"alter_external_connection.go",
1314
"alter_function.go",
1415
"alter_index.go",
1516
"alter_index_visible.go",
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2022 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the CockroachDB Software License
4+
// included in the /LICENSE file.
5+
6+
package sql
7+
8+
import (
9+
"context"
10+
11+
"github.com/cockroachdb/cockroach/pkg/cloud/externalconn"
12+
"github.com/cockroachdb/cockroach/pkg/sql/privilege"
13+
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
14+
"github.com/cockroachdb/cockroach/pkg/sql/syntheticprivilege"
15+
"github.com/cockroachdb/errors"
16+
)
17+
18+
const alterExternalConnectionOp = "ALTER EXTERNAL CONNECTION"
19+
20+
type alterExternalConnectionNode struct {
21+
zeroInputPlanNode
22+
n *tree.AlterExternalConnection
23+
}
24+
25+
func (p *planner) AlterExternalConnection(
26+
ctx context.Context, n *tree.AlterExternalConnection,
27+
) (planNode, error) {
28+
return &alterExternalConnectionNode{n: n}, nil
29+
}
30+
31+
func (alt *alterExternalConnectionNode) startExec(params runParams) error {
32+
return params.p.alterExternalConnection(params, alt.n)
33+
}
34+
35+
func (p *planner) alterExternalConnection(params runParams, n *tree.AlterExternalConnection) error {
36+
txn := p.InternalSQLTxn()
37+
38+
exprEval := p.ExprEvaluator(alterExternalConnectionOp)
39+
name, err := exprEval.String(params.ctx, n.ConnectionLabelSpec.Label)
40+
if err != nil {
41+
return err
42+
}
43+
endpoint, err := exprEval.String(params.ctx, n.As)
44+
if err != nil {
45+
return err
46+
}
47+
48+
ecPrivilege := &syntheticprivilege.ExternalConnectionPrivilege{
49+
ConnectionName: name,
50+
}
51+
if err := p.CheckPrivilege(params.ctx, ecPrivilege, privilege.UPDATE); err != nil {
52+
return err
53+
}
54+
55+
existingConn, err := externalconn.LoadExternalConnection(params.ctx, name, txn)
56+
var notFoundErr *externalconn.ExternalConnectionNotFoundError
57+
if err != nil {
58+
if errors.As(err, &notFoundErr) && n.IfExists {
59+
return nil
60+
}
61+
return err
62+
}
63+
64+
ex, ok := existingConn.(*externalconn.MutableExternalConnection)
65+
if !ok {
66+
return errors.AssertionFailedf("Failed to cast externalConnection (%s) to MutableExtneralConnection type", existingConn.ConnectionName())
67+
}
68+
69+
if err = logAndSanitizeExternalConnectionURI(params.ctx, endpoint); err != nil {
70+
return errors.Wrap(err, "failed to log and santitize External Connection")
71+
}
72+
73+
env := externalconn.MakeExternalConnEnv(
74+
params.ExecCfg().Settings,
75+
&params.ExecCfg().ExternalIODirConfig,
76+
params.ExecCfg().InternalDB,
77+
p.User(),
78+
params.ExecCfg().DistSQLSrv.ExternalStorageFromURI,
79+
false,
80+
false,
81+
&params.ExecCfg().DistSQLSrv.ServerConfig,
82+
)
83+
84+
alterConn, err := externalconn.ExternalConnectionFromURI(
85+
params.ctx, env, endpoint,
86+
)
87+
if err != nil {
88+
return errors.Wrap(err, "failed to construct External Connection details")
89+
}
90+
91+
ex.SetConnectionDetails(*alterConn.ConnectionProto())
92+
93+
if err := ex.Update(params.ctx, txn); err != nil {
94+
return errors.Wrap(err, "failed to alter external connection")
95+
}
96+
97+
return nil
98+
99+
}
100+
101+
func (alt *alterExternalConnectionNode) Close(_ context.Context) {}
102+
103+
func (alt *alterExternalConnectionNode) Next(params runParams) (bool, error) { return false, nil }
104+
105+
func (alt *alterExternalConnectionNode) Values() tree.Datums { return nil }
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# LogicTest: local
2+
3+
statement ok
4+
CREATE EXTERNAL CONNECTION conn_1 AS 'nodelocal://1/conn_1';
5+
CREATE EXTERNAL CONNECTION conn_2 AS 'nodelocal://1/conn_2';
6+
7+
query TTT colnames,rowsort
8+
SHOW EXTERNAL CONNECTIONS
9+
----
10+
connection_name connection_uri connection_type
11+
conn_1 nodelocal://1/conn_1 STORAGE
12+
conn_2 nodelocal://1/conn_2 STORAGE
13+
14+
15+
user testuser
16+
17+
statement error pq: user testuser does not have UPDATE privilege on external_connection conn_1
18+
ALTER EXTERNAL CONNECTION conn_1 AS 'nodelocal://1/conn_update';
19+
20+
user root
21+
22+
statement ok
23+
GRANT UPDATE ON EXTERNAL CONNECTION conn_1 TO testuser;
24+
GRANT USAGE ON EXTERNAL CONNECTION conn_1 TO testuser;
25+
26+
user testuser
27+
28+
statement ok
29+
ALTER EXTERNAL CONNECTION conn_1 AS 'nodelocal://1/conn_update_with_privilege';
30+
31+
query TTT colnames
32+
SHOW EXTERNAL CONNECTION conn_1
33+
----
34+
connection_name connection_uri connection_type
35+
conn_1 nodelocal://1/conn_update_with_privilege STORAGE
36+
37+
38+
39+
statement error pq: user testuser does not have UPDATE privilege on external_connection conn_2
40+
ALTER EXTERNAL CONNECTION conn_2 AS 'nodelocal://1/conn_update';
41+
42+
user root
43+
44+
statement ok
45+
GRANT UPDATE ON EXTERNAL CONNECTION conn_2 TO testuser;
46+
GRANT USAGE ON EXTERNAL CONNECTION conn_2 TO testuser;
47+
48+
user testuser
49+
50+
statement error pq: user testuser does not have UPDATE privilege on external_connection conn_not_exist
51+
ALTER EXTERNAL CONNECTION IF EXISTS conn_not_exist AS 'nodelocal://1/not_exist';
52+
53+
statement ok
54+
ALTER EXTERNAL CONNECTION conn_2 AS 'nodelocal://1/connection_2_alter';
55+
56+
query TTT colnames
57+
SHOW EXTERNAL CONNECTION conn_2
58+
----
59+
connection_name connection_uri connection_type
60+
conn_2 nodelocal://1/connection_2_alter STORAGE

pkg/sql/logictest/testdata/logic_test/external_connection_privileges

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,57 +15,61 @@ GRANT USAGE ON EXTERNAL CONNECTION foo TO testuser
1515
statement error pq: failed to resolve External Connection: external connection with name foo does not exist
1616
GRANT DROP ON EXTERNAL CONNECTION foo TO testuser
1717

18+
19+
statement error pq: failed to resolve External Connection: external connection with name foo does not exist
20+
GRANT UPDATE ON EXTERNAL CONNECTION foo TO testuser
21+
1822
statement ok
1923
CREATE EXTERNAL CONNECTION foo AS 'nodelocal://1/foo'
2024

2125
statement ok
22-
GRANT USAGE,DROP ON EXTERNAL CONNECTION foo TO testuser
26+
GRANT USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo TO testuser
2327

2428
query TTTT
2529
SELECT username, path, privileges, grant_options FROM system.privileges ORDER by username
2630
----
2731
root /externalconn/foo {ALL} {}
28-
testuser /externalconn/foo {DROP,USAGE} {}
32+
testuser /externalconn/foo {DROP,UPDATE,USAGE} {}
2933

3034
statement ok
31-
REVOKE USAGE,DROP ON EXTERNAL CONNECTION foo FROM testuser
35+
REVOKE USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo FROM testuser
3236

3337
query TTTT
3438
SELECT username, path, privileges, grant_options FROM system.privileges ORDER by username
3539
----
3640
root /externalconn/foo {ALL} {}
3741

3842
statement ok
39-
GRANT USAGE,DROP ON EXTERNAL CONNECTION foo TO testuser
43+
GRANT USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo TO testuser
4044

4145
statement ok
4246
CREATE USER bar
4347

4448
# Attempt to grant usage as testuser, this should fail since we did not specify WITH GRANT OPTION
4549
user testuser
4650

47-
statement error pq: user testuser missing WITH GRANT OPTION privilege on one or more of USAGE, DROP
48-
GRANT USAGE,DROP ON EXTERNAL CONNECTION foo TO bar
51+
statement error pq: user testuser missing WITH GRANT OPTION privilege on one or more of USAGE, DROP, UPDATE
52+
GRANT USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo TO bar
4953

5054
user root
5155

5256
statement ok
53-
GRANT USAGE,DROP ON EXTERNAL CONNECTION foo TO testuser WITH GRANT OPTION
57+
GRANT USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo TO testuser WITH GRANT OPTION
5458

5559
# Attempt to grant usage as testuser, this should succeed since we did specify WITH GRANT OPTION
5660
user testuser
5761

5862
statement ok
59-
GRANT USAGE,DROP ON EXTERNAL CONNECTION foo TO bar
63+
GRANT USAGE,DROP,UPDATE ON EXTERNAL CONNECTION foo TO bar
6064

6165
user root
6266

6367
query TTTT
6468
SELECT username, path, privileges, grant_options FROM system.privileges ORDER BY username
6569
----
66-
bar /externalconn/foo {DROP,USAGE} {}
70+
bar /externalconn/foo {DROP,UPDATE,USAGE} {}
6771
root /externalconn/foo {ALL} {}
68-
testuser /externalconn/foo {DROP,USAGE} {DROP,USAGE}
72+
testuser /externalconn/foo {DROP,UPDATE,USAGE} {DROP,UPDATE,USAGE}
6973

7074
# Invalid grants on external connections.
7175

@@ -79,25 +83,30 @@ statement ok
7983
CREATE ROLE testuser2
8084

8185
statement ok
82-
GRANT DROP, USAGE ON EXTERNAL CONNECTION foo TO testuser2 WITH GRANT OPTION
86+
GRANT DROP,UPDATE,USAGE ON EXTERNAL CONNECTION foo TO testuser2 WITH GRANT OPTION
8387

8488
query TTTB colnames,rowsort
8589
SHOW GRANTS ON EXTERNAL CONNECTION foo
8690
----
8791
connection_name grantee privilege_type is_grantable
8892
foo bar DROP false
93+
foo bar UPDATE false
8994
foo bar USAGE false
9095
foo root ALL false
9196
foo testuser DROP true
97+
foo testuser UPDATE true
9298
foo testuser USAGE true
9399
foo testuser2 DROP true
100+
foo testuser2 UPDATE true
94101
foo testuser2 USAGE true
95102

96103
query TTTB colnames,rowsort
97104
SHOW GRANTS ON EXTERNAL CONNECTION foo FOR testuser, testuser2
98105
----
99106
connection_name grantee privilege_type is_grantable
100107
foo testuser DROP true
108+
foo testuser UPDATE true
101109
foo testuser USAGE true
102110
foo testuser2 DROP true
111+
foo testuser2 UPDATE true
103112
foo testuser2 USAGE true

pkg/sql/logictest/tests/local/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/sql/opaque.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ func planOpaque(ctx context.Context, p *planner, stmt tree.Statement) (planNode,
100100
return p.AlterDatabaseSetZoneConfigExtension(ctx, n)
101101
case *tree.AlterDefaultPrivileges:
102102
return p.alterDefaultPrivileges(ctx, n)
103+
case *tree.AlterExternalConnection:
104+
return p.AlterExternalConnection(ctx, n)
103105
case *tree.AlterFunctionOptions:
104106
return p.AlterFunctionOptions(ctx, n)
105107
case *tree.AlterRoutineRename:
@@ -371,6 +373,7 @@ func init() {
371373
&tree.CreateDatabase{},
372374
&tree.CreateExtension{},
373375
&tree.CreateExternalConnection{},
376+
&tree.AlterExternalConnection{},
374377
&tree.CreateTenant{},
375378
&tree.CreateIndex{},
376379
&tree.CreatePolicy{},

pkg/sql/plan_names.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ var planNodeNames = map[reflect.Type]string{
5858
reflect.TypeOf(&alterDatabaseDropSecondaryRegion{}): "alter database secondary region",
5959
reflect.TypeOf(&alterDatabaseSetZoneConfigExtensionNode{}): "alter database configure zone extension",
6060
reflect.TypeOf(&alterDefaultPrivilegesNode{}): "alter default privileges",
61+
reflect.TypeOf(&alterExternalConnectionNode{}): "alter external connection",
6162
reflect.TypeOf(&alterFunctionOptionsNode{}): "alter function",
6263
reflect.TypeOf(&alterFunctionRenameNode{}): "alter function rename",
6364
reflect.TypeOf(&alterFunctionSetOwnerNode{}): "alter function owner",

pkg/sql/privilege/privilege.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ var (
103103
REPAIRCLUSTER, BYPASSRLS, REPLICATIONDEST, REPLICATIONSOURCE,
104104
}
105105
VirtualTablePrivileges = List{ALL, SELECT}
106-
ExternalConnectionPrivileges = List{ALL, USAGE, DROP}
106+
ExternalConnectionPrivileges = List{ALL, USAGE, DROP, UPDATE}
107107
)
108108

109109
// Mask returns the bitmask for a given privilege.

0 commit comments

Comments
 (0)