1+ /* *
2+ * Copyright (C) 2020-present MongoDB, Inc.
3+ *
4+ * This program is free software: you can redistribute it and/or modify
5+ * it under the terms of the Server Side Public License, version 1,
6+ * as published by MongoDB, Inc.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * Server Side Public License for more details.
12+ *
13+ * You should have received a copy of the Server Side Public License
14+ * along with this program. If not, see
15+ * <http://www.mongodb.com/licensing/server-side-public-license>.
16+ *
17+ * As a special exception, the copyright holders give permission to link the
18+ * code of portions of this program with the OpenSSL library under certain
19+ * conditions as described in each individual source file and distribute
20+ * linked combinations including the program with the OpenSSL library. You
21+ * must comply with the Server Side Public License in all respects for
22+ * all of the code used other than as permitted herein. If you modify file(s)
23+ * with this exception, you may extend this exception to your version of the
24+ * file(s), but you are not obligated to do so. If you do not wish to do so,
25+ * delete this exception statement from your version. If you delete this
26+ * exception statement from all source files in the program, then also delete
27+ * it in the license file.
28+ */
29+
30+ #include " mongo/db/exec/sbe/expression_test_base.h"
31+
32+ namespace mongo ::sbe {
33+
34+ class SBEModExprTest : public EExpressionTestFixture {
35+ protected:
36+ void runAndAssertExpression (const vm::CodeFragment* compiledExpr, double expectedVal) {
37+ auto [tag, val] = runCompiledExpression (compiledExpr);
38+ value::ValueGuard guard (tag, val);
39+ ASSERT_EQUALS (value::TypeTags::NumberDouble, tag);
40+ ASSERT_APPROX_EQUAL (value::bitcastTo<double >(val), expectedVal, 0.000001 );
41+ }
42+
43+ void runAndAssertExpression (const vm::CodeFragment* compiledExpr, Decimal128 expectedVal) {
44+ auto [tag, val] = runCompiledExpression (compiledExpr);
45+ value::ValueGuard guard (tag, val);
46+
47+ ASSERT_EQUALS (value::TypeTags::NumberDecimal, tag);
48+ ASSERT (value::bitcastTo<Decimal128>(val)
49+ .subtract (expectedVal)
50+ .toAbs ()
51+ .isLessEqual (Decimal128 (" .000001" )));
52+ }
53+
54+ void runAndAssertExpression (const vm::CodeFragment* compiledExpr, int32_t expectedVal) {
55+ auto [tag, val] = runCompiledExpression (compiledExpr);
56+ value::ValueGuard guard (tag, val);
57+ ASSERT_EQUALS (value::TypeTags::NumberInt32, tag);
58+ ASSERT_EQUALS (value::bitcastTo<int32_t >(val), expectedVal);
59+ }
60+
61+ void runAndAssertExpression (const vm::CodeFragment* compiledExpr, int64_t expectedVal) {
62+ auto [tag, val] = runCompiledExpression (compiledExpr);
63+ value::ValueGuard guard (tag, val);
64+ ASSERT_EQUALS (value::TypeTags::NumberInt64, tag);
65+ ASSERT_EQUALS (value::bitcastTo<int64_t >(val), expectedVal);
66+ }
67+
68+ void runAndAssertNothing (const vm::CodeFragment* compiledExpr) {
69+ auto [tag, val] = runCompiledExpression (compiledExpr);
70+ value::ValueGuard guard (tag, val);
71+ ASSERT_EQUALS (value::TypeTags::Nothing, tag);
72+ }
73+
74+ void runAndAssertThrows (const vm::CodeFragment* compiledExpr) {
75+ ASSERT_THROWS_CODE (runCompiledExpression (compiledExpr), AssertionException, 4848403 );
76+ }
77+ };
78+
79+ TEST_F (SBEModExprTest, ComputesMod) {
80+ value::OwnedValueAccessor slotAccessor1, slotAccessor2;
81+ auto argSlot1 = bindAccessor (&slotAccessor1);
82+ auto argSlot2 = bindAccessor (&slotAccessor2);
83+ auto modExpr = sbe::makeE<sbe::EFunction>(
84+ " mod" , sbe::makeEs (makeE<EVariable>(argSlot1), makeE<EVariable>(argSlot2)));
85+ auto compiledExpr = compileExpression (*modExpr);
86+
87+ const int32_t i32Val = 16 ;
88+ const int32_t i32Mod = 4 ;
89+ const int64_t i64Val = 2147483648 ;
90+ const int64_t i64Mod = 2147483649 ;
91+ const Decimal128 decVal (123.4 );
92+ const Decimal128 decMod (43.2 );
93+ const double dblVal (9.9 );
94+ const double dblMod (2.3 );
95+
96+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
97+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
98+ runAndAssertExpression (compiledExpr.get (), i32Val % i32Mod);
99+
100+ slotAccessor1.reset (value::TypeTags::NumberInt64, value::bitcastFrom<int64_t >(i64Val));
101+ slotAccessor2.reset (value::TypeTags::NumberInt64, value::bitcastFrom<int64_t >(i64Mod));
102+ runAndAssertExpression (compiledExpr.get (), i64Val % i64Mod);
103+
104+ auto [tagArgDecVal, valArgDecVal] = value::makeCopyDecimal (decVal);
105+ slotAccessor1.reset (tagArgDecVal, valArgDecVal);
106+ auto [tagArgDecMod, valArgDecMod] = value::makeCopyDecimal (decMod);
107+ slotAccessor2.reset (tagArgDecMod, valArgDecMod);
108+ runAndAssertExpression (compiledExpr.get (), decVal.modulo (decMod));
109+
110+ slotAccessor1.reset (value::TypeTags::NumberDouble, value::bitcastFrom<double >(dblVal));
111+ slotAccessor2.reset (value::TypeTags::NumberDouble, value::bitcastFrom<double >(dblMod));
112+ runAndAssertExpression (compiledExpr.get (), std::fmod (dblVal, dblMod));
113+ }
114+
115+ TEST_F (SBEModExprTest, ComputesModDifferentWidths) {
116+ value::OwnedValueAccessor slotAccessor1, slotAccessor2;
117+ auto argSlot1 = bindAccessor (&slotAccessor1);
118+ auto argSlot2 = bindAccessor (&slotAccessor2);
119+ auto modExpr = sbe::makeE<sbe::EFunction>(
120+ " mod" , sbe::makeEs (makeE<EVariable>(argSlot1), makeE<EVariable>(argSlot2)));
121+ auto compiledExpr = compileExpression (*modExpr);
122+
123+ const int32_t i32Val = 16 ;
124+ const int32_t i32Mod = 4 ;
125+ const int64_t i64Val = 2147483648 ;
126+ const int64_t i64Mod = 2147483649 ;
127+
128+ slotAccessor1.reset (value::TypeTags::NumberInt64, value::bitcastFrom<int64_t >(i64Val));
129+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
130+ runAndAssertExpression (compiledExpr.get (), i64Val % i32Mod);
131+
132+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
133+ slotAccessor2.reset (value::TypeTags::NumberInt64, value::bitcastFrom<int64_t >(i64Mod));
134+ runAndAssertExpression (compiledExpr.get (), i32Val % i64Mod);
135+ }
136+
137+ TEST_F (SBEModExprTest, ComputesNothingIfNotNumeric) {
138+ value::OwnedValueAccessor slotAccessor1, slotAccessor2;
139+ auto argSlot1 = bindAccessor (&slotAccessor1);
140+ auto argSlot2 = bindAccessor (&slotAccessor2);
141+ auto modExpr = sbe::makeE<sbe::EFunction>(
142+ " mod" , sbe::makeEs (makeE<EVariable>(argSlot1), makeE<EVariable>(argSlot2)));
143+ auto compiledExpr = compileExpression (*modExpr);
144+
145+ const int32_t i32Val = 16 ;
146+ const int32_t i32Mod = 4 ;
147+
148+ auto [tagStrArg1, valStrArg1] = value::makeNewString (" abc" );
149+ slotAccessor1.reset (tagStrArg1, valStrArg1);
150+ auto [tagStrArg2, valStrArg2] = value::makeNewString (" xyz" );
151+ slotAccessor2.reset (tagStrArg2, valStrArg2);
152+ runAndAssertNothing (compiledExpr.get ());
153+
154+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
155+ slotAccessor2.reset (tagStrArg2, valStrArg2);
156+ runAndAssertNothing (compiledExpr.get ());
157+
158+ slotAccessor1.reset (tagStrArg1, valStrArg1);
159+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
160+ runAndAssertNothing (compiledExpr.get ());
161+ }
162+
163+ TEST_F (SBEModExprTest, ComputesNothingIfNullOrMissing) {
164+ value::OwnedValueAccessor slotAccessor1, slotAccessor2;
165+ auto argSlot1 = bindAccessor (&slotAccessor1);
166+ auto argSlot2 = bindAccessor (&slotAccessor2);
167+ auto modExpr = sbe::makeE<sbe::EFunction>(
168+ " mod" , sbe::makeEs (makeE<EVariable>(argSlot1), makeE<EVariable>(argSlot2)));
169+ auto compiledExpr = compileExpression (*modExpr);
170+
171+ const int32_t i32Val = 16 ;
172+ const int32_t i32Mod = 4 ;
173+
174+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
175+ slotAccessor2.reset (value::TypeTags::Nothing, 0 );
176+ runAndAssertNothing (compiledExpr.get ());
177+
178+ slotAccessor1.reset (value::TypeTags::Nothing, 0 );
179+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
180+ runAndAssertNothing (compiledExpr.get ());
181+
182+ slotAccessor1.reset (value::TypeTags::Nothing, 0 );
183+ slotAccessor2.reset (value::TypeTags::Nothing, 0 );
184+ runAndAssertNothing (compiledExpr.get ());
185+
186+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
187+ slotAccessor2.reset (value::TypeTags::Null, 0 );
188+ runAndAssertNothing (compiledExpr.get ());
189+
190+ slotAccessor1.reset (value::TypeTags::Null, 0 );
191+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
192+ runAndAssertNothing (compiledExpr.get ());
193+
194+ slotAccessor1.reset (value::TypeTags::Null, 0 );
195+ slotAccessor2.reset (value::TypeTags::Null, 0 );
196+ runAndAssertNothing (compiledExpr.get ());
197+
198+ slotAccessor1.reset (value::TypeTags::Null, 0 );
199+ slotAccessor2.reset (value::TypeTags::Nothing, 0 );
200+ runAndAssertNothing (compiledExpr.get ());
201+
202+ slotAccessor1.reset (value::TypeTags::Nothing, 0 );
203+ slotAccessor2.reset (value::TypeTags::Null, 0 );
204+ runAndAssertNothing (compiledExpr.get ());
205+ }
206+
207+ TEST_F (SBEModExprTest, ErrorIfModRHSIsZero) {
208+ value::OwnedValueAccessor slotAccessor1, slotAccessor2;
209+ auto argSlot1 = bindAccessor (&slotAccessor1);
210+ auto argSlot2 = bindAccessor (&slotAccessor2);
211+ auto modExpr = sbe::makeE<sbe::EFunction>(
212+ " mod" , sbe::makeEs (makeE<EVariable>(argSlot1), makeE<EVariable>(argSlot2)));
213+ auto compiledExpr = compileExpression (*modExpr);
214+
215+ const int32_t i32Val = 16 ;
216+ const int32_t i32Mod = 0 ;
217+ const Decimal128 decVal (123.4 );
218+ const Decimal128 decMod (0 );
219+
220+ slotAccessor1.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Val));
221+ slotAccessor2.reset (value::TypeTags::NumberInt32, value::bitcastFrom<int32_t >(i32Mod));
222+ runAndAssertThrows (compiledExpr.get ());
223+
224+ auto [tagArgDecVal, valArgDecVal] = value::makeCopyDecimal (decVal);
225+ slotAccessor1.reset (tagArgDecVal, valArgDecVal);
226+ auto [tagArgDecMod, valArgDecMod] = value::makeCopyDecimal (decMod);
227+ slotAccessor2.reset (tagArgDecMod, valArgDecMod);
228+ runAndAssertThrows (compiledExpr.get ());
229+ }
230+
231+ } // namespace mongo::sbe
0 commit comments