Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions api/src/org/labkey/api/data/DbSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -612,15 +612,16 @@ public static class DDLMethodsTestCase extends Assert
public void testDDLMethods() throws Exception
{
DbSchema testSchema = test.getSchema();
DbScope testScope = testSchema.getScope();

// create test objects
//start with cleanup
testSchema.getSqlDialect().dropSchema(testSchema, "testdrop");
testSchema.getSqlDialect().dropSchema(testSchema,"testdrop2");
testSchema.getSqlDialect().dropSchema(testSchema, "testdrop3");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop");
testSchema.getSqlDialect().dropSchema(testScope,"testdrop2");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop3");
testSchema.dropTableIfExists(tempTableName);

SqlExecutor executor = new SqlExecutor(testSchema);
SqlExecutor executor = new SqlExecutor(testScope);

executor.execute("CREATE SCHEMA testdrop");
executor.execute("CREATE SCHEMA testdrop2");
Expand Down Expand Up @@ -654,16 +655,16 @@ public void testDDLMethods() throws Exception
testSchema.dropTableIfExists(tempTableName);
executor.execute(sqlCreateTempTable);

testSchema.getSqlDialect().dropSchema(testSchema, "testdrop");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop");

// these don't exist
testSchema.dropIndexIfExists("T", "T_notexist") ;
testSchema.dropTableIfExists("V1");
testSchema.dropTableIfExists("Tnot");
testSchema.getSqlDialect().dropSchema(testSchema, "testdrop");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop");

testSchema.getSqlDialect().dropSchema(testSchema, "testdrop2");
testSchema.getSqlDialect().dropSchema(testSchema, "testdrop3");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop2");
testSchema.getSqlDialect().dropSchema(testScope, "testdrop3");
}

@After
Expand Down
8 changes: 5 additions & 3 deletions api/src/org/labkey/api/data/dialect/SqlDialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -1588,11 +1588,13 @@ public String getDatabaseName(String url) throws ServletException
/**
* Drop a schema if it exists.
* Throws an exception if schema exists and could not be dropped.
* @param scope DbScope where the schema might exist
* @param schemaName Name of the schema to drop. Casing must match the name in the database exactly. By convention,
* schema names are all lowercase, but quoting supports mixed case and uppercase as well.
*/
public void dropSchema(DbSchema schema, String schemaName)
public void dropSchema(DbScope scope, String schemaName)
{
SQLFragment sql = schema.getSqlDialect().execute(CoreSchema.getInstance().getSchema(), "fn_dropifexists", new SQLFragment("?, ?, ?, ?", "*", schemaName, "SCHEMA", null));
new SqlExecutor(schema).execute(sql);
new SqlExecutor(scope).execute("DROP SCHEMA IF EXISTS " + quoteIdentifier(schemaName)+ " CASCADE");
}

/**
Expand Down
10 changes: 5 additions & 5 deletions api/src/org/labkey/api/module/ModuleLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -1885,11 +1885,11 @@ public void removeModule(ModuleContext context, boolean deleteFiles)
Module m = getModule(moduleName);
SchemaActions schemaActions = getSchemaActions(m, context);

schemaActions.deleteList().forEach(schema -> {
_log.info("Dropping schema \"{}\"", schema);
new SqlExecutor(_core.getSchema()).execute(sql, moduleName, schema + "-%");
scope.getSqlDialect().dropSchema(_core.getSchema(), schema);
scope.invalidateSchema(schema, DbSchemaType.Unknown); // Invalidates all versions of the schema and tables in the non-provisioned caches (e.g., module, bare, fast)
schemaActions.deleteList().forEach(schemaName -> {
_log.info("Dropping schema \"{}\"", schemaName);
new SqlExecutor(_core.getSchema()).execute(sql, moduleName, schemaName + "-%");
scope.getSqlDialect().dropSchema(scope, schemaName);
scope.invalidateSchema(schemaName, DbSchemaType.Unknown); // Invalidates all versions of the schema and tables in the non-provisioned caches (e.g., module, bare, fast)
SchemaNameCache.get().remove(scope); // Invalidates the list of schema names associated with this scope
});

Expand Down
2 changes: 1 addition & 1 deletion core/module.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name: Core
ModuleClass: org.labkey.core.CoreModule
SchemaVersion: 26.007
SchemaVersion: 26.008
Label: Administration and Essential Services
Description: The Core module provides central services such as login, \
security, administration, folder management, user management, \
Expand Down
100 changes: 100 additions & 0 deletions core/resources/schemas/dbscripts/postgresql/core-26.007-26.008.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
-- Remove the ability to drop AGGREGATE, DEFAULT, FUNCTION, PROCEDURE, and SCHEMA. These are not used in current
-- scripts and, if needed in the future, conditional drops are easy to affect in standard PostgreSQL DDL now. This
-- stored function is deprecated and all calls have been removed on develop. However, we're leaving the function in
-- place (supporting only five object types) for now to ease merges forward from older branches that might use it.
CREATE OR REPLACE FUNCTION core.fn_dropifexists(
text,
text,
text,
text)
RETURNS integer AS
$BODY$
/*
Function to safely drop database object types without error if the object does not exist.

Column deletion will cascade to any dependent objects.

Usage:
SELECT core.fn_dropifexists(objname, objschema, objtype, subobjname), where:

objname Required. For TABLE or VIEW, the name of the object to be dropped.
For INDEX, CONSTRAINT, or COLUMN, the name of the associated table.
objschema Required. The name of the object's schema
objtype Required. The type of object being dropped. Valid values are TABLE, VIEW, INDEX, CONSTRAINT, COLUMN.
subobjtype Required. When dropping INDEX, CONSTRAINT, or COLUMN, the name of the object being dropped. Otherwise, NULL.
*/
DECLARE
objname ALIAS FOR $1;
objschema ALIAS FOR $2;
objtype ALIAS FOR $3;
subobjname ALIAS FOR $4;
ret_code INTEGER;
fullname TEXT;
tempschema TEXT;
BEGIN
ret_code := 0;
fullname := (LOWER(objschema) || '.' || LOWER(objname));
IF (UPPER(objtype)) = 'TABLE' THEN
BEGIN
IF EXISTS( SELECT * FROM pg_tables WHERE tablename = LOWER(objname) AND schemaname = LOWER(objschema) )
THEN
EXECUTE 'DROP TABLE ' || fullname;
ret_code = 1;
ELSE
BEGIN
SELECT INTO tempschema schemaname FROM pg_tables WHERE tablename = LOWER(objname) AND schemaname LIKE '%temp%';
IF (tempschema IS NOT NULL)
THEN
EXECUTE 'DROP TABLE ' || tempschema || '.' || objname;
ret_code = 1;
END IF;
END;
END IF;
END;
ELSEIF (UPPER(objtype)) = 'VIEW' THEN
BEGIN
IF EXISTS( SELECT * FROM pg_views WHERE viewname = LOWER(objname) AND schemaname = LOWER(objschema) )
THEN
EXECUTE 'DROP VIEW ' || fullname;
ret_code = 1;
END IF;
END;
ELSEIF (UPPER(objtype)) = 'INDEX' THEN
BEGIN
fullname := LOWER(objschema) || '.' || LOWER(subobjname);
IF EXISTS( SELECT * FROM pg_indexes WHERE tablename = LOWER(objname) AND indexname = LOWER(subobjname) AND schemaname = LOWER(objschema) )
THEN
EXECUTE 'DROP INDEX ' || fullname;
ret_code = 1;
ELSE
IF EXISTS( SELECT * FROM pg_indexes WHERE indexname = LOWER(subobjname) AND schemaname = LOWER(objschema) )
THEN RAISE EXCEPTION 'INDEX - % defined on a different table.', subobjname;
END IF;
END IF;
END;
ELSEIF (UPPER(objtype)) = 'CONSTRAINT' THEN
BEGIN
IF EXISTS( SELECT * FROM pg_class LEFT JOIN pg_constraint ON conrelid = pg_class.oid INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE relkind = 'r' AND contype IS NOT NULL AND nspname = LOWER(objschema) AND relname = LOWER(objname) AND conname = LOWER(subobjname) )
THEN
EXECUTE 'ALTER TABLE ' || fullname || ' DROP CONSTRAINT ' || subobjname;
ret_code = 1;
END IF;
END;
ELSEIF (UPPER(objtype)) = 'COLUMN' THEN
BEGIN
IF EXISTS( SELECT * FROM information_schema.columns WHERE table_name = LOWER(objname) AND table_schema = LOWER(objschema) AND column_name = LOWER(subobjname))
THEN
EXECUTE 'ALTER TABLE ' || fullname || ' DROP COLUMN ' || subobjname || ' CASCADE';
ret_code = 1;
END IF;
END;
ELSE
RAISE EXCEPTION 'Invalid object type - %; Valid values are TABLE, VIEW, INDEX, CONSTRAINT, COLUMN ', objtype;
END IF;

RETURN ret_code;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
2 changes: 0 additions & 2 deletions core/src/org/labkey/core/admin/sql/ScriptReorderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ public String getReorderedScript(boolean isHtml)

patterns.add(new SqlPattern("ALTER TABLE " + TABLE_NAME_REGEX + " RENAME TO " + TABLE_NAME2_REGEX + STATEMENT_ENDING_REGEX, Type.Table, Operation.RenameTable));
patterns.add(new SqlPattern(getRegExWithPrefix("CREATE (TEMPORARY )?TABLE "), Type.Table, Operation.Other));
patterns.add(new SqlPattern("SELECT core\\.fn_dropifexists\\s*\\('(?<table>\\w+)'\\s*,\\s*'(?<schema>\\w+)'\\s*,\\s*'(TABLE|COLUMN|INDEX|DEFAULT|CONSTRAINT)'.+?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern("SELECT core\\.fn_dropifexists\\s*\\('(\\w+)'\\s*,\\s*'(?<schema>\\w+)'.+?" + STATEMENT_ENDING_REGEX, Type.NonTable, Operation.Other));
patterns.add(new SqlPattern("SELECT SETVAL\\('" + TABLE_NAME_NO_UNDERSCORE_REGEX + "_.+?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern(getRegExWithPrefix("CLUSTER \\w+ ON "), Type.Table, Operation.Other)); // e.g. CLUSTER PK_Keyword ON flow.Keyword
patterns.add(new SqlPattern(getRegExWithPrefix("CLUSTER "), Type.Table, Operation.Other));
Expand Down