// wrap the error with a runtime exception that we can trap in our code.
if (CoreUtils.isStoredProcThrowableFatalToServer(cause)) {
throw (Error)cause;
}
else {
throw m_compiler.new VoltCompilerException(String.format(
"Cannot load class for procedure: %s",
className), cause);
}
}
ProcedureDescriptor descriptor = m_compiler.new ProcedureDescriptor(
new ArrayList<String>(), Language.JAVA, null, clazz);
// Add roles if specified.
if (statementMatcher.group(1) != null) {
for (String roleName : StringUtils.split(statementMatcher.group(1), ',')) {
// Don't put the same role in the list more than once.
String roleNameFixed = roleName.trim().toLowerCase();
if (!descriptor.m_authGroups.contains(roleNameFixed)) {
descriptor.m_authGroups.add(roleNameFixed);
}
}
}
// track the defined procedure
m_tracker.add(descriptor);
return true;
}
// matches if it is CREATE PROCEDURE <proc-name> [ALLOW <role> ...] AS <select-or-dml-statement>
statementMatcher = procedureSingleStatementPattern.matcher(statement);
if (statementMatcher.matches()) {
String clazz = checkIdentifierStart(statementMatcher.group(1), statement);
String sqlStatement = statementMatcher.group(3) + ";";
ProcedureDescriptor descriptor = m_compiler.new ProcedureDescriptor(
new ArrayList<String>(), clazz, sqlStatement, null, null, false, null, null, null);
// Add roles if specified.
if (statementMatcher.group(2) != null) {
for (String roleName : StringUtils.split(statementMatcher.group(2), ',')) {
descriptor.m_authGroups.add(roleName.trim().toLowerCase());
}
}
m_tracker.add(descriptor);
return true;
}
// matches if it is CREATE PROCEDURE <proc-name> [ALLOW <role> ...] AS
// ### <code-block> ### LANGUAGE <language-name>
statementMatcher = procedureWithScriptPattern.matcher(statement);
if (statementMatcher.matches()) {
String className = checkIdentifierStart(statementMatcher.group(1), statement);
String codeBlock = statementMatcher.group(3);
Language language = Language.valueOf(statementMatcher.group(4).toUpperCase());
Class<?> scriptClass = null;
if (language == Language.GROOVY) {
try {
scriptClass = GroovyCodeBlockCompiler.instance().parseCodeBlock(codeBlock, className);
} catch (CodeBlockCompilerException ex) {
throw m_compiler.new VoltCompilerException(String.format(
"Procedure \"%s\" code block has syntax errors:\n%s",
className, ex.getMessage()));
} catch (Exception ex) {
throw m_compiler.new VoltCompilerException(ex);
}
} else {
throw m_compiler.new VoltCompilerException(String.format(
"Language \"%s\" is not a supported", language.name()));
}
ProcedureDescriptor descriptor = m_compiler.new ProcedureDescriptor(
new ArrayList<String>(), language, codeBlock, scriptClass);
// Add roles if specified.
if (statementMatcher.group(2) != null) {
for (String roleName : StringUtils.split(statementMatcher.group(2), ',')) {
descriptor.m_authGroups.add(roleName.trim().toLowerCase());
}
}
// track the defined procedure
m_tracker.add(descriptor);
return true;
}
// Matches if it is DROP PROCEDURE <proc-name or classname>
statementMatcher = procedureDropPattern.matcher(statement);
if (statementMatcher.matches()) {
String classOrProcName = checkIdentifierStart(statementMatcher.group(1), statement);
m_tracker.removeProcedure(classOrProcName);
return true;
}
// matches if it is the beginning of a partition statement
statementMatcher = prePartitionPattern.matcher(statement);
if (statementMatcher.matches()) {
// either TABLE or PROCEDURE
String partitionee = statementMatcher.group(1).toUpperCase();
if (TABLE.equals(partitionee)) {
// matches if it is PARTITION TABLE <table> ON COLUMN <column>
statementMatcher = partitionTablePattern.matcher(statement);
if ( ! statementMatcher.matches()) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid PARTITION statement: \"%s\", " +
"expected syntax: PARTITION TABLE <table> ON COLUMN <column>",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
// group(1) -> table, group(2) -> column
String tableName = checkIdentifierStart(statementMatcher.group(1), statement);
String columnName = checkIdentifierStart(statementMatcher.group(2), statement);
VoltXMLElement tableXML = m_schema.findChild("table", tableName.toUpperCase());
if (tableXML != null) {
tableXML.attributes.put("partitioncolumn", columnName.toUpperCase());
// Column validity check done by VoltCompiler in post-processing
}
else {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid PARTITION statement: table %s does not exist", tableName));
}
return true;
}
else if (PROCEDURE.equals(partitionee)) {
if (whichProcs != DdlProceduresToLoad.ALL_DDL_PROCEDURES) {
return true;
}
// matches if it is
// PARTITION PROCEDURE <procedure>
// ON TABLE <table> COLUMN <column> [PARAMETER <parameter-index-no>]
statementMatcher = partitionProcedurePattern.matcher(statement);
if ( ! statementMatcher.matches()) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid PARTITION statement: \"%s\", " +
"expected syntax: PARTITION PROCEDURE <procedure> ON "+
"TABLE <table> COLUMN <column> [PARAMETER <parameter-index-no>]",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
// check the table portion of the partition info
String tableName = checkIdentifierStart(statementMatcher.group(2), statement);
// check the column portion of the partition info
String columnName = checkIdentifierStart(statementMatcher.group(3), statement);
// if not specified default parameter index to 0
String parameterNo = statementMatcher.group(4);
if (parameterNo == null) {
parameterNo = "0";
}
String partitionInfo = String.format("%s.%s: %s", tableName, columnName, parameterNo);
// procedureName -> group(1), partitionInfo -> group(2)
m_tracker.addProcedurePartitionInfoTo(
checkIdentifierStart(statementMatcher.group(1), statement),
partitionInfo
);
return true;
}
// can't get here as regex only matches for PROCEDURE or TABLE
}
// matches if it is REPLICATE TABLE <table-name>
statementMatcher = replicatePattern.matcher(statement);
if (statementMatcher.matches()) {
// group(1) -> table
String tableName = checkIdentifierStart(statementMatcher.group(1), statement);
VoltXMLElement tableXML = m_schema.findChild("table", tableName.toUpperCase());
if (tableXML != null) {
tableXML.attributes.remove("partitioncolumn");
}
else {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid REPLICATE statement: table %s does not exist", tableName));
}
return true;
}
// match IMPORT CLASS statements
statementMatcher = importClassPattern.matcher(statement);
if (statementMatcher.matches()) {
if (whichProcs == DdlProceduresToLoad.ALL_DDL_PROCEDURES) {
// Semi-hacky way of determining if we're doing a cluster-internal compilation.
// Command-line compilation will never have an InMemoryJarfile.
if (!(m_classLoader instanceof InMemoryJarfile.JarLoader)) {
// Only process the statement if this is not for the StatementPlanner
String classNameStr = statementMatcher.group(1);
// check that the match pattern is a valid match pattern
checkIdentifierWithWildcard(classNameStr, statement);
ClassNameMatchStatus matchStatus = m_classMatcher.addPattern(classNameStr);
if (matchStatus == ClassNameMatchStatus.NO_EXACT_MATCH) {
throw m_compiler.new VoltCompilerException(String.format(
"IMPORT CLASS not found: '%s'",
classNameStr)); // remove trailing semicolon
}
else if (matchStatus == ClassNameMatchStatus.NO_WILDCARD_MATCH) {
m_compiler.addWarn(String.format(
"IMPORT CLASS no match for wildcarded class: '%s'",
classNameStr), ddlStatement.lineNo);
}
}
else {
m_compiler.addInfo("Internal cluster recompilation ignoring IMPORT CLASS line: " +
statement);
}
// Need to track the IMPORT CLASS lines even on internal compiles so that
// we don't lose them from the DDL source. When the @UAC path goes away,
// we could change this.
m_tracker.addImportLine(statement);
}
return true;
}
// matches if it is CREATE ROLE [WITH <permission> [, <permission> ...]]
// group 1 is role name
// group 2 is comma-separated permission list or null if there is no WITH clause
statementMatcher = createRolePattern.matcher(statement);
if (statementMatcher.matches()) {
String roleName = statementMatcher.group(1).toLowerCase();
CatalogMap<Group> groupMap = db.getGroups();
if (groupMap.get(roleName) != null) {
throw m_compiler.new VoltCompilerException(String.format(
"Role name \"%s\" in CREATE ROLE statement already exists.",
roleName));
}
org.voltdb.catalog.Group catGroup = groupMap.add(roleName);
if (statementMatcher.group(2) != null) {
try {
EnumSet<Permission> permset =
Permission.getPermissionsFromAliases(Arrays.asList(StringUtils.split(statementMatcher.group(2), ',')));
Permission.setPermissionsInGroup(catGroup, permset);
} catch (IllegalArgumentException iaex) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid permission \"%s\" in CREATE ROLE statement: \"%s\", " +
"available permissions: %s", iaex.getMessage(),
statement.substring(0,statement.length()-1), // remove trailing semicolon
Permission.toListString()));
}
}
return true;
}
statementMatcher = exportPattern.matcher(statement);
if (statementMatcher.matches()) {
// check the table portion
String tableName = checkIdentifierStart(statementMatcher.group(1), statement);
VoltXMLElement tableXML = m_schema.findChild("table", tableName.toUpperCase());
if (tableXML != null) {
tableXML.attributes.put("export", "true");
}
else {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid EXPORT statement: table %s was not present in the catalog.",
tableName));
}
return true;
}
/*
* if no correct syntax regex matched above then at this juncture
* the statement is syntax incorrect
*/
if (PARTITION.equals(commandPrefix)) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid PARTITION statement: \"%s\", " +
"expected syntax: \"PARTITION TABLE <table> ON COLUMN <column>\" or " +
"\"PARTITION PROCEDURE <procedure> ON " +
"TABLE <table> COLUMN <column> [PARAMETER <parameter-index-no>]\"",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
if (REPLICATE.equals(commandPrefix)) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid REPLICATE statement: \"%s\", " +
"expected syntax: REPLICATE TABLE <table>",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
if (PROCEDURE.equals(commandPrefix)) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid CREATE PROCEDURE statement: \"%s\", " +
"expected syntax: \"CREATE PROCEDURE [ALLOW <role> [, <role> ...] FROM CLASS <class-name>\" " +
"or: \"CREATE PROCEDURE <name> [ALLOW <role> [, <role> ...] AS <single-select-or-dml-statement>\" " +
"or: \"CREATE PROCEDURE <proc-name> [ALLOW <role> ...] AS ### <code-block> ### LANGUAGE GROOVY\"",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
if (ROLE.equals(commandPrefix)) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid CREATE ROLE statement: \"%s\", " +
"expected syntax: CREATE ROLE <role>",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}
if (EXPORT.equals(commandPrefix)) {
throw m_compiler.new VoltCompilerException(String.format(
"Invalid EXPORT TABLE statement: \"%s\", " +
"expected syntax: EXPORT TABLE <table>",
statement.substring(0,statement.length()-1))); // remove trailing semicolon
}