Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 85cd592

Browse files
authored
Merge pull request #482 from theodesp/feature/logarithms
Feature/logarithms
2 parents 9776bbd + b577732 commit 85cd592

File tree

3 files changed

+418
-0
lines changed

3 files changed

+418
-0
lines changed

sql/expression/function/logarithm.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package function
2+
3+
import (
4+
"math"
5+
"reflect"
6+
"fmt"
7+
8+
"gopkg.in/src-d/go-mysql-server.v0/sql/expression"
9+
"gopkg.in/src-d/go-mysql-server.v0/sql"
10+
"gopkg.in/src-d/go-errors.v1"
11+
)
12+
13+
// ErrInvalidArgumentForLogarithm is returned when an invalid argument value is passed to a
14+
// logarithm function
15+
var ErrInvalidArgumentForLogarithm = errors.NewKind("invalid argument value for logarithm: %v")
16+
17+
// LogBaseMaker returns LogBase creator functions with a specific base.
18+
func LogBaseMaker(base float64) func(e sql.Expression) sql.Expression {
19+
return func(e sql.Expression) sql.Expression {
20+
return NewLogBase(base, e)
21+
}
22+
}
23+
24+
// LogBase is a function that returns the logarithm of a value with a specific base.
25+
type LogBase struct {
26+
expression.UnaryExpression
27+
base float64
28+
}
29+
30+
// NewLogBase creates a new LogBase expression.
31+
func NewLogBase(base float64, e sql.Expression) sql.Expression {
32+
return &LogBase{UnaryExpression: expression.UnaryExpression{Child: e}, base: base}
33+
}
34+
35+
func (l *LogBase) String() string {
36+
switch l.base {
37+
case float64(math.E):
38+
return fmt.Sprintf("ln(%s)", l.Child)
39+
case float64(10):
40+
return fmt.Sprintf("log10(%s)", l.Child)
41+
case float64(2):
42+
return fmt.Sprintf("log2(%s)", l.Child)
43+
default:
44+
return fmt.Sprintf("log(%v, %s)", l.base, l.Child)
45+
}
46+
}
47+
48+
// TransformUp implements the Expression interface.
49+
func (l *LogBase) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
50+
child, err := l.Child.TransformUp(f)
51+
if err != nil {
52+
return nil, err
53+
}
54+
return f(NewLogBase(l.base, child))
55+
}
56+
57+
// Type returns the resultant type of the function.
58+
func (l *LogBase) Type() sql.Type {
59+
return sql.Float64
60+
}
61+
62+
// IsNullable implements the sql.Expression interface.
63+
func (l *LogBase) IsNullable() bool {
64+
return l.base == float64(1) || l.base <= float64(0) || l.Child.IsNullable()
65+
}
66+
67+
// Eval implements the Expression interface.
68+
func (l *LogBase) Eval(
69+
ctx *sql.Context,
70+
row sql.Row,
71+
) (interface{}, error) {
72+
v, err := l.Child.Eval(ctx, row)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
if v == nil {
78+
return nil, nil
79+
}
80+
81+
val, err := sql.Float64.Convert(v)
82+
if err != nil {
83+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(v))
84+
}
85+
return computeLog(val.(float64), l.base)
86+
}
87+
88+
// Log is a function that returns the natural logarithm of a value.
89+
type Log struct {
90+
expression.BinaryExpression
91+
}
92+
93+
// NewLn creates a new Log expression.
94+
func NewLog(args ...sql.Expression) (sql.Expression, error) {
95+
argLen := len(args)
96+
if argLen == 0 || argLen > 2 {
97+
return nil, sql.ErrInvalidArgumentNumber.New("1 or 2", argLen)
98+
}
99+
100+
if argLen == 1 {
101+
return &Log{expression.BinaryExpression{Left: expression.NewLiteral(math.E, sql.Float64), Right: args[0]}}, nil
102+
} else {
103+
return &Log{expression.BinaryExpression{Left: args[0], Right: args[1]}}, nil
104+
}
105+
}
106+
107+
func (l *Log) String() string {
108+
return fmt.Sprintf("log(%s, %s)", l.Left, l.Right)
109+
}
110+
111+
// TransformUp implements the Expression interface.
112+
func (l *Log) TransformUp(f sql.TransformExprFunc) (sql.Expression, error) {
113+
var args = make([]sql.Expression, 2)
114+
arg, err := l.Left.TransformUp(f)
115+
if err != nil {
116+
return nil, err
117+
}
118+
args[0] = arg
119+
120+
arg, err = l.Right.TransformUp(f)
121+
if err != nil {
122+
return nil, err
123+
}
124+
args[1] = arg
125+
expr, err := NewLog(args...)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
return f(expr)
131+
}
132+
133+
// Children implements the Expression interface.
134+
func (l *Log) Children() []sql.Expression {
135+
return []sql.Expression{l.Left, l.Right}
136+
}
137+
138+
// Type returns the resultant type of the function.
139+
func (l *Log) Type() sql.Type {
140+
return sql.Float64
141+
}
142+
143+
// IsNullable implements the Expression interface.
144+
func (l *Log) IsNullable() bool {
145+
return l.Left.IsNullable() || l.Right.IsNullable()
146+
}
147+
148+
// Eval implements the Expression interface.
149+
func (l *Log) Eval(
150+
ctx *sql.Context,
151+
row sql.Row,
152+
) (interface{}, error) {
153+
left, err := l.Left.Eval(ctx, row)
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
if left == nil {
159+
return nil, nil
160+
}
161+
162+
lhs, err := sql.Float64.Convert(left)
163+
if err != nil {
164+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(left))
165+
}
166+
167+
right, err := l.Right.Eval(ctx, row)
168+
if err != nil {
169+
return nil, err
170+
}
171+
172+
if right == nil {
173+
return nil, nil
174+
}
175+
176+
rhs, err := sql.Float64.Convert(right)
177+
if err != nil {
178+
return nil, sql.ErrInvalidType.New(reflect.TypeOf(right))
179+
}
180+
181+
// rhs becomes value, lhs becomes base
182+
return computeLog(rhs.(float64), lhs.(float64))
183+
}
184+
185+
func computeLog(v float64, base float64) (float64, error) {
186+
if v <= 0 {
187+
return float64(0), ErrInvalidArgumentForLogarithm.New(v)
188+
}
189+
if base == float64(1) || base <= float64(0) {
190+
return float64(0), ErrInvalidArgumentForLogarithm.New(base)
191+
}
192+
switch base {
193+
case float64(2):
194+
return math.Log2(v), nil
195+
case float64(10):
196+
return math.Log10(v), nil
197+
case math.E:
198+
return math.Log(v), nil
199+
default:
200+
// LOG(BASE,V) is equivalent to LOG(V) / LOG(BASE).
201+
return float64(math.Log(v) / math.Log(base)), nil
202+
}
203+
}

0 commit comments

Comments
 (0)