Skip to content

Commit 072336b

Browse files
mkargMalloD12
andauthored
Fixed issue liquibase#7415: MSSQL, SQL Anywhere: Does not quote tablespace name (liquibase#7426)
* Fixed issue liquibase#7415: MSSQL, SQL Anywhere: Does not quote tablespace name * Tests added. --------- Co-authored-by: Daniel Mallorga <dmallorga@liquibase.com>
1 parent c5003d9 commit 072336b

File tree

9 files changed

+183
-5
lines changed

9 files changed

+183
-5
lines changed

liquibase-standard/src/main/java/liquibase/database/Database.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import liquibase.statement.SqlStatement;
2727
import liquibase.structure.DatabaseObject;
2828
import liquibase.structure.core.*;
29-
import liquibase.util.SqlUtil;
3029
import liquibase.util.StringUtil;
3130

3231
import java.io.IOException;
@@ -334,6 +333,10 @@ default void addCompleteSqlToScope(String completeSql) {
334333

335334
String escapeTableName(String catalogName, String schemaName, String tableName);
336335

336+
default String escapeTablespaceName(String tablespaceName) {
337+
return escapeObjectName(tablespaceName, Tablespace.class);
338+
}
339+
337340
String escapeIndexName(String catalogName, String schemaName, String indexName);
338341

339342
String escapeObjectName(String objectName, Class<? extends DatabaseObject> objectType);

liquibase-standard/src/main/java/liquibase/database/core/MSSQLDatabase.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,20 @@ public String escapeTableName(String catalogName, String schemaName, String tabl
315315
return tableName;
316316
}
317317

318+
@Override
319+
public String escapeTablespaceName(String tablespaceName) {
320+
//
321+
// If the tablespace name has a parenthesis in it, the escape logic might mistake it for stored logic
322+
// and not add quotes. We can check for a space and the lack of a quote at the beginning and quote
323+
// it anyway.
324+
//
325+
tablespaceName = escapeObjectName(tablespaceName, Tablespace.class);
326+
if (tablespaceName != null && tablespaceName.contains(" ") && ! tablespaceName.startsWith("\"")) {
327+
tablespaceName = "\"" + tablespaceName + "\"";
328+
}
329+
return tablespaceName;
330+
}
331+
318332
@Override
319333
public boolean supportsTablespaces() {
320334
return true;

liquibase-standard/src/main/java/liquibase/sqlgenerator/core/AddPrimaryKeyGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public Sql[] generateSql(AddPrimaryKeyStatement statement, Database database, Sq
6363

6464
if ((StringUtil.trimToNull(statement.getTablespace()) != null) && database.supportsTablespaces()) {
6565
if (database instanceof MSSQLDatabase) {
66-
sql += " ON "+statement.getTablespace();
66+
sql += " ON " + database.escapeTablespaceName(statement.getTablespace());
6767
} else if ((database instanceof AbstractDb2Database) || (database instanceof SybaseASADatabase)) {
6868
//not supported
6969
} else {

liquibase-standard/src/main/java/liquibase/sqlgenerator/core/AddUniqueConstraintGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public Sql[] generateSql(AddUniqueConstraintStatement statement, Database databa
8181

8282
if ((StringUtil.trimToNull(statement.getTablespace()) != null) && database.supportsTablespaces()) {
8383
if (database instanceof MSSQLDatabase) {
84-
sql += " ON " + statement.getTablespace();
84+
sql += " ON " + database.escapeTablespaceName(statement.getTablespace());
8585
} else if ((database instanceof AbstractDb2Database) || (database instanceof SybaseASADatabase) || (database
8686
instanceof InformixDatabase)) {
8787
//not supported

liquibase-standard/src/main/java/liquibase/sqlgenerator/core/CreateIndexGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public Sql[] generateSql(CreateIndexStatement statement, Database database, SqlG
184184

185185
if ((StringUtil.trimToNull(statement.getTablespace()) != null) && database.supportsTablespaces()) {
186186
if ((database instanceof MSSQLDatabase) || (database instanceof SybaseASADatabase)) {
187-
buffer.append(" ON ").append(statement.getTablespace());
187+
buffer.append(" ON ").append(database.escapeTablespaceName(statement.getTablespace()));
188188
} else if ((database instanceof AbstractDb2Database) || (database instanceof InformixDatabase)) {
189189
buffer.append(" IN ").append(statement.getTablespace());
190190
} else {

liquibase-standard/src/main/java/liquibase/sqlgenerator/core/CreateTableGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ public Sql[] generateSql(CreateTableStatement statement, Database database, SqlG
347347

348348
if ((statement.getTablespace() != null) && database.supportsTablespaces()) {
349349
if ((database instanceof MSSQLDatabase) || (database instanceof SybaseASADatabase)) {
350-
sql += " ON " + statement.getTablespace();
350+
sql += " ON " + database.escapeTablespaceName(statement.getTablespace());
351351
} else if ((database instanceof AbstractDb2Database) || (database instanceof InformixDatabase)) {
352352
sql += " IN " + statement.getTablespace();
353353
} else {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package liquibase.structure.core;
2+
3+
import liquibase.structure.DatabaseObject;
4+
5+
/**
6+
* Marker type, solely used as type input for for escapeObjectName()
7+
*/
8+
public abstract class Tablespace implements DatabaseObject {};

liquibase-standard/src/test/java/liquibase/database/core/MSSQLDatabaseTest.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,100 @@ protected List<SqlVisitor> getExpectedSqlVisitors(Database database) {
161161
}
162162
return super.getExpectedSqlVisitors(database);
163163
}
164+
165+
@Test
166+
public void testEscapeTablespaceName_withoutSpaces() throws DatabaseException {
167+
// Given: A tablespace name without spaces
168+
try (Database database = new MSSQLDatabase()) {
169+
String tablespaceName = "MyTablespace";
170+
171+
// When: Escaping the tablespace name
172+
String result = database.escapeTablespaceName(tablespaceName);
173+
174+
// Then: The tablespace name should be properly escaped without additional quotes
175+
assertEquals("MyTablespace", result);
176+
} catch (final DatabaseException e) {
177+
throw e;
178+
}
179+
}
180+
181+
@Test
182+
public void testEscapeTablespaceName_withSpaces() throws DatabaseException {
183+
// Given: A tablespace name with spaces
184+
try (Database database = new MSSQLDatabase()) {
185+
String tablespaceName = "My Tablespace";
186+
187+
// When: Escaping the tablespace name
188+
String result = database.escapeTablespaceName(tablespaceName);
189+
190+
// Then: The tablespace name should be bracket-escaped (spaces require brackets) and wrapped in double quotes
191+
assertEquals("\"[My Tablespace]\"", result);
192+
} catch (final DatabaseException e) {
193+
throw e;
194+
}
195+
}
196+
197+
@Test
198+
public void testEscapeTablespaceName_null() throws DatabaseException {
199+
// Given: A null tablespace name
200+
try (Database database = new MSSQLDatabase()) {
201+
String tablespaceName = null;
202+
203+
// When: Escaping the tablespace name
204+
String result = database.escapeTablespaceName(tablespaceName);
205+
206+
// Then: The result should be null
207+
assertNull(result);
208+
} catch (final DatabaseException e) {
209+
throw e;
210+
}
211+
}
212+
213+
@Test
214+
public void testEscapeTablespaceName_withParenthesis() throws DatabaseException {
215+
// Given: A tablespace name with parenthesis
216+
try (Database database = new MSSQLDatabase()) {
217+
String tablespaceName = "Tablespace(1)";
218+
219+
// When: Escaping the tablespace name
220+
String result = database.escapeTablespaceName(tablespaceName);
221+
222+
// Then: The tablespace name should not be escaped (treated as function)
223+
assertEquals("Tablespace(1)", result);
224+
} catch (final DatabaseException e) {
225+
throw e;
226+
}
227+
}
228+
229+
@Test
230+
public void testEscapeTablespaceName_withSpecialCharacters() throws DatabaseException {
231+
// Given: A tablespace name with special characters
232+
try (Database database = new MSSQLDatabase()) {
233+
String tablespaceName = "My€Tablespace";
234+
235+
// When: Escaping the tablespace name
236+
String result = database.escapeTablespaceName(tablespaceName);
237+
238+
// Then: The tablespace name should be bracket-escaped
239+
assertEquals("[My€Tablespace]", result);
240+
} catch (final DatabaseException e) {
241+
throw e;
242+
}
243+
}
244+
245+
@Test
246+
public void testEscapeTablespaceName_withSpacesAndSpecialCharacters() throws DatabaseException {
247+
// Given: A tablespace name with both spaces and special characters
248+
try (Database database = new MSSQLDatabase()) {
249+
String tablespaceName = "My €Tablespace";
250+
251+
// When: Escaping the tablespace name
252+
String result = database.escapeTablespaceName(tablespaceName);
253+
254+
// Then: The tablespace name should be bracket-escaped and double-quoted
255+
assertEquals("\"[My €Tablespace]\"", result);
256+
} catch (final DatabaseException e) {
257+
throw e;
258+
}
259+
}
164260
}

liquibase-standard/src/test/java/liquibase/sqlgenerator/core/CreateTableGeneratorTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,4 +1281,61 @@ public void testInvalidColumnDataType() {
12811281
// This sql is intended to be invalid
12821282
assertEquals("CREATE TABLE schema.some_table (col1 CHAR(2, col2 CHAR(2))", generatedSql[0].toSql());
12831283
}
1284+
1285+
@Test
1286+
public void testTablespaceEscaping_mssql_withoutSpaces() {
1287+
// Given: A MSSQL database and a create table statement with a tablespace without spaces
1288+
for (Database database : TestContext.getInstance().getAllDatabases()) {
1289+
if (database instanceof MSSQLDatabase) {
1290+
CreateTableStatement statement = new CreateTableStatement(CATALOG_NAME, SCHEMA_NAME, TABLE_NAME);
1291+
statement.addColumn(COLUMN_NAME1, DataTypeFactory.getInstance().fromDescription("int", database));
1292+
statement.setTablespace("MyTablespace");
1293+
1294+
// When: Generating SQL
1295+
Sql[] generatedSql = this.generatorUnderTest.generateSql(statement, database, null);
1296+
1297+
// Then: The tablespace should be properly escaped
1298+
assertTrue("Expected tablespace without spaces to not need quotes, but got: " + generatedSql[0].toSql(),
1299+
generatedSql[0].toSql().contains("ON MyTablespace"));
1300+
}
1301+
}
1302+
}
1303+
1304+
@Test
1305+
public void testTablespaceEscaping_mssql_withSpaces() {
1306+
// Given: A MSSQL database and a create table statement with a tablespace containing spaces
1307+
for (Database database : TestContext.getInstance().getAllDatabases()) {
1308+
if (database instanceof MSSQLDatabase) {
1309+
CreateTableStatement statement = new CreateTableStatement(CATALOG_NAME, SCHEMA_NAME, TABLE_NAME);
1310+
statement.addColumn(COLUMN_NAME1, DataTypeFactory.getInstance().fromDescription("int", database));
1311+
statement.setTablespace("My Tablespace");
1312+
1313+
// When: Generating SQL
1314+
Sql[] generatedSql = this.generatorUnderTest.generateSql(statement, database, null);
1315+
1316+
// Then: The tablespace should be escaped with brackets and wrapped in double quotes
1317+
assertTrue(
1318+
"Expected tablespace with spaces to be bracket-escaped and double-quoted, but got: " + generatedSql[0].toSql(), generatedSql[0].toSql().contains("ON \"[My Tablespace]\""));
1319+
}
1320+
}
1321+
}
1322+
1323+
@Test
1324+
public void testTablespaceEscaping_mssql_withSpecialCharacters() {
1325+
// Given: A MSSQL database and a create table statement with a tablespace containing special characters
1326+
for (Database database : TestContext.getInstance().getAllDatabases()) {
1327+
if (database instanceof MSSQLDatabase) {
1328+
CreateTableStatement statement = new CreateTableStatement(CATALOG_NAME, SCHEMA_NAME, TABLE_NAME);
1329+
statement.addColumn(COLUMN_NAME1, DataTypeFactory.getInstance().fromDescription("int", database));
1330+
statement.setTablespace("My€Tablespace");
1331+
1332+
// When: Generating SQL
1333+
Sql[] generatedSql = this.generatorUnderTest.generateSql(statement, database, null);
1334+
1335+
// Then: The tablespace should be escaped with brackets
1336+
assertTrue("Expected tablespace with special chars to be bracket-escaped, but got: " + generatedSql[0].toSql(),
1337+
generatedSql[0].toSql().contains("ON [My€Tablespace]"));
1338+
}
1339+
}
1340+
}
12841341
}

0 commit comments

Comments
 (0)