Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sql: internal error: DECIMAL: could not evaluate #40327

Closed
maddyblue opened this issue Aug 29, 2019 · 4 comments · Fixed by #40379
Closed

sql: internal error: DECIMAL: could not evaluate #40327

maddyblue opened this issue Aug 29, 2019 · 4 comments · Fixed by #40379
Assignees
Labels
C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. O-sqlsmith

Comments

@maddyblue
Copy link
Contributor

CREATE TABLE table1 ();

SELECT
        3.432390042674610877E+21 / 'Infinity'
        + (-8328160919613561056)
FROM
        table1;
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/expr.go:78: processExpression()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/expr.go:160: init()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/processors.go:146: Init()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/processors.go:844: InitWithEvalCtx()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/processors.go:820: Init()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/tablereader.go:112: newTableReader()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/processors.go:1028: newProcessor()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/flow.go:321: makeProcessor()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/flow.go:396: setupProcessors()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/flow.go:479: setup()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/server.go:457: setupFlow()
github.com/cockroachdb/cockroach/pkg/sql/distsqlrun/server.go:512: SetupLocalSyncFlow()
github.com/cockroachdb/cockroach/pkg/sql/distsql_running.go:237: setupFlows()
github.com/cockroachdb/cockroach/pkg/sql/distsql_running.go:326: Run()
github.com/cockroachdb/cockroach/pkg/sql/distsql_running.go:962: PlanAndRun()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor_exec.go:816: execWithDistSQLEngine()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor_exec.go:708: dispatchToExecutionEngine()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor_exec.go:417: execStmtInOpenState()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor_exec.go:98: execStmt()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor.go:1238: execCmd()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor.go:1167: run()
github.com/cockroachdb/cockroach/pkg/sql/conn_executor.go:444: ServeConn()
github.com/cockroachdb/cockroach/pkg/sql/pgwire/conn.go:584: func1()

@maddyblue maddyblue added C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. O-sqlsmith labels Aug 29, 2019
@rafiss
Copy link
Collaborator

rafiss commented Aug 29, 2019

I've been looking into this. The problem seems to be coming from constant.go ResolveAsType() when it tries to deal with the following value:

8328160919613561056.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

It calls the following function, which can only deal with a precision of 2000 places, but the above string has 2019 trailing zeroes.

// SetString sets d to s. Any non-standard NaN values are converted to a
// normal NaN. Any negative zero is converted to positive.
func (d *DDecimal) SetString(s string) error {
	// Using HighPrecisionCtx here restricts the max and min exponents to 2000,
	// and the precision to 2000 places. Any rounding or other inexact conversion
	// will result in an error.
	_, res, err := HighPrecisionCtx.SetString(&d.Decimal, s)
	if res != 0 || err != nil {
		return makeParseError(s, types.Decimal, nil)
	}
        // ...
}

Next I'll try to figure out why the number ends up getting represented like that.

@rafiss
Copy link
Collaborator

rafiss commented Aug 29, 2019

I found that any decimal division with a divisor of Infinity will result in a value with an Exponent of etiny(). That comes from the quoSpecials() function in apd, which sets d.Exponent = c.etiny()

// etiny returns the smallest value an Exponent can contain.
func (c *Context) etiny() int32 {
	return c.MinExponent - int32(c.Precision) + 1
}

In this case, the constant folding uses eval.go, which uses the context DecimalCtx, which has a precision of 20 and a min exponent of -2000.

But later (as mentioned in the above comment), SetString() in datum.go uses HighPrecisionCtx, which cannot handle that exponent.

@mjibson I believe you have the most knowledge about how we should be using decimals and the various contexts. Do you know why HighPrecisionCtx is used in datum.go, and why DecimalCtx is used in eval.go? Does your comment here (#39540 (comment)) indicate that we should actually be using ExactCtx everywhere?

Also, as an aside, I am curious why we have implemented x / Inf to result in 0E-2019 instead of 0.

@maddyblue
Copy link
Contributor Author

Do you know why HighPrecisionCtx is used in datum.go, and why DecimalCtx is used in eval.go?

The various decimal contexts were chosen by me to try to match the postgres behavior of the corresponding operation, but stuff dealing with precision and exponents near the edge has not been fully tested or made identical. We could probably change folding to use HighPrecisionCtx? Although I don't think that would affect this bug at all.

I'm not exactly clear on why or when SetString is getting called? Is it at the end of constant folding when converting to a typed datum?

We have a few options:

  1. SetString shouldn't care about the number of digits in the decimal. It doesn't in postgres, which appears to accept a decimal of any length. Unclear what affect this will have on the rest of the decimal operations. We could try using ExactCtx in SetString?
  2. Constant folding could call Reduce to remove trailing zeros. But I think we could still come up with things that SetString would error on.

Does your comment here (#39540 (comment)) indicate that we should actually be using ExactCtx everywhere?

No, it should be used only in the same places where it's used in normal execution. It's used in decimal - decimal subtraction:

_, err := ExactCtx.Sub(&dd.Decimal, l, r)

I am curious why we have implemented x / Inf to result in 0E-2019 instead of 0.

This is to match the gda spec for divide. See the tests at https://github.com/cockroachdb/apd/blob/master/testdata/divide.decTest#L601. That is, none of here made this decision, we just followed the spec. Most decimal implementations around follow that spec (java, python, etc.).

@rafiss
Copy link
Collaborator

rafiss commented Aug 30, 2019

Thanks for the very helpful background and suggestions! I have a slightly better picture of what's going on.

First off, here is a simple repro:

	var d1, d2, d3 apd.Decimal
	var final, quo, sum apd.Decimal
	d1.SetString("1")
	d2.SetString("Infinity")
	d3.SetString("1")

        // DecimalCtx is used in eval.go division
	tree.DecimalCtx.Quo(&quo, &d1, &d2)
        // ExactCtx is used in eval.go addition
	tree.ExactCtx.Add(&sum, &quo, &d3)
        // HighPrecisionCtx is used in datum.go SetString
	_, res, err := tree.HighPrecisionCtx.SetString(&final, sum.String())
	if res != 0 || err != nil {
		panic("parse error")
	}

I'm not exactly clear on why or when SetString is getting called? Is it at the end of constant folding when converting to a typed datum?

SetString ends up getting called during the distsql engine's setup flow stage. It makes a distsql processor for each node in the SQL command. In this case it fails when handling the RenderExpr as it deals with the result of the constant-folding. It calls processExpression() in distsqlrun/expr.go and that is the thing that calls SetString with that really long decimal value as an argument.

As it turns out, any one of the following things ends up solving the error.

  1. Using ExactCtx in datum.go SetString.
  2. Using HighPrecisionCtx for eval.go addition.
  3. Calling Reduce after constant folding. Though I'm not sure where exactly we would want to do this, one option is in pkg/sql/opt/norm/factory.go ConstructConstVal.

If we do options 1 or 2, then the SQL query in the original bug report produces a result that has 2019 decimal places. If we do option 3, then the result has no decimal places.

Based on your comment, it seems like maybe option 1 is the best approach and is desirable for other reasons. And maybe it would be safe to do 3 as well for good measure, though I am not sure if this would violate the spec that you referenced.

I'm guessing you already knew this, but it seems like as far as Postgres goes, it does not support Infinity for decimal types, so it's possible we have some flexibility around some of these decisions.

rafiss added a commit to rafiss/cockroach that referenced this issue Aug 30, 2019
Allow decimals of any length, like how Postgres does. This also prevents
errors from happening in some cases like division by infinity.

relates to cockroachdb#40327

Release note: None
@craig craig bot closed this as completed in b4caac8 Sep 3, 2019
craig bot pushed a commit that referenced this issue Sep 3, 2019
39685: workload/tpcc: extend --wait to also support fractions r=danhhz,petermattis a=knz

In order to use TPC-C for operational testing (i.e. NOT benchmarking)
it is useful to increase the rate of transactions without
altogether saturating the TPS capacity (`--wait=false`).

For this purpose, this commit extends the syntax accepted by `--wait`
to recognize a multiplier which is applied to all wait times. The
default is 1.0 (i.e. 100% of the wait time required by a benchmark).
The words `true`/`on` and `false`/`off` are aliases for 1.0 and 0.0,
respectively

40380: exec: use same decimal context as non-vec r=rafiss a=rafiss

Use the same decimal contexts that eval.go uses for decimal arithmetic.

based on discussion in #40327

Release note: None

Co-authored-by: Raphael 'kena' Poss <knz@cockroachlabs.com>
Co-authored-by: Rafi Shamim <rafi@cockroachlabs.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. O-sqlsmith
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants