Repository: mathesar-foundation/mathesar

Issue:
Duplicate schema creation
**Describe the bug**
We are currently able to create a new schema with an existing schema name, creating duplicates on our mathesar_schema table.

**Expected behavior**
* Schema name should be unique per db in mathesar_schema table.
* If a new schema creation is attempted with the same name as an existing schema, a 400 should be thrown with proper error message.


Discussion:
This can be implemented by making the schema name unique in the Django DB model.

Resolving patch:
diff --git a/mathesar/utils/schemas.py b/mathesar/utils/schemas.py
--- a/mathesar/utils/schemas.py
+++ b/mathesar/utils/schemas.py
@@ -1,5 +1,8 @@
+from rest_framework.exceptions import ValidationError
+
 from db.schemas import (
-    create_schema, get_schema_oid_from_name, get_mathesar_schemas_with_oids
+    create_schema, get_schema_oid_from_name, get_mathesar_schemas,
+    get_mathesar_schemas_with_oids
 )
 from mathesar.database.base import create_mathesar_engine
 from mathesar.models import Schema
@@ -7,6 +10,11 @@
 
 def create_schema_and_object(name, database):
     engine = create_mathesar_engine(database)
+
+    all_schemas = get_mathesar_schemas(engine)
+    if name in all_schemas:
+        raise ValidationError({"name": "Schema name is not unique"})
+
     create_schema(name, engine)
     schema_oid = get_schema_oid_from_name(name, engine)
     schema = Schema.objects.create(oid=schema_oid, database=database)
diff --git a/mathesar/views/api.py b/mathesar/views/api.py
--- a/mathesar/views/api.py
+++ b/mathesar/views/api.py
@@ -42,9 +42,13 @@ def get_queryset(self):
     filterset_class = SchemaFilter
 
     def create(self, request):
-        schema = create_schema_and_object(request.data['name'], request.data['database'])
-        serializer = SchemaSerializer(schema)
-        return Response(serializer.data, status=status.HTTP_201_CREATED)
+        serializer = SchemaSerializer(data=request.data)
+        if serializer.is_valid():
+            schema = create_schema_and_object(request.data['name'], request.data['database'])
+            serializer = SchemaSerializer(schema)
+            return Response(serializer.data, status=status.HTTP_201_CREATED)
+        else:
+            raise ValidationError(serializer.errors)
 
 
 class TableViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin,


Test patch:
diff --git a/mathesar/tests/views/api/test_schema_api.py b/mathesar/tests/views/api/test_schema_api.py
--- a/mathesar/tests/views/api/test_schema_api.py
+++ b/mathesar/tests/views/api/test_schema_api.py
@@ -197,6 +197,17 @@ def test_schema_get_with_reflect_change(client, test_db_name):
     assert orig_id == modified_id
 
 
+def test_schema_create_duplicate(client, test_db_name):
+    data = {
+        'name': 'Test Duplication Schema',
+        'database': test_db_name
+    }
+    response = client.post('/api/v0/schemas/', data=data)
+    assert response.status_code == 201
+    response = client.post('/api/v0/schemas/', data=data)
+    assert response.status_code == 400
+
+
 def test_schema_get_with_reflect_delete(client, test_db_name):
     engine = create_mathesar_engine(test_db_name)
     schema_name = 'a_new_schema'