Skip to content

Commit cfacce1

Browse files
[Wasm] Implement Decimal type.
1 parent fb919ad commit cfacce1

File tree

2 files changed

+317
-1
lines changed

2 files changed

+317
-1
lines changed

src/backend/WasmUtil.hpp

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
#include "backend/PhysicalOperator.hpp"
44
#include "backend/WasmDSL.hpp"
5+
#include <functional>
56
#include <mutable/catalog/Schema.hpp>
67
#include <mutable/parse/AST.hpp>
7-
#include <functional>
8+
#include <mutable/util/concepts.hpp>
89
#include <optional>
910
#include <variant>
1011

@@ -72,6 +73,188 @@ struct NChar : Ptr<Char>
7273
bool guarantees_terminating_nul() const { return type_->is_varying; }
7374
};
7475

76+
77+
/*======================================================================================================================
78+
* Decimal
79+
*====================================================================================================================*/
80+
81+
template<signed_integral Base>
82+
struct Decimal : Expr<Base>
83+
{
84+
using expr_type = Expr<Base>;
85+
using arithmetic_type = Base;
86+
87+
private:
88+
///> the number of decimal digits right of the decimal point
89+
uint32_t scale_;
90+
91+
public:
92+
/** Constructs a `Decimal` from a given \p value at the given \p scale. For example, a \p value of `142` at a \p
93+
* scale of `2` would represent `1.42`. */
94+
Decimal(Base value, uint32_t scale) : expr_type(value), scale_(scale) {
95+
M_insist(scale != 0);
96+
}
97+
98+
/** Constructs a `Decimal` from a given \p value at the given \p scale. For example, a \p value of `142` at a \p
99+
* scale of `2` would represent `1.42`. */
100+
Decimal(expr_type value, uint32_t scale) : expr_type(value), scale_(scale) { }
101+
102+
Decimal(Decimal &other) : Decimal(other.val(), other.scale_) { }
103+
Decimal(Decimal &&other) : Decimal(other.val(), other.scale_) { }
104+
105+
/** Constructs a `Decimal` from a given \p value at the given \p scale. For example, a \p value of `142` at a \p
106+
* scale of `2` would represent `142.00`. */
107+
static Decimal Scaled(expr_type value, uint32_t scale) {
108+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), scale);
109+
return Decimal(value * scaling_factor, scale);
110+
}
111+
112+
113+
public:
114+
expr_type val() { return *this; }
115+
116+
uint32_t scale() const { return scale_; }
117+
118+
template<signed_integral To>
119+
Expr<To> to() { return val().template to<To>() / powi(To(10), To(scale_)); }
120+
121+
Decimal clone() const { return Decimal(expr_type::clone(), scale_); }
122+
bool can_be_null() const { return expr_type::can_be_null(); }
123+
Bool is_null() {
124+
if (can_be_null()) {
125+
return expr_type::is_null();
126+
} else {
127+
expr_type::discard();
128+
return Bool(false);
129+
}
130+
}
131+
Bool not_null() {
132+
if (can_be_null()) {
133+
return expr_type::not_null();
134+
} else {
135+
expr_type::discard();
136+
return Bool(true);
137+
}
138+
}
139+
140+
141+
/*------------------------------------------------------------------------------------------------------------------
142+
* Unary operations
143+
*----------------------------------------------------------------------------------------------------------------*/
144+
145+
146+
/*------------------------------------------------------------------------------------------------------------------
147+
* Binary operations
148+
*----------------------------------------------------------------------------------------------------------------*/
149+
150+
Decimal operator+(Decimal other) {
151+
if (this->scale_ == other.scale_)
152+
return Decimal(this->val() + other.val(), scale_); // fall back to regular integer arithmetic
153+
154+
if (this->scale() > other.scale()) {
155+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), this->scale() - other.scale());
156+
return Decimal(this->val() + (other * scaling_factor).val() , this->scale());
157+
} else {
158+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), other.scale() - this->scale());
159+
return Decimal((*this * scaling_factor).val() + other.val() , other.scale());
160+
}
161+
}
162+
163+
template<typename T>
164+
requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
165+
Decimal operator+(T &&other) { return Decimal(*this + Scaled(expr_t<T>(other), scale_)); }
166+
167+
Decimal operator-(Decimal other) {
168+
if (this->scale_ == other.scale_)
169+
return Decimal(this->val() - other.val(), scale_); // fall back to regular integer arithmetic
170+
171+
if (this->scale() > other.scale()) {
172+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), this->scale() - other.scale());
173+
return Decimal(this->val() - (other * scaling_factor).val() , this->scale());
174+
} else {
175+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), other.scale() - this->scale());
176+
return Decimal((*this * scaling_factor).val() - other.val() , other.scale());
177+
}
178+
}
179+
180+
template<typename T>
181+
requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
182+
Decimal operator-(T &&other) { return Decimal(*this - Scaled(expr_t<T>(other), scale_)); }
183+
184+
Decimal operator*(Decimal other) {
185+
uint32_t smaller_scale = this->scale();
186+
uint32_t higher_scale = other.scale();
187+
if (smaller_scale > higher_scale)
188+
std::swap(smaller_scale, higher_scale);
189+
M_insist(smaller_scale <= higher_scale);
190+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), smaller_scale);
191+
return Decimal((this->val() * other.val()) / scaling_factor, higher_scale);
192+
}
193+
194+
template<typename T>
195+
requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
196+
Decimal operator*(T &&other) { return Decimal(this->val() * expr_t<T>(other), scale()); }
197+
198+
Decimal operator/(Decimal other) {
199+
const uint32_t scale_res = std::max(this->scale(), other.scale());
200+
const arithmetic_type scaling_factor = powi(arithmetic_type(10), scale_res + other.scale() - this->scale());
201+
return Decimal((this->val() * scaling_factor) / other.val(), scale_res);
202+
}
203+
204+
template<typename T>
205+
requires expr_convertible<T> and signed_integral<typename expr_t<T>::type>
206+
Decimal operator/(T &&other) { return Decimal(this->val() / expr_t<T>(other), scale()); }
207+
208+
/** Modulo division is not supported by `Decimal` type. */
209+
template<typename T>
210+
requires false
211+
Decimal operator%(T&&) { M_unreachable("modulo division on decimals is not defined"); }
212+
213+
friend std::ostream & operator<<(std::ostream &out, const Decimal &d) {
214+
return out << "Decimal: " << static_cast<const expr_type&>(d) << ", scale = " << d.scale_;
215+
}
216+
217+
void dump(std::ostream &out) const { out << *this << std::endl; }
218+
void dump() const { dump(std::cerr); }
219+
};
220+
221+
/*----------------------------------------------------------------------------------------------------------------------
222+
* Binary operations
223+
*--------------------------------------------------------------------------------------------------------------------*/
224+
225+
template<typename Base>
226+
requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
227+
Decimal<typename expr_t<Base>::type> operator+(Base &&left, Decimal<typename expr_t<Base>::type> right)
228+
{
229+
return right + left;
230+
}
231+
232+
template<typename Base>
233+
requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
234+
Decimal<typename expr_t<Base>::type> operator-(Base &&left, Decimal<typename expr_t<Base>::type> right)
235+
{
236+
return Decimal<typename expr_t<Base>::type>::Scaled(expr_t<Base>(left), right.scale()) - right;
237+
}
238+
239+
template<typename Base>
240+
requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
241+
Decimal<typename expr_t<Base>::type> operator*(Base &&left, Decimal<typename expr_t<Base>::type> right)
242+
{
243+
return right * left;
244+
}
245+
246+
template<typename Base>
247+
requires expr_convertible<Base> and signed_integral<typename expr_t<Base>::type>
248+
Decimal<typename expr_t<Base>::type> operator/(Base &&left, Decimal<typename expr_t<Base>::type> right)
249+
{
250+
return Decimal<typename expr_t<Base>::type>(expr_t<Base>(left), 0) / right;
251+
}
252+
253+
using Decimal32 = Decimal<int32_t>;
254+
using Decimal64 = Decimal<int64_t>;
255+
256+
257+
75258
/*======================================================================================================================
76259
* Declare valid SQL types
77260
*====================================================================================================================*/

unittest/backend/WasmUtilTest.tpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,3 +491,136 @@ TEST_CASE("Wasm/" BACKEND_NAME "/strncpy", "[core][wasm]")
491491
CodeGenContext::Dispose();
492492
Module::Dispose();
493493
}
494+
495+
TEMPLATE_TEST_CASE("Wasm/" BACKEND_NAME "/Decimal", "[core][wasm]",
496+
Decimal32, Decimal64)
497+
{
498+
Module::Init();
499+
500+
using decimal = TestType;
501+
using arithmetic_type = typename decimal::arithmetic_type;
502+
503+
#define A decimal( 142, 2)
504+
#define B decimal(1337, 2)
505+
#define C decimal(-256, 2)
506+
#define D decimal( 113, 3)
507+
#define E decimal( 12, 1)
508+
509+
#define I PrimitiveExpr<arithmetic_type>(13)
510+
511+
SECTION("add")
512+
{
513+
CHECK_RESULT_INLINE(1479, arithmetic_type(void), { RETURN((A + B).val()); });
514+
CHECK_RESULT_INLINE(1479, arithmetic_type(void), { RETURN((B + A).val()); });
515+
516+
CHECK_RESULT_INLINE(-114, arithmetic_type(void), { RETURN((A + C).val()); });
517+
CHECK_RESULT_INLINE(-114, arithmetic_type(void), { RETURN((C + A).val()); });
518+
519+
CHECK_RESULT_INLINE(1533, arithmetic_type(void), { RETURN((A + D).val()); });
520+
CHECK_RESULT_INLINE(1533, arithmetic_type(void), { RETURN((D + A).val()); });
521+
522+
CHECK_RESULT_INLINE(262, arithmetic_type(void), { RETURN((A + E).val()); });
523+
CHECK_RESULT_INLINE(262, arithmetic_type(void), { RETURN((E + A).val()); });
524+
525+
CHECK_RESULT_INLINE(1313, arithmetic_type(void), { RETURN((D + E).val()); });
526+
CHECK_RESULT_INLINE(1313, arithmetic_type(void), { RETURN((E + D).val()); });
527+
528+
CHECK_RESULT_INLINE(-2447, arithmetic_type(void), { RETURN((C + D).val()); });
529+
CHECK_RESULT_INLINE(-2447, arithmetic_type(void), { RETURN((D + C).val()); });
530+
531+
CHECK_RESULT_INLINE(-136, arithmetic_type(void), { RETURN((C + E).val()); });
532+
CHECK_RESULT_INLINE(-136, arithmetic_type(void), { RETURN((E + C).val()); });
533+
534+
CHECK_RESULT_INLINE(1442, arithmetic_type(void), { RETURN((A + I).val()); });
535+
CHECK_RESULT_INLINE(1442, arithmetic_type(void), { RETURN((I + A).val()); });
536+
}
537+
538+
SECTION("subtract")
539+
{
540+
CHECK_RESULT_INLINE(-1195, arithmetic_type(void), { RETURN((A - B).val()); });
541+
CHECK_RESULT_INLINE(1195, arithmetic_type(void), { RETURN((B - A).val()); });
542+
543+
CHECK_RESULT_INLINE(398, arithmetic_type(void), { RETURN((A - C).val()); });
544+
CHECK_RESULT_INLINE(-398, arithmetic_type(void), { RETURN((C - A).val()); });
545+
546+
CHECK_RESULT_INLINE(1307, arithmetic_type(void), { RETURN((A - D).val()); });
547+
CHECK_RESULT_INLINE(-1307, arithmetic_type(void), { RETURN((D - A).val()); });
548+
549+
CHECK_RESULT_INLINE(22, arithmetic_type(void), { RETURN((A - E).val()); });
550+
CHECK_RESULT_INLINE(-22, arithmetic_type(void), { RETURN((E - A).val()); });
551+
552+
CHECK_RESULT_INLINE(-1087, arithmetic_type(void), { RETURN((D - E).val()); });
553+
CHECK_RESULT_INLINE(1087, arithmetic_type(void), { RETURN((E - D).val()); });
554+
555+
CHECK_RESULT_INLINE(-2673, arithmetic_type(void), { RETURN((C - D).val()); });
556+
CHECK_RESULT_INLINE(2673, arithmetic_type(void), { RETURN((D - C).val()); });
557+
558+
CHECK_RESULT_INLINE(-376, arithmetic_type(void), { RETURN((C - E).val()); });
559+
CHECK_RESULT_INLINE(376, arithmetic_type(void), { RETURN((E - C).val()); });
560+
561+
CHECK_RESULT_INLINE(-1158, arithmetic_type(void), { RETURN((A - I).val()); });
562+
CHECK_RESULT_INLINE(1158, arithmetic_type(void), { RETURN((I - A).val()); });
563+
}
564+
565+
SECTION("multiply")
566+
{
567+
CHECK_RESULT_INLINE(1898, arithmetic_type(void), { RETURN((A * B).val()); });
568+
CHECK_RESULT_INLINE(1898, arithmetic_type(void), { RETURN((B * A).val()); });
569+
570+
CHECK_RESULT_INLINE(-363, arithmetic_type(void), { RETURN((A * C).val()); });
571+
CHECK_RESULT_INLINE(-363, arithmetic_type(void), { RETURN((C * A).val()); });
572+
573+
CHECK_RESULT_INLINE(160, arithmetic_type(void), { RETURN((A * D).val()); });
574+
CHECK_RESULT_INLINE(160, arithmetic_type(void), { RETURN((D * A).val()); });
575+
576+
CHECK_RESULT_INLINE(170, arithmetic_type(void), { RETURN((A * E).val()); });
577+
CHECK_RESULT_INLINE(170, arithmetic_type(void), { RETURN((E * A).val()); });
578+
579+
CHECK_RESULT_INLINE(135, arithmetic_type(void), { RETURN((D * E).val()); });
580+
CHECK_RESULT_INLINE(135, arithmetic_type(void), { RETURN((E * D).val()); });
581+
582+
CHECK_RESULT_INLINE(-289, arithmetic_type(void), { RETURN((C * D).val()); });
583+
CHECK_RESULT_INLINE(-289, arithmetic_type(void), { RETURN((D * C).val()); });
584+
585+
CHECK_RESULT_INLINE(-307, arithmetic_type(void), { RETURN((C * E).val()); });
586+
CHECK_RESULT_INLINE(-307, arithmetic_type(void), { RETURN((E * C).val()); });
587+
588+
CHECK_RESULT_INLINE(1846, arithmetic_type(void), { RETURN((A * I).val()); });
589+
CHECK_RESULT_INLINE(1846, arithmetic_type(void), { RETURN((I * A).val()); });
590+
}
591+
592+
SECTION("divide")
593+
{
594+
CHECK_RESULT_INLINE(10, arithmetic_type(void), { RETURN((A / B).val()); });
595+
CHECK_RESULT_INLINE(941, arithmetic_type(void), { RETURN((B / A).val()); });
596+
597+
CHECK_RESULT_INLINE(-55, arithmetic_type(void), { RETURN((A / C).val()); });
598+
CHECK_RESULT_INLINE(-180, arithmetic_type(void), { RETURN((C / A).val()); });
599+
600+
CHECK_RESULT_INLINE(12566, arithmetic_type(void), { RETURN((A / D).val()); });
601+
CHECK_RESULT_INLINE(79, arithmetic_type(void), { RETURN((D / A).val()); });
602+
603+
CHECK_RESULT_INLINE(118, arithmetic_type(void), { RETURN((A / E).val()); });
604+
CHECK_RESULT_INLINE(84, arithmetic_type(void), { RETURN((E / A).val()); });
605+
606+
CHECK_RESULT_INLINE(94, arithmetic_type(void), { RETURN((D / E).val()); });
607+
CHECK_RESULT_INLINE(10619, arithmetic_type(void), { RETURN((E / D).val()); });
608+
609+
CHECK_RESULT_INLINE(-22654, arithmetic_type(void), { RETURN((C / D).val()); });
610+
CHECK_RESULT_INLINE(-44, arithmetic_type(void), { RETURN((D / C).val()); });
611+
612+
CHECK_RESULT_INLINE(-213, arithmetic_type(void), { RETURN((C / E).val()); });
613+
CHECK_RESULT_INLINE(-46, arithmetic_type(void), { RETURN((E / C).val()); });
614+
615+
CHECK_RESULT_INLINE(10, arithmetic_type(void), { RETURN((A / I).val()); });
616+
CHECK_RESULT_INLINE(915, arithmetic_type(void), { RETURN((I / A).val()); });
617+
}
618+
619+
#undef A
620+
#undef B
621+
#undef C
622+
#undef D
623+
#undef E
624+
#undef I
625+
Module::Dispose();
626+
}

0 commit comments

Comments
 (0)