Returns if `functionName` with the given `argTypes` is deterministic. Returns true if the function was not found or determinism cannot be established.
| 359 | /// Returns true if the function was not found or determinism cannot be |
| 360 | /// established. |
| 361 | bool isDeterministic( |
| 362 | const std::string& functionName, |
| 363 | const std::vector<TypePtr>& argTypes) { |
| 364 | // We know that the 'cast', 'and', and 'or' special forms are deterministic. |
| 365 | // Hard-code them here because they are not real functions and hence cannot |
| 366 | // be resolved by the code below. |
| 367 | if (functionName == "and" || functionName == "or" || |
| 368 | functionName == "coalesce" || functionName == "if" || |
| 369 | functionName == "switch" || functionName == "cast") { |
| 370 | return true; |
| 371 | } |
| 372 | |
| 373 | // Check if this is a simple function. |
| 374 | if (auto simpleFunctionEntry = |
| 375 | exec::simpleFunctions().resolveFunction(functionName, argTypes)) { |
| 376 | return simpleFunctionEntry->getMetadata().isDeterministic(); |
| 377 | } |
| 378 | |
| 379 | // Vector functions are a bit more complicated. We need to fetch the list of |
| 380 | // available signatures and check if any of them bind given the current |
| 381 | // input arg types. If it binds (if there's a match), we fetch the function |
| 382 | // and return the isDeterministic bool. |
| 383 | try { |
| 384 | if (auto vectorFunctionSignatures = |
| 385 | exec::getVectorFunctionSignatures(functionName)) { |
| 386 | core::QueryConfig config({}); |
| 387 | for (const auto& signature : *vectorFunctionSignatures) { |
| 388 | if (exec::SignatureBinder(*signature, argTypes).tryBind()) { |
| 389 | if (auto vectorFunction = |
| 390 | exec::getVectorFunction(functionName, argTypes, {}, config)) { |
| 391 | return vectorFunction->isDeterministic(); |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | } |
| 396 | } |
| 397 | // TODO: Some stateful functions can only be built when constant arguments |
| 398 | // are passed, making the getVectorFunction() call above to throw. We only |
| 399 | // have a few of these functions, so for now we assume they are |
| 400 | // deterministic so they are picked for Fuzz testing. Once we make the |
| 401 | // isDeterministic() flag static (and hence we won't need to build the |
| 402 | // function object in here) we can clean up this code. |
| 403 | catch (const std::exception& e) { |
| 404 | LOG(WARNING) << "Unable to determine if '" << functionName |
| 405 | << "' is deterministic or not. Assuming it is."; |
| 406 | return true; |
| 407 | } |
| 408 | |
| 409 | // functionName must be a special form. |
| 410 | LOG(WARNING) << "Unable to determine if '" << functionName |
| 411 | << "' is deterministic or not. Assuming it is."; |
| 412 | return true; |
| 413 | } |
| 414 | |
| 415 | std::optional<CallableSignature> processConcreteSignature( |
| 416 | const std::string& functionName, |
no test coverage detected