Skip to content

Commit 2fbc79d

Browse files
committed
Implement schemas.delete RPC method
1 parent bb111fc commit 2fbc79d

File tree

9 files changed

+104
-108
lines changed

9 files changed

+104
-108
lines changed

conftest.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from db.engine import add_custom_types_to_ischema_names, create_engine as sa_create_engine
1414
from db.types import install
1515
from db.sql import install as sql_install
16-
from db.schemas.operations.drop import drop_schema as drop_sa_schema
16+
from db.schemas.operations.drop import drop_schema_via_name as drop_sa_schema
1717
from db.schemas.operations.create import create_schema as create_sa_schema
1818
from db.schemas.utils import get_schema_oid_from_name, get_schema_name_from_oid
1919

@@ -225,7 +225,7 @@ def _create_schema(schema_name, engine, schema_mustnt_exist=True):
225225
# Handle schemas being renamed during test
226226
schema_name = get_schema_name_from_oid(schema_oid, engine)
227227
if schema_name:
228-
drop_sa_schema(schema_name, engine, cascade=True, if_exists=True)
228+
drop_sa_schema(engine, schema_name, cascade=True)
229229
logger.debug(f'dropping {schema_name}')
230230
except OperationalError as e:
231231
logger.debug(f'ignoring operational error: {e}')

db/schemas/operations/drop.py

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
1-
from db.connection import execute_msar_func_with_engine
1+
from db.connection import execute_msar_func_with_engine, exec_msar_func
22

33

4-
def drop_schema(schema_name, engine, cascade=False, if_exists=False):
4+
def drop_schema_via_name(engine, name, cascade=False):
55
"""
6-
Drop a schema.
6+
Drop a schema by its name.
7+
8+
If no schema exists with the given name, an exception will be raised.
9+
10+
Deprecated:
11+
Use drop_schema_via_oid instead. This function is deprecated because we
12+
are phasing out name-based operations in favor of OID-based operations
13+
and we are phasing out SQLAlchemy in favor of psycopg.
714
815
Args:
9-
schema_name: Name of the schema to drop.
10-
engine: SQLAlchemy engine object for connecting.
11-
cascade: Whether to drop the dependent objects.
12-
if_exists: Whether to ignore an error if the schema doesn't
13-
exist.
16+
engine: SQLAlchemy engine object for connecting. name: Name of the
17+
schema to drop. cascade: Whether to drop the dependent objects.
18+
"""
19+
execute_msar_func_with_engine(engine, 'drop_schema', name, cascade).fetchone()
20+
1421

15-
Returns:
16-
Returns a string giving the command that was run.
22+
def drop_schema_via_oid(conn, id, cascade=False):
23+
"""
24+
Drop a schema by its OID.
25+
26+
If no schema exists with the given oid, an exception will be raised.
27+
28+
Args:
29+
conn: a psycopg connection
30+
id: the OID of the schema to drop.
31+
cascade: Whether to drop the dependent objects.
1732
"""
18-
return execute_msar_func_with_engine(
19-
engine, 'drop_schema', schema_name, cascade, if_exists
20-
).fetchone()[0]
33+
exec_msar_func(conn, 'drop_schema', id, cascade).fetchone()

db/sql/00_msar.sql

+20-39
Original file line numberDiff line numberDiff line change
@@ -957,61 +957,42 @@ $$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT;
957957
----------------------------------------------------------------------------------------------------
958958

959959

960-
-- Drop schema -------------------------------------------------------------------------------------
961-
962960
CREATE OR REPLACE FUNCTION
963-
__msar.drop_schema(sch_name text, cascade_ boolean, if_exists boolean) RETURNS TEXT AS $$/*
964-
Drop a schema, returning the command executed.
961+
msar.drop_schema(sch_name text, cascade_ boolean) RETURNS void AS $$/*
962+
Drop a schema
963+
964+
If no schema exists with the given name, an exception will be raised.
965965
966966
Args:
967-
sch_name: A properly quoted name of the schema to be dropped
968-
cascade_: Whether to drop dependent objects.
969-
if_exists: Whether to ignore an error if the schema doesn't exist
967+
sch_name: An unqoted name of the schema to be dropped
968+
cascade_: When true, dependent objects will be dropped automatically
970969
*/
971970
DECLARE
972-
cmd_template text;
971+
cascade_sql text = CASE cascade_ WHEN TRUE THEN ' CASCADE' ELSE '' END;
973972
BEGIN
974-
IF if_exists
975-
THEN
976-
cmd_template := 'DROP SCHEMA IF EXISTS %s';
977-
ELSE
978-
cmd_template := 'DROP SCHEMA %s';
979-
END IF;
980-
IF cascade_
981-
THEN
982-
cmd_template = cmd_template || ' CASCADE';
983-
END IF;
984-
RETURN __msar.exec_ddl(cmd_template, sch_name);
973+
EXECUTE 'DROP SCHEMA ' || quote_ident(sch_name) || cascade_sql;
985974
END;
986975
$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT;
987976

988977

989978
CREATE OR REPLACE FUNCTION
990-
msar.drop_schema(sch_id oid, cascade_ boolean, if_exists boolean) RETURNS TEXT AS $$/*
991-
Drop a schema, returning the command executed.
992-
993-
Args:
994-
sch_id: The OID of the schema to drop
995-
cascade_: Whether to drop dependent objects.
996-
if_exists: Whether to ignore an error if the schema doesn't exist
997-
*/
998-
BEGIN
999-
RETURN __msar.drop_schema(__msar.get_schema_name(sch_id), cascade_, if_exists);
1000-
END;
1001-
$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT;
979+
msar.drop_schema(sch_id oid, cascade_ boolean) RETURNS void AS $$/*
980+
Drop a schema
1002981
1003-
1004-
CREATE OR REPLACE FUNCTION
1005-
msar.drop_schema(sch_name text, cascade_ boolean, if_exists boolean) RETURNS TEXT AS $$/*
1006-
Drop a schema, returning the command executed.
982+
If no schema exists with the given oid, an exception will be raised.
1007983
1008984
Args:
1009-
sch_name: An unqoted name of the schema to be dropped
1010-
cascade_: Whether to drop dependent objects.
1011-
if_exists: Whether to ignore an error if the schema doesn't exist
985+
sch_id: The OID of the schema to drop
986+
cascade_: When true, dependent objects will be dropped automatically
1012987
*/
988+
DECLARE
989+
sch_name text;
1013990
BEGIN
1014-
RETURN __msar.drop_schema(quote_ident(sch_name), cascade_, if_exists);
991+
SELECT nspname INTO sch_name FROM pg_namespace WHERE oid = sch_id;
992+
IF sch_name IS NULL THEN
993+
RAISE EXCEPTION 'No schema with OID % exists.', sch_id;
994+
END IF;
995+
PERFORM msar.drop_schema(sch_name, cascade_);
1015996
END;
1016997
$$ LANGUAGE plpgsql RETURNS NULL ON NULL INPUT;
1017998

db/sql/test_00_msar.sql

+29-45
Original file line numberDiff line numberDiff line change
@@ -1213,63 +1213,51 @@ END;
12131213
$$ LANGUAGE plpgsql;
12141214

12151215

1216-
CREATE OR REPLACE FUNCTION test_drop_schema_if_exists_false() RETURNS SETOF TEXT AS $$
1216+
CREATE OR REPLACE FUNCTION test_drop_schema_using_name() RETURNS SETOF TEXT AS $$
12171217
BEGIN
12181218
PERFORM __setup_drop_schema();
12191219
PERFORM msar.drop_schema(
12201220
sch_name => 'drop_test_schema',
1221-
cascade_ => false,
1222-
if_exists => false
1221+
cascade_ => false
12231222
);
12241223
RETURN NEXT hasnt_schema('drop_test_schema');
12251224
RETURN NEXT throws_ok(
1226-
format(
1227-
'SELECT msar.drop_schema(
1228-
sch_name => ''%s'',
1229-
cascade_ => false,
1230-
if_exists => false
1231-
);',
1232-
'drop_non_existing_schema'
1233-
),
1234-
'3F000',
1235-
'schema "drop_non_existing_schema" does not exist'
1225+
$d$
1226+
SELECT msar.drop_schema(
1227+
sch_name => 'drop_non_existing_schema',
1228+
cascade_ => false
1229+
)
1230+
$d$,
1231+
'3F000'
12361232
);
12371233
END;
12381234
$$ LANGUAGE plpgsql;
12391235

12401236

1241-
CREATE OR REPLACE FUNCTION test_drop_schema_if_exists_true() RETURNS SETOF TEXT AS $$
1237+
CREATE OR REPLACE FUNCTION test_drop_schema_using_oid() RETURNS SETOF TEXT AS $$
12421238
BEGIN
12431239
PERFORM __setup_drop_schema();
12441240
PERFORM msar.drop_schema(
1245-
sch_name => 'drop_test_schema',
1246-
cascade_ => false,
1247-
if_exists => true
1241+
sch_id => 'drop_test_schema'::regnamespace::oid,
1242+
cascade_ => false
12481243
);
12491244
RETURN NEXT hasnt_schema('drop_test_schema');
1250-
RETURN NEXT lives_ok(
1251-
format(
1252-
'SELECT msar.drop_schema(
1253-
sch_name => ''%s'',
1254-
cascade_ => false,
1255-
if_exists => true
1256-
);',
1257-
'drop_non_existing_schema'
1258-
)
1259-
);
12601245
END;
12611246
$$ LANGUAGE plpgsql;
12621247

12631248

1264-
CREATE OR REPLACE FUNCTION test_drop_schema_using_oid() RETURNS SETOF TEXT AS $$
1249+
CREATE OR REPLACE FUNCTION test_drop_schema_using_invalid_oid() RETURNS SETOF TEXT AS $$
12651250
BEGIN
12661251
PERFORM __setup_drop_schema();
1267-
PERFORM msar.drop_schema(
1268-
sch_id => 'drop_test_schema'::regnamespace::oid,
1269-
cascade_ => false,
1270-
if_exists => false
1252+
RETURN NEXT throws_ok(
1253+
$d$
1254+
SELECT msar.drop_schema(
1255+
sch_id => 0,
1256+
cascade_ => false
1257+
)
1258+
$d$,
1259+
'P0001'
12711260
);
1272-
RETURN NEXT hasnt_schema('drop_test_schema');
12731261
END;
12741262
$$ LANGUAGE plpgsql;
12751263

@@ -1290,8 +1278,7 @@ BEGIN
12901278
PERFORM __setup_schema_with_dependent_obj();
12911279
PERFORM msar.drop_schema(
12921280
sch_name => 'schema1',
1293-
cascade_ => true,
1294-
if_exists => false
1281+
cascade_ => true
12951282
);
12961283
RETURN NEXT hasnt_schema('schema1');
12971284
END;
@@ -1302,16 +1289,13 @@ CREATE OR REPLACE FUNCTION test_drop_schema_restricted() RETURNS SETOF TEXT AS $
13021289
BEGIN
13031290
PERFORM __setup_schema_with_dependent_obj();
13041291
RETURN NEXT throws_ok(
1305-
format(
1306-
'SELECT msar.drop_schema(
1307-
sch_name => ''%s'',
1308-
cascade_ => false,
1309-
if_exists => false
1310-
);',
1311-
'schema1'
1312-
),
1313-
'2BP01',
1314-
'cannot drop schema schema1 because other objects depend on it'
1292+
$d$
1293+
SELECT msar.drop_schema(
1294+
sch_name => 'schema1',
1295+
cascade_ => false
1296+
)
1297+
$d$,
1298+
'2BP01'
13151299
);
13161300
END;
13171301
$$ LANGUAGE plpgsql;

db/tests/schemas/operations/test_drop.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@
33
import db.schemas.operations.drop as sch_drop
44

55

6-
@pytest.mark.parametrize(
7-
"cascade, if_exists", [(True, True), (False, True), (True, False), (False, False)]
8-
)
9-
def test_drop_schema(engine_with_schema, cascade, if_exists):
6+
@pytest.mark.parametrize("cascade", [True, False])
7+
def test_drop_schema(engine_with_schema, cascade):
108
engine = engine_with_schema
119
with patch.object(sch_drop, 'execute_msar_func_with_engine') as mock_exec:
12-
sch_drop.drop_schema('drop_test_schema', engine, cascade, if_exists)
10+
sch_drop.drop_schema_via_name(engine, 'drop_test_schema', cascade)
1311
call_args = mock_exec.call_args_list[0][0]
1412
assert call_args[0] == engine
1513
assert call_args[1] == "drop_schema"
1614
assert call_args[2] == "drop_test_schema"
1715
assert call_args[3] == cascade
18-
assert call_args[4] == if_exists

docs/docs/api/rpc.md

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ To use an RPC function:
5252
options:
5353
members:
5454
- list_
55+
- delete
5556
- SchemaInfo
5657

5758
---

mathesar/models/base.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from db.records.operations.select import get_column_cast_records, get_count, get_record
2929
from db.records.operations.select import get_records
3030
from db.records.operations.update import update_record
31-
from db.schemas.operations.drop import drop_schema
31+
from db.schemas.operations.drop import drop_schema_via_name
3232
from db.schemas.operations.select import get_schema_description
3333
from db.schemas import utils as schema_utils
3434
from db.tables import utils as table_utils
@@ -241,9 +241,8 @@ def update_sa_schema(self, update_params):
241241
return result
242242

243243
def delete_sa_schema(self):
244-
result = drop_schema(self.name, self._sa_engine, cascade=True)
244+
drop_schema_via_name(self._sa_engine, self.name, cascade=True)
245245
reset_reflection(db_name=self.database.name)
246-
return result
247246

248247
def clear_name_cache(self):
249248
cache_key = f"{self.database.name}_schema_name_{self.oid}"

mathesar/rpc/schemas.py

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from db.constants import INTERNAL_SCHEMAS
1010
from db.schemas.operations.select import get_schemas
11+
from db.schemas.operations.drop import drop_schema_via_oid
1112
from mathesar.rpc.exceptions.handlers import handle_rpc_exceptions
1213
from mathesar.rpc.utils import connect
1314

@@ -53,3 +54,18 @@ def list_(*, database_id: int, **kwargs) -> list[SchemaInfo]:
5354
# refactored the models so that each exploration is associated with a schema
5455
# (by oid) in a specific database.
5556
return [{**s, "exploration_count": 0} for s in user_defined_schemas]
57+
58+
59+
@rpc_method(name="schemas.delete")
60+
@http_basic_auth_login_required
61+
@handle_rpc_exceptions
62+
def delete(*, database_id: int, schema_id: int, **kwargs) -> None:
63+
"""
64+
Delete a schema, given its OID.
65+
66+
Args:
67+
database_id: The Django id of the database containing the schema.
68+
schema_id: The OID of the schema to delete.
69+
"""
70+
with connect(database_id, kwargs.get(REQUEST_KEY).user) as conn:
71+
drop_schema_via_oid(conn, schema_id)

mathesar/tests/rpc/test_endpoints.py

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
"schemas.list",
4040
[user_is_authenticated]
4141
),
42+
(
43+
schemas.delete,
44+
"schemas.delete",
45+
[user_is_authenticated]
46+
),
4247
(
4348
tables.list_,
4449
"tables.list",

0 commit comments

Comments
 (0)