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

Feature/logarithms #482

Merged
merged 4 commits into from
Oct 25, 2018
Merged

Feature/logarithms #482

merged 4 commits into from
Oct 25, 2018

Conversation

theodesp
Copy link
Contributor

PR adds relevant Logarithm functions:
Some examples:

mysql> select log();
ERROR 1105 (HY000): unknown error: expecting 1 or 2 arguments for calling this function, 0 received
mysql> select log(1, 2, 3);
ERROR 1105 (HY000): unknown error: expecting 1 or 2 arguments for calling this function, 3 received
mysql> select log(1, 2);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: 1
mysql> select log(-1);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -1
mysql> select log(-1, 100);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -1
mysql> select log(2, 10);
+-------------------+
| log(2, 10)        |
+-------------------+
| 3.321928094887362 |
+-------------------+
1 row in set (0.00 sec)

mysql> select ln(0);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: 0
mysql> select ln(-2);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -2
mysql> select ln(20);
+-------------------+
| ln(20)            |
+-------------------+
| 2.995732273553991 |
+-------------------+
1 row in set (0.00 sec)

mysql> select log2(0);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: 0
mysql> select log2(-2);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -2
mysql> select log2(2);
+---------+
| log2(2) |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)


mysql> select log2('5');
+-------------------+
| log2("5")         |
+-------------------+
| 2.321928094887362 |
+-------------------+
1 row in set (0.00 sec)

mysql> select log10(-10);
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -10
mysql> select log10('-20');
ERROR 1105 (HY000): unknown error: invalid argument value for logarithm: -20
mysql> select log10(100);
+------------+
| log10(100) |
+------------+
|          2 |
+------------+
1 row in set (0.00 sec)

mysql>

Signed-off-by: Theo Despoudis <thdespou@hotmail.com>
Signed-off-by: Theo Despoudis <thdespou@hotmail.com>
sql/expression/function/logarithm.go Show resolved Hide resolved
ctx *sql.Context,
row sql.Row,
) (interface{}, error) {
left, err := l.Left.Eval(ctx, row)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This common block which repeats multiple times (eval + convert) we can extract to some private function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure will push a fix today

sql/expression/function/logarithm.go Show resolved Hide resolved
sql/expression/function/registry.go Outdated Show resolved Hide resolved
@theodesp
Copy link
Contributor Author

I've consolidated Log2, Ln and Log10. @kuba-- Please take a look

Copy link
Contributor

@kuba-- kuba-- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also sign a request, thanks.


// IsNullable implements the sql.Expression interface.
func (l *LogBase) IsNullable() bool {
return l.Child.IsNullable()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO Logarithm can be NULL in ranges where function y=log b(x) doesn't exist:

  • b or x is NULL
  • base is <= 0 or base == 1
  • x <= 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I will add the check here also

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW how can I resolve the Child value here so I can check that x <= 0?
Also as base is float there is no nil value unless I change it to a pointer.

So far I've done something like:

func (l *LogBase) IsNullable() bool {
	return l.base == float64(1) || l.base <= float64(0) || l.Child.IsNullable()
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may work.

right = args[1]
}

return &Log{expression.BinaryExpression{Left: args[0], Right: right}}, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't better to always set b and x instead of checking somewhere else which one is nil?

  • if len == 1 => left = E, right = args[0]
  • if len == 2 => left = args[0], right = args[1]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe, internally it's better to have everything explicit.


// Children implements the Expression interface.
func (l *Log) Children() []sql.Expression {
if l.Right == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO internally we should explicitly say - left is a base, right is argument.
If base is not specified in constructor then we set it up to E, but internally we shouldn't have hidden logic, which argument stands for what.

Copy link
Contributor Author

@theodesp theodesp Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we setup base to Math.E wouldn't that make difficult to detect if the request was using 1 argument instead of 2? For example when l.Right == nil we know that the request was log(%s) but now every time it would be log(%s, %s) which looks wierd.

mysql> select log(2);
+---------------------------+
| log(2.718281828459045, 2) |
+---------------------------+
|        0.6931471805599453 |
+---------------------------+

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the header comes from the String - right?
If yes, maybe we can add extra nice formatting

Copy link
Contributor Author

@theodesp theodesp Oct 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok we can just always show the base even if its not specified. and maybe use "e"

}

// IsNullable implements the Expression interface.
func (l *Log) IsNullable() bool { return l.Left.IsNullable() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comments above


// Resolved implements the Expression interface.
func (l *Log) Resolved() bool {
return l.Left.Resolved() && (l.Right == nil || l.Right.Resolved())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's only resolvable for arguments from domain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but I copied that from:

https://github.com/src-d/go-mysql-server/blob/master/sql/expression/function/ceil_round_floor.go#L258-L259

Does it mean that I don't have to provide a Resolved method?

sql/expression/function/logarithm.go Show resolved Hide resolved

// LogBaseMaker returns LogBase creator functions with a specific base.
func LogBaseMaker(base float64) func(e sql.Expression) sql.Expression {
b := base
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't you pass directly the argument base to the function NewLogBase in the closure?

sql/expression/function/logarithm_test.go Show resolved Hide resolved
@@ -3,6 +3,7 @@ package function
import (
"gopkg.in/src-d/go-mysql-server.v0/sql"
"gopkg.in/src-d/go-mysql-server.v0/sql/expression/function/aggregation"
"math"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same about import style

Signed-off-by: Theo Despoudis <thdespou@hotmail.com>
Signed-off-by: Theo Despoudis <thdespou@hotmail.com>
@theodesp
Copy link
Contributor Author

I've simplified the logic even more @kuba-- Please take a look

@ajnavarro ajnavarro merged commit 85cd592 into src-d:master Oct 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants