Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public ValidationResult visit(EnumValidationContext enumValidationContext)
List<String> validEnumValues = enumValidationContext.validEnumValues;
if (value instanceof List)
{
throw new IllegalArgumentException("Collection of Enums (" + value + ") is not supported as service parameter");
return ((List<?>) value).stream().allMatch(item -> validEnumValues.contains(item.toString())) ? ValidationResult.successValidationResult() : ValidationResult.errorValidationResult("Invalid enum value " + value + " for " + ((PackageableType) var.genericType.rawType).fullPath + ", valid enum values: " + validEnumValues);
}
return (validEnumValues.contains(value.toString())) ? ValidationResult.successValidationResult() : ValidationResult.errorValidationResult("Invalid enum value " + value + " for " + ((PackageableType) var.genericType.rawType).fullPath + ", valid enum values: " + validEnumValues);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ function meta::pure::executionPlan::generatePlatformCode(plan:ExecutionPlan[1],
function <<access.private>> meta::pure::executionPlan::addFunctionParametersValidationNode(node:ExecutionNode[1], f:FunctionDefinition<Any>[1], planVarPlaceHolders:PlanVarPlaceHolder[*], routedFunction:FunctionDefinition<Any>[1]):ExecutionNode[1]
{
let collectionEnumParams = $planVarPlaceHolders->filter(p | $p.genericType.rawType->toOne()->instanceOf(Enumeration) && $p.multiplicity == ZeroMany);
assert($collectionEnumParams->size() == 0, |'Collection of Enums is not supported as service parameter ' + $collectionEnumParams->map(p | $p.name)->makeString('[',', ',']'));
let parameterValidationContexts = $planVarPlaceHolders->map(p |
if(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,47 @@ public void testEnumPlaceHolderIfOp() throws Exception
Assert.assertEquals("select case when 'EMEA' = 'EMEA' then \"root\".COUNTY else \"root\".COUNTY end as \"county\", \"root\".FIPS as \"fips\" from testTable as \"root\"", result4);
}


@Test
public void testTemplatesEnumOne()
{
String testQuery = "select \"root\".id as \"Id\", \"root\".myEnum as \"EnumCol\" from ItemTable as \"root\" where (${optionalVarPlaceHolderOperationSelector(oneEnum, equalEnumOperationSelector(enumMap_mappings_MyMapping_MyEnumMapping(oneEnum), '\"root\".myEnum in (${enumMap_mappings_MyMapping_MyEnumMapping(oneEnum)})', '\"root\".myEnum = ${enumMap_mappings_MyMapping_MyEnumMapping(oneEnum)}'), '0 = 1')})";
String enumFullPath = "mappings_MyMapping_MyEnumMapping";
String enumHashMap = "\"VALUE_1\":\"'VAL1A', 'VAL1B', 'VAL1C'\", \"VALUE_2\":\"'VAL2'\", \"VALUE_3\":\"'VAL3'\", \"VALUE_4\":\"'VAL_FILL'\", \"VALUE_5\":\"'VAL_FILL'\"";

Map vars = new HashMap<>();
vars.put("oneEnum", "VALUE_1");

String templates = renderCollectionWithDefaultTemplate() + collectionSizeTemplate() + optionalVarPlaceHolderOperationSelectorTemplate() + varPlaceHolderToStringTemplate() + equalEnumOperationSelector();
String result = RelationalExecutor.process(testQuery, vars, templates + enumSupportFunction(enumFullPath, enumHashMap));
Assert.assertEquals("select \"root\".id as \"Id\", \"root\".myEnum as \"EnumCol\" from ItemTable as \"root\" where (\"root\".myEnum in ('VAL1A', 'VAL1B', 'VAL1C'))", result.trim());

// Test that old enum map function (which does not handle sequences) gives the same output - backwards compatibility.
String oldEnumMapSupportFunction = "<#function enumMap_mappings_MyMapping_MyEnumMapping inputVal> <#assign enumMap = { \"VALUE_1\":\"'VAL1A', 'VAL1B', 'VAL1C'\", \"VALUE_2\":\"'VAL2'\", \"VALUE_3\":\"'VAL3'\", \"VALUE_4\":\"'VAL_FILL'\", \"VALUE_5\":\"'VAL_FILL'\" }> <#if inputVal?has_content> <#return enumMap[inputVal]> <#else> <#return \"\"> </#if> </#function>";
String oldResult = RelationalExecutor.process(testQuery, vars, templates + oldEnumMapSupportFunction);
Assert.assertEquals(result.trim(), oldResult.trim());
}

@Test
public void testTemplatesEnumList()
{
String testQuery = "select \"root\".id as \"Id\", \"root\".myEnum as \"EnumCol\" from ItemTable as \"root\" where (${optionalVarPlaceHolderOperationSelector(enumList![], equalEnumOperationSelector(enumMap_mappings_MyMapping_MyEnumMapping(enumList![]), '\"root\".myEnum in (${enumMap_mappings_MyMapping_MyEnumMapping(enumList![])})', '\"root\".myEnum = ${enumMap_mappings_MyMapping_MyEnumMapping(enumList![])}'), '0 = 1')})";
String enumFullPath = "mappings_MyMapping_MyEnumMapping";
String enumHashMap = "\"VALUE_1\":\"'VAL1A', 'VAL1B', 'VAL1C'\", \"VALUE_2\":\"'VAL2'\", \"VALUE_3\":\"'VAL3'\", \"VALUE_4\":\"'VAL_FILL'\", \"VALUE_5\":\"'VAL_FILL'\"";

ArrayList<String> enumList = new ArrayList<>();
enumList.add("VALUE_1");
enumList.add("VALUE_2");
enumList.add("VALUE_3");
enumList.add("VALUE_4");
enumList.add("VALUE_5");
Map vars = new HashMap<>();
vars.put("enumList", enumList);

String result = RelationalExecutor.process(testQuery, vars, renderCollectionWithDefaultTemplate() + collectionSizeTemplate() + optionalVarPlaceHolderOperationSelectorTemplate() + varPlaceHolderToStringTemplate() + equalEnumOperationSelector() + enumSupportFunction(enumFullPath, enumHashMap));
Assert.assertEquals("select \"root\".id as \"Id\", \"root\".myEnum as \"EnumCol\" from ItemTable as \"root\" where (\"root\".myEnum in ('VAL1A', 'VAL1B', 'VAL1C', 'VAL2', 'VAL3', 'VAL_FILL', 'VAL_FILL'))", result.trim());
}

@Test
public void testTimeZoneOffsetAndCode()
{
Expand Down Expand Up @@ -328,6 +369,47 @@ public static String collectionSizeTemplate()
"</#function>";
}

public static String optionalVarPlaceHolderOperationSelectorTemplate()
{
return "<#function optionalVarPlaceHolderOperationSelector optionalParameter trueClause falseClause>" +
"<#if optionalParameter?has_content || optionalParameter?is_string>" +
"<#return trueClause>" +
"<#else>" +
"<#return falseClause></#if>" +
"</#function>";
}

public static String varPlaceHolderToStringTemplate()
{
return "<#function varPlaceHolderToString optionalParameter prefix suffix replacementMap defaultValue>" +
"<#if optionalParameter?is_enumerable && !optionalParameter?has_content>" +
"<#return defaultValue>" +
"<#else>" +
"<#assign newParam = optionalParameter>" +
"<#list replacementMap as oldValue, newValue>" +
"<#assign newParam = newParam?replace(oldValue, newValue)>" +
"</#list>" +
"<#return prefix + newParam + suffix></#if>" +
"</#function>";
}

public static String enumSupportFunction(String enumFullPath, String enumHashMap)
{
return "<#function enumMap_" + enumFullPath + " inputVal>" +
"<#assign enumMap = {" + enumHashMap + "}>" +
"<#if inputVal?has_content>" +
"<#if inputVal?is_sequence>" +
"<#assign results = []>" +
"<#list inputVal as item>" +
"<#assign results += [enumMap[item]!\"\"]></#list>" +
"<#return results?join(\", \")>" +
"<#else>" +
"<#return enumMap[inputVal]!\"\"></#if>" +
"<#else>" +
"<#return \"\"> </#if>" +
" </#function>";
}

@Test
public void roleTransform() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ public class TestPlanExecutionForIn extends AlloyTestServer
"{\n" +
" name : String[1];\n" +
" street : String[0..1];\n" +
"}\n" +
"Class test::Location\n" +
"{\n" +
" id: String[1];\n" +
" city: test::LocationEnum[1];\n" +
"}\n" +
"Enum test::LocationEnum\n" +
"{\n" +
" AMERICAS,\n" +
" EMEA,\n" +
" ASIA_PACIFIC\n" +
"}\n";

public static final String STORE_MODEL = "###Relational\n" +
Expand All @@ -70,6 +81,10 @@ public class TestPlanExecutionForIn extends AlloyTestServer
" name VARCHAR(255) PRIMARY KEY,\n" +
" address_name VARCHAR(255) \n" +
" )\n" +
" Table Location(\n" +
" id VARCHAR(200) PRIMARY KEY,\n" +
" city VARCHAR(200) \n" +
" )\n" +
"Join Address_Street(Address.name = Street.address_name)\n" +
")\n\n\n";

Expand All @@ -93,6 +108,22 @@ public class TestPlanExecutionForIn extends AlloyTestServer
" name: [test::DB]Address.name,\n" +
" street: [test::DB]@Address_Street | Street.name\n" +
" }\n" +
" test::Location: Relational\n" +
" {\n" +
" ~primaryKey\n" +
" (\n" +
" [test::DB]Location.id\n" +
" )\n" +
" ~mainTable [test::DB]Location\n" +
" id: [test::DB]Location.id,\n" +
" city: EnumerationMapping LocationEnumMapping: [test::DB]Location.city\n" +
" }\n" +
"test::LocationEnum: EnumerationMapping LocationEnumMapping\n" +
" {\n" +
" AMERICAS: ['NewYork', 'Dallas'],\n" +
" EMEA: ['Paris', 'Stockholm'],\n" +
" ASIA_PACIFIC: ['Singapore', 'HongKong']\n" +
" }\n" +
")\n\n\n";

public static final String RUNTIME = "###Runtime\n" +
Expand Down Expand Up @@ -146,6 +177,16 @@ protected void insertTestData(Statement statement) throws SQLException
statement.execute("create schema user_view;");
statement.execute("Create Table user_view.UV_User_Roles_Public(CptyrRole VARCHAR(128) NOT NULL,RoleOrder INT NULL,UserID VARCHAR(32) NOT NULL, PRIMARY KEY(CptyrRole,UserID));");
statement.execute("Create Table user_view.UV_INQUIRY__PL_CADM(rpt_inq_oid BIGINT NOT NULL, rpt_inq_sourceinquiryid VARCHAR(200) NULL, PRIMARY KEY(rpt_inq_oid));");

statement.execute("Drop table if exists Location;");
statement.execute("Create Table Location(id VARCHAR(200) NOT NULL, city VARCHAR(200))");
statement.execute("insert into Location (id, city) values ('L1','NewYork');");
statement.execute("insert into Location (id, city) values ('L2','Paris');");
statement.execute("insert into Location (id, city) values ('L3','NewYork');");
statement.execute("insert into Location (id, city) values ('L4','Singapore');");
statement.execute("insert into Location (id, city) values ('L5','Singapore');");
statement.execute("insert into Location (id, city) values ('L6','Dallas');");
statement.execute("insert into Location (id, city) values ('L7','SomewhereElse');");
}

@Test
Expand Down Expand Up @@ -364,6 +405,31 @@ public void testInExecutionWithDateTimeListAndTimeZone() throws JsonProcessingEx
Assert.assertEquals(expectedResWithMultipleValues, RelationalResultToJsonDefaultSerializer.removeComment(executePlan(plan, paramWithMultipleValues)));
}

@Test
public void testInExecutionWithEnumList() throws JsonProcessingException
{
String fetchFunction = "###Pure\n" +
"function test::fetch(): Any[1]\n" +
"{\n" +
" {cityList:test::LocationEnum[*] | test::Location.all()\n" +
" ->filter(l:test::Location[1] | $l.city->in($cityList))\n" +
" ->project([x | $x.id], ['Id'])}\n" +
"}";

SingleExecutionPlan plan = buildPlanForFetchFunction(fetchFunction, false);
Map<String, ?> paramWithEmptyList = Maps.mutable.with("cityList", Lists.mutable.empty());
Map<String, ?> paramWithSingleValue = Maps.mutable.with("cityList", Lists.mutable.with("AMERICAS"));
Map<String, ?> paramWithMultipleValues = Maps.mutable.with("cityList", Lists.mutable.with("AMERICAS", "ASIA_PACIFIC"));

String expectedResWithEmptyList = "{\"builder\":{\"_type\":\"tdsBuilder\",\"columns\":[{\"name\":\"Id\",\"type\":\"String\",\"relationalType\":\"VARCHAR(200)\"}]},\"activities\":[{\"_type\":\"relational\",\"sql\":\"select \\\"root\\\".id as \\\"Id\\\" from Location as \\\"root\\\" where (0 = 1)\"}],\"result\":{\"columns\":[\"Id\"],\"rows\":[]}}";
String expectedResWithSingleValue = "{\"builder\":{\"_type\":\"tdsBuilder\",\"columns\":[{\"name\":\"Id\",\"type\":\"String\",\"relationalType\":\"VARCHAR(200)\"}]},\"activities\":[{\"_type\":\"relational\",\"sql\":\"select \\\"root\\\".id as \\\"Id\\\" from Location as \\\"root\\\" where (\\\"root\\\".city in ('NewYork', 'Dallas'))\"}],\"result\":{\"columns\":[\"Id\"],\"rows\":[{\"values\":[\"L1\"]},{\"values\":[\"L3\"]},{\"values\":[\"L6\"]}]}}";
String expectedResWithMultipleValues = "{\"builder\":{\"_type\":\"tdsBuilder\",\"columns\":[{\"name\":\"Id\",\"type\":\"String\",\"relationalType\":\"VARCHAR(200)\"}]},\"activities\":[{\"_type\":\"relational\",\"sql\":\"select \\\"root\\\".id as \\\"Id\\\" from Location as \\\"root\\\" where (\\\"root\\\".city in ('NewYork', 'Dallas', 'Singapore', 'HongKong'))\"}],\"result\":{\"columns\":[\"Id\"],\"rows\":[{\"values\":[\"L1\"]},{\"values\":[\"L3\"]},{\"values\":[\"L4\"]},{\"values\":[\"L5\"]},{\"values\":[\"L6\"]}]}}";

Assert.assertEquals(expectedResWithEmptyList, RelationalResultToJsonDefaultSerializer.removeComment(executePlan(plan, paramWithEmptyList)));
Assert.assertEquals(expectedResWithSingleValue, RelationalResultToJsonDefaultSerializer.removeComment(executePlan(plan, paramWithSingleValue)));
Assert.assertEquals(expectedResWithMultipleValues, RelationalResultToJsonDefaultSerializer.removeComment(executePlan(plan, paramWithMultipleValues)));
}

@Test
public void testTempTableFlowWithPostProcessor()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ function meta::protocols::pure::v1_33_0::extension::getRelationalExtension():met
name = $c.name,
doc = $c.documentation,
type = $c.type->toOne()->elementToPath(),
enumMapping = if($c.enumMappingId->isEmpty(),|[],|meta::protocols::pure::v1_33_0::transformation::fromPureGraph::executionPlan::transformEnumMapping($mapping->enumerationMappingByName($c.enumMappingId->toOne())->toOne())),
enumMapping = if($c.enumMappingId->isEmpty() || $mapping->enumerationMappingByName($c.enumMappingId->toOne())->isEmpty(),|[],|meta::protocols::pure::v1_33_0::transformation::fromPureGraph::executionPlan::transformEnumMapping($mapping->enumerationMappingByName($c.enumMappingId->toOne())->toOne())),
relationalType = $c.sourceDataType->match(
[
d:meta::relational::metamodel::datatype::DataType[0]|[],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@ function meta::relational::mapping::addEnumMapSupportFunctions(sq:meta::pure::ma
{
let enumParam = $sq.inScopeVars->keyValues().second->map(p | $p.values)->filter(e | $e->instanceOf(PlanVarPlaceHolder))->map(p | $p->cast(@PlanVarPlaceHolder).genericType.rawType->toOne())->filter(e | $e->instanceOf(Enumeration));
let enumMapTemplateFunctions = if($enumParam->isNotEmpty(),
|$m->toOne()->allEnumerationMappings()->filter(e | $e.enumeration->in($enumParam))->map(enum | let concatStr = if($enum.enumValueMappings->at(0).sourceValues->type() == String, |'\'', |'');
let enumHashMap = $enum.enumValueMappings->map(e | '"'+ $e.enum->cast(@Enum).name +'":"'+ $concatStr + $e.sourceValues->makeString('\', \'') + $concatStr + '"')->makeString(', ');
'<#function enumMap_' + fetchEnumFullPath($enum) + ' inputVal> <#assign enumMap = {'->concatenate($enumHashMap)->concatenate('}> <#if inputVal?has_content> <#return enumMap[inputVal]> <#else> <#return ""> </#if> </#function>')->makeString(' ');),
|$m->toOne()->allEnumerationMappings()->filter(e | $e.enumeration->in($enumParam);)->map(enum | let concatStr = if($enum.enumValueMappings->at(0).sourceValues->type() == String, |'\'', |'');
let enumHashMap = $enum.enumValueMappings->map(e | '"'+ $e.enum.name +'":"'+ $concatStr + $e.sourceValues->makeString('\', \'') + $concatStr + '"')->makeString(', ');
meta::relational::mapping::createEnumSupportFunctions(fetchEnumFullPath($enum), $enumHashMap);),
|[]);
}

Expand All @@ -259,6 +259,23 @@ function meta::relational::mapping::relationalPlanSupportFunctions(c:meta::exter
if($c.timeZone->isNotEmpty() && !meta::pure::functions::date::systemDefaultTimeZones()->contains($c.timeZone->toOne()),|utcToTimeZoneSupportFunction(),|[])->concatenate(collectionRenderFunction())->concatenate(collectionSizeFunction())->concatenate(optionalVarPlaceHolderOperationSelectorFunction())->concatenate(varPlaceHolderToStringFunction())->concatenate(equalEnumOperationSelectorFunction());
}

function meta::relational::mapping::createEnumSupportFunctions(enumFullPath: String[1], enumHashMap: String[1]):String[*]
{
'<#function enumMap_' + $enumFullPath + ' inputVal>' +
'<#assign enumMap = {' + $enumHashMap + '}>' +
'<#if inputVal?has_content>' +
'<#if inputVal?is_sequence>' +
'<#assign results = []>' +
'<#list inputVal as item>' +
'<#assign results += [enumMap[item]!""]>' +
'</#list><#return results?join(", ")>' +
'<#else>' +
'<#return enumMap[inputVal]!""></#if>' +
'<#else>' +
'<#return ""> </#if>' +
' </#function>';
}

function meta::relational::mapping::varPlaceHolderToStringFunction():String[*]
{
'<#function varPlaceHolderToString optionalParameter prefix suffix replacementMap defaultValue>' +
Expand Down
Loading
Loading