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
15 changes: 4 additions & 11 deletions api/src/org/labkey/api/data/FileSqlScriptProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import org.apache.commons.collections4.ComparatorUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -34,6 +33,7 @@
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.util.Path;
import org.labkey.api.util.logging.LogHelper;
import org.labkey.api.vcs.Vcs;
import org.labkey.api.vcs.VcsService;
import org.labkey.api.view.JspTemplate;
Expand All @@ -57,14 +57,9 @@

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
* User: adam
* Date: Sep 18, 2007
* Time: 10:26:29 AM
*/
public class FileSqlScriptProvider implements SqlScriptProvider
{
private static final Logger _log = LogManager.getLogger(FileSqlScriptProvider.class);
private static final Logger _log = LogHelper.getLogger(FileSqlScriptProvider.class, "SQL script issues");

private final Module _module;

Expand Down Expand Up @@ -237,16 +232,14 @@ public String getProviderName()

public void saveScript(DbSchema schema, String description, String contents) throws IOException
{
saveScript(schema, description, contents, false);
saveScript(description, contents, getScriptDirectory(schema.getSqlDialect()), false);
}

public void saveScript(DbSchema schema, String description, String contents, boolean overwrite) throws IOException
public void saveScript(String description, String contents, File scriptsDir, boolean overwrite) throws IOException
{
if (!AppProps.getInstance().isDevMode())
throw new IllegalStateException("Can't save scripts while in production mode");

File scriptsDir = getScriptDirectory(schema.getSqlDialect());

if (!scriptsDir.exists())
throw new IllegalStateException("SQL scripts directory not found");

Expand Down
13 changes: 7 additions & 6 deletions api/src/org/labkey/api/mcp/McpContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
import org.labkey.api.data.Container;
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.ReadPermission;
import org.labkey.api.util.QuietCloser;
import org.labkey.api.view.UnauthorizedException;
import org.labkey.api.writer.ContainerUser;
import org.springframework.ai.chat.model.ToolContext;
import java.util.Map;

/**
* TODO MCP tool calling supports passing along a ToolContext. And most all
* interesting tools probably need a User and Container. This is not all hooked-up
* yet. This is an area for further investiation.
* TODO MCP tool calling supports passing along a ToolContext. And most all
* interesting tools probably need a User and Container. This is not all hooked-up
* yet. This is an area for further investigation.
*/
public class McpContext implements ContainerUser
{
Expand Down Expand Up @@ -67,17 +68,17 @@ public User getUser()
return ret;
}

public static AutoCloseable withContext(ContainerUser ctx)
public static QuietCloser withContext(ContainerUser ctx)
{
return with(new McpContext(ctx));
}

public static AutoCloseable withContext(Container container, User user)
public static QuietCloser withContext(Container container, User user)
{
return with(new McpContext(container, user));
}

private static AutoCloseable with(McpContext ctx)
private static QuietCloser with(McpContext ctx)
{
final McpContext prev = contexts.get();
contexts.set(ctx);
Expand Down
22 changes: 22 additions & 0 deletions api/src/org/labkey/api/util/SqlUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.labkey.api.util;

public class SqlUtil
{
public static String extractSql(String text)
{
if (text.startsWith("SELECT "))
return text;
if (text.startsWith("WITH ") && text.contains("SELECT "))
return text;
if (text.startsWith("PARAMETERS ") && text.contains("SELECT "))
return text;
var sql = text.indexOf("```sql\n");
if (sql >= 0)
{
var end = text.indexOf("```", sql+7);
if (end >= 0)
return text.substring(sql+7,end);
}
return null;
}
}
23 changes: 14 additions & 9 deletions core/src/org/labkey/core/admin/sql/ScriptReorderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package org.labkey.core.admin.sql;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.data.DbSchema;
import org.labkey.api.util.PageFlowUtil;
Expand All @@ -33,18 +33,19 @@
public class ScriptReorderer
{
public static final String COMMENT_REGEX = "((/\\*.+?\\*/)|(^[ \\t]*--.*?$))\\s*"; // Single-line or block comment, followed by white space
private static final String SCHEMA_NAME_REGEX = "((\\w+)\\.)?";

private final List<Map<String, Collection<Statement>>> _statementLists = new LinkedList<>();
private final List<String> _endingStatements = new LinkedList<>();

private Map<String, Collection<Statement>> _currentStatements;

private final DbSchema _schema;
private final String SCHEMA_NAME_REGEX;
private final String TABLE_NAME_REGEX;
private final String TABLE_NAME2_REGEX;
private final String TABLE_NAME_NO_UNDERSCORE_REGEX;
private final String STATEMENT_ENDING_REGEX;
private final String CONSTRAINT_NAME_REGEX;

private String _contents;
private int _row = 0;
Expand All @@ -56,15 +57,19 @@ public class ScriptReorderer

if (_schema.getSqlDialect().isSqlServer())
{
TABLE_NAME_REGEX = "(?<table>" + SCHEMA_NAME_REGEX + "(#?\\w+))"; // # allows for temp table names
SCHEMA_NAME_REGEX = "(((\\w+)|(\\[\\w+\\]))\\.)?"; // optional [] around schema name
TABLE_NAME_REGEX = "(?<table>" + SCHEMA_NAME_REGEX + "((#?\\w+)|(\\[#?\\w+\\])))"; // # allows for temp table names, optional [] around table name
TABLE_NAME_NO_UNDERSCORE_REGEX = null;
STATEMENT_ENDING_REGEX = "((; GO\\s*$)|(;\\s*$)|( GO\\s*$))\\s*"; // Semicolon, GO, or both
CONSTRAINT_NAME_REGEX = "((\\w+)|(\\[\\w+\\]))"; // optional [] around name
}
else
{
SCHEMA_NAME_REGEX = "((\\w+)\\.)?";
TABLE_NAME_REGEX = "(?<table>" + SCHEMA_NAME_REGEX + "(\\w+))";
TABLE_NAME_NO_UNDERSCORE_REGEX = "(?<table>" + SCHEMA_NAME_REGEX + "([[a-zA-Z0-9]]+))";
STATEMENT_ENDING_REGEX = ";(\\s*?)((--)[^\\n]*)?$(\\s*)";
CONSTRAINT_NAME_REGEX = "(\\w+)";
}

TABLE_NAME2_REGEX = TABLE_NAME_REGEX.replace("table", "table2");
Expand All @@ -89,7 +94,7 @@ public String getReorderedScript(boolean isHtml)
patterns.add(new SqlPattern(getRegExWithPrefix("UPDATE (ON )?"), Type.Table, Operation.AlterRows));
patterns.add(new SqlPattern(getRegExWithPrefix("DELETE FROM "), Type.Table, Operation.AlterRows));

patterns.add(new SqlPattern("CREATE (UNIQUE )?((NON)?CLUSTERED )?INDEX (IF NOT EXISTS )?\\w+? ON " + TABLE_NAME_REGEX + ".+?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern("CREATE (UNIQUE )?((NON)?CLUSTERED )?INDEX (IF NOT EXISTS )?\\[?(\\w+?)\\]? ON " + TABLE_NAME_REGEX + ".+?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern(getRegExWithPrefix("CREATE TABLE "), Type.Table, Operation.Other));
patterns.add(new SqlPattern(getRegExWithPrefix("TRUNCATE( TABLE)? "), Type.Table, Operation.Other));

Expand All @@ -104,7 +109,7 @@ public String getReorderedScript(boolean isHtml)

// All other sp_renames
patterns.add(new SqlPattern("(EXEC(UTE)? )?sp_rename (@objname\\s*=\\s*)?'" + TABLE_NAME_REGEX + ".*?'.+?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern("EXEC(UTE)? 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("EXEC(UTE)? core\\.fn_dropifexists\\s*(@objname\\s*=\\s*)?'(?<table>\\w+)'\\s*,\\s*(@objschema\\s*=\\s*)?'(?<schema>\\w+)'\\s*,\\s*(@objtype\\s*=\\s*)?'(TABLE|COLUMN|INDEX|DEFAULT|CONSTRAINT)'.*?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern("EXEC(UTE)? core\\.fn_dropifexists\\s*'(\\w+)'\\s*,\\s*'(?<schema>\\w+)'.*?" + STATEMENT_ENDING_REGEX, Type.NonTable, Operation.Other));

// DROP INDEX on SQL Server follows a similar pattern to CREATE INDEX (above)
Expand Down Expand Up @@ -134,7 +139,7 @@ public String getReorderedScript(boolean isHtml)
patterns.add(new SqlPattern("DO (\\S+) (.+?) END \\1" + STATEMENT_ENDING_REGEX, Type.NonTable, Operation.Other));
}

patterns.add(new SqlPattern("ALTER TABLE " + TABLE_NAME_REGEX + " ADD CONSTRAINT \\w+ FOREIGN KEY \\([^\\)]+?\\) REFERENCES " + TABLE_NAME2_REGEX + " \\([^\\)]+?\\).*?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
patterns.add(new SqlPattern("ALTER TABLE " + TABLE_NAME_REGEX + " (WITH CHECK )?ADD CONSTRAINT " + CONSTRAINT_NAME_REGEX + " FOREIGN KEY\\s*\\([^\\)]+?\\) REFERENCES " + TABLE_NAME2_REGEX + " \\([^\\)]+?\\).*?" + STATEMENT_ENDING_REGEX, Type.Table, Operation.Other));
// Put this at the end to capture all other ALTER TABLE statements (i.e., not RENAMEs)
patterns.add(new SqlPattern(getRegExWithPrefix("ALTER TABLE (IF EXISTS )?(ONLY )?"), Type.Table, Operation.Other));

Expand Down Expand Up @@ -320,7 +325,7 @@ private void addStatement(String tableName, @Nullable String tableName2, String
if (null != tableName2 && index(tableName2) > index(tableName))
tableName = tableName2;

String key = tableName.toLowerCase();
String key = tableName.replace("[", "").replace("]", "").toLowerCase();

Collection<Statement> tableStatements = _currentStatements.computeIfAbsent(key, k -> new LinkedList<>());

Expand Down Expand Up @@ -377,14 +382,14 @@ private void appendStatement(StringBuilder sb, Statement statement)
if (null != tableName)
{
String schemaName = null;
boolean containsTableName = StringUtils.containsIgnoreCase(sql, tableName);
boolean containsTableName = Strings.CI.contains(sql, tableName);

if (!containsTableName && tableName.contains("."))
{
String[] parts = tableName.split("\\.");
tableName = parts[0];
schemaName = parts[1];
containsTableName = StringUtils.containsIgnoreCase(sql, tableName);
containsTableName = Strings.CI.contains(sql, tableName);
}

if (containsTableName)
Expand Down
Loading