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
67 changes: 25 additions & 42 deletions moxygen/main/default/classes/database/MockDatabaseTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -4728,48 +4728,6 @@ private class MockDatabaseTest {
Assert.isTrue(isExceptionThrown, 'Exception should have been thrown');
}

@IsTest
static void testWhereClauseComparisonToNull9() {
Account acct1 = new Account(Name = 'Test1', NumberOfEmployees = 5);
Account acct2 = new Account(Name = 'Test2');
List<Account> acctList = new List<Account>{ acct1, acct2 };
MockDatabase.doInsert(acctList, true);

Test.startTest();
Boolean isExceptionThrown = false;
try {
MockDatabase.query(
'SELECT Id, Name FROM Account WHERE Name IN NULL'
);
} catch (QueryException e) {
isExceptionThrown = true;
}
Test.stopTest();

Assert.isTrue(isExceptionThrown, 'Exception should have been thrown');
}

@IsTest
static void testWhereClauseComparisonToNull10() {
Account acct1 = new Account(Name = 'Test1', NumberOfEmployees = 5);
Account acct2 = new Account(Name = 'Test2');
List<Account> acctList = new List<Account>{ acct1, acct2 };
MockDatabase.doInsert(acctList, true);

Test.startTest();
Boolean isExceptionThrown = false;
try {
MockDatabase.query(
'SELECT Id, Name FROM Account WHERE Name NOT IN NULL'
);
} catch (QueryException e) {
isExceptionThrown = true;
}
Test.stopTest();

Assert.isTrue(isExceptionThrown, 'Exception should have been thrown');
}

@IsTest
static void ensureTodayLiteralWorksForDateEqual() {
Opportunity opp1 = new Opportunity(
Expand Down Expand Up @@ -7561,4 +7519,29 @@ private class MockDatabaseTest {
'Incorrect number of opportunities'
);
}

@IsTest
static void ensureInOperatorWorksWithLiterals() {
List<Opportunity> oppList = new List<Opportunity>();
oppList.add(new Opportunity(Name = 'Opp1', CloseDate = Gmt.today()));
oppList.add(
new Opportunity(Name = 'Opp2', CloseDate = Gmt.today().addDays(-1))
);
oppList.add(
new Opportunity(Name = 'Opp3', CloseDate = Gmt.today().addDays(1))
);
MockDatabase.doInsert(oppList, true);

Test.startTest();
List<Opportunity> opportunities = MockDatabase.query(
'SELECT Id, CloseDate FROM Opportunity WHERE CloseDate IN (TODAY, TOMORROW, YESTERDAY)'
);
Test.stopTest();

Assert.areEqual(
3,
opportunities.size(),
'Incorrect number of opportunities'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,9 @@ public with sharing class WhereLogicHandler extends BooleanLogicHandler {
fieldValue = new DateLiteral.Builder()
.setFieldValue((String) fieldValue)
.build();
} else {
} else if (valueNode.nodeType != NodeType.VALUE_LIST) {
fieldValue = adjustForSoqlDateString(fieldValue, fieldType);
}

return handler.handle(sObjectFieldValue, fieldValue);
}

Expand Down Expand Up @@ -141,7 +140,7 @@ public with sharing class WhereLogicHandler extends BooleanLogicHandler {
sObject sObj,
Map<String, Object> params
) {
Boolean isBindVar = (valueNode.nodeType == 'bind variable');
Boolean isBindVar = (valueNode.nodeType == NodeType.XBIND_VARIABLE);
Boolean isParamsNull = (params == null);
Boolean isValueInParams = (!isParamsNull &&
params.containsKey(valueNode.id));
Expand All @@ -154,21 +153,21 @@ public with sharing class WhereLogicHandler extends BooleanLogicHandler {
return params.get(valueNode.id);
}

if (valueNode.nodeType == 'value list') {
if (valueNode.nodeType == NodeType.VALUE_LIST) {
List<Object> values = new List<Object>();
Node valueListNode = valueNode.left;
while (valueListNode != null) {
values.add(getFieldNodeValue(valueListNode, sObj, params));
valueListNode = valueListNode.left;
Node valueListNode = valueNode;
while (valueListNode != null && valueListNode.left != null) {
values.add(valueListNode.left);
valueListNode = valueListNode.left.left;
}
return values;
}

if (valueNode.nodeType == 'null') {
if (valueNode.nodeType == NodeType.XNULL) {
return null;
}

if (valueNode.nodeType == 'subquery') {
if (valueNode.nodeType == NodeType.SUBQUERY) {
Node subquerySelectNode = valueNode.right;
Node subqueryFieldNode = subquerySelectNode.left;
Integer fieldsList = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>63.0</apiVersion>
<status>Active</status>
</ApexClass>
</ApexClass>
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,41 @@
* @group Soql Engine
*/
public class InHandler implements IOperatorHandler {
static EqualityHandler equalityHandle = new EqualityHandler();
/**
* @description Handles the IN operator
* @param sObjectFieldValue `Object`
* @param fieldValue `Object`
* @return `Boolean`
*/
public Boolean handle(Object sObjectFieldValue, Object fieldValue) {
if (sObjectFieldValue == null) {
List<Object> nodes = (List<Object>) fieldValue;
return handleList(sObjectFieldValue, nodes, 0);
}

private Boolean handleList(
Object fieldValue,
List<Object> probablyNodes,
Integer index
) {
if (index >= probablyNodes.size()) {
return false;
} else if (fieldValue == null) {
throw new QueryException('invalid operator');
}

List<Object> values = (List<Object>) fieldValue;
for (Integer i = 0; i < values.size(); i++) {
if (values[i] instanceof String) {
values[i] = (Object) ((String) values[i]).toLowerCase();
Object currValue = probablyNodes[index];
if (currValue instanceof Node) {
Node currNode = (Node) currValue;
if (currNode.nodeType == NodeType.DATE_LITERAL) {
currValue = new DateLiteral.Builder()
.setFieldValue((String) currNode.id)
.build();
} else {
currValue = currNode.id;
}
}
String nodeValue = String.valueOf(sObjectFieldValue).toLowerCase();
return values.contains(nodeValue);
Boolean isEqual = equalityHandle.handle(fieldValue, currValue);
if (!isEqual) {
isEqual = handleList(fieldValue, probablyNodes, index + 1);
}
return isEqual;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class IncludesHandler implements IOperatorHandler {
try {
List<Object> objList = (List<Object>) fieldValue;
for (Object obj : objList) {
values.add(((String) obj).toLowerCase() + ';');
values.add(((Node) obj).id.toLowerCase() + ';');
}
} catch (Exception ex) {
throw new QueryException('Includes expects a list of strings');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,14 @@
* @group Soql Engine
*/
public class NotInHandler implements IOperatorHandler {
static InHandler inHandle = new InHandler();
/**
* @description Handles the NotIn operator
* @param sObjectFieldValue `Object`
* @param fieldValue `Object`
* @return `Boolean`
*/
public Boolean handle(Object sObjectFieldValue, Object fieldValue) {
if (sObjectFieldValue == null) {
return false;
} else if (fieldValue == null) {
throw new QueryException('invalid operator');
}

List<Object> values = (List<Object>) fieldValue;
for (Integer i = 0; i < values.size(); i++) {
if (values[i] instanceof String) {
values[i] = (Object) ((String) values[i]).toLowerCase();
}
}
String nodeValue = String.valueOf(sObjectFieldValue).toLowerCase();
return !values.contains(nodeValue);
return !inHandle.handle(sObjectFieldValue, fieldValue);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -968,4 +968,93 @@ private class ParserTest {
}
Test.stopTest();
}

@IsTest
static void ensureInOperatorWorksWithDateLiterals() {
Parser p = new Parser();
String soqlQuery = 'SELECT Id FROM Opportunity WHERE CreatedDate IN (TODAY, TOMORROW, YESTERDAY)';
Test.startTest();
Node head = p.parse(soqlQuery);
Test.stopTest();

Node whereNode = head.right.right;
Assert.areEqual(
whereNode.nodeType,
NodeType.XWHERE,
'Should be a where clause'
);
Node inNode = whereNode.left;
Assert.areEqual('in', inNode.id, 'Should be an in operator');
Node valueListNode = inNode.right;
Assert.areEqual(
valueListNode.nodeType,
NodeType.VALUE_LIST,
'Should be a value list'
);
Node todayNode = valueListNode.left;
Assert.areEqual(
todayNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);
Node tomorrowNode = todayNode.left.left;
Assert.areEqual(
tomorrowNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);
Node yesterdayNode = tomorrowNode.left.left;
Assert.areEqual(
yesterdayNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);

Assert.areEqual(
todayNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);
Assert.areEqual(
tomorrowNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);
Assert.areEqual(
yesterdayNode.nodeType,
NodeType.DATE_LITERAL,
'Should be a date literal'
);

Assert.areEqual(
todayNode.id,
Token.TODAY_LITERAL,
'Should be a today literal'
);
Assert.areEqual(
tomorrowNode.id,
Token.TOMORROW_LITERAL,
'Should be a tomorrow literal'
);
Assert.areEqual(
yesterdayNode.id,
Token.YESTERDAY_LITERAL,
'Should be a yesterday literal'
);
}

@IsTest
static void ensureInDoesNotAcceptInNull() {
Parser p = new Parser();
String soqlQuery = 'SELECT Id FROM Opportunity WHERE CreatedDate IN NULL';
Boolean isExceptionThrown = false;
Test.startTest();
try {
p.parse(soqlQuery);
} catch (Exception e) {
isExceptionThrown = true;
}
Test.stopTest();
Assert.isTrue(isExceptionThrown, 'Expected exception to be thrown');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* @group Soql Engine
*/
public with sharing class ValueParser extends SubParser {
final static String ERROR_MESSAGE = 'Error parsing value: {0} Failed with exception: {1}';
/**
* @description Parses the value in the query string
* @param query `String`
Expand All @@ -45,10 +46,10 @@ public with sharing class ValueParser extends SubParser {
return primitiveParser.parse(query);
} catch (Exception e) {
throw new ParsingException(
'Error parsing value: ' +
query +
' Failed with exception: ' +
e.getMessage()
String.format(
ERROR_MESSAGE,
new List<String>{ query, e.getMessage() }
)
);
}
}
Expand Down
Loading