Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support long database names in PostgreSQL 17.x #6059

Open
wants to merge 2 commits into
base: release24.11-SNAPSHOT
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions api/src/org/labkey/api/data/DbScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,21 @@ public LabKeyDataSource(DataSource ds, String dsName) throws ServletException
_dialect = SqlDialectManager.getFromDriverClassname(_dsName, _driverClassName);
MemTracker.get().remove(_dialect);
_driverClass = initializeDriver();

if (_dialect.isPostgreSQL())
{
// Starting with PostgreSQL 17.x, we can't connect with a database name longer than 63 chars, so we have
// to truncate and replace the URL in the DataSource. Yuck. Issue #51676.
String url = _dsPropertyReader.getUrl();
String name = _dialect.getDatabaseName(url);
if (name.length() > _dialect.getIdentifierMaxLength())
{
String truncated = StringUtils.truncate(name, _dialect.getIdentifierMaxLength());
String newUrl = url.replace(name, truncated);
_dsPropertyReader.setUrl(newUrl);
}
}

_url = _dsPropertyReader.getUrl();

// Validate that data source is using a supported connection pool
Expand Down
6 changes: 2 additions & 4 deletions api/src/org/labkey/api/data/LookupColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
import org.labkey.api.util.Pair;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
* A {@link org.labkey.api.data.ColumnInfo} that is part of a lookup target. This implementation knows
Expand Down Expand Up @@ -133,7 +131,7 @@ public LookupColumn(ColumnInfo foreignKey, ColumnInfo lookupKey, ColumnInfo look
_joinType = joinType;
setSqlTypeName(lookupColumn.getSqlTypeName());
String alias = foreignKey.getAlias() + "$" + lookupColumn.getAlias();
int maxLength = lookupColumn.getSqlDialect().getIdentifierMaxLength();
int maxLength = lookupColumn.getSqlDialect().getIdentifierMaxLength() - 3; // Leave room for "$" and possible suffixes
if (alias.length() > maxLength)
alias = AliasManager.truncate(foreignKey.getAlias(),maxLength/2) + "$" + AliasManager.truncate(lookupColumn.getAlias(),maxLength/2);
setAlias(alias);
Expand Down Expand Up @@ -282,7 +280,7 @@ public static String getTableAlias(String baseAlias, String fkAlias, SqlDialect
{
String alias = baseAlias + (baseAlias.endsWith("$")?"":"$") + fkAlias + "$";

alias = AliasManager.truncate(alias, dialect.getIdentifierMaxLength());
alias = AliasManager.truncate(alias, dialect.getIdentifierMaxLength() - 3 /* leave room for possible suffixes */);
return alias;
}

Expand Down
3 changes: 1 addition & 2 deletions api/src/org/labkey/api/data/dialect/PostgreSql91Dialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,7 @@ public String getCreateDatabaseSql(String dbName)
{
// This will handle both mixed case and special characters on PostgreSQL
String legal = getSelectNameFromMetaDataName(dbName);
return "CREATE DATABASE " + legal + " WITH ENCODING 'UTF8';\n" +
"ALTER DATABASE " + legal + " SET default_with_oids TO OFF";
return "CREATE DATABASE " + legal + " WITH ENCODING 'UTF8'";
}

@Override
Expand Down
22 changes: 19 additions & 3 deletions api/src/org/labkey/api/data/dialect/SqlDialect.java
Original file line number Diff line number Diff line change
Expand Up @@ -958,11 +958,13 @@ public void testKeywordCandidates(SqlExecutor executor) throws IOException, SQLE
throw new IllegalStateException(getProductName() + " reserved words are not all in the keyword candidate list (sqlKeywords.txt). See log for details.");
}

/**
* @return The absolute maximum length for this database. Callers are responsible for truncating generated names,
* handing suffixes, etc.
*/
public int getIdentifierMaxLength()
{
// 63 probably works, but save 2 chars for appending chars to
// create aliases for extra tables used in the lookup (e.g. junctionAlias = getTableAlias() + "_j")
return 61;
return 63;
}

protected SQLFragment getIdentifierTestSql(String candidate)
Expand Down Expand Up @@ -1344,6 +1346,20 @@ public Long getMaxWaitMillis()
return null;
}
}

public void setUrl(String url) throws ServletException
{
String methodName = "setUrl";
try
{
Method method = _ds.getClass().getMethod(methodName, String.class);
method.invoke(_ds, url);
}
catch (Exception e)
{
throw new ServletException("Unable to set DataSource property via " + methodName, e);
}
}
}

// All statement creation passes through these two methods. We return our standard statement wrappers in most
Expand Down
15 changes: 10 additions & 5 deletions api/src/org/labkey/api/query/AliasManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ private static String makeLegalName(String str, @Nullable SqlDialect dialect, bo
ret = "X_";
if (dialect != null)
ret = dialect.makeLegalIdentifierName(ret);
// we use 28 here because Oracle has a limit or 30 characters, and that is likely the shortest restriction
int maxLength = useLegacyMaxLength ? 40 : (dialect == null ? 28 : dialect.getIdentifierMaxLength());
int maxLength = getMaxLength(dialect, useLegacyMaxLength);
if (reserveCount > 0)
maxLength -= reserveCount;
if (maxLength < 5)
Expand All @@ -202,12 +201,18 @@ public static String makeLegalName(FieldKey key, @Nullable SqlDialect dialect, b
sb.append(legalNameFromName(part));
connector = "_";
}
// we use 28 here because Oracle has a limit or 30 characters, and that is likely the shortest restriction
var ret = truncate(sb.toString(), useLegacyMaxLength ? 40 : (dialect == null ? 28 : dialect.getIdentifierMaxLength()));
var ret = truncate(sb.toString(), getMaxLength(dialect, useLegacyMaxLength));
assert isLegalName(ret);
return ret;
}

private static int getMaxLength(@Nullable SqlDialect dialect, boolean useLegacyMaxLength)
{
// we use 28 here because Oracle has a limit of 30 characters, and that is likely the shortest restriction

// But note: Oracle 12c raised the limit to 128 characters, so perhaps increase the fall-back length now?
return useLegacyMaxLength ? 40 : (dialect == null ? 28 : dialect.getIdentifierMaxLength() - 3 /* leave room for possible suffixes */);
}

public static String truncate(String str, int to)
{
Expand Down Expand Up @@ -375,7 +380,7 @@ public boolean isReserved(String word)

assertEquals("select_", m.decideAlias("select"));

assertEquals(m._dialect.getIdentifierMaxLength(), m.decideAlias("This is a very long name for a column, but it happens! go figure.").length());
assertEquals(m._dialect.getIdentifierMaxLength() - 3, m.decideAlias("This is a very long name for a column, but it happens! go figure. " + StringUtils.repeat('x', 100)).length());
}
}
}