diff --git a/cmake/v8.cmake b/cmake/v8.cmake new file mode 100644 index 000000000000..1dd3666b2926 --- /dev/null +++ b/cmake/v8.cmake @@ -0,0 +1,45 @@ +# Copyright (c) 2024, Percona and/or its affiliates. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +# Simplify Google V8 usage by defining ext::v8 interface library. + +MACRO(MYSQL_CHECK_V8) + IF(NOT DEFINED V8_INCLUDE_DIR OR NOT DEFINED V8_LIB_DIR) + MESSAGE(FATAL_ERROR "No path to V8 headers and libraries. Please set V8_INCLUDE_DIR and V8_LIB_DIR.") + ENDIF() + + # Check that V8_INCLUDE_DIR contains necessary headers. + IF(NOT EXISTS ${V8_INCLUDE_DIR}/v8.h) + MESSAGE(FATAL_ERROR "Bad path to V8 headers. No v8.h in V8_INCLUDE_DIR.") + ENDIF() + + # Also check that V8_LIB_DIR contains necessary libraries. + FIND_LIBRARY(V8_LIB v8_monolith PATHS ${V8_LIB_DIR} NO_DEFAULT_PATH) + IF(NOT V8_LIB) + MESSAGE(FATAL_ERROR "Bad path to V8 libraries. No v8_monolith library in V8_LIB_DIR.") + ENDIF() + + ADD_LIBRARY(v8_interface INTERFACE) + + TARGET_INCLUDE_DIRECTORIES(v8_interface SYSTEM INTERFACE ${V8_INCLUDE_DIR}) + + TARGET_LINK_LIBRARIES(v8_interface INTERFACE ${V8_LIB}) + + # Uncomment below if you are playing with debug build of V8 library. + # TODO: Add check which will determine if we need to use this option automagically? + # TARGET_COMPILE_DEFINITIONS(v8_interface INTERFACE V8_ENABLE_CHECKS) + + ADD_LIBRARY(ext::v8 ALIAS v8_interface) +ENDMACRO() diff --git a/components/js_lang/CMakeLists.txt b/components/js_lang/CMakeLists.txt new file mode 100644 index 000000000000..82acad998af4 --- /dev/null +++ b/components/js_lang/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights +# reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License, version 2.0, +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License, version 2.0, for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +OPTION(WITH_JS_LANG "Build JS stored routine language component" OFF) + +IF(WITH_JS_LANG) + MESSAGE(STATUS "Building JS stored routine language component") +ELSE() + MESSAGE(STATUS "Not building JS stored routine language component") + RETURN() +ENDIF() + +# Prepare for using Google V8 as ext::v8 interface library. +INCLUDE(v8) +MYSQL_CHECK_V8() + +DISABLE_MISSING_PROFILE_WARNING() + +MYSQL_ADD_COMPONENT(js_lang + component.cc + js_lang_core.cc + js_lang_param.cc + LINK_LIBRARIES ext::v8 + MODULE_ONLY +) diff --git a/components/js_lang/README.md b/components/js_lang/README.md new file mode 100644 index 000000000000..492380cc0a59 --- /dev/null +++ b/components/js_lang/README.md @@ -0,0 +1,181 @@ +About this component +==================== + +This component adds to Percona Server for MySQL support of Stored Functions +and Procedures written in JS language or, more formally, ECMAScript by +employing Google's V8 engine. + +JS is an abbreviation for JavaScript, but the latter is a trademark of Oracle. + +Support for JS language is provided by implementing External Program Execution +service. OTOH the component uses MySQL Stored Program family of services as +well as some other exposed by SQL core to achieve this. + + +Building and installing +======================= + +To enable support for JS Stored Programs in Percona Server for MySQL using +this component one needs to configure the server project using `cmake` with +the following additional flags: + + -DWITH_JS_LANG=ON \ + -DV8_INCLUDE_DIR=/include \ + -DV8_LIB_DIR= + +and then build the server as usual. + +After installing and starting-up the built server one needs to install the +component using: + + INSTALL COMPONENT 'file://component_js_lang'; + +One also needs to grant new global dynamic `CREATE_JS_ROUTINE` privilege after +that to users who will be creating JS routines (in addition to standard +`CREATE ROUTINE` privilege). For example: + + GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; + + +Recommended V8 version and build process for it +----------------------------------------------- + +This component was developed and tested with 12.9.202.22 version of V8, +newer versions might not work. Some older versions are known not to work. + +We use static, monolith version of V8 library in our component, which is +configured and built using the following commands: + + gn gen "out.gn/static" -vv --fail-on-unused-args \ + --args='v8_monolithic=true + v8_static_library=true + v8_enable_sandbox=false + v8_enable_pointer_compression=false + is_clang=false + is_asan=false + is_debug=false + is_official_build=false + treat_warnings_as_errors=false + v8_enable_i18n_support=false + v8_use_external_startup_data=false + use_custom_libcxx=false + v8_enable_maglev=false + use_sysroot=false + is_component_build=false' + ninja -C out.gn/static + +Note that using these non-default options solves potential problems with +compatibility of our component with V8 library version built using default +V8 build process, such as usage of clang compiler (moreover own version of +it), custom libcxx and so on. + +It is good idea to use build steps (taking into account different V8 version +used and minimal changes to build options) from the `build_v8.sh` script from +Percona's https://github.com/percona/mysql-shell-packaging/ project to prepare +version of V8 library which is compatible with this component. + + +Examples +======== + +Some examples of routines in JS language implemented by this component are +available in `component_js_lang` test suite and `js_lang_basic.test` +specifically. + + +TODO/Future work: +================= + +Short-term +---------- + +- Better error reporting (stacktraces!) +- Console support +- Handling of KILL/timeouts. +- Memory tracking/limiting? +- Handling of async JS features +- Execution of SQL + +Long-term +--------- + +- Observability improvements? +- Debugging? +- Environment support in general? +- Modules support? +- Function attributes and optional behavior (see TODOs)? +- Custom classes for some of types (see TODOs)? + +License information: +==================== + +This component relies on Google V8 engine to execute JS code. +We include V8 headers as well as link with V8 library(ies). + +The below you can find the license information for Google V8 engine +(version 12.9.202.22): + +V8 License: +----------- + +This license applies to all parts of V8 that are not externally +maintained libraries. The externally maintained libraries used by V8 +are: + + - PCRE test suite, located in + test/mjsunit/third_party/regexp-pcre/regexp-pcre.js. This is based on the + test suite from PCRE-7.3, which is copyrighted by the University + of Cambridge and Google, Inc. The copyright notice and license + are embedded in regexp-pcre.js. + + - Layout tests, located in test/mjsunit/third_party/object-keys. These are + based on layout tests from webkit.org which are copyrighted by + Apple Computer, Inc. and released under a 3-clause BSD license. + + - Strongtalk assembler, the basis of the files assembler-arm-inl.h, + assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, + assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h, + assembler-x64.cc, assembler-x64.h, assembler.cc and assembler.h. + This code is copyrighted by Sun Microsystems Inc. and released + under a 3-clause BSD license. + + - Valgrind client API header, located at src/third_party/valgrind/valgrind.h + This is released under the BSD license. + + - The Wasm C/C++ API headers, located at third_party/wasm-api/wasm.{h,hh} + This is released under the Apache license. The API's upstream prototype + implementation also formed the basis of V8's implementation in + src/wasm/c-api.cc. + +These libraries have their own licenses; we recommend you read them, +as their terms may differ from the terms below. + +Further license information can be found in LICENSE files located in +sub-directories. + +Copyright 2014, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/js_lang/component.cc b/components/js_lang/component.cc new file mode 100644 index 000000000000..d5bd048df76f --- /dev/null +++ b/components/js_lang/component.cc @@ -0,0 +1,332 @@ +/* Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This component adds to Percona Server for MySQL support of Stored Functions + and Procedures written in JS language or, more formally, ECMAScript by + employing Google's V8 engine. + + See README.md for more details. + + This file only contains code which makes our implementation of support for JS + routines a component. I.e. code related to component definition, its + initialization/shutdown, preparing for usage of SQL core services our code + depends on, some glue code exposing our JS support as a service. + + The central part of our implementation of support for JS routines can be + found in js_lang_core.cc/.h. +*/ + +// Service that our component implements. +#include + +#include + +#include "js_lang_common.h" +#include "js_lang_core.h" + +REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); +REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); +REQUIRES_SERVICE_PLACEHOLDER(mysql_charset); +REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); +REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error); +REQUIRES_SERVICE_PLACEHOLDER(mysql_security_context_options); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_argument_metadata_query); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_metadata_query); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_metadata_query); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_float); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_int); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_null); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_string); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_unsigned_int); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_float); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_int); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_null); +REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_string); +REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_runtime_argument_unsigned_int); +REQUIRES_SERVICE_PLACEHOLDER(mysql_string_charset_converter); +REQUIRES_SERVICE_PLACEHOLDER(mysql_string_copy_converter); +REQUIRES_SERVICE_PLACEHOLDER(mysql_string_factory); +REQUIRES_SERVICE_PLACEHOLDER(mysql_string_get_data_in_charset); +REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_attributes); +REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); +REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_store); + +/** + Implementation of External Program Capability Query and External Program + Execution services. +*/ +class Js_lang_service_imp { + public: + /** + Implementation of external_program_capability_query::get() method by our + component. + + Reports to SQL core that we support "JS" language for external stored + programs and only it. Fails if queried about any other capabilities. + + @retval False - Success. This was a request if specific language is + supported. "*(bool*)value" is set to indicate whether + language which name was passed as "property" is + supported. + @retval True - Failure due to unsupported capability query. + Error has been reported. + */ + static DEFINE_BOOL_METHOD(get, (const char *capability, char *property, + void *value)) { + // Mimic mysql_stored_program_* services which use case-sensitive + // comparisons for string key names. + if (strcmp(capability, "supports_language") != 0) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Unknown capability request from " CURRENT_COMPONENT_NAME_STR + " component."); + return true; + } + + /* + Use case-insensitive comparison for language name, this is more + user-friendly and consistent with data-dictionary layer behavior, + which uppercases external language name before storing it in + 'routines' table. + */ + *reinterpret_cast(value) = + (strcasecmp(property, LANGUAGE_NAME) == 0); + + return false; + } + + /** + Implementation of external_program_execution::init() method by our + component. + + Create and initialize component's counterpart for sp_head object for + JS routine (fail for other languages). + + @retval False - Success. + @retval True - Failure (Wrong language or routine type, caller is + responsible for reporting an error). + */ + static DEFINE_BOOL_METHOD(init, (stored_program_handle sp, + stored_program_statement_handle, + external_program_handle *lang_sp)) { + /* + TODO: Note that if we are to support simultaneous use of several + components implementing support for different languages, we will need a + special router component which will serve as default implementation of + language service API, and route calls among other implementations + according to language passed to it (e.g. by keeping language => component + mapping). + + At the moment we simply check that we are not called for routine in + language other than one this component implements. + + Note that for "init" method caller invokes my_error() in case of failure + (unlike for "parse" and "execute" methods). + */ + mysql_cstring_with_length language; + always_ok(mysql_service_mysql_stored_program_metadata_query->get( + sp, "sp_language", &language)); + + if (strcasecmp(language.str, LANGUAGE_NAME) != 0) { + // We do not support this language. + return true; + } + + uint16_t sql_sp_type; + always_ok(mysql_service_mysql_stored_program_metadata_query->get( + sp, "sp_type", &sql_sp_type)); + + if (!(sql_sp_type == MYSQL_STORED_PROGRAM_DATA_QUERY_TYPE_FUNCTION || + sql_sp_type == MYSQL_STORED_PROGRAM_DATA_QUERY_TYPE_PROCEDURE)) { + /* + Even though at the moment SQL-layer doesn't allow stored programs + other than Functions and Procedures in external language, we still + play safe and fail in case of attempt to create usupported type + of program (e.g. trigger) in JS. + */ + return true; + } + + *lang_sp = + reinterpret_cast(new Js_sp(sp, sql_sp_type)); + return false; + } + + /** + Implementation of external_program_execution::deinit() method by our + component. + + Destroy component's counterpart for sp_head object for JS routine. + */ + static DEFINE_BOOL_METHOD(deinit, (MYSQL_THD, external_program_handle lang_sp, + stored_program_handle)) { + delete reinterpret_cast(lang_sp); + return false; + } + + /** + Implementation of external_program_execution::parse() method by our + component. + + Parse JS routine and do some preparations for its execution. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + static DEFINE_BOOL_METHOD(parse, (external_program_handle lang_sp, + stored_program_statement_handle)) { + Js_sp *sp = reinterpret_cast(lang_sp); + return sp->parse(); + } + + /** + Implementation of external_program_execution::execute() method by our + component. + + Execute JS routine (which was parsed and prepared for execution earlier). + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + static DEFINE_BOOL_METHOD(execute, (external_program_handle lang_sp, + stored_program_statement_handle)) { + Js_sp *sp = reinterpret_cast(lang_sp); + return sp->execute(); + } +}; + +static mysql_service_status_t component_init() { + /* + First, let us try to do things which can fail, at least in theory. + + V8 doesn't support V8::Dispose() -> V8::Initialize() state transition, + so we have to block UNISTALL COMPONENT -> INSTALL COMPONENT case. + */ + if (Js_v8::is_used_or_shutdown()) { + my_error( + ER_LANGUAGE_COMPONENT, MYF(0), + "Re-installing the component without server restart is not supported."); + return 1; + } + + // Play safe, even though the below can't fail at the moment. + if (register_create_privilege()) return 1; + + // Registering slot in THD for the component always succeeds (unless OOM)! + Js_thd::register_slot(); + + // Prepare V8 for usage. + Js_v8::init(); + + return 0; +} + +static mysql_service_status_t component_deinit() { + /* + Block component shutdown (and V8 deinitialization specifically), + if there are outstanding Isolates around. + + Note that component shutdown can't happen concurrently with a call + to any of methods provided by external_program_execution service. + This is ensured by the fact that these methods are always called + using my_service wrapper, which does service acquire/release, and + the fact that component infrastructure blocks unloading of component + while any service provided by it is acquired (i.e. does its own + reference counting). + + So the goal of below check is to block component unloading between + calls to methods of this service, until connections which used + our component at some point are closed/THDs with associated Js_thd + contexts/isolates are gone. + */ + if (Js_v8::is_used_or_shutdown()) { + my_error(ER_LANGUAGE_COMPONENT_CANNOT_UNINSTALL, MYF(0)); + return 1; + } + + // Play safe, even though the below can't fail at the moment. + if (unregister_create_privilege()) return 1; + + Js_thd::unregister_slot(); + + // Shutdown V8. + Js_v8::shutdown(); + + return 0; +} + +// clang-format off +BEGIN_SERVICE_IMPLEMENTATION(js_lang, external_program_capability_query) + Js_lang_service_imp::get, +END_SERVICE_IMPLEMENTATION(); + +BEGIN_SERVICE_IMPLEMENTATION(js_lang, external_program_execution) + Js_lang_service_imp::init, + Js_lang_service_imp::deinit, + Js_lang_service_imp::parse, + Js_lang_service_imp::execute, +END_SERVICE_IMPLEMENTATION(); + +BEGIN_COMPONENT_PROVIDES(js_lang) + PROVIDES_SERVICE(js_lang, external_program_capability_query), + PROVIDES_SERVICE(js_lang, external_program_execution), +END_COMPONENT_PROVIDES(); + +BEGIN_COMPONENT_REQUIRES(js_lang) + REQUIRES_SERVICE(dynamic_privilege_register), + REQUIRES_SERVICE(global_grants_check), + REQUIRES_SERVICE(mysql_charset), + REQUIRES_SERVICE(mysql_current_thread_reader), + REQUIRES_SERVICE(mysql_runtime_error), + REQUIRES_SERVICE(mysql_security_context_options), + REQUIRES_SERVICE(mysql_stored_program_argument_metadata_query), + REQUIRES_SERVICE(mysql_stored_program_metadata_query), + REQUIRES_SERVICE(mysql_stored_program_return_metadata_query), + REQUIRES_SERVICE(mysql_stored_program_return_value_float), + REQUIRES_SERVICE(mysql_stored_program_return_value_int), + REQUIRES_SERVICE(mysql_stored_program_return_value_null), + REQUIRES_SERVICE(mysql_stored_program_return_value_string), + REQUIRES_SERVICE(mysql_stored_program_return_value_unsigned_int), + REQUIRES_SERVICE(mysql_stored_program_runtime_argument_float), + REQUIRES_SERVICE(mysql_stored_program_runtime_argument_int), + REQUIRES_SERVICE(mysql_stored_program_runtime_argument_null), + REQUIRES_SERVICE(mysql_stored_program_runtime_argument_string), + REQUIRES_SERVICE(mysql_stored_program_runtime_argument_unsigned_int), + REQUIRES_SERVICE(mysql_string_charset_converter), + REQUIRES_SERVICE(mysql_string_copy_converter), + REQUIRES_SERVICE(mysql_string_factory), + REQUIRES_SERVICE(mysql_string_get_data_in_charset), + REQUIRES_SERVICE(mysql_thd_attributes), + REQUIRES_SERVICE(mysql_thd_security_context), + REQUIRES_SERVICE(mysql_thd_store), +END_COMPONENT_REQUIRES(); + +BEGIN_COMPONENT_METADATA(js_lang) + METADATA("mysql.author", "Percona Corporation"), + METADATA("mysql.license", "GPL"), +END_COMPONENT_METADATA(); + +DECLARE_COMPONENT(js_lang, CURRENT_COMPONENT_NAME_STR) + component_init, + component_deinit, +END_DECLARE_COMPONENT(); + +DECLARE_LIBRARY_COMPONENTS + &COMPONENT_REF(js_lang) +END_DECLARE_LIBRARY_COMPONENTS + // clang-format on diff --git a/components/js_lang/js_lang_common.h b/components/js_lang/js_lang_common.h new file mode 100644 index 000000000000..313aff9bc7b6 --- /dev/null +++ b/components/js_lang/js_lang_common.h @@ -0,0 +1,100 @@ +/* Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This header provides some component-wide declarations which are + used by several other files in this component. +*/ +#ifndef COMPONENT_JS_LANG_JS_LANG_COMMON_H +#define COMPONENT_JS_LANG_JS_LANG_COMMON_H + +#include +#include + +/* + Services and helper headers provided by SQL core which our component uses. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + Placeholders for services from SQL core used by our component. +*/ +extern REQUIRES_SERVICE_PLACEHOLDER(dynamic_privilege_register); +extern REQUIRES_SERVICE_PLACEHOLDER(global_grants_check); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_charset); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_current_thread_reader); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_runtime_error); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_security_context_options); +extern REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_argument_metadata_query); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_metadata_query); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_metadata_query); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_float); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_int); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_null); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_return_value_string); +extern REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_return_value_unsigned_int); +extern REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_runtime_argument_float); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_int); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_stored_program_runtime_argument_null); +extern REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_runtime_argument_string); +extern REQUIRES_SERVICE_PLACEHOLDER( + mysql_stored_program_runtime_argument_unsigned_int); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_string_charset_converter); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_string_copy_converter); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_string_factory); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_string_get_data_in_charset); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_attributes); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_security_context); +extern REQUIRES_SERVICE_PLACEHOLDER(mysql_thd_store); + +/** + Helper function which is used to absorb results of service calls + which are not supposed to fail. + + @sa Note about our approach to error handling in js_lang_core.cc. +*/ +inline void always_ok(bool service_result) { assert(!service_result); } + +/* + Some global constants and defines used through the component. +*/ + +// Defined as a macro so we can easier concatenate it with other literals. +#define CURRENT_COMPONENT_NAME_STR "js_lang" + +// Name/Identifier of the language this component implements. +static constexpr const char LANGUAGE_NAME[] = "JS"; + +// Name of global privilege required from user creating JS routine +// in addition to usual CREATE ROUTINE privilege on the schema. +static constexpr std::string_view CREATE_PRIVILEGE_NAME = "CREATE_JS_ROUTINE"; + +#endif /* COMPONENT_JS_LANG_JS_LANG_COMMON_H */ diff --git a/components/js_lang/js_lang_core.cc b/components/js_lang/js_lang_core.cc new file mode 100644 index 000000000000..25397e14ad82 --- /dev/null +++ b/components/js_lang/js_lang_core.cc @@ -0,0 +1,519 @@ +/* Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This and corresponding header file contain classes and routines which are + the central part of our implementation of support for JS routines. + + Note about error handling and Out-Of-Memory conditions + ------------------------------------------------------ + + In general case, this component tries to gracefully handle potential errors + from calls to services provided by SQL core and V8 engine. + However, in some trivial cases, when we know that service/V8 call can't fail + in practice (e.g. because it is simple accessor to members of some object) + we have chosen to ignore instead, even though it theoretically possible + according to API. This allows to avoid clobbering our code with dead error + handling branches. + + Code in SQL core is not very consistent regarding OOM handling. In some + places it tries to catch std::bad_alloc/to handle NULL return value from + malloc() and in some other places it does not. Failures to insert data + into container/to append to strings are also often ignored. + And V8 simply crashes process on OOM in most cases. + + Taking into account the above this component doesn't try to handle OOM + gracefully either (i.e. will crash on OOM). +*/ + +#include "js_lang_core.h" + +#include "js_lang_common.h" + +std::unique_ptr Js_v8::s_platform; + +std::atomic Js_v8::s_ref_count = 0; + +void Js_v8::init() { + // We would like to enforce strict mode for code of JS routines right + // from the start. + v8::V8::SetFlagsFromString("--use_strict"); + + // v8::V8::InitializeICUDefaultLocation(argv[0]); + // v8::V8::InitializeExternalStartupData(argv[0]); + s_platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(s_platform.get()); + // The below call will simply crash the process if something is wrong. + (void)v8::V8::Initialize(); +} + +void Js_v8::shutdown() { + // We should have checked that there are no used isolates around earlier. + assert(s_ref_count.load() == 0); + + // Set flag indicating that component shutdown has been started for + // debug/assertion purposes. + // + // We also piggy-back on this flag to block attempts to install this + // component without server restart again. It is not possible due to + // V8 not supporting re-initialization. + s_ref_count.store(-1); + + // Shutdown V8. + v8::V8::Dispose(); + v8::V8::DisposePlatform(); + s_platform.reset(); +} + +std::shared_ptr Js_isolate::create() { + v8::Isolate::CreateParams create_params; + v8::Isolate *isolate; + + // Increment global Isolate reference counter to block component + // shutdown while we creating Isolate and as long as it is around. + // + // Component infrastructure ensures that methods of language service and + // thus this code can't be called after component shutdown starts. + Js_v8::inc_ref_count(); + + create_params.array_buffer_allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + + isolate = v8::Isolate::New(create_params); + return std::make_shared(isolate, + create_params.array_buffer_allocator); +} + +mysql_thd_store_slot Js_thd::s_thd_slot = nullptr; + +void Js_thd::register_slot() { + auto free_fn_lambda = [](void *resource) { + delete reinterpret_cast(resource); + return 0; + }; + always_ok(mysql_service_mysql_thd_store->register_slot( + CURRENT_COMPONENT_NAME_STR, free_fn_lambda, &s_thd_slot)); +} + +void Js_thd::unregister_slot() { + always_ok(mysql_service_mysql_thd_store->unregister_slot(s_thd_slot)); + s_thd_slot = nullptr; +} + +Js_sp::Js_sp(stored_program_handle sp, uint16_t sql_sp_type) : m_sql_sp(sp) { + assert(sql_sp_type == MYSQL_STORED_PROGRAM_DATA_QUERY_TYPE_FUNCTION || + sql_sp_type == MYSQL_STORED_PROGRAM_DATA_QUERY_TYPE_PROCEDURE); + m_type = (sql_sp_type == MYSQL_STORED_PROGRAM_DATA_QUERY_TYPE_FUNCTION) + ? Js_sp::FUNCTION + : Js_sp::PROCEDURE; + + always_ok(mysql_service_mysql_stored_program_metadata_query->get( + sp, "argument_count", &m_arg_count)); +} + +Js_sp::~Js_sp() { + for (auto &p : m_funcs) { + v8::Locker locker(p.second.isolate_ptr->get_v8_isolate()); + p.second.func.Reset(); + } +} + +void Js_sp::prepare_wrapped_body_and_out_param_indexes() { + mysql_cstring_with_length body; + + always_ok(mysql_service_mysql_stored_program_metadata_query->get( + m_sql_sp, "sp_body", &body)); + + // Comma separated list of INOUT and OUT parameter names. + // Empty if this is FUNCTION or PROCEDURE without such parameters. + std::string out_params_list; + + m_wrapped_body.append("(("); + + for (size_t i = 0; i < m_arg_count; ++i) { + if (i != 0) m_wrapped_body.append(", "); + + // We do not support JS routines with parameter names which are not + // valid JS identifiers. + // + // MySQL rules for identifiers and thus parameter names are somewhat + // more relaxed than JS rules. + // + // For example, MySQL identifier can start with digit (which JS doesn't + // allow). If quoted identifier is used as parameter name in MySQL then + // it can be any non-empty sequence consisting of Unicode BMP characters + // other than U+0000 which doesn't end with space. This includes JS + // reserved words. And some of JS reserved words can be used as MySQL + // parameter names even without quoting (because they are not reserved + // in SQL). + // + // Instead of detecting bad parameter names here, we rely on discovering + // them later when wrapped routine body is compiled. The error produced + // in this case might be cryptic but this probably acceptable for such + // a corner case. + // + // TODO: Consider trying to produce better error message by checking + // parameter name when routine is created. This can be achieved + // by trying to compile, for each parameter separately, small + // piece of code which uses the name as identifier. + // Alternative is to require parameter names to start with letter, + // but this might be too limiting (what about '$' or '_' ?) and + // doesn't solve problem with reserved words. + const char *arg_name; + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + m_sql_sp, i, "argument_name", &arg_name)); + + m_wrapped_body.append(arg_name); + + if (m_type == Js_sp::PROCEDURE) { + // MySQL only supports OUT and INOUT parameters in procedures. + bool is_out_param; + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + m_sql_sp, i, "out_variable", &is_out_param)); + + if (is_out_param) { + // Name of second and later parameters needs to be separated from + // the preceding one. + if (!out_params_list.empty()) out_params_list.append(", "); + out_params_list.append(arg_name); + m_out_param_indexes.push_back(i); + } + } + } + m_wrapped_body.append(") => {"); + + // For PROCEDURES in case of INOUT/OUT parameters we need to wrap JS code + // passed to us in additional function/layer, so we can return values of + // these parameters to our component as an array. + // + // We also do this to be able to detect and error out in situation when + // one tries to return value from PROCEDURE. + assert(out_params_list.empty() || m_type == Js_sp::PROCEDURE); + if (m_type == Js_sp::PROCEDURE) + m_wrapped_body.append("let js_lang_int_res = (() => {"); + + m_wrapped_body.append(body.str); + + // Finalize extra wrapping for PROCEDURE and pack IN/OUT parameter values + // in an array to be returned. + if (m_type == Js_sp::PROCEDURE) { + m_wrapped_body.append("})();"); + m_wrapped_body.append("return [js_lang_int_res"); + if (!out_params_list.empty()) { + m_wrapped_body.append(","); + m_wrapped_body.append(out_params_list); + } + m_wrapped_body.append("];"); + } + + m_wrapped_body.append("})"); +} + +v8::Local Js_sp::prepare_func(v8::Isolate *isolate, + v8::Local &context) { + v8::EscapableHandleScope handle_scope(isolate); + + v8::TryCatch try_catch(isolate); + + // Make v8::Function out of the wrapped code so we can use its Call() + // method to execute the code with parameters passed in the array. + + v8::Local source; + if (!v8::String::NewFromUtf8(isolate, m_wrapped_body.c_str(), + v8::NewStringType::kNormal, + m_wrapped_body.length()) + .ToLocal(&source)) { + // Wrapped routine body can exceed V8 string length limit. + // Though, in practice, InnoDB will complain in the error log about + // very big rows/types if routine body is huge enough, even when it + // is still below this limit. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Routine body exceeds V8 string length"); + return v8::Local(); + } + + v8::Local script; + + if (!v8::Script::Compile(context, source).ToLocal(&script)) { + v8::String::Utf8Value exception(isolate, try_catch.Exception()); + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + *exception ? *exception : "Unknown compilation error."); + return v8::Local(); + } + + v8::Local func_result; + if (!script->Run(context).ToLocal(&func_result)) { + v8::String::Utf8Value exception(isolate, try_catch.Exception()); + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + *exception ? *exception : "Unknown wrapper preparation error."); + return v8::Local(); + } + + if (!func_result->IsFunction()) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Unable to compile wrapper function."); + return v8::Local(); + } + + return handle_scope.Escape(func_result.As()); +} + +bool Js_sp::parse() { + Js_thd *js_thd = Js_thd::get_or_create_current_js_thd(); + + /* + If this parse() call happens during CREATE FUNCTION/PROCEDURE for the + routine we check if current user (i.e. user creating routine) has a global + CREATE_JS_ROUTINE privilege in addition to usual CREATE_ROUTINE privilege + on schema level required and checked by SQL core. + */ + if (js_thd->is_create_routine_command() && + !js_thd->current_user_has_create_js_privilege()) { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), + CREATE_PRIVILEGE_NAME.data()); + return true; + } + + // Prepare text of wrapper function for JS code. + prepare_wrapped_body_and_out_param_indexes(); + + /* + Get Isolate and JS context for current connection and active user. + Create one if needed. + + For security reasons, we use separate Isolates for execution of routines + under different users within the same connection (this can happen e.g. + due to calls of SQL SECURITY DEFINER routines). + + We use separate JS context for each connection. Additionally, we use + separate contexts for execution of routines under different users within + the same connection. + + Routines which are executed under the same user in the same connection + use the same JS context. + + We played with idea of having separate context for each routine call. + While it is nice from security point of view, it turned out to introduce + too much performance overhead for smaller functions (e.g. for simple + function calls calculating 50! we observed 10x slowdown caused by this). + + TODO: Consider supporting usage of different contexts for the same + connection/account depending on system variable value or routine + attribute. + */ + std::string auth_id = js_thd->get_current_auth_id(); + + auto auth_id_ctx = js_thd->get_or_create_auth_id_context(auth_id); + + v8::Isolate *isolate = auth_id_ctx->get_isolate(); + + // Lock the isolate to follow convention. + v8::Locker locker(isolate); + + // Make this isolate the current one. + v8::Isolate::Scope isolate_scope(isolate); + + // Create a stack-allocated handle scope. + v8::HandleScope handle_scope(isolate); + + v8::Local context = auth_id_ctx->get_context(); + + // Enter the context for compiling and getting the wrapper function. + v8::Context::Scope context_scope(context); + + /* + Compile and execute script to get wrapper function object. Later we + will use this function's Call() method for running our routine. + + For SQL SECURITY DEFINER routines this will be the only Function + object we will use. + SQL SECURITY INVOKER routines might be called with different user + account active within the same connection. So we might need to build + more Function objects for this account's JS context. + */ + v8::Local func = prepare_func(isolate, context); + + if (func.IsEmpty()) return true; + + m_funcs.try_emplace(auth_id, auth_id_ctx->isolate_ptr, isolate, func); + + // Prepare functions for getting parameter values. + if (prepare_get_param_funcs()) return true; + // And setting out parameter and return values. + if (m_type == Js_sp::FUNCTION) { + if (prepare_set_retval_func()) return true; + } else if (has_out_param()) { + if (prepare_set_out_param_funcs()) return true; + } + + return false; +} + +bool Js_sp::execute() { + Js_thd *js_thd = Js_thd::get_or_create_current_js_thd(); + + /* + Get Isolate and JS context for current connection and active user. + For SQL SECURITY INVOKER routines such Isolate/Context might not exist yet + (since earlier parse() call might have happened under different user, so + create one if needed. + + TODO: Consider optimizing case for SQL SECURITY DEFINER routines? + This can be non-trivial due to Js_thd vs Js_sp lifetime issues + and case when Isolate have been destroyed due to OOM. + */ + std::string auth_id = js_thd->get_current_auth_id(); + + auto auth_id_ctx = js_thd->get_or_create_auth_id_context(auth_id); + + v8::Isolate *isolate = auth_id_ctx->get_isolate(); + + v8::Locker locker(isolate); + + // Make this isolate the current one. + v8::Isolate::Scope isolate_scope(isolate); + + // Create a stack-allocated handle scope. + v8::HandleScope handle_scope(isolate); + + v8::Local context = auth_id_ctx->get_context(); + + // Enter the context for executing the function. + v8::Context::Scope context_scope(context); + + /* + Get Function object for the routine in this context. + + Again, object for active account and its context might be missing + in case of SQL SECURITY INVOKER routine, so prepare a new one if + necessary. + */ + v8::Local func; + + auto j = m_funcs.find(auth_id); + if (j == m_funcs.end()) { + func = prepare_func(isolate, context); + if (func.IsEmpty()) return true; + m_funcs.try_emplace(auth_id, auth_id_ctx->isolate_ptr, isolate, func); + } else { + func = v8::Local::New(isolate, j->second.func); + } + + v8::TryCatch try_catch(isolate); + + // Build array with program parameters. + v8::Local *arg_arr = nullptr; + + if (m_arg_count) { + arg_arr = new v8::Local[m_arg_count]; + for (size_t i = 0; i < m_arg_count; ++i) { + arg_arr[i] = m_get_param_func[i](isolate, i); + + if (arg_arr[i].IsEmpty()) { + // It is responsibility of get_param_func function to + // report error to user. + delete[] arg_arr; + return true; + } + } + } + + // Call the wrapper function passing parameters to it. + v8::Local result; + if (!func->Call(context, context->Global(), m_arg_count, arg_arr) + .ToLocal(&result)) { + v8::String::Utf8Value exception(isolate, try_catch.Exception()); + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + *exception ? *exception : "Unknown execution error"); + delete[] arg_arr; + return true; + } + delete[] arg_arr; + + if (m_type == Js_sp::FUNCTION) { + if (m_set_retval_func(context, 0, result)) return true; + } else { + // Values of INOUT and OUT parameters need to be unpacked from the array + // returned as result of function execution to corresponding parameters + // in runtime context. + // + // We also bark if one tries to return value other than undefined from + // PROCEDURE using return statement. + // + // TODO: Discuss with reviewer, I am not quite convinced it is worth + // doing it taking into account that extra wrapping + creation of array + // costs something. + + // Execution of wrapper function in case of PROCEDURE must return + // an array or throw (which is handled above). + assert(result->IsArray()); + v8::Local arr_result = result.As(); + + // The array returned must have the first element representing return + // value. + if (!arr_result->Get(context, 0).ToLocalChecked()->IsUndefined()) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Returning value from PROCEDURE is not allowed"); + return true; + } + + if (has_out_param()) { + size_t res_arr_idx = 1; + for (auto param_idx : m_out_param_indexes) { + if (m_set_out_param_func[res_arr_idx - 1]( + context, param_idx, + // The array returned must have an element for each OUT param. + arr_result->Get(context, res_arr_idx).ToLocalChecked())) { + // OUT param setter function is responsible for reporting error + // in case of its failure. + return true; + } + ++res_arr_idx; + } + } + } + + return false; +} + +bool register_create_privilege() { + if (mysql_service_dynamic_privilege_register->register_privilege( + CREATE_PRIVILEGE_NAME.data(), CREATE_PRIVILEGE_NAME.length())) { + // With current implementation of the dynamic_privilege_register + // service this should never happen. But we try to play safe since what + // the service does is non-trivial and implementation might change. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't register privilege for " CURRENT_COMPONENT_NAME_STR + " component."); + return true; + } + return false; +} + +bool unregister_create_privilege() { + if (mysql_service_dynamic_privilege_register->unregister_privilege( + CREATE_PRIVILEGE_NAME.data(), CREATE_PRIVILEGE_NAME.length())) { + // With current implementation of the dynamic_privilege_register + // service this should never happen. But we try to play safe since what + // the service does is non-trivial and implementation might change. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't unregister privilege for " CURRENT_COMPONENT_NAME_STR + " component."); + return true; + } + return false; +} diff --git a/components/js_lang/js_lang_core.h b/components/js_lang/js_lang_core.h new file mode 100644 index 000000000000..ad6c1d9db85b --- /dev/null +++ b/components/js_lang/js_lang_core.h @@ -0,0 +1,542 @@ +/* Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This and corresponding .cc file contain classes and routines which are + the central part of our implementation of support for JS routines. +*/ + +#ifndef COMPONENT_JS_LANG_JS_LANG_CORE_H +#define COMPONENT_JS_LANG_JS_LANG_CORE_H + +#include +#include +#include +#include + +#include +#include + +#include // stored_program_handle + +#include "js_lang_common.h" + +/** + Representation of V8 engine in our component. + + @note Since there can be only one V8 engine in an application it + contains static methods/members only. Could be made a singleton, + but we decided to avoid extra complexity related to this for now. +*/ +class Js_v8 { + public: + /** Initialize V8 for use in our component. */ + static void init(); + + /** + Shutdown V8 before shutting down component. + + @note Can't be called if V8 is in use/there are still V8 isolates around. + */ + static void shutdown(); + + /** + Checks if V8 is in use/there are any v8::Isolates around (so V8/component + shutdown is not allowed) or V8 has been shutdown earlier (so V8/component + initialization are not allowed). + */ + static bool is_used_or_shutdown() { return s_ref_count.load() != 0; } + + private: + // Increment/decrement V8 usage/isolate global reference counter. + static void inc_ref_count() { + assert(s_ref_count.load() >= 0); + ++s_ref_count; + } + static void dec_ref_count() { --s_ref_count; } + + // Js_isolate methods need access to inc/dec_ref_count; + friend class Js_isolate; + + // V8 object representing Platform we are on. + static std::unique_ptr s_platform; + + // Global reference counter which aims to block component shutdown and V8 + // deinitialization while V8 is being used/having v8::Isolate around. + // + // Set to -1 to indicate that process of component shutdown has started + // (used for debug purposes) or completed (we rely on this to block + // UNINSTALL -> INSTALL scenario). + static std::atomic s_ref_count; +}; + +/** + Wrapper class around v8::Isolate which simplifies its lifetime management + through reference counting pointers by packaging it with its allocator. + + It also contains static methods for V8 initialization/shutdown as well as + global reference counter for isolates, so we can block component shutdown + if there are connections around that use isolates and therefore V8. +*/ +class Js_isolate { + public: + // TODO: Make ctor private once we stop using std::shared_ptr with this class. + Js_isolate(v8::Isolate *iso, v8::ArrayBuffer::Allocator *alloc) + : m_isolate(iso), m_allocator(alloc) {} + + ~Js_isolate() { + m_isolate->Dispose(); + delete m_allocator; + // Decrement global V8/Isolate reference counter to allow component + // shutdown if there are no other Isolates around. + Js_v8::dec_ref_count(); + } + + // Block default copy/move semantics. + Js_isolate(Js_isolate const &rhs) = delete; + Js_isolate(Js_isolate &&rhs) = delete; + Js_isolate &operator=(Js_isolate const &rhs) = delete; + Js_isolate &operator=(Js_isolate &&rhs) = delete; + + /** + Create new Js_isolate and increments global counter of isolates + accordingly. + */ + static std::shared_ptr create(); + + v8::Isolate *get_v8_isolate() const { return m_isolate; } + + private: + v8::Isolate *m_isolate; + v8::ArrayBuffer::Allocator *m_allocator; +}; + +/** + Component's per connection context. + + @note Corresponds to and is associated with core's THD object. +*/ +class Js_thd { + public: + explicit Js_thd(MYSQL_THD thd) : m_thd(thd) {} + ~Js_thd() { + // Play safe. Lock the isolate before deleting context associated with it. + for (auto &p : m_auth_id_contexts) { + { + v8::Locker locker(p.second.isolate_ptr->get_v8_isolate()); + p.second.context.Reset(); + } + } + } + + // Block default copy/move semantics. + Js_thd(Js_thd const &rhs) = delete; + Js_thd(Js_thd &&rhs) = delete; + Js_thd &operator=(Js_thd const &rhs) = delete; + Js_thd &operator=(Js_thd &&rhs) = delete; + + /** + Register slot for keeping Js_thd value associated with current connection/ + its THD object using mysql_thd_store service. + */ + static void register_slot(); + /** + Unregister slot used for keeping Js_thd value associated with current + connection/its THD object. + */ + static void unregister_slot(); + + /** Get Js_thd for current connection/THD, create if necessary. */ + static Js_thd *get_or_create_current_js_thd() { + MYSQL_THD thd; + always_ok(mysql_service_mysql_current_thread_reader->get(&thd)); + + void *opaque = mysql_service_mysql_thd_store->get(thd, s_thd_slot); + Js_thd *js_thd = reinterpret_cast(opaque); + if (js_thd == nullptr) { + js_thd = new Js_thd(thd); + always_ok(mysql_service_mysql_thd_store->set(thd, s_thd_slot, js_thd)); + } + return js_thd; + } + + /** + Return user@host pair representing user account currently active in the + connection (it can be different than user that was authenticated at connect + time if we are executing SQL SECURITY DEFINER routine, for example). + */ + std::string get_current_auth_id() { + Security_context_handle sctx; + always_ok(mysql_service_mysql_thd_security_context->get(m_thd, &sctx)); + + MYSQL_LEX_CSTRING user, host; + always_ok(mysql_service_mysql_security_context_options->get( + sctx, "priv_user", &user)); + always_ok(mysql_service_mysql_security_context_options->get( + sctx, "priv_host", &host)); + + std::string result; + result.append(user.str); + result.append("@"); + result.append(host.str); + + return result; + } + + /** + Per user account connection's context. + + Within the same connection JS code can be executed under different users + (e.g. due to SQL SECURITY DEFINER/INVOKER clauses). + + To prevent information leakage, we use different Isolates to execute + JS code under different users. + + TODO: At the moment we use an individual isolate for each connection and + user pair, but we might change this in future if we discover that with + such approach too much memory is consumed (current test show that execution + of the whole js_lang_basic test causes heap to grown a bit below 2Mb). + + OTOH V8 Isolates can only be used by a single thread at each moment. + So having a single isolate in the server per user sounds like not the best + idea from concurrency point of view. So perhaps we should have a few + isolates (for each user) partitioned between connection? (But then the + question of freeing Isolates which are not in use for a long time raises). + + Also we use separate JS context for each connection (and separate contexts + for execution of routines under different users within the same connection). + + Routines which are executed under the same user in the same connection + share the same JS context. + + TODO: Make it a class once support for KILLability/OOM handling is added. + */ + struct Auth_id_context { + /** + Pointer to isolate for this specific connection and user pair. + + We have to use reference counting pointer here since SQL code imposes + akward restrictions on Js_thd and Js_sp lifetime - Js_sp object + representing routine in the connection can outlive connection's Js_thd + object [sic!]. + */ + std::shared_ptr isolate_ptr; + + /** + JS context for the specific connection and user pair. + + @note Belongs to the Isolate referenced from this context. + */ + v8::Global context; + + Auth_id_context(const std::shared_ptr &iso_ptr, + v8::Local &ctx) + : isolate_ptr(iso_ptr), context(iso_ptr->get_v8_isolate(), ctx) {} + + v8::Isolate *get_isolate() const { return isolate_ptr->get_v8_isolate(); } + + v8::Local get_context() const { + return v8::Local::New(isolate_ptr->get_v8_isolate(), + context); + } + }; + + /** + Get Auth_id_context representing context for the user account in the + current connection. Create one if necessary. + */ + const Auth_id_context *get_or_create_auth_id_context( + const std::string &auth_id) { + auto i = m_auth_id_contexts.find(auth_id); + if (i == m_auth_id_contexts.end()) { + // Active user didn't run any JS in this connection before. + // We need to create new Isolate and Context. + auto isolate_ptr = Js_isolate::create(); + + v8::Isolate *isolate = isolate_ptr->get_v8_isolate(); + + // Lock the isolate and make it the current one to follow convention. + v8::Locker locker(isolate); + + v8::Isolate::Scope isolate_scope(isolate); + + v8::HandleScope handle_scope(isolate); + + v8::Local context = v8::Context::New(isolate); + + auto r = m_auth_id_contexts.try_emplace(auth_id, isolate_ptr, context); + + return &(r.first->second); + } + + return &(i->second); + } + + /** Checks if connection executes CREATE FUNCTION or PROCEDURE statement. */ + bool is_create_routine_command() { + mysql_cstring_with_length sql_command; + always_ok(mysql_service_mysql_thd_attributes->get(m_thd, "sql_command", + &sql_command)); + return (strcmp(sql_command.str, "create_procedure") == 0 || + strcmp(sql_command.str, "create_spfunction") == 0); + } + + /** + Check if current active user account/security context has + CREATE_JS_PRIVILEGE privilege. + */ + bool current_user_has_create_js_privilege() { + Security_context_handle sctx; + always_ok(mysql_service_mysql_thd_security_context->get(m_thd, &sctx)); + + return mysql_service_global_grants_check->has_global_grant( + sctx, CREATE_PRIVILEGE_NAME.data(), CREATE_PRIVILEGE_NAME.length()); + } + + private: + // Opaque handle for corresponding THD object. + MYSQL_THD m_thd; + + // Slot to associate Js_thd context with core's THD. + static mysql_thd_store_slot s_thd_slot; + + // Map with per user-account contexts for the connection. + std::unordered_map m_auth_id_contexts; +}; + +/** + Representation of stored program instance in our component. + + @note Corresponds to core's sp_head object and is used to store + component specific data about stored program, as well as cache data + from sp_head/sp_pcontext to reduce overhead from service + calls to the server core. + + @note Note that since core's sp_head objects are associated with + specific connection (unless they are for trigger), each + object of this class is also associated with specific + connection. + @note However, due to the way SQL core implements per-component + THD store, Js_sp object can temporarily outlive Js_thd object + for its connection! +*/ +class Js_sp { + public: + Js_sp(stored_program_handle sp, uint16_t sql_sp_type); + + ~Js_sp(); + + // Block default copy/move semantics. + Js_sp(Js_sp const &rhs) = delete; + Js_sp(Js_sp &&rhs) = delete; + Js_sp &operator=(Js_sp const &rhs) = delete; + Js_sp &operator=(Js_sp &&rhs) = delete; + + /** + Parse JS routine and prepare for its execution by building and compiling + wrapper function around its JS code. Also setup functions for getting/ + setting values of parameters and return value. + + @note When called during CREATE FUNCTION/PROCEDURE statement also checks + if current user has CREATE_JS_ROUTINE privilege, and fails + otherwise. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + bool parse(); + + /** + Execute JS routine, by invoking wrapper function. + + Use getter functions, which we set up during parse() phase, to get JS + values to be passed as wrapper function parameters from SQL routine params + (obtained from SQL routine runtime context). + Use setter functions, which we set up at the same phase, to convert + and store JS values returned by wrapper function to SQL routine OUT + params values/return value. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + bool execute(); + + private: + /** + Prepare code of wrapper function for our JS routine. + Also fill array with indexes of OUT/INOUT parameters in the array of all + routine parameters. + */ + void prepare_wrapped_body_and_out_param_indexes(); + + /** Check if routine has any OUT or INOUT parameters. */ + bool has_out_param() const { return !m_out_param_indexes.empty(); } + + /** + Setup functions for getting JS values corresponding to SQL values of + IN/INOUT parameters of the routine. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + bool prepare_get_param_funcs(); + /** + Setup function for converting JS return value to SQL value and + setting it as return value of the routine. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + bool prepare_set_retval_func(); + /** + Setup functions for converting JS values to SQL values and setting + it as values of OUT/INOUT parameters of the routine. + + @retval False - Success. + @retval True - Failure (error has been reported). + */ + bool prepare_set_out_param_funcs(); + + /** + Type of function for getting JS values corresponding to SQL values of + IN/INOUT parameters of the routine + + These functions take current Isolate and parameter index as arguments. + They return handle for JS value in case of success, and empty handle + in case of failure (the error has been reported by the function in the + latter case). + */ + using get_param_func_t = + std::function(v8::Isolate *, size_t)>; + + /** + Get function for getting JS value corresponding to SQL value of + the specific IN/INOUT parameter of the routine. + + @retval Function wrapped in std::function<> class. + @retval Empty std::function<> object in case of failure + (error has been reported already). + */ + get_param_func_t prepare_get_param_func(size_t idx); + + /** + Type of function for converting JS value and setting it as value of the + OUT/INOUT parameter or as a returv value of routine. + + These functions take current v8::Context, parameter index and the JS + value as as arguments. They return false in case of success, and + true in case of failure (the error has been reported by the function + in the latter case). + */ + using set_param_func_t = + std::function, size_t, v8::Local)>; + + /** + Get function for converting JS value to SQL value and setting it as + value of the specific OUT/INOUT parameter or as a return value of the + routine. + + @retval Function wrapped in std::function<> class. + @retval Empty std::function<> object in case of failure + (error has been reported already). + */ + template + set_param_func_t prepare_set_param_func(size_t idx); + + /** + Compile wrapper Function object for routine for the specific JS context. + + @param isolate Isolate in which Function object should be prepared. + @param context Context which should be used for preparing Function object. + + @retval Handle for v8::Function object if all went well or empty handle + in case of failure (the error has been reported already in the + latter case). + */ + v8::Local prepare_func(v8::Isolate *isolate, + v8::Local &context); + + // Handle for core's stored program object (sp_head). + stored_program_handle m_sql_sp; + // Number of routine parameters (cached value from SQL core). + uint32_t m_arg_count; + // Routine type. At the moment we only support functions and procedures in JS. + enum sp_type { FUNCTION, PROCEDURE }; + sp_type m_type; + + /** + Wrapped body of stored program which was passed in CREATE FUNCTION/ + PROCEDURE statement. + + We do this to make a JS function with the same parameter names as + SQL program has. After that we can compile this code to produce + v8::Function object which can be called from our componets with + parameters passed as an array. + */ + std::string m_wrapped_body; + // Indexes of OUT/INOUT parameters in array of all routine parameters. + std::vector m_out_param_indexes; + + /** + Map with Function objects for this routine (with user account as a key). + + SQL SECURITY DEFINER routines are always executed under the same account, + so the are using the same JS context within connection and need only one + Function object (for connection). + + However, SQL SECURITY INVOKER routines might be executed with different + user account being active within the same connection. So they might use + different JS contexts thus require separate Function objects for each + context/account being used. + + To support correct release of Function handle, we also keep pointer + to the corresponding Isolate in which this function was allocated. + Since Js_sp can temporarily outlive Js_thd [sic!] this has to be + reference counting pointer. + */ + struct Function_handle_wrapper { + std::shared_ptr isolate_ptr; + v8::Global func; + + Function_handle_wrapper(const std::shared_ptr &iso_ptr, + v8::Isolate *v8_iso, v8::Local f) + : isolate_ptr(iso_ptr), func(v8_iso, f) {} + }; + std::unordered_map m_funcs; + + // Parameter and return value getters/setters. + std::vector m_get_param_func; + set_param_func_t m_set_retval_func; + std::vector m_set_out_param_func; +}; + +/** + Register global dynamic CREATE_JS_ROUTINE privilege. + + @retval False - Success. + @retval True - Failure (error has been reported). +*/ +bool register_create_privilege(); +/** + Unregister global dynamic CREATE_JS_ROUTINE privilege. + + @retval False - Success. + @retval True - Failure (error has been reported). +*/ +bool unregister_create_privilege(); + +#endif /* COMPONENT_JS_LANG_JS_LANG_CORE_H */ diff --git a/components/js_lang/js_lang_param.cc b/components/js_lang/js_lang_param.cc new file mode 100644 index 000000000000..93ac4501aae2 --- /dev/null +++ b/components/js_lang/js_lang_param.cc @@ -0,0 +1,1216 @@ +/* Copyright (c) 2023, 2024 Percona LLC and/or its affiliates. All rights + reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 of + the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + This file contains methods of Js_sp class and some helpers which are + responsible for converting and storing JS values to/from SQL routine + parameters and return values. +*/ + +#include "js_lang_core.h" + +#include + +// Names of important character sets. +static constexpr char UTF8_CS_NAME[] = "utf8mb4"; +static constexpr char BINARY_CS_NAME[] = "binary"; + +// Biggest integer which is safe to store as JS Number value. +static constexpr int64_t NUMBER_MAX_SAFE_INTEGER = 9007199254740991; // 2^53-1 + +/** + Traits class which encapsulates knowledge how to get metadata for routine + return value as well as set this value (using mysql_stored_program_* + family services). + + @note In cases when methods for getting metadata or setting values can fail + they return false in case of success and true in case of failure (the + error is reported by the method in the latter case). +*/ +class Return_value_handler { + public: + static bool get_param_type(stored_program_handle sql_sp, size_t /* idx */, + uint64_t *sql_type) { + if (mysql_service_mysql_stored_program_return_metadata_query->get( + sql_sp, "sql_type", sql_type)) { + // Getting return value type can fail if the SQL type was added recently + // and Stored Program Service implementation was not updated to handle + // it correctly. Since such scenario is not unlikely we try to play + // safe in such a case. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine return value metadata (unhandled type?)"); + return true; + } + return false; + } + static void get_param_signedness(stored_program_handle sql_sp, + size_t /* idx */, bool *is_signed) { + always_ok(mysql_service_mysql_stored_program_return_metadata_query->get( + sql_sp, "is_signed", is_signed)); + } + static void get_param_charset(stored_program_handle sql_sp, size_t /* idx */, + const char **cs_name) { + always_ok(mysql_service_mysql_stored_program_return_metadata_query->get( + sql_sp, "charset", cs_name)); + } + static void get_param_collation(stored_program_handle sql_sp, + size_t /* idx */, const char **coll_name) { + always_ok(mysql_service_mysql_stored_program_return_metadata_query->get( + sql_sp, "collation", coll_name)); + } + static void get_param_max_display_length(stored_program_handle sql_sp, + size_t /* idx */, + size_t *max_display_length) { + always_ok(mysql_service_mysql_stored_program_return_metadata_query->get( + sql_sp, "max_display_length", max_display_length)); + } + + static bool set_param_null(size_t /* idx */) { + return mysql_service_mysql_stored_program_return_value_null->set(nullptr); + } + static bool set_param_int(size_t /* idx */, int64_t value) { + return mysql_service_mysql_stored_program_return_value_int->set(nullptr, + value); + } + static bool set_param_uint(size_t /* idx */, uint64_t value) { + return mysql_service_mysql_stored_program_return_value_unsigned_int->set( + nullptr, value); + } + static bool set_param_string(size_t /* idx */, const char *string, + size_t length) { + return mysql_service_mysql_stored_program_return_value_string->set( + nullptr, string, length); + } + static bool set_param_float(size_t /* idx */, double value) { + return mysql_service_mysql_stored_program_return_value_float->set(nullptr, + value); + } +}; + +/** + Ancestor for traits classes which encapsulate knowledge how to get metadata + for routine's IN/INOUT and OUT parameters (using mysql_stored_program_* + family of services). +*/ +class Param_handler { + public: + static bool get_param_type(stored_program_handle sql_sp, size_t idx, + uint64_t *sql_type) { + if (mysql_service_mysql_stored_program_argument_metadata_query->get( + sql_sp, idx, "sql_type", sql_type)) { + // Getting parameter type can fail if the SQL type was added recently + // and Stored Program Service implementation was not updated to handle + // it correctly. Since such scenario is not unlikely we try to play + // safe in such a case. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine parameter metadata (unhandled type?)"); + return true; + } + return false; + } + static void get_param_signedness(stored_program_handle sql_sp, size_t idx, + bool *is_signed) { + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + sql_sp, idx, "is_signed", is_signed)); + } + static void get_param_charset(stored_program_handle sql_sp, size_t idx, + const char **cs_name) { + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + sql_sp, idx, "charset", cs_name)); + } + static void get_param_collation(stored_program_handle sql_sp, size_t idx, + const char **coll_name) { + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + sql_sp, idx, "collation", coll_name)); + } + static void get_param_max_display_length(stored_program_handle sql_sp, + size_t idx, + size_t *max_display_length) { + always_ok(mysql_service_mysql_stored_program_argument_metadata_query->get( + sql_sp, idx, "max_display_length", max_display_length)); + } +}; + +/** + Traits class which encapsulates knowledge how to get metadata and value + of routine's IN and INOUT parameters (using mysql_stored_program_* family + of services). + + @note In cases when methods for getting metadata or getting values can fail + they return false in case of success and true in case of failure (the + error is reported by the method in the latter case). +*/ +class In_param_handler : public Param_handler { + public: + static bool get_param_int(size_t idx, int64_t *value, bool *is_null) { + if (mysql_service_mysql_stored_program_runtime_argument_int->get( + nullptr, idx, value, is_null)) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine parameter value"); + return true; + } + return false; + } + static bool get_param_uint(size_t idx, uint64_t *value, bool *is_null) { + if (mysql_service_mysql_stored_program_runtime_argument_unsigned_int->get( + nullptr, idx, value, is_null)) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine parameter value"); + return true; + } + return false; + } + static bool get_param_float(size_t idx, double *value, bool *is_null) { + if (mysql_service_mysql_stored_program_runtime_argument_float->get( + nullptr, idx, value, is_null)) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine parameter value"); + return true; + } + return false; + } + static bool get_param_string(size_t idx, const char **string, size_t *length, + bool *is_null) { + if (mysql_service_mysql_stored_program_runtime_argument_string->get( + nullptr, idx, string, length, is_null)) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't get routine parameter value"); + return true; + } + return false; + } +}; + +/** + Traits class which encapsulates knowledge how to get metadata and set value + of routine's INOUT and OUT parameters (using mysql_stored_program_* family + of services). + + @note In cases when methods for getting metadata or setting values can fail + they return false in case of success and true in case of failure (the + error is reported by the method in the latter case). +*/ +class Out_param_handler : public Param_handler { + public: + static bool set_param_null(size_t idx) { + return mysql_service_mysql_stored_program_runtime_argument_null->set( + nullptr, idx); + } + static bool set_param_int(size_t idx, int64_t value) { + return mysql_service_mysql_stored_program_runtime_argument_int->set( + nullptr, idx, value); + } + static bool set_param_uint(size_t idx, uint64_t value) { + return mysql_service_mysql_stored_program_runtime_argument_unsigned_int + ->set(nullptr, idx, value); + } + static bool set_param_string(size_t idx, const char *string, size_t length) { + return mysql_service_mysql_stored_program_runtime_argument_string->set( + nullptr, idx, string, length); + } + static bool set_param_float(size_t idx, double value) { + return mysql_service_mysql_stored_program_runtime_argument_float->set( + nullptr, idx, value); + } +}; + +// Helper functions which constructs no-JS-value-error return value for +// the routine parameter getter functions. +static v8::Local no_js_value_for_param() { + return v8::Local(); +} + +// Helper functions which constructs no-JS-value-error return value for +// the routine parameter getter functions and reports error to the user. +static v8::Local no_js_value_for_param_with_error() { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't create JS value from routine parameter value"); + return v8::Local(); +} + +Js_sp::get_param_func_t Js_sp::prepare_get_param_func(size_t idx) { + uint64_t sql_type; + if (In_param_handler::get_param_type(m_sql_sp, idx, &sql_type)) + return get_param_func_t(); + + switch (sql_type) { + // BOOLEAN type is just alias for TINYINT at the moment, so we + // should never meet it. If there will be separate boolean type + // we need to consider mapping it to JS booleans. + // For now we treat it as integer for compatibility sake. + case MYSQL_SP_ARG_TYPE_BOOL: { + assert(false); + [[fallthrough]]; + } + // YEAR values are mapped to integers. + case MYSQL_SP_ARG_TYPE_YEAR: { + [[fallthrough]]; + } + case MYSQL_SP_ARG_TYPE_TINY: + case MYSQL_SP_ARG_TYPE_SHORT: + case MYSQL_SP_ARG_TYPE_INT24: + case MYSQL_SP_ARG_TYPE_LONG: { + bool is_signed; + In_param_handler::get_param_signedness(m_sql_sp, idx, &is_signed); + + if (is_signed) { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + int64_t value; + bool is_null; + if (In_param_handler::get_param_int(idx, &value, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + // V8 aborts the process in case of OOM. So we don't try to + // handle OOM gracefully here either. + return v8::Integer::New(isolate, static_cast(value)); + } + }; + } else { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + uint64_t value; + bool is_null; + if (In_param_handler::get_param_uint(idx, &value, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + // V8 aborts the process in case of OOM. So we don't try to + // handle OOM gracefully here either. + return v8::Integer::NewFromUnsigned(isolate, + static_cast(value)); + } + }; + } + break; + } + case MYSQL_SP_ARG_TYPE_LONGLONG: { + bool is_signed; + In_param_handler::get_param_signedness(m_sql_sp, idx, &is_signed); + + // Lots of integers which are stored in BIGINT fields are probably + // less than 2^53-1 and might benefit from optimized handling as + // primitive types. + // + // OTOH it might be better to always use BigInt for 64-bit integers, + // to create less surprises during development of JS routines. + // + // We follow the first approach at the moment. + // + // TODO: Consider implementing the second approach as an option. + if (is_signed) { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + int64_t value; + bool is_null; + if (In_param_handler::get_param_int(idx, &value, &is_null)) + return no_js_value_for_param(); + + // V8 aborts the process in case of OOM. So we don't try to + // handle OOM gracefully here either. + if (is_null) + return v8::Null(isolate); + else if (value >= INT32_MIN && value <= INT32_MAX) { + return v8::Integer::New(isolate, static_cast(value)); + } else if (value >= -NUMBER_MAX_SAFE_INTEGER && + value <= NUMBER_MAX_SAFE_INTEGER) { + return v8::Number::New(isolate, static_cast(value)); + } else { + return v8::BigInt::New(isolate, value); + } + }; + } else { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + uint64_t value; + bool is_null; + if (In_param_handler::get_param_uint(idx, &value, &is_null)) + return no_js_value_for_param(); + + // V8 aborts the process in case of OOM. So we don't try to + // handle OOM gracefully here either. + if (is_null) + return v8::Null(isolate); + else if (value <= UINT32_MAX) { + return v8::Integer::NewFromUnsigned(isolate, + static_cast(value)); + } else if (value <= NUMBER_MAX_SAFE_INTEGER) { + return v8::Number::New(isolate, static_cast(value)); + } else { + return v8::BigInt::NewFromUnsigned(isolate, value); + } + }; + } + break; + } + case MYSQL_SP_ARG_TYPE_FLOAT: + case MYSQL_SP_ARG_TYPE_DOUBLE: { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + // Even though FLOAT and DOUBLE parameters can be UNSIGNED this + // doesn't affect their storage. + double value; + bool is_null; + if (In_param_handler::get_param_float(idx, &value, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + // V8 doesn't handle allocation failure gracefully, + // so neither do we here. + return v8::Number::New(isolate, value); + } + }; + break; + } + // We map BIT parameters with width <= 53 bits to integer JS Number + // values. + // BIT parameters with size > 53 bits can't be safely represented as + // JS Number values in generic case, so we map them to BigInt values. + // + // TODO: Consider mapping to custom type as an option. + case MYSQL_SP_ARG_TYPE_BIT: { + size_t max_display_length; + In_param_handler::get_param_max_display_length(m_sql_sp, idx, + &max_display_length); + + // Maximum size of BIT type which safe to store in JS Number. + const size_t MAX_BIT_WIDTH_SAFE_FOR_NUMBER = 53; + static_assert(((uint64_t)(1) << MAX_BIT_WIDTH_SAFE_FOR_NUMBER) - 1 <= + NUMBER_MAX_SAFE_INTEGER); + + if (max_display_length > MAX_BIT_WIDTH_SAFE_FOR_NUMBER) { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + uint64_t value; + bool is_null; + if (In_param_handler::get_param_uint(idx, &value, &is_null)) + return no_js_value_for_param(); + if (is_null) + return v8::Null(isolate); + else { + // V8 doesn't handle allocation failure gracefully, + // so neither do we here. + return v8::BigInt::NewFromUnsigned(isolate, value); + } + }; + } else { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + uint64_t value; + bool is_null; + if (In_param_handler::get_param_uint(idx, &value, &is_null)) + return no_js_value_for_param(); + + // V8 doesn't handle allocation failure gracefully, + // so neither do we here. + if (is_null) + return v8::Null(isolate); + else if (value <= UINT32_MAX) { + return v8::Integer::NewFromUnsigned(isolate, + static_cast(value)); + } else { + assert(value <= NUMBER_MAX_SAFE_INTEGER); + return v8::Number::New(isolate, static_cast(value)); + } + }; + } + break; + } + // JS has no native fixed precision type. So we map DECIMAL values + // to strings to avoid loss of precision. + // + // TODO: Consider using custom type as possible future option? + case MYSQL_SP_ARG_TYPE_NEWDECIMAL: + // + // TIME, DATE, DATETIME and TIMESTAMP values are mapped to strings. + // + // For TIME type there is no corresponding native JS type. + // + // While converting DATETIME/TIMESTMAMP and DATE values to JS Date + // objects might seem like a natural thing, it turns out to be not + // that good idea in practice. + // To begin with Date objects are constructed from UTC seconds since + // the Unix Epoch values which don't present in arguments API. + // The second issue is time zone influence - we do not have much control + // over the time zone used for conversions by V8 (most probably it is + // the same as MySQL's SYSTEM), OTOH each MySQL connection can use its + // own time zone. Even if we will use SYSTEM timezone/mktime_r() to + // produce values equivalent to argument values in Isolate time zone, + // connection vs JS time zone difference still might surprising. + // So we take the lazy path for now and convert date and datetime values + // to strings. + // + // TODO: Consider using custom types for this as an option? + case MYSQL_SP_ARG_TYPE_TIME2: + case MYSQL_SP_ARG_TYPE_TIMESTAMP2: + case MYSQL_SP_ARG_TYPE_DATETIME2: + case MYSQL_SP_ARG_TYPE_NEWDATE: { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + /* + Note the below call builds string representation of SQL value + without a native one, and then copies it to MEM_ROOT-allocated + buffer to be able to return pointer to it to the caller. + + TODO: Study how much performance overhead this creates and possibly + optimize. + */ + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + if (is_null) + return v8::Null(isolate); + else { + // String representation of DECIMAL type is always UTF8 compatible + // (uses my_charset_numeric). + // + // The same is true for datetime types. + // + // Also these string representations can never exceed JS string + // length limit, so the below call can fail only in case of OOM. + // Since V8 handles OOM by aborting the process we do not try to + // handle it gracefully here either. + return v8::String::NewFromUtf8(isolate, str, + v8::NewStringType::kNormal, length) + .ToLocalChecked(); + } + }; + break; + } + // Internal representation of GEOMETRY objects is based on WKB + // standard. So we simply provide access to it using JS DataView. + case MYSQL_SP_ARG_TYPE_GEOMETRY: { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + if (is_null) + return v8::Null(isolate); + else { + // At the moment V8 simply aborts the process if it fails to + // allocate ArrayBuffer. It also aborts on other allocation + // failures. So we don't try to handle allocation failures + // here gracefully either. + v8::Local buffer = + v8::ArrayBuffer::New(isolate, length); + v8::Local view = v8::DataView::New(buffer, 0, length); + memcpy(buffer->Data(), str, length); + return view; + } + }; + break; + } + // We map arguments of JSON SQL type to JS objects/values which we + // reconstruct from JSON string using JSON.parse() call. + case MYSQL_SP_ARG_TYPE_JSON: { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + /* + Note the below call builds string which corresponds to SQL's JSON + binary value. In then without a native one, and then copies it to + MEM_ROOT-allocated buffer to be able to return pointer to it to + the caller. This introduces some performance and memory overhead + as this MEM_ROOT-allocated buffer will stay around until the end + of routine execution. + + TODO: Consider improving memory usage and performance in this case + possibly by introducing new service API which will skip extra + allocation and copying by just handing over the original + string buffer to the caller. + */ + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + if (is_null) + return v8::Null(isolate); + else { + // String representation of JSON SQL type value should be always + // UTF8 compatible. + v8::Local json_str; + if (!v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kNormal, + length) + .ToLocal(&json_str)) { + // String representation of SQL JSON value can exceed V8 string + // length limit. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't create JS value from routine parameter value of " + "JSON type (V8 string length limit excedeed)"); + return no_js_value_for_param(); + } + + v8::Local result; + if (!v8::JSON::Parse(isolate->GetCurrentContext(), json_str) + .ToLocal(&result)) { + // In theory whatever is stored in JSON SQL type should be always + // JSON.parse()-able. But we play safe. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't create JS value from routine parameter value of " + "JSON type (can't parse JSON string)"); + return no_js_value_for_param(); + } + return result; + } + }; + break; + } + // Obsolete types which shold not be possible to use as parameter + // for newly created routine. + case MYSQL_SP_ARG_TYPE_DECIMAL: + case MYSQL_SP_ARG_TYPE_DATE: + case MYSQL_SP_ARG_TYPE_VAR_STRING: + case MYSQL_SP_ARG_TYPE_TIME: + case MYSQL_SP_ARG_TYPE_TIMESTAMP: + case MYSQL_SP_ARG_TYPE_DATETIME: { + [[fallthrough]]; + } + // Special types which should not be possible to use as parameter + // for routine. + case MYSQL_SP_ARG_TYPE_INVALID: + case MYSQL_SP_ARG_TYPE_NULL: + case MYSQL_SP_ARG_TYPE_TYPED_ARRAY: { + [[fallthrough]]; + } + // New types will require analysis and handling. + default: { + assert(false); + [[fallthrough]]; + } + // CHAR/VARCHAR and TEXT types are naturally mapped to strings. + // BINARY/VARBINARY and BLOBs are string types with binary + // charset. We map them to JS DataView on top of ArrayBuffer. + // (as DataViews are more flexible than JS Typed Arrays). + case MYSQL_SP_ARG_TYPE_VARCHAR: + case MYSQL_SP_ARG_TYPE_STRING: + case MYSQL_SP_ARG_TYPE_TINY_BLOB: + case MYSQL_SP_ARG_TYPE_MEDIUM_BLOB: + case MYSQL_SP_ARG_TYPE_BLOB: + case MYSQL_SP_ARG_TYPE_LONG_BLOB: + // SET parameters are mapped to string values. + // + // Ideally, SETs should be mapped to JS Set objects. However, we lack + // API which allows to get SET values element by element to be able to + // do this. + // + // Another alternative is to map SET values to numbers but we + // consider this less user friendly. + case MYSQL_SP_ARG_TYPE_SET: + // We also map ENUM values to strings. + case MYSQL_SP_ARG_TYPE_ENUM: { + const char *cs_name; + In_param_handler::get_param_charset(m_sql_sp, idx, &cs_name); + + // In theory handling of utf8mb3 and latin1 character sets can be + // optimized as well. However, we prefer to keep the code simple + // instead. People should upgrade to utf8mb4 or accept performance + // penalty. + if (strcmp(cs_name, UTF8_CS_NAME) == 0) { + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + /* + N.B. The below call only works well for parameters with types + having native string representation internally, so the pointer + to this internal representation can be returned. + + For types which have non-string native representation, like JSON, + SET, DECIMAL or datetime, string representation is constructed in + a heap-allocated memory buffer. + Ideally, this buffer should have been handed over to and its + lifetime should have been controlled by code calling the service. + However, mysql_stored_program_runtime_argument_string service API + doesn't allow that. Instead SQL-core creates yet another copy of + this buffer on current memory root (which happens to be in our + case, "call" memory root (sp_head::execute_function()) and returns + pointer to this copy. + + This wastes memory since the memory root is kept around until end + of the call and introduces performance overhead. + + TODO: Consider improving memory usage and performance in this case + possibly by introducing new service API which will handover + buffer to caller. + */ + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + v8::Local result; + if (!v8::String::NewFromUtf8(isolate, str, + v8::NewStringType::kNormal, length) + .ToLocal(&result)) { + // Length of SQL LONGTEXT value can exceed V8 string length + // limit, so we handle it gracefully. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't create JS value from routine parameter " + "value (V8 string length limit excedeed)"); + return no_js_value_for_param(); + } + return result; + } + }; + } else if (strcmp(cs_name, BINARY_CS_NAME) == 0) { + if (sql_type == MYSQL_SP_ARG_TYPE_SET) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "SET parameters using binary character set are not " + "supported."); + return get_param_func_t(); + } + return [](v8::Isolate *isolate, size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + + // See comment for UTF8 case. + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + // At the moment V8 simply aborts the process if it fails to + // allocate ArrayBuffer. It also aborts on other allocation + // failures. So we don't try to handle allocation failures + // here gracefully either. + v8::Local buffer = + v8::ArrayBuffer::New(isolate, length); + v8::Local view = v8::DataView::New(buffer, 0, length); + memcpy(buffer->Data(), str, length); + return view; + } + }; + } else { + const char *coll_name; + In_param_handler::get_param_collation(m_sql_sp, idx, &coll_name); + + CHARSET_INFO_h src_cs_h = mysql_service_mysql_charset->get(coll_name); + // Collation of parameter must be always available. + assert(src_cs_h != nullptr); + CHARSET_INFO_h utf8_cs_h = mysql_service_mysql_charset->get_utf8mb4(); + // UTF8mb4 must be always available. + assert(utf8_cs_h != nullptr); + + return [src_cs_h, utf8_cs_h](v8::Isolate *isolate, + size_t idx) -> v8::Local { + const char *str; + size_t length; + bool is_null; + + // See comment for UTF8 case. + if (In_param_handler::get_param_string(idx, &str, &length, &is_null)) + return no_js_value_for_param(); + + if (is_null) + return v8::Null(isolate); + else { + my_h_string str_h; + if (mysql_service_mysql_string_factory->create(&str_h)) + return no_js_value_for_param_with_error(); + + auto str_h_guard = create_scope_guard([str_h]() { + mysql_service_mysql_string_factory->destroy(str_h); + }); + + uint errors; + if (mysql_service_mysql_string_copy_converter->copy_convert( + str_h, str, length, src_cs_h, utf8_cs_h, &errors) || + errors != 0) { + // In theory should not happed as we are converting to + // UTF-8. + return no_js_value_for_param_with_error(); + } + + const char *utf8_buff; + size_t utf8_length; + CHARSET_INFO_h not_used; + always_ok(mysql_service_mysql_string_get_data_in_charset->get_data( + str_h, &utf8_buff, &utf8_length, ¬_used)); + + v8::Local result; + if (!v8::String::NewFromUtf8(isolate, utf8_buff, + v8::NewStringType::kNormal, + utf8_length) + .ToLocal(&result)) { + // Length of SQL LONGTEXT value can exceed V8 string length + // limit, so we handle it gracefully. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't create JS value from routine parameter " + "value (V8 string length limit excedeed)"); + return no_js_value_for_param(); + } + return result; + } + }; + } + break; + } + } +} + +bool Js_sp::prepare_get_param_funcs() { + for (size_t param_idx = 0; param_idx < m_arg_count; ++param_idx) { + get_param_func_t func = prepare_get_param_func(param_idx); + if (!func) return true; + m_get_param_func.push_back(func); + } + return false; +} + +static void my_error_js_value_to_string_param() { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't convert JS value to string to be stored in out parameter or " + "return value"); +} + +template +Js_sp::set_param_func_t Js_sp::prepare_set_param_func(size_t param_idx) { + uint64_t sql_type; + if (H::get_param_type(m_sql_sp, param_idx, &sql_type)) + return set_param_func_t(); + + switch (sql_type) { + // BOOLEAN type is just alias for TINYINT at the moment, so we + // should never meet it. If there will be separate boolean type + // we need to consider special handling for it. + // For now we treat it as integer for compatibility sake. + case MYSQL_SP_ARG_TYPE_BOOL: { + assert(false); + [[fallthrough]]; + } + // We treat YEAR type similarly to integer types. + case MYSQL_SP_ARG_TYPE_YEAR: { + [[fallthrough]]; + } + // In theory, we can handle return values of integer SQL-types by + // simply converting JS value to string and then letting SQL core + // to try to extract value from this string. + // However, this might be sub-optimal in many cases, so we try to + // convert JS integers to SQL integers directly. + case MYSQL_SP_ARG_TYPE_TINY: + case MYSQL_SP_ARG_TYPE_SHORT: + case MYSQL_SP_ARG_TYPE_INT24: + case MYSQL_SP_ARG_TYPE_LONG: + case MYSQL_SP_ARG_TYPE_LONGLONG: { + bool is_signed; + H::get_param_signedness(m_sql_sp, param_idx, &is_signed); + + if (is_signed) { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsInt32()) { + // Number already, so no real conversion that can fail. + return H::set_param_int(idx, + result->Int32Value(context).ToChecked()); + } else if (result->IsBigInt()) { + v8::Local bigint_result = result.As(); + bool lossless; + int64_t int64_result = bigint_result->Int64Value(&lossless); + if (lossless) { + return H::set_param_int(idx, int64_result); + } + // If direct conversion to int64_t is lossy, resort to + // converting through string. + } + // A natural thing would be to have alternative for JS Number as well + // (i.e. get it as double and pass as double to SQL core). + // + // However, such behavior can be confusing in some cases as MySQL + // uses different rounding when storing in integer field values like: + // 4.5 (decimal literal, rounded to 5, round()-style conversion), + // 4.5e+0 (floating-point literal, rounded to 4 [sic!], rint()-style + // conversion), "4.5" (decimal value in string, rounded to 5), and + // "4.5e+0" (floating-point value in string, rounded to 5). + // + // To avoid the confusion we stick to converting JS Numbers to + // SQL integers through strings, i.e. sticking to round()-style + // conversion. + // + // TODO: Discuss this, perhaps this should be yet another mode. + + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + return H::set_param_string(idx, *utf8, utf8.length()); + }; + } else { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsUint32()) { + // Number already, so no real conversion that can fail. + return H::set_param_uint(idx, + result->Uint32Value(context).ToChecked()); + } else if (result->IsBigInt()) { + v8::Local bigint_result = result.As(); + bool lossless; + uint64_t uint64_result = bigint_result->Uint64Value(&lossless); + if (lossless) { + return H::set_param_uint(idx, uint64_result); + } + // If direct conversion to uint64_t is lossy, resort to + // converting through string. + } + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + return H::set_param_string(idx, *utf8, utf8.length()); + }; + } + break; + } + // We try to handle OUT parameters/return values of SET type similarly + // to how SQL core handles integer/floating-point/string values which + // stored in SET type. + // + // Unsigned 32-bit integer Number values and unsigned 64-bit BigInt + // values are passed to SQL core as integers. The latter interprets + // such values as bitmaps representing SET. + // Other JS Number values (including integers which do not fit into + // 32 bits and negative integers) are passed to SQL core as floating- + // point values. SQL core casts them to integers, which again are + // interpreted as bitmaps for corresponding SETs. + // + // JS string values and other JS values are passed to SQL core as strings. + // The latter are interpreted as a comma-separated lists of SET elements. + // Additionally if string contains integer value SQL core will try to + // interpret it as SET's bitmap. + // + // Thanks to the above any integer value, be it represented as Number, + // BigInt or a string will be normally treated as SET's bitmap. + // + // Handling of Number and string representation of non-integer number + // values is not consistent, but it is not the case for SQL core either. + // + // Also some confusion is possible if SET uses strings with integers as + // its elements, but this case is problematic in SQL core as well. + case MYSQL_SP_ARG_TYPE_SET: { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsUint32()) { + // By having separate branch for Uint32 we probably save a few + // cycles for the most common case when number is really is a SMI. + // + // Number already, so no real conversion that can fail. + return H::set_param_uint(idx, + result->Uint32Value(context).ToChecked()); + } else if (result->IsNumber()) { + // Number already, so no real conversion that can fail. + double float_result = result->NumberValue(context).ToChecked(); + return H::set_param_float(idx, float_result); + } else if (result->IsBigInt()) { + v8::Local bigint_result = result.As(); + bool lossless; + uint64_t uint64_result = bigint_result->Uint64Value(&lossless); + if (lossless) { + return H::set_param_uint(idx, uint64_result); + } + // If direct conversion to uint64_t is lossy, resort to converting + // through string. + } + + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + + // SQL-core code does conversion from UTF8 to the parameter charset. + return H::set_param_string(idx, *utf8, utf8.length()); + }; + } + // We handle BIT type by converting JS value to Number and then + // letting SQL core to convert this value into bitmap. We do not + // fallback to going through string if conversion to Number fails. + // + // The problem is that for BIT type integer and floating-point + // values are interpreted differently than strings representing + // the same integer/floating-point values. + // + // To avoid confusion we decided not to allow storing string JS + // values which are not convertible to Number in BIT parameters. + case MYSQL_SP_ARG_TYPE_BIT: { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsBigInt()) { + v8::Local bigint_result = result.As(); + bool lossless; + uint64_t uint64_result = bigint_result->Uint64Value(&lossless); + if (lossless) { + return H::set_param_uint(idx, uint64_result); + } else { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't convert BigInt value to BIT type (value " + "out of range)"); + return true; + } + } else { + double fp_result; + if (!result->NumberValue(context).To(&fp_result) || + std::isnan(fp_result)) { + // Conversion to Number might fail by throwing or result in NaN, + // we bark in either case. + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Can't convert JS value to BIT type (possibly non-numeric" + " value)"); + return true; + } + return H::set_param_float(idx, fp_result); + } + }; + } + // Again, we can handle return values of floating-point SQL types + // by converting any JS value to string and then letting SQL core + // parse the string. But we optimize by trying to convert JS Number + // values to SQL type directly. + case MYSQL_SP_ARG_TYPE_FLOAT: + case MYSQL_SP_ARG_TYPE_DOUBLE: { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsNumber()) { + // Number already, so no real conversion that can fail. + double float_result = result->NumberValue(context).ToChecked(); + return H::set_param_float(idx, float_result); + } else { + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + return H::set_param_string(idx, *utf8, utf8.length()); + } + }; + } + // We use JSON.stringify() to get JSON representation of JS value and + // then try to store the resulting UTF8 string into OUT parameter or + // return value of JSON SQL type. + // + // Both the former and the latter steps can fail. + case MYSQL_SP_ARG_TYPE_JSON: { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else { + v8::Local json_str; + if (!v8::JSON::Stringify(context, result).ToLocal(&json_str)) { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "For JSON return type only values supported by " + "JSON.stringify() are allowed."); + return true; + } + + v8::String::Utf8Value utf8(context->GetIsolate(), json_str); + + if (!*utf8) { + // Should not happen in theory, but we play safe. + my_error_js_value_to_string_param(); + return true; + } + + return H::set_param_string(idx, *utf8, utf8.length()); + } + }; + } + // Since MySQL GEOMETRY type accepts only binary data in certain + // format we only allow ArrayBuffer-based values for it. + case MYSQL_SP_ARG_TYPE_GEOMETRY: { + return [](v8::Local, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (result->IsArrayBufferView()) { + v8::Local buff_view_result = + result.As(); + v8::Local arr_buff = buff_view_result->Buffer(); + const char *ptr = static_cast(arr_buff->Data()) + + buff_view_result->ByteOffset(); + return H::set_param_string(idx, ptr, buff_view_result->ByteLength()); + } else if (result->IsArrayBuffer()) { + // We also support direct passing of ArrayBuffer objects. + v8::Local arr_buff = result.As(); + const char *ptr = static_cast(arr_buff->Data()); + return H::set_param_string(idx, ptr, arr_buff->ByteLength()); + } else { + my_error(ER_LANGUAGE_COMPONENT, MYF(0), + "Only ArrayBuffer-based values are supported for GEOMETRY " + "return type."); + return true; + } + }; + } + // We handle datetime types by converting JS value to UTF-8 string and + // then letting SQL core parse the result. + // Note that we don't want to use generic code that used for string + // types in this case. Datetime types report "numeric" (i.e. "latin1") + // as their charset, so using generic code in their case would introduce + // overhead due to conversion from UTF-8 to latin1. + // OTOH implementation of datetime types in SQL core handles string input + // in any ASCII-compatible charset (including UTF-8) directly, without + // conversion. + case MYSQL_SP_ARG_TYPE_NEWDATE: + case MYSQL_SP_ARG_TYPE_TIMESTAMP2: + case MYSQL_SP_ARG_TYPE_DATETIME2: + case MYSQL_SP_ARG_TYPE_TIME2: { + return [](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else { + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + // Implementation of datetime types in SQL core accepts input + // in UTF-8 without requiring conversion. + return H::set_param_string(idx, *utf8, utf8.length()); + } + }; + } + // Obsolete types which shold not be possible to use as return + // value for newly created routine. + case MYSQL_SP_ARG_TYPE_DECIMAL: + case MYSQL_SP_ARG_TYPE_DATE: + case MYSQL_SP_ARG_TYPE_VAR_STRING: + case MYSQL_SP_ARG_TYPE_TIME: + case MYSQL_SP_ARG_TYPE_TIMESTAMP: + case MYSQL_SP_ARG_TYPE_DATETIME: { + [[fallthrough]]; + } + // Special types which should not be possible to use as return + // value for routine. + case MYSQL_SP_ARG_TYPE_INVALID: + case MYSQL_SP_ARG_TYPE_NULL: + case MYSQL_SP_ARG_TYPE_TYPED_ARRAY: { + [[fallthrough]]; + } + // New SQL-types need analysis to be handled correctly. + default: { + assert(false); + [[fallthrough]]; + } + // We handle DECIMAL SQL-type by converting JS value to string and + // then letting SQL core to extract fixed point value. + // + // Unlike for integer or floating-point values it doesn't make sense + // to optimize conversion from Number type, as SQL core handles int/ + // double -> DECIMAL conversions through strings. + case MYSQL_SP_ARG_TYPE_NEWDECIMAL: { +#ifndef NDEBUG + // It is important to avoid unnecessary work in the generic string + // handling code below and not to do charset conversions. + // Code which does parsing of decimal values only cares about latin1 + // digits and spaces, so no conversion from UTF8 should be required. + const char *cs_name; + H::get_param_charset(m_sql_sp, param_idx, &cs_name); + assert(strcmp(cs_name, UTF8_CS_NAME) == 0); +#endif + [[fallthrough]]; + } + // We handle ENUMs similarly to strings. + case MYSQL_SP_ARG_TYPE_ENUM: { + [[fallthrough]]; + } + case MYSQL_SP_ARG_TYPE_VARCHAR: + case MYSQL_SP_ARG_TYPE_STRING: + case MYSQL_SP_ARG_TYPE_TINY_BLOB: + case MYSQL_SP_ARG_TYPE_MEDIUM_BLOB: + case MYSQL_SP_ARG_TYPE_BLOB: + case MYSQL_SP_ARG_TYPE_LONG_BLOB: { + const char *cs_name; + H::get_param_charset(m_sql_sp, param_idx, &cs_name); + + const bool is_binary = (strcmp(cs_name, BINARY_CS_NAME) == 0); + + return [is_binary](v8::Local context, size_t idx, + v8::Local result) -> bool { + if (result->IsUndefined() || result->IsNull()) { + return H::set_param_null(idx); + } else if (is_binary && result->IsArrayBufferView()) { + v8::Local buff_view_result = + result.As(); + v8::Local arr_buff = buff_view_result->Buffer(); + const char *ptr = static_cast(arr_buff->Data()) + + buff_view_result->ByteOffset(); + return H::set_param_string(idx, ptr, buff_view_result->ByteLength()); + } else if (is_binary && result->IsArrayBuffer()) { + // We also support direct passing of ArrayBuffer objects. + v8::Local arr_buff = result.As(); + const char *ptr = static_cast(arr_buff->Data()); + return H::set_param_string(idx, ptr, arr_buff->ByteLength()); + } else { + // Convert JS value to JS string and get it as UTF-8. + v8::String::Utf8Value utf8(context->GetIsolate(), result); + if (!*utf8) { + // Conversion of JS value to JS string can fail (e.g. throw). + my_error_js_value_to_string_param(); + return true; + } + + // SQL-core code does conversion from UTF8 to the parameter charset. + return H::set_param_string(idx, *utf8, utf8.length()); + } + }; + } + } + // We didn't manage to build setter. Report error. + return set_param_func_t(); +} + +bool Js_sp::prepare_set_retval_func() { + m_set_retval_func = prepare_set_param_func(0); + return !m_set_retval_func; +} + +bool Js_sp::prepare_set_out_param_funcs() { + for (auto param_idx : m_out_param_indexes) { + set_param_func_t func = + prepare_set_param_func(param_idx); + if (!func) return true; + m_set_out_param_func.push_back(func); + } + return false; +} diff --git a/include/mysql/components/services/mysql_stored_program.h b/include/mysql/components/services/mysql_stored_program.h index 87dcbac68ea6..59ed628e25f2 100644 --- a/include/mysql/components/services/mysql_stored_program.h +++ b/include/mysql/components/services/mysql_stored_program.h @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_STORED_PROGRAM_H #include +#include #include // size_t #include // intXX_t #include "bits/mysql_stored_program_bits.h" @@ -438,7 +439,7 @@ DECLARE_BOOL_METHOD(get, (stored_program_runtime_context sp_runtime_context, uint16_t index, char const **value, size_t *length, bool *is_null)); /** - Set value of a string argument + Set value of a string argument with utf8mb4 as charset @param [in] sp_runtime_context stored program runtime context. If null, current runtime context will be @@ -457,6 +458,27 @@ DECLARE_BOOL_METHOD(set, (stored_program_runtime_context sp_runtime_context, END_SERVICE_DEFINITION(mysql_stored_program_runtime_argument_string) +BEGIN_SERVICE_DEFINITION(mysql_stored_program_runtime_argument_string_charset) +/** + Set value of a string argument + + @param [in] sp_runtime_context stored program runtime context. + If null, current runtime context will be + used. + @param [in] index Argument position + @param [in] string Value of the argument + @param [in] length Length of the string + @param [in] charset The character set of the string + + @returns Status of operation + @retval false Success + @retval true Error +*/ +DECLARE_BOOL_METHOD(set, (stored_program_runtime_context sp_runtime_context, + uint16_t index, char const *string, size_t length, + CHARSET_INFO_h charset)); +END_SERVICE_DEFINITION(mysql_stored_program_runtime_argument_string_charset) + BEGIN_SERVICE_DEFINITION(mysql_stored_program_runtime_argument_int) /** @@ -718,7 +740,7 @@ END_SERVICE_DEFINITION(mysql_stored_program_return_value_null) BEGIN_SERVICE_DEFINITION(mysql_stored_program_return_value_string) /** - Set value of a string return value + Set value of a string return value with utf8mb4 as charset @param [in] sp_runtime_context stored program runtime context. If null, current runtime context will be @@ -736,6 +758,26 @@ DECLARE_BOOL_METHOD(set, (stored_program_runtime_context sp_runtime_context, END_SERVICE_DEFINITION(mysql_stored_program_return_value_string) +BEGIN_SERVICE_DEFINITION(mysql_stored_program_return_value_string_charset) +/** + Set value of a string return value + + @param [in] sp_runtime_context stored program runtime context. + If null, current runtime context will be + used. + @param [in] string Value of the argument + @param [in] length Length of the string + @param [in] charset The character set of the input string + + @returns Status of operation + @retval false Success + @retval true Error +*/ +DECLARE_BOOL_METHOD(set, (stored_program_runtime_context sp_runtime_context, + char const *string, size_t length, + CHARSET_INFO_h charset)); +END_SERVICE_DEFINITION(mysql_stored_program_return_value_string_charset) + BEGIN_SERVICE_DEFINITION(mysql_stored_program_return_value_int) /** diff --git a/include/mysql/components/services/mysql_string.h b/include/mysql/components/services/mysql_string.h index f9cac705a148..02ff29f8836a 100644 --- a/include/mysql/components/services/mysql_string.h +++ b/include/mysql/components/services/mysql_string.h @@ -222,6 +222,27 @@ convert_from_buffer_v2_t convert_from_buffer; convert_to_buffer_v2_t convert_to_buffer; END_SERVICE_DEFINITION(mysql_string_charset_converter) +BEGIN_SERVICE_DEFINITION(mysql_string_copy_converter) +/** + Copies string stored in source charset in a buffer to a string object + with conversion to destination charset. + + @param dest_string Handle for destination string object. + @param src_buffer Buffer with a source string + @param src_length Length of the source string (in bytes). + @param src_charset Handle for source charset. + @param dest_charset Handle for destination charset. + @param[out] errors Number of conversion errors. + + @retval False - Success. + @retval True - Failure. +*/ +DECLARE_BOOL_METHOD(copy_convert, + (my_h_string dest_string, const char *src_buffer, + uint64 src_length, CHARSET_INFO_h src_charset, + CHARSET_INFO_h dest_charset, uint *errors)); +END_SERVICE_DEFINITION(mysql_string_copy_converter) + /** @ingroup group_string_component_services_inventory diff --git a/mysql-test/include/have_js_lang_component.inc b/mysql-test/include/have_js_lang_component.inc new file mode 100644 index 000000000000..e74f66da3768 --- /dev/null +++ b/mysql-test/include/have_js_lang_component.inc @@ -0,0 +1,17 @@ +--disable_query_log + +# +# Check if the variable JS_LANG_COMPONENT is set +# +if (!$JS_LANG_COMPONENT) { + --skip component requires the environment variable \$JS_LANG_COMPONENT to be set (normally done by mtr), see the file plugin.defs +} + +# +# Check if --plugin-dir was setup for component_js_lang +# +if (`SELECT CONCAT('--plugin-dir=', REPLACE(@@plugin_dir, '\\\\', '/')) != '$JS_LANG_COMPONENT_OPT/'`) { + --skip component_js_lang requires that --plugin-dir is set to the component_js_lang dir (the .opt file does not contain \$JS_LANG_COMPONENT_OPT) +} + +--enable_query_log diff --git a/mysql-test/include/plugin.defs b/mysql-test/include/plugin.defs index ed168b4a773f..63d70f77c0aa 100644 --- a/mysql-test/include/plugin.defs +++ b/mysql-test/include/plugin.defs @@ -210,3 +210,4 @@ component_binlog_utils_udf plugin_output_directory no BINLOG_UTILS_UD component_encryption_udf plugin_output_directory no ENCRYPTION_UDF_COMPONENT component_masking_functions plugin_output_directory no MASKING_FUNCTIONS_COMPONENT component_percona_udf plugin_output_directory no PERCONA_UDF_COMPONENT +component_js_lang plugin_output_directory no JS_LANG_COMPONENT diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 6985944f645b..ad3a2e20cc69 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -301,6 +301,7 @@ sub set_term_args { component_audit_log_filter component_encryption_udf + component_js_lang percona percona_innodb percona-pam-for-mysql diff --git a/mysql-test/suite/component_js_lang/r/js_lang_basic.result b/mysql-test/suite/component_js_lang/r/js_lang_basic.result new file mode 100644 index 000000000000..25b427ead93f --- /dev/null +++ b/mysql-test/suite/component_js_lang/r/js_lang_basic.result @@ -0,0 +1,7935 @@ +INSTALL COMPONENT 'file://component_js_lang'; +GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; +# +# Some basic tests. +# +CREATE FUNCTION f1() RETURNS INT LANGUAGE JS AS $$ return 2*2 $$; +SELECT f1(); +f1() +4 +DROP FUNCTION f1; +CREATE FUNCTION fact(n INT) RETURNS INT LANGUAGE JS AS $$ +let result = 1; +while (n > 1) { +result *= n; +n--; +} +return result; +$$| +SELECT fact(5); +fact(5) +120 +DROP FUNCTION fact; +CREATE PROCEDURE p1(a INT, b INT, OUT r INT) LANGUAGE JS AS $$ +r = a * b; +$$| +CALL p1(7, 11, @r); +SELECT @r; +@r +77 +DROP PROCEDURE p1; +CREATE FUNCTION f2() RETURNS INT LANGUAGE JS AS $$ Syntax error ! $$; +ERROR HY000: SyntaxError: Unexpected identifier 'error' +CREATE PROCEDURE p2(OUT i INT, j INT, INOUT k INT) LANGUAGE JS AS $$ i = 5; k = k * j $$ | +SET @k := 7; +CALL p2(@i, 11, @k); +SELECT @i, @k; +@i @k +5 77 +DROP PROCEDURE p2; + +# +# Test that creation of routine with parameter name which is +# valid in MySQL but is not valid JS identifier fails. +# +# Error messages might be cryptic at the moment. +CREATE FUNCTION f3(1param INT) RETURNS INT LANGUAGE JS AS $$ return 1 $$; +ERROR HY000: SyntaxError: Invalid or unexpected token +CREATE PROCEDURE p3(`123` INT) LANGUAGE JS AS $$ return $$; +ERROR HY000: SyntaxError: Invalid destructuring assignment target +CREATE FUNCTION f3(`for` INT) RETURNS INT LANGUAGE JS AS $$ return 1 $$; +ERROR HY000: SyntaxError: Unexpected token 'for' +CREATE PROCEDURE p3(throw INT) LANGUAGE JS AS $$ return $$; +ERROR HY000: SyntaxError: Unexpected token 'throw' + +# +# Test that returning values from procedures is not allowed. +# +CREATE PROCEDURE p3(a INT) LANGUAGE JS AS $$ return 1 $$; +CALL p3(1); +ERROR HY000: Returning value from PROCEDURE is not allowed +DROP PROCEDURE p3; +# However, return without value should be fine. +CREATE PROCEDURE p3(a INT) LANGUAGE JS AS $$ return $$; +CALL p3(2); +DROP PROCEDURE p3; + +# +# Test that 'strict' mode is enforced for our routines without +# it being enabled it explicitly. +# +# Assigning to undeclared variables is banned in strict mode. +CREATE FUNCTION f_strict() RETURNS INT LANGUAGE JS AS $$ +no_such_var = 1; +return 1; +$$ | +SELECT f_strict(); +ERROR HY000: ReferenceError: no_such_var is not defined +DROP FUNCTION f_strict; +CREATE PROCEDURE p_strict() LANGUAGE JS AS $$ no_such_var = 1 $$; +CALL p_strict(); +ERROR HY000: ReferenceError: no_such_var is not defined +DROP PROCEDURE p_strict; + +# +# Let us test how stored program parameters are converted to JS values. +# +# +# Let us start with numeric types. +# +CREATE FUNCTION f(arg TINYINT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, -1, 127, -128]', '$[*]' COLUMNS(val TINYINT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<127> is number +<-128> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg TINYINT UNSIGNED) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, 255]', '$[*]' COLUMNS(val TINYINT UNSIGNED PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<255> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg SMALLINT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, -1, 32767, -32768]', '$[*]' COLUMNS(val SMALLINT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<32767> is number +<-32768> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg SMALLINT UNSIGNED) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, 65535]', '$[*]' COLUMNS(val SMALLINT UNSIGNED PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<65535> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg MEDIUMINT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, -1, 8388607, -8388608]', '$[*]' COLUMNS(val MEDIUMINT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<8388607> is number +<-8388608> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg MEDIUMINT UNSIGNED) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, 16777215]', '$[*]' COLUMNS(val MEDIUMINT UNSIGNED PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<16777215> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg INT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: -2^30 and 2^30-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit systems, on 64-bit systems these are -2^31 and 2^31-1. +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, -1, 1073741823, -1073741824, 1073741824, -1073741825, -2147483648, 2147483647]', '$[*]' COLUMNS(val INT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<1073741823> is number +<-1073741824> is number +<1073741824> is number +<-1073741825> is number +<-2147483648> is number +<2147483647> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg INT UNSIGNED) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: 2^30-1 is interesting because this is the border of V8 SMall Integer (SMI) optimization on 32-bit systems, on 64-bit systems this is 2^31-1. +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, 1073741823, 1073741824, 2147483647, 2147483648, 4294967295]', '$[*]' COLUMNS(val INT UNSIGNED PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<1073741823> is number +<1073741824> is number +<2147483647> is number +<2147483648> is number +<4294967295> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg BIGINT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: -2^30, 2^30-1 and -2^31, 2^31-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit and 64-bit systems. +/-2^53-1 are interesting becauses this is max/min safe integer representable as Numeric type (primitive) in JS. +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, -1, 1073741823, -1073741824, 1073741824, -1073741825, -2147483648, 2147483647, -2147483649, 2147483648, 9007199254740991, -9007199254740991, 9007199254740992,-9007199254740992, 9223372036854775806, -9223372036854775807]', '$[*]' COLUMNS(val BIGINT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<1073741823> is number +<-1073741824> is number +<1073741824> is number +<-1073741825> is number +<-2147483648> is number +<2147483647> is number +<-2147483649> is number +<2147483648> is number +<9007199254740991> is number +<-9007199254740991> is number +<9007199254740992> is bigint +<-9007199254740992> is bigint +<9223372036854775806> is bigint +<-9223372036854775807> is bigint +DROP FUNCTION f; +CREATE FUNCTION f(arg BIGINT UNSIGNED) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: 2^30-1 and 2^31-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit and 64-bit systems. 2^53-1 is interesting becauses this is max safe integer representable as Numeric type (primitive) in JS. +SELECT f(val) FROM JSON_TABLE('[null, 0, 1, 1073741823, 1073741824, 2147483647, 2147483648, 4294967295, 4294967296, 9007199254740991, 9007199254740992, 18446744073709551615]', '$[*]' COLUMNS(val BIGINT UNSIGNED PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<1073741823> is number +<1073741824> is number +<2147483647> is number +<2147483648> is number +<4294967295> is number +<4294967296> is number +<9007199254740991> is number +<9007199254740992> is bigint +<18446744073709551615> is bigint +DROP FUNCTION f; +CREATE FUNCTION f(arg FLOAT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: UNSIGNED attribute doesn't affect floating and fixed point type storage and is deprecated for them. +SELECT f(val) FROM JSON_TABLE('[null, 0e0, 1e0, -1e0, 1e1, 5e-1, -3.4028234e+38, -1.1754943e-38, 1.1754943e-38, 3.4028234E+38]', '$[*]' COLUMNS(val FLOAT PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<10> is number +<0.5> is number +<-3.4028234663852886e+38> is number +<-1.1754943508222875e-38> is number +<1.1754943508222875e-38> is number +<3.4028234663852886e+38> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg DOUBLE) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: UNSIGNED attribute doesn't affect floating and fixed point type storage and is deprecated for them. +SELECT f(val) FROM JSON_TABLE('[null, 0e0, 1e0, -1e0, 1e1, 5e-1, -1.7976931348623157e+308, -2.2250738585072014e-308, 2.2250738585072014e-308, 1.7976931348623157e+308]', '$[*]' COLUMNS(val DOUBLE PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<-1> is number +<10> is number +<0.5> is number +<-1.7976931348623157e+308> is number +<-2.2250738585072014e-308> is number +<2.2250738585072014e-308> is number +<1.7976931348623157e+308> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg DECIMAL(10,5)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: JS doesn't have primitive fixed precision type. So we convert such parameters to strings to avoid precision loss. +SELECT f(val) FROM JSON_TABLE('[null, 0.0, 1.0, -1.0, 10.0, 0.1, 1.23456, -1.23456, 99999.99999, -99999.99999]', '$[*]' COLUMNS(val DECIMAL(10,5) PATH '$')) as v; +f(val) + is object +<0.00000> is string +<1.00000> is string +<-1.00000> is string +<10.00000> is string +<0.10000> is string +<1.23456> is string +<-1.23456> is string +<99999.99999> is string +<-99999.99999> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg BOOLEAN) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +# Note: BOOLEAN type is an alias for TINYINT at the moment and is indistinguishable from it. +SELECT f(val) FROM JSON_TABLE('[null, false, true, 0, 1, 100]', '$[*]' COLUMNS(val BOOLEAN PATH '$')) as v; +f(val) + is object +<0> is number +<1> is number +<0> is number +<1> is number +<100> is number +DROP FUNCTION f; + +# +# Now let us test string types using different charsets. +# +# They are naturally mapped to JS strings (using Unicode). +CREATE FUNCTION f(arg CHAR(40) CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg CHAR(40) CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg CHAR(40) CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg VARCHAR(50) CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg VARCHAR(50) CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg VARCHAR(50) CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TINYTEXT CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TINYTEXT CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TINYTEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TEXT CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TEXT CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg MEDIUMTEXT CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg MEDIUMTEXT CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg MEDIUMTEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg LONGTEXT CHARACTER SET utf8mb4) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_utf8mb4 'Twas brillig, and the slithy toves') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg LONGTEXT CHARACTER SET latin1) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_latin1 X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573') AS r;; +nil e a r + is object <> is string is string is string +DROP FUNCTION f; +CREATE FUNCTION f(arg LONGTEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg; $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_cp1251 X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8') AS r;; +nil e a r + is object <> is string is string <Бе сгладне и честлинните комбурси> is string +DROP FUNCTION f; + +# +# Binary/BLOB types are mapped to TypedArray objects though. +# +# Notice the padding for BINARY(10)! +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg BINARY(10)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <0,0,0,0,0,0,0,0,0,0> is DataView <65,0,0,0,0,0,0,0,0,0> is DataView <0,1,2,3,4,0,0,0,0,0> is DataView +DROP FUNCTION f; +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg VARBINARY(15)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <> is DataView <65> is DataView <0,1,2,3,4> is DataView +DROP FUNCTION f; +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg TINYBLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <> is DataView <65> is DataView <0,1,2,3,4> is DataView +DROP FUNCTION f; +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg BLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <> is DataView <65> is DataView <0,1,2,3,4> is DataView +DROP FUNCTION f; +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg MEDIUMBLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <> is DataView <65> is DataView <0,1,2,3,4> is DataView +DROP FUNCTION f; +# We do a bit of pretty-printing of DataView contents. +CREATE FUNCTION f(arg LONGBLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); $$; +SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; +nil e a r + is object <> is DataView <65> is DataView <0,1,2,3,4> is DataView +DROP FUNCTION f; + +# +# Test for various datetime types. +# + +# +# YEAR parameters are mapped to integers. +# +CREATE FUNCTION f(arg YEAR) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f('00') AS a, f('01') AS b, f('99') AS c, f('1901') AS d, f('2155') AS e; +nil z a b c d e + is object <0> is number <2000> is number <2001> is number <1999> is number <1901> is number <2155> is number +DROP FUNCTION f; + +# +# TIME parameters are mapped to strings as JS doesn't have +# corresponding type. +# +CREATE FUNCTION f(arg TIME) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('0:0:0') AS z, f('-838:59:59') AS a, f('838:59:59') AS b, f('01:02:03') AS c; +nil z a b c + is object <00:00:00> is string <-838:59:59> is string <838:59:59> is string <01:02:03> is string +DROP FUNCTION f; +# +# Also test TIME with fractional part. +# +CREATE FUNCTION f(arg TIME(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('0:0:0.0') AS z, f('-838:59:59.000000') AS a, f('838:59:59.000000') AS b, f('01:02:03.999999') AS c; +nil z a b c + is object <00:00:00.000000> is string <-838:59:59.000000> is string <838:59:59.000000> is string <01:02:03.999999> is string +DROP FUNCTION f; + +# +# DATETIME and TIMESTAMP parameters are mapped to strings as +# TZ-related and API issues make their mapping to JS Date +# not the best idea. +# +CREATE FUNCTION f(arg DATETIME) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01 00:00:00') AS a, f('9999-12-31 23:59:59') AS b, f('2023-12-07 11:04:42') AS c; +nil a b c + is object <1000-01-01 00:00:00> is string <9999-12-31 23:59:59> is string <2023-12-07 11:04:42> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg DATETIME(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01 00:00:00.000000') AS a, f('9999-12-31 23:59:59.499999') AS b, f('2023-12-07 11:04:42.123456') AS c; +nil a b c + is object <1000-01-01 00:00:00.000000> is string <9999-12-31 23:59:59.499999> is string <2023-12-07 11:04:42.123456> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TIMESTAMP) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1970-01-01 03:00:01') AS a, f('2038-01-19 06:14:07') AS b, f('2023-12-07 11:04:42') AS c; +nil a b c + is object <1970-01-01 03:00:01> is string <2038-01-19 06:14:07> is string <2023-12-07 11:04:42> is string +DROP FUNCTION f; +CREATE FUNCTION f(arg TIMESTAMP(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1970-01-01 03:00:01.000000') AS a, f('2038-01-19 06:14:07.499999') AS b, f('2023-12-07 11:04:42.123456') AS c; +nil a b c + is object <1970-01-01 03:00:01.000000> is string <2038-01-19 06:14:07.499999> is string <2023-12-07 11:04:42.123456> is string +DROP FUNCTION f; + +# +# DATE parameters are mapped to strings for the same reasons as well. +# +CREATE FUNCTION f(arg DATE) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01') AS a, f('9999-12-31') AS b, f('2023-12-07') AS c; +nil a b c + is object <1000-01-01> is string <9999-12-31> is string <2023-12-07> is string +DROP FUNCTION f; + +# +# ENUM parameters are mapped to strings as well. +# +CREATE FUNCTION f(arg ENUM('a', 'b', 'c')) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("a") AS a, f(2) AS t; +nil a t + is object is string is string +DROP FUNCTION f; +# +# We do charset conversions for ENUM as well. +# +CREATE FUNCTION f(arg ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("Додо") AS Dodo, f(2) AS Tweedledum; +nil Dodo Tweedledum + is object <Додо> is string <Туидълди> is string +DROP FUNCTION f; + +# +# SET parameters are mapped to strings of comma-separated list of set +# elements. +# +CREATE FUNCTION f(arg SET('a', 'b', 'c')) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("a,b") AS ab, f("c") AS c, f(6) AS bc; +nil ab c bc + is object is string is string is string +DROP FUNCTION f; +# +# Check SET with non-UTF8 charset as well. +# +CREATE FUNCTION f(arg SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("Додо") AS D, f("Туидълдъм,Туидълди") AS TT, f(5) AS TD; +nil D TT TD + is object <Додо> is string <Туидълдъм,Туидълди> is string <Туидълдъм,Додо> is string +DROP FUNCTION f; + +# +# BIT parameters with size <= 53 bits are mapped to integer JS Number +# values. +# +# BIT parameters with size > 53 bits can't be safely represented as JS +# Number values in generic case. So we map them to BigInt values. +# +CREATE FUNCTION f(arg BIT(11)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS m; +nil z f s m + is object <0> is number <512> is number <7> is number <2047> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(53)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'1FFFFFFFFFFFFF') AS m; +nil z f s b m + is object <0> is number <512> is number <7> is number <2047> is number <9007199254740991> is number +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(54)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'3FFFFFFFFFFFFF') AS m; +nil z f s b m + is object <0> is bigint <512> is bigint <7> is bigint <2047> is bigint <18014398509481983> is bigint +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(64)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'FFFFFFFFFFFFFFFF') AS m; +nil z f s b m + is object <0> is bigint <512> is bigint <7> is bigint <2047> is bigint <18446744073709551615> is bigint +DROP FUNCTION f; + +# +# GEOMETRY arguments are mapped to JS DataView objects over +# MySQL internal representation of this type. This internal +# representation is documented and based on standard WKB format. +# +CREATE FUNCTION f(arg GEOMETRY) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg) $$; +SELECT f(NULL) AS nil, f(ST_GeomFromText('POINT(15 20)')) AS g; +nil g + is object <0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,46,64,0,0,0,0,0,0,52,64> is DataView +DROP FUNCTION f; + +# +# JSON arguments are mapped to corresponding JS objects which +# are constructed using JSON.parse() method. +# +CREATE FUNCTION f(arg JSON) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil1, f("null") AS nil2, f("1") AS i, f("1.01") AS n, f('"alpha"') AS s, f("[1, 2, 3]") AS arr, f('{"a": 1, "b": "alpha"}') AS obj; +nil1 nil2 i n s arr obj + is object is object <1> is number <1.01> is number is string <1,2,3> is object <[object Object]> is object +DROP FUNCTION f; + +# +# Additional test coverage for scenarios in which SQL to JS value +# conversion fails can be found in js_lang_big.test. +# + +# +# Now let us test how JS values are converted to SQL types for return +# values. +# +# +# Let us start with string SQL types. +# +# Values of all JS types are converted to non-binary string SQL-types +# using JS toString() conversion. +# The exception are JS 'null' and 'undefined' values which are mapped +# to SQL NULL. +CREATE FUNCTION f_undefined() RETURNS CHAR(50) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS CHAR(50) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS CHAR(50) LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS CHAR(50) LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS CHAR(50) LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS CHAR(50) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS CHAR(50) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS CHAR(50) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS CHAR(50) CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS CHAR(50) CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS CHAR(50) CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS CHAR(50) CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS CHAR(50) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS CHAR(50) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS CHAR(50) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS CHAR(50) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS CHAR(50) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS CHAR(50) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS VARCHAR(60) CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS VARCHAR(60) CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS VARCHAR(60) CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS VARCHAR(60) CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS VARCHAR(60) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS VARCHAR(60) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TINYTEXT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TINYTEXT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS TINYTEXT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS TINYTEXT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS TINYTEXT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS TINYTEXT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TINYTEXT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS TINYTEXT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TINYTEXT CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS TINYTEXT CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS TINYTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS TINYTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS TINYTEXT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TINYTEXT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TINYTEXT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TINYTEXT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TINYTEXT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TINYTEXT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TEXT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TEXT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS TEXT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS TEXT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS TEXT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS TEXT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TEXT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS TEXT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TEXT CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS TEXT CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS TEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS TEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS TEXT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TEXT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TEXT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TEXT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TEXT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TEXT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS MEDIUMTEXT CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS MEDIUMTEXT CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS MEDIUMTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS MEDIUMTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS MEDIUMTEXT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS LONGTEXT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS LONGTEXT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS LONGTEXT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS LONGTEXT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS LONGTEXT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS LONGTEXT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS LONGTEXT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_0() RETURNS LONGTEXT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS LONGTEXT CHARACTER SET utf8mb4 LANGUAGE JS AS $$ return "Far over the misty mountains cold"; $$; +CREATE FUNCTION f_str_2() RETURNS LONGTEXT CHARACTER SET latin1 LANGUAGE JS AS $$ return "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE FUNCTION f_str_3() RETURNS LONGTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "Там отвъд мъглявите студени планини"; $$; +CREATE FUNCTION f_str_cerr() RETURNS LONGTEXT CHARACTER SET cp1251 LANGUAGE JS AS $$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE FUNCTION f_array() RETURNS LONGTEXT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS LONGTEXT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS LONGTEXT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS LONGTEXT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS LONGTEXT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS LONGTEXT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +se s0 s1 s2 s3 + alpha Far over the misty mountains cold Au-delà des montagnes glaciales et embrumées Там отвъд мъглявите студени планини +SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, +f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, +f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r1 r2 r3 +1 1 1 +SELECT f_str_cerr(); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT f_typed_arr() AS ta, f_data_view() AS dv; +ta dv +0,1,2,3,5 [object DataView] +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; + +# +# For binary string/BLOB SQL-types in addition to 'null' and 'undefined' +# values, ArrayBuffer-based objects are also getting special treatment. +# All other JS values are converted using the same toString() approach +# as for non-binary strings. +CREATE FUNCTION f_undefined() RETURNS BINARY(50) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS BINARY(50) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS BINARY(50) LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS BINARY(50) LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS BINARY(50) LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS BINARY(50) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS BINARY(50) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS BINARY(50) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS BINARY(50) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS BINARY(50) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS BINARY(50) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS BINARY(50) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS BINARY(50) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS BINARY(50) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS BINARY(50) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +# BINARY type does 0-padding so we use HEX to correctly print returned value. +SELECT HEX(f_int()) AS i, HEX(f_num()) AS num, HEX(f_bigint()) AS bi, HEX(f_bool()) AS bo; +i num bi bo +3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 312E323500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 3130300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 7472756500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +SELECT HEX(f_str_e()) AS se, HEX(f_str_a()) AS sa; +se sa +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 616C706861000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +SELECT HEX(f_array()) AS a, HEX(f_object()) AS o, HEX(f_func()) AS f; +a o f +312C322C33000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 5B6F626A656374204F626A6563745D0000000000000000000000000000000000000000000000000000000000000000000000 66756E6374696F6E20286129207B2072657475726E20313B7D00000000000000000000000000000000000000000000000000 +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000103000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000001030007000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS VARBINARY(60) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS VARBINARY(60) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS VARBINARY(60) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa + alpha +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305 00000001030007 000000000103000700 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TINYBLOB LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TINYBLOB LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS TINYBLOB LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS TINYBLOB LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS TINYBLOB LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS TINYBLOB LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TINYBLOB LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TINYBLOB LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS TINYBLOB LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TINYBLOB LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TINYBLOB LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TINYBLOB LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TINYBLOB LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS TINYBLOB LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS TINYBLOB LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa + alpha +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305 00000001030007 000000000103000700 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS BLOB LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS BLOB LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS BLOB LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS BLOB LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS BLOB LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS BLOB LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS BLOB LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS BLOB LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS BLOB LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS BLOB LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS BLOB LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS BLOB LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS BLOB LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS BLOB LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS BLOB LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa + alpha +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305 00000001030007 000000000103000700 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS MEDIUMBLOB LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa + alpha +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305 00000001030007 000000000103000700 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS LONGBLOB LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS LONGBLOB LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int() RETURNS LONGBLOB LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_num() RETURNS LONGBLOB LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_bigint() RETURNS LONGBLOB LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bool() RETURNS LONGBLOB LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS LONGBLOB LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS LONGBLOB LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_array() RETURNS LONGBLOB LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS LONGBLOB LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS LONGBLOB LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS LONGBLOB LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS LONGBLOB LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv $$; +CREATE FUNCTION f_arr_buff() RETURNS LONGBLOB LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab $$; +CREATE FUNCTION f_object_serr() RETURNS LONGBLOB LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; +i num bi bo +1 1.25 100 true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa + alpha +SELECT f_array() AS a, f_object() AS o, f_func() AS f; +a o f +1,2,3 [object Object] function (a) { return 1;} +SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; +ta dv ab +0001020305 00000001030007 000000000103000700 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_arr_buff; +DROP FUNCTION f_object_serr; +# +# For integer SQL-types conversion works in the following way: +# - JS 'null' and 'undefined' values which are mapped to SQL NULL. +# - If JS numeric or BigInt value can be safely converted to SQL-type +# we use direct conversion (for performance reasons). +# - Otherwise, as well as for all other types of JS values conversion +# is done through strings (i.e. by doing JS toString() conversion +# and trying to store resulting string as SQL integer value). + +# Maximum BIGINT value can't be represented as exact numeric in JS, +# so we resort to using 2^53-1 (max safe in JS) instead and reduce +# testing for it. +CREATE FUNCTION f_undefined() RETURNS TINYINT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TINYINT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS TINYINT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS TINYINT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS TINYINT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_m() RETURNS TINYINT LANGUAGE JS AS $$ return 127; $$; +CREATE FUNCTION f_int_m1() RETURNS TINYINT LANGUAGE JS AS $$ return 127 + 1; $$; +CREATE FUNCTION f_int_nm() RETURNS TINYINT LANGUAGE JS AS $$ return -127 - 1; $$; +CREATE FUNCTION f_int_nm1() RETURNS TINYINT LANGUAGE JS AS $$ return -127 - 2; $$; +CREATE FUNCTION f_uint_m() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return 2*127 + 1; $$; +CREATE FUNCTION f_uint_m1() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return 2*127 + 2; $$; +CREATE FUNCTION f_uint_n() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return - 1; $$; +CREATE FUNCTION f_num_1() RETURNS TINYINT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS TINYINT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS TINYINT LANGUAGE JS AS $$ return 5e-2; $$; +CREATE FUNCTION f_num_4() RETURNS TINYINT LANGUAGE JS AS $$ return 1.2345e+2; $$; +CREATE FUNCTION f_num_5() RETURNS TINYINT LANGUAGE JS AS $$ return -1.2345e+1; $$; +CREATE FUNCTION f_num_r1() RETURNS TINYINT LANGUAGE JS AS $$ return 4.5; $$; +CREATE FUNCTION f_num_r2() RETURNS TINYINT LANGUAGE JS AS $$ return 4.5e+0; $$; +CREATE FUNCTION f_num_un() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return -1.5; $$; +CREATE FUNCTION f_num_tb() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return 1e+70; $$; +CREATE FUNCTION f_bigint() RETURNS TINYINT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_n() RETURNS TINYINT LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_u() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return BigInt(9007199254740991); $$; +CREATE FUNCTION f_bigint_un() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_tb() RETURNS TINYINT LANGUAGE JS AS $$ return BigInt(1e+25); $$; +CREATE FUNCTION f_bool() RETURNS TINYINT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TINYINT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TINYINT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS TINYINT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS TINYINT LANGUAGE JS AS $$ return "-2"; $$; +CREATE FUNCTION f_str_n3() RETURNS TINYINT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_nu() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return "-1"; $$; +CREATE FUNCTION f_str_nr1() RETURNS TINYINT LANGUAGE JS AS $$ return "4.5"; $$; +CREATE FUNCTION f_str_nr2() RETURNS TINYINT LANGUAGE JS AS $$ return "4.5e+0"; $$; +CREATE FUNCTION f_str_tb1() RETURNS TINYINT LANGUAGE JS AS $$ return "1e+25"; $$; +CREATE FUNCTION f_str_tb2() RETURNS TINYINT LANGUAGE JS AS $$ return "18446744073709551616"; $$; +CREATE FUNCTION f_array() RETURNS TINYINT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TINYINT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TINYINT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TINYINT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TINYINT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TINYINT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +CREATE FUNCTION f_object_userr() RETURNS TINYINT UNSIGNED LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; +i0 i1 n im nm um +0 1 -1 127 -128 255 +SELECT f_int_m1(); +ERROR 22003: Out of range value for column 'f_int_m1()' at row 1 +SELECT f_int_nm1(); +ERROR 22003: Out of range value for column 'f_int_nm1()' at row 1 +SELECT f_uint_m1(); +ERROR 22003: Out of range value for column 'f_uint_m1()' at row 1 +SELECT f_uint_n(); +ERROR 22003: Out of range value for column 'f_uint_n()' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); +n1 n2 n3 n4 f_num_5() +1 1 0 123 -12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +SELECT f_num_r1() AS r1, f_num_r2() AS r2; +r1 r2 +5 5 +SELECT f_num_un(); +ERROR 22003: Out of range value for column 'f_num_un()' at row 1 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi, f_bigint_n() AS bn; +bi bn +100 -42 +SELECT f_bigint_un(); +ERROR 22003: Out of range value for column 'f_bigint_un()' at row 1 +SELECT f_bigint_tb(); +ERROR 22003: Out of range value for column 'f_bigint_tb()' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect integer value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect integer value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect integer value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +123 -2 13 +SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; +nr1 nr2 +5 5 +SELECT f_str_nu(); +ERROR 22003: Out of range value for column 'f_str_nu()' at row 1 +SELECT f_str_tb1(); +ERROR 22003: Out of range value for column 'f_str_tb1()' at row 1 +SELECT f_str_tb2(); +ERROR 22003: Out of range value for column 'f_str_tb2()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +SELECT f_object_userr() AS userr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_m; +DROP FUNCTION f_int_m1; +DROP FUNCTION f_int_nm; +DROP FUNCTION f_int_nm1; +DROP FUNCTION f_uint_m; +DROP FUNCTION f_uint_m1; +DROP FUNCTION f_uint_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_5; +DROP FUNCTION f_num_r1; +DROP FUNCTION f_num_r2; +DROP FUNCTION f_num_un; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bigint_u; +DROP FUNCTION f_bigint_un; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_nu; +DROP FUNCTION f_str_nr1; +DROP FUNCTION f_str_nr2; +DROP FUNCTION f_str_tb1; +DROP FUNCTION f_str_tb2; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +DROP FUNCTION f_object_userr; +CREATE FUNCTION f_undefined() RETURNS SMALLINT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS SMALLINT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS SMALLINT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS SMALLINT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS SMALLINT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_m() RETURNS SMALLINT LANGUAGE JS AS $$ return 32767; $$; +CREATE FUNCTION f_int_m1() RETURNS SMALLINT LANGUAGE JS AS $$ return 32767 + 1; $$; +CREATE FUNCTION f_int_nm() RETURNS SMALLINT LANGUAGE JS AS $$ return -32767 - 1; $$; +CREATE FUNCTION f_int_nm1() RETURNS SMALLINT LANGUAGE JS AS $$ return -32767 - 2; $$; +CREATE FUNCTION f_uint_m() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return 2*32767 + 1; $$; +CREATE FUNCTION f_uint_m1() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return 2*32767 + 2; $$; +CREATE FUNCTION f_uint_n() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return - 1; $$; +CREATE FUNCTION f_num_1() RETURNS SMALLINT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS SMALLINT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS SMALLINT LANGUAGE JS AS $$ return 5e-2; $$; +CREATE FUNCTION f_num_4() RETURNS SMALLINT LANGUAGE JS AS $$ return 1.2345e+2; $$; +CREATE FUNCTION f_num_5() RETURNS SMALLINT LANGUAGE JS AS $$ return -1.2345e+1; $$; +CREATE FUNCTION f_num_r1() RETURNS SMALLINT LANGUAGE JS AS $$ return 4.5; $$; +CREATE FUNCTION f_num_r2() RETURNS SMALLINT LANGUAGE JS AS $$ return 4.5e+0; $$; +CREATE FUNCTION f_num_un() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return -1.5; $$; +CREATE FUNCTION f_num_tb() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return 1e+70; $$; +CREATE FUNCTION f_bigint() RETURNS SMALLINT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_n() RETURNS SMALLINT LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_u() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return BigInt(9007199254740991); $$; +CREATE FUNCTION f_bigint_un() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_tb() RETURNS SMALLINT LANGUAGE JS AS $$ return BigInt(1e+25); $$; +CREATE FUNCTION f_bool() RETURNS SMALLINT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS SMALLINT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS SMALLINT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS SMALLINT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS SMALLINT LANGUAGE JS AS $$ return "-2"; $$; +CREATE FUNCTION f_str_n3() RETURNS SMALLINT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_nu() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return "-1"; $$; +CREATE FUNCTION f_str_nr1() RETURNS SMALLINT LANGUAGE JS AS $$ return "4.5"; $$; +CREATE FUNCTION f_str_nr2() RETURNS SMALLINT LANGUAGE JS AS $$ return "4.5e+0"; $$; +CREATE FUNCTION f_str_tb1() RETURNS SMALLINT LANGUAGE JS AS $$ return "1e+25"; $$; +CREATE FUNCTION f_str_tb2() RETURNS SMALLINT LANGUAGE JS AS $$ return "18446744073709551616"; $$; +CREATE FUNCTION f_array() RETURNS SMALLINT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS SMALLINT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS SMALLINT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS SMALLINT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS SMALLINT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS SMALLINT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +CREATE FUNCTION f_object_userr() RETURNS SMALLINT UNSIGNED LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; +i0 i1 n im nm um +0 1 -1 32767 -32768 65535 +SELECT f_int_m1(); +ERROR 22003: Out of range value for column 'f_int_m1()' at row 1 +SELECT f_int_nm1(); +ERROR 22003: Out of range value for column 'f_int_nm1()' at row 1 +SELECT f_uint_m1(); +ERROR 22003: Out of range value for column 'f_uint_m1()' at row 1 +SELECT f_uint_n(); +ERROR 22003: Out of range value for column 'f_uint_n()' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); +n1 n2 n3 n4 f_num_5() +1 1 0 123 -12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +SELECT f_num_r1() AS r1, f_num_r2() AS r2; +r1 r2 +5 5 +SELECT f_num_un(); +ERROR 22003: Out of range value for column 'f_num_un()' at row 1 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi, f_bigint_n() AS bn; +bi bn +100 -42 +SELECT f_bigint_un(); +ERROR 22003: Out of range value for column 'f_bigint_un()' at row 1 +SELECT f_bigint_tb(); +ERROR 22003: Out of range value for column 'f_bigint_tb()' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect integer value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect integer value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect integer value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +123 -2 13 +SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; +nr1 nr2 +5 5 +SELECT f_str_nu(); +ERROR 22003: Out of range value for column 'f_str_nu()' at row 1 +SELECT f_str_tb1(); +ERROR 22003: Out of range value for column 'f_str_tb1()' at row 1 +SELECT f_str_tb2(); +ERROR 22003: Out of range value for column 'f_str_tb2()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +SELECT f_object_userr() AS userr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_m; +DROP FUNCTION f_int_m1; +DROP FUNCTION f_int_nm; +DROP FUNCTION f_int_nm1; +DROP FUNCTION f_uint_m; +DROP FUNCTION f_uint_m1; +DROP FUNCTION f_uint_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_5; +DROP FUNCTION f_num_r1; +DROP FUNCTION f_num_r2; +DROP FUNCTION f_num_un; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bigint_u; +DROP FUNCTION f_bigint_un; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_nu; +DROP FUNCTION f_str_nr1; +DROP FUNCTION f_str_nr2; +DROP FUNCTION f_str_tb1; +DROP FUNCTION f_str_tb2; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +DROP FUNCTION f_object_userr; +CREATE FUNCTION f_undefined() RETURNS MEDIUMINT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS MEDIUMINT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS MEDIUMINT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_m() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 8388607; $$; +CREATE FUNCTION f_int_m1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 8388607 + 1; $$; +CREATE FUNCTION f_int_nm() RETURNS MEDIUMINT LANGUAGE JS AS $$ return -8388607 - 1; $$; +CREATE FUNCTION f_int_nm1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return -8388607 - 2; $$; +CREATE FUNCTION f_uint_m() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return 2*8388607 + 1; $$; +CREATE FUNCTION f_uint_m1() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return 2*8388607 + 2; $$; +CREATE FUNCTION f_uint_n() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return - 1; $$; +CREATE FUNCTION f_num_1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 5e-2; $$; +CREATE FUNCTION f_num_4() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 1.2345e+2; $$; +CREATE FUNCTION f_num_5() RETURNS MEDIUMINT LANGUAGE JS AS $$ return -1.2345e+1; $$; +CREATE FUNCTION f_num_r1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 4.5; $$; +CREATE FUNCTION f_num_r2() RETURNS MEDIUMINT LANGUAGE JS AS $$ return 4.5e+0; $$; +CREATE FUNCTION f_num_un() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return -1.5; $$; +CREATE FUNCTION f_num_tb() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return 1e+70; $$; +CREATE FUNCTION f_bigint() RETURNS MEDIUMINT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_n() RETURNS MEDIUMINT LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_u() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return BigInt(9007199254740991); $$; +CREATE FUNCTION f_bigint_un() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_tb() RETURNS MEDIUMINT LANGUAGE JS AS $$ return BigInt(1e+25); $$; +CREATE FUNCTION f_bool() RETURNS MEDIUMINT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS MEDIUMINT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "-2"; $$; +CREATE FUNCTION f_str_n3() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_nu() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return "-1"; $$; +CREATE FUNCTION f_str_nr1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "4.5"; $$; +CREATE FUNCTION f_str_nr2() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "4.5e+0"; $$; +CREATE FUNCTION f_str_tb1() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "1e+25"; $$; +CREATE FUNCTION f_str_tb2() RETURNS MEDIUMINT LANGUAGE JS AS $$ return "18446744073709551616"; $$; +CREATE FUNCTION f_array() RETURNS MEDIUMINT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS MEDIUMINT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS MEDIUMINT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS MEDIUMINT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS MEDIUMINT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS MEDIUMINT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +CREATE FUNCTION f_object_userr() RETURNS MEDIUMINT UNSIGNED LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; +i0 i1 n im nm um +0 1 -1 8388607 -8388608 16777215 +SELECT f_int_m1(); +ERROR 22003: Out of range value for column 'f_int_m1()' at row 1 +SELECT f_int_nm1(); +ERROR 22003: Out of range value for column 'f_int_nm1()' at row 1 +SELECT f_uint_m1(); +ERROR 22003: Out of range value for column 'f_uint_m1()' at row 1 +SELECT f_uint_n(); +ERROR 22003: Out of range value for column 'f_uint_n()' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); +n1 n2 n3 n4 f_num_5() +1 1 0 123 -12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +SELECT f_num_r1() AS r1, f_num_r2() AS r2; +r1 r2 +5 5 +SELECT f_num_un(); +ERROR 22003: Out of range value for column 'f_num_un()' at row 1 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi, f_bigint_n() AS bn; +bi bn +100 -42 +SELECT f_bigint_un(); +ERROR 22003: Out of range value for column 'f_bigint_un()' at row 1 +SELECT f_bigint_tb(); +ERROR 22003: Out of range value for column 'f_bigint_tb()' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect integer value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect integer value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect integer value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +123 -2 13 +SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; +nr1 nr2 +5 5 +SELECT f_str_nu(); +ERROR 22003: Out of range value for column 'f_str_nu()' at row 1 +SELECT f_str_tb1(); +ERROR 22003: Out of range value for column 'f_str_tb1()' at row 1 +SELECT f_str_tb2(); +ERROR 22003: Out of range value for column 'f_str_tb2()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +SELECT f_object_userr() AS userr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_m; +DROP FUNCTION f_int_m1; +DROP FUNCTION f_int_nm; +DROP FUNCTION f_int_nm1; +DROP FUNCTION f_uint_m; +DROP FUNCTION f_uint_m1; +DROP FUNCTION f_uint_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_5; +DROP FUNCTION f_num_r1; +DROP FUNCTION f_num_r2; +DROP FUNCTION f_num_un; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bigint_u; +DROP FUNCTION f_bigint_un; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_nu; +DROP FUNCTION f_str_nr1; +DROP FUNCTION f_str_nr2; +DROP FUNCTION f_str_tb1; +DROP FUNCTION f_str_tb2; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +DROP FUNCTION f_object_userr; +CREATE FUNCTION f_undefined() RETURNS INT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS INT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS INT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS INT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS INT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_m() RETURNS INT LANGUAGE JS AS $$ return 2147483647; $$; +CREATE FUNCTION f_int_m1() RETURNS INT LANGUAGE JS AS $$ return 2147483647 + 1; $$; +CREATE FUNCTION f_int_nm() RETURNS INT LANGUAGE JS AS $$ return -2147483647 - 1; $$; +CREATE FUNCTION f_int_nm1() RETURNS INT LANGUAGE JS AS $$ return -2147483647 - 2; $$; +CREATE FUNCTION f_uint_m() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return 2*2147483647 + 1; $$; +CREATE FUNCTION f_uint_m1() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return 2*2147483647 + 2; $$; +CREATE FUNCTION f_uint_n() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return - 1; $$; +CREATE FUNCTION f_num_1() RETURNS INT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS INT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS INT LANGUAGE JS AS $$ return 5e-2; $$; +CREATE FUNCTION f_num_4() RETURNS INT LANGUAGE JS AS $$ return 1.2345e+2; $$; +CREATE FUNCTION f_num_5() RETURNS INT LANGUAGE JS AS $$ return -1.2345e+1; $$; +CREATE FUNCTION f_num_r1() RETURNS INT LANGUAGE JS AS $$ return 4.5; $$; +CREATE FUNCTION f_num_r2() RETURNS INT LANGUAGE JS AS $$ return 4.5e+0; $$; +CREATE FUNCTION f_num_un() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return -1.5; $$; +CREATE FUNCTION f_num_tb() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return 1e+70; $$; +CREATE FUNCTION f_bigint() RETURNS INT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_n() RETURNS INT LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_u() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return BigInt(9007199254740991); $$; +CREATE FUNCTION f_bigint_un() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_tb() RETURNS INT LANGUAGE JS AS $$ return BigInt(1e+25); $$; +CREATE FUNCTION f_bool() RETURNS INT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS INT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS INT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS INT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS INT LANGUAGE JS AS $$ return "-2"; $$; +CREATE FUNCTION f_str_n3() RETURNS INT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_nu() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return "-1"; $$; +CREATE FUNCTION f_str_nr1() RETURNS INT LANGUAGE JS AS $$ return "4.5"; $$; +CREATE FUNCTION f_str_nr2() RETURNS INT LANGUAGE JS AS $$ return "4.5e+0"; $$; +CREATE FUNCTION f_str_tb1() RETURNS INT LANGUAGE JS AS $$ return "1e+25"; $$; +CREATE FUNCTION f_str_tb2() RETURNS INT LANGUAGE JS AS $$ return "18446744073709551616"; $$; +CREATE FUNCTION f_array() RETURNS INT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS INT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS INT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS INT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS INT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS INT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +CREATE FUNCTION f_object_userr() RETURNS INT UNSIGNED LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; +i0 i1 n im nm um +0 1 -1 2147483647 -2147483648 4294967295 +SELECT f_int_m1(); +ERROR 22003: Out of range value for column 'f_int_m1()' at row 1 +SELECT f_int_nm1(); +ERROR 22003: Out of range value for column 'f_int_nm1()' at row 1 +SELECT f_uint_m1(); +ERROR 22003: Out of range value for column 'f_uint_m1()' at row 1 +SELECT f_uint_n(); +ERROR 22003: Out of range value for column 'f_uint_n()' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); +n1 n2 n3 n4 f_num_5() +1 1 0 123 -12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +SELECT f_num_r1() AS r1, f_num_r2() AS r2; +r1 r2 +5 5 +SELECT f_num_un(); +ERROR 22003: Out of range value for column 'f_num_un()' at row 1 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi, f_bigint_n() AS bn; +bi bn +100 -42 +SELECT f_bigint_un(); +ERROR 22003: Out of range value for column 'f_bigint_un()' at row 1 +SELECT f_bigint_tb(); +ERROR 22003: Out of range value for column 'f_bigint_tb()' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect integer value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect integer value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect integer value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +123 -2 13 +SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; +nr1 nr2 +5 5 +SELECT f_str_nu(); +ERROR 22003: Out of range value for column 'f_str_nu()' at row 1 +SELECT f_str_tb1(); +ERROR 22003: Out of range value for column 'f_str_tb1()' at row 1 +SELECT f_str_tb2(); +ERROR 22003: Out of range value for column 'f_str_tb2()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +SELECT f_object_userr() AS userr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_m; +DROP FUNCTION f_int_m1; +DROP FUNCTION f_int_nm; +DROP FUNCTION f_int_nm1; +DROP FUNCTION f_uint_m; +DROP FUNCTION f_uint_m1; +DROP FUNCTION f_uint_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_5; +DROP FUNCTION f_num_r1; +DROP FUNCTION f_num_r2; +DROP FUNCTION f_num_un; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bigint_u; +DROP FUNCTION f_bigint_un; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_nu; +DROP FUNCTION f_str_nr1; +DROP FUNCTION f_str_nr2; +DROP FUNCTION f_str_tb1; +DROP FUNCTION f_str_tb2; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +DROP FUNCTION f_object_userr; +CREATE FUNCTION f_undefined() RETURNS BIGINT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS BIGINT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS BIGINT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS BIGINT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS BIGINT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_m() RETURNS BIGINT LANGUAGE JS AS $$ return 9007199254740991; $$; +CREATE FUNCTION f_int_m1() RETURNS BIGINT LANGUAGE JS AS $$ return 9007199254740991 + 1; $$; +CREATE FUNCTION f_int_nm() RETURNS BIGINT LANGUAGE JS AS $$ return -9007199254740991 - 1; $$; +CREATE FUNCTION f_int_nm1() RETURNS BIGINT LANGUAGE JS AS $$ return -9007199254740991 - 2; $$; +CREATE FUNCTION f_uint_m() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return 2*9007199254740991 + 1; $$; +CREATE FUNCTION f_uint_m1() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return 2*9007199254740991 + 2; $$; +CREATE FUNCTION f_uint_n() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return - 1; $$; +CREATE FUNCTION f_num_1() RETURNS BIGINT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS BIGINT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS BIGINT LANGUAGE JS AS $$ return 5e-2; $$; +CREATE FUNCTION f_num_4() RETURNS BIGINT LANGUAGE JS AS $$ return 1.2345e+2; $$; +CREATE FUNCTION f_num_5() RETURNS BIGINT LANGUAGE JS AS $$ return -1.2345e+1; $$; +CREATE FUNCTION f_num_r1() RETURNS BIGINT LANGUAGE JS AS $$ return 4.5; $$; +CREATE FUNCTION f_num_r2() RETURNS BIGINT LANGUAGE JS AS $$ return 4.5e+0; $$; +CREATE FUNCTION f_num_un() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return -1.5; $$; +CREATE FUNCTION f_num_tb() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return 1e+70; $$; +CREATE FUNCTION f_bigint() RETURNS BIGINT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_n() RETURNS BIGINT LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_u() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return BigInt(9007199254740991); $$; +CREATE FUNCTION f_bigint_un() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return BigInt(-42); $$; +CREATE FUNCTION f_bigint_tb() RETURNS BIGINT LANGUAGE JS AS $$ return BigInt(1e+25); $$; +CREATE FUNCTION f_bool() RETURNS BIGINT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS BIGINT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS BIGINT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS BIGINT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS BIGINT LANGUAGE JS AS $$ return "-2"; $$; +CREATE FUNCTION f_str_n3() RETURNS BIGINT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_nu() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return "-1"; $$; +CREATE FUNCTION f_str_nr1() RETURNS BIGINT LANGUAGE JS AS $$ return "4.5"; $$; +CREATE FUNCTION f_str_nr2() RETURNS BIGINT LANGUAGE JS AS $$ return "4.5e+0"; $$; +CREATE FUNCTION f_str_tb1() RETURNS BIGINT LANGUAGE JS AS $$ return "1e+25"; $$; +CREATE FUNCTION f_str_tb2() RETURNS BIGINT LANGUAGE JS AS $$ return "18446744073709551616"; $$; +CREATE FUNCTION f_array() RETURNS BIGINT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS BIGINT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS BIGINT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS BIGINT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS BIGINT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS BIGINT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +CREATE FUNCTION f_object_userr() RETURNS BIGINT UNSIGNED LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; +i0 i1 n im nm um +0 1 -1 9007199254740991 -9007199254740992 18014398509481984 +SELECT f_uint_n(); +ERROR 22003: Out of range value for column 'f_uint_n()' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); +n1 n2 n3 n4 f_num_5() +1 1 0 123 -12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +SELECT f_num_r1() AS r1, f_num_r2() AS r2; +r1 r2 +5 5 +SELECT f_num_un(); +ERROR 22003: Out of range value for column 'f_num_un()' at row 1 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi, f_bigint_n() AS bn; +bi bn +100 -42 +SELECT f_bigint_u() AS bu; +bu +9007199254740991 +SELECT f_bigint_un(); +ERROR 22003: Out of range value for column 'f_bigint_un()' at row 1 +SELECT f_bigint_tb(); +ERROR 22003: Out of range value for column 'f_bigint_tb()' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect integer value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect integer value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect integer value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +123 -2 13 +SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; +nr1 nr2 +5 5 +SELECT f_str_nu(); +ERROR 22003: Out of range value for column 'f_str_nu()' at row 1 +SELECT f_str_tb1(); +ERROR 22003: Out of range value for column 'f_str_tb1()' at row 1 +SELECT f_str_tb2(); +ERROR 22003: Out of range value for column 'f_str_tb2()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +SELECT f_object_userr() AS userr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_m; +DROP FUNCTION f_int_m1; +DROP FUNCTION f_int_nm; +DROP FUNCTION f_int_nm1; +DROP FUNCTION f_uint_m; +DROP FUNCTION f_uint_m1; +DROP FUNCTION f_uint_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_5; +DROP FUNCTION f_num_r1; +DROP FUNCTION f_num_r2; +DROP FUNCTION f_num_un; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bigint_u; +DROP FUNCTION f_bigint_un; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_nu; +DROP FUNCTION f_str_nr1; +DROP FUNCTION f_str_nr2; +DROP FUNCTION f_str_tb1; +DROP FUNCTION f_str_tb2; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +DROP FUNCTION f_object_userr; +# +# For floating point SQL-types conversion works in similar way: +# - JS 'null' and 'undefined' values which are mapped to SQL NULL. +# - If JS numeric value can be safely converted to SQL-type we use +# direct conversion (for performance reasons). +# - Otherwise, as well as for all other types of JS values conversion +# is done through strings (i.e. by doing JS toString() conversion +# and trying to store resulting string as SQL floating-point value). +CREATE FUNCTION f_undefined() RETURNS FLOAT LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS FLOAT LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS FLOAT LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS FLOAT LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS FLOAT LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_num_1() RETURNS FLOAT LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS FLOAT LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS FLOAT LANGUAGE JS AS $$ return -5e-2; $$; +CREATE FUNCTION f_num_m() RETURNS FLOAT LANGUAGE JS AS $$ return 3.4028234e+38; $$; +CREATE FUNCTION f_bigint() RETURNS FLOAT LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_pl() RETURNS FLOAT LANGUAGE JS AS $$ return BigInt("36028797018963967"); $$; +CREATE FUNCTION f_bool() RETURNS FLOAT LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS FLOAT LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS FLOAT LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS FLOAT LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS FLOAT LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_n_pl() RETURNS FLOAT LANGUAGE JS AS $$ return "36028797018963967"; $$; +CREATE FUNCTION f_str_n_tb() RETURNS FLOAT LANGUAGE JS AS $$ return "1.1e+400"; $$; +CREATE FUNCTION f_array() RETURNS FLOAT LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS FLOAT LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS FLOAT LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS FLOAT LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS FLOAT LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS FLOAT LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_m() AS nm; +i0 i1 n n1 n2 n3 nm +0 1 -1 1.25 0.5 -0.05 3.40282e38 +SELECT f_bigint() AS bi; +bi +100 +SELECT f_bigint_pl() AS bipl; +bipl +3.60288e16 +SELECT f_bool() AS bo; +ERROR 01000: Data truncated for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR 01000: Data truncated for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR 01000: Data truncated for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2; +n1 n2 +123 12.65 +SELECT f_str_n_pl() AS spl; +spl +3.60288e16 +SELECT f_str_n_tb() AS stb; +ERROR 22003: Out of range value for column 'stb' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 01000: Data truncated for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 01000: Data truncated for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 01000: Data truncated for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_m; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_pl; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n_pl; +DROP FUNCTION f_str_n_tb; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS DOUBLE LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS DOUBLE LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS DOUBLE LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS DOUBLE LANGUAGE JS AS $$ return 1; $$; +CREATE FUNCTION f_int_n() RETURNS DOUBLE LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_num_1() RETURNS DOUBLE LANGUAGE JS AS $$ return 1.25; $$; +CREATE FUNCTION f_num_2() RETURNS DOUBLE LANGUAGE JS AS $$ return 5e-1; $$; +CREATE FUNCTION f_num_3() RETURNS DOUBLE LANGUAGE JS AS $$ return -5e-2; $$; +CREATE FUNCTION f_num_m() RETURNS DOUBLE LANGUAGE JS AS $$ return 1.7976931348623157e+308; $$; +CREATE FUNCTION f_bigint() RETURNS DOUBLE LANGUAGE JS AS $$ return BigInt(100); $$; +CREATE FUNCTION f_bigint_pl() RETURNS DOUBLE LANGUAGE JS AS $$ return BigInt("36028797018963967"); $$; +CREATE FUNCTION f_bool() RETURNS DOUBLE LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS DOUBLE LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS DOUBLE LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_n1() RETURNS DOUBLE LANGUAGE JS AS $$ return "123"; $$; +CREATE FUNCTION f_str_n2() RETURNS DOUBLE LANGUAGE JS AS $$ return "12.65"; $$; +CREATE FUNCTION f_str_n_pl() RETURNS DOUBLE LANGUAGE JS AS $$ return "36028797018963967"; $$; +CREATE FUNCTION f_str_n_tb() RETURNS DOUBLE LANGUAGE JS AS $$ return "1.1e+400"; $$; +CREATE FUNCTION f_array() RETURNS DOUBLE LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DOUBLE LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DOUBLE LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS DOUBLE LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS DOUBLE LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS DOUBLE LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_m() AS nm; +i0 i1 n n1 n2 n3 nm +0 1 -1 1.25 0.5 -0.05 1.7976931348623157e308 +SELECT f_bigint() AS bi; +bi +100 +SELECT f_bigint_pl() AS bipl; +bipl +3.602879701896397e16 +SELECT f_bool() AS bo; +ERROR 01000: Data truncated for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR 01000: Data truncated for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR 01000: Data truncated for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2; +n1 n2 +123 12.65 +SELECT f_str_n_pl() AS spl; +spl +3.602879701896397e16 +SELECT f_str_n_tb() AS stb; +ERROR 22003: Out of range value for column 'stb' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 01000: Data truncated for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 01000: Data truncated for column 'f' at row 1 +SELECT f_typed_arr() AS ta; +ERROR 01000: Data truncated for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 01000: Data truncated for column 'dv' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_m; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_pl; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n_pl; +DROP FUNCTION f_str_n_tb; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +# +# For DECIMAL SQL-type conversion is done through strings (by doing JS +# toString() conversion and trying to store resulting string as SQL +# type value). There is no point in optimizing conversion from Number +# type, like it is done for floating point SQL-types, as SQL core does +# double -> DECIMAL conversions through strings. As usual JS 'null' +# and 'undefined' values are mapped to SQL NULL. +# +CREATE FUNCTION f_undefined() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int_0() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_int_n() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_int_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 100 $$; +CREATE FUNCTION f_num_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 5e-1 $$; +CREATE FUNCTION f_num_3() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return -5e-2 $$; +CREATE FUNCTION f_num_m() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 99.99 $$; +CREATE FUNCTION f_num_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 100.01 $$; +CREATE FUNCTION f_num_tl() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 0.0125 $$; +CREATE FUNCTION f_bigint_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return BigInt(10) $$; +CREATE FUNCTION f_bigint_2() RETURNS DECIMAL(20,2) LANGUAGE JS AS $$ return BigInt("10000000000000000") $$; +CREATE FUNCTION f_bigint_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return BigInt(1000) $$; +CREATE FUNCTION f_bool() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_n1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12" $$; +CREATE FUNCTION f_str_n2() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12.65" $$; +CREATE FUNCTION f_str_n3() RETURNS DECIMAL(20,2) LANGUAGE JS AS $$ return "10000000000000000.12" $$; +CREATE FUNCTION f_str_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "123" $$; +CREATE FUNCTION f_str_tl() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12.324" $$; +CREATE FUNCTION f_str_api() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "π" $$; +CREATE FUNCTION f_array() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return function (a) { return 1} $$; +CREATE FUNCTION f_object_serr() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n; +i0 i1 n +0.00 1.00 -1.00 +SELECT f_int_tb() AS itb; +ERROR 22003: Out of range value for column 'itb' at row 1 +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_m() AS nm; +n1 n2 n3 nm +1.25 0.50 -0.05 99.99 +SELECT f_num_tb() AS ntb; +ERROR 22003: Out of range value for column 'ntb' at row 1 +SELECT f_num_tl() AS ntl; +ntl +0.01 +Warnings: +Note 1265 Data truncated for column 'ntl' at row 1 +SELECT f_bigint_1() AS bi1, f_bigint_2() AS bi2; +bi1 bi2 +10.00 10000000000000000.00 +SELECT f_bigint_tb() AS bitb; +ERROR 22003: Out of range value for column 'bitb' at row 1 +SELECT f_bool() AS bo; +ERROR HY000: Incorrect decimal value: 'true' for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR HY000: Incorrect decimal value: '' for column 'se' at row 1 +SELECT f_str_a() AS sa; +ERROR HY000: Incorrect decimal value: 'alpha' for column 'sa' at row 1 +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; +n1 n2 n3 +12.00 12.65 10000000000000000.12 +SELECT f_str_tb() AS stb; +ERROR 22003: Out of range value for column 'stb' at row 1 +SELECT f_str_tl() AS stl; +stl +12.32 +Warnings: +Note 1265 Data truncated for column 'stl' at row 1 +SELECT f_str_api() AS sapi; +ERROR HY000: Incorrect decimal value: 'π' for column 'sapi' at row 1 +SELECT f_array() AS a; +ERROR HY000: Incorrect decimal value: '1,2,3' for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect decimal value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect decimal value: 'function (a) { return 1}' for column 'f' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_m; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_num_tl; +DROP FUNCTION f_bigint_1; +DROP FUNCTION f_bigint_2; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_tb; +DROP FUNCTION f_str_tl; +DROP FUNCTION f_str_api; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; +# +# YEAR return type is handled similarly to integer types. +# +CREATE FUNCTION f_undefined() RETURNS YEAR LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS YEAR LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int_0() RETURNS YEAR LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS YEAR LANGUAGE JS AS $$ return 7 $$; +CREATE FUNCTION f_int_2() RETURNS YEAR LANGUAGE JS AS $$ return 69 $$; +CREATE FUNCTION f_int_3() RETURNS YEAR LANGUAGE JS AS $$ return 70 $$; +CREATE FUNCTION f_int_o() RETURNS YEAR LANGUAGE JS AS $$ return 123 $$; +CREATE FUNCTION f_int_mi() RETURNS YEAR LANGUAGE JS AS $$ return 1901 $$; +CREATE FUNCTION f_int_mx() RETURNS YEAR LANGUAGE JS AS $$ return 2155 $$; +CREATE FUNCTION f_int_mi1() RETURNS YEAR LANGUAGE JS AS $$ return 1900 $$; +CREATE FUNCTION f_int_mx1() RETURNS YEAR LANGUAGE JS AS $$ return 2156 $$; +CREATE FUNCTION f_int_n() RETURNS YEAR LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_num_1() RETURNS YEAR LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS YEAR LANGUAGE JS AS $$ return 5e-1 $$; +CREATE FUNCTION f_num_3() RETURNS YEAR LANGUAGE JS AS $$ return 5e+1 $$; +CREATE FUNCTION f_num_4() RETURNS YEAR LANGUAGE JS AS $$ return 1.901e+3 $$; +CREATE FUNCTION f_num_tb() RETURNS YEAR LANGUAGE JS AS $$ return 2.2e+3 $$; +CREATE FUNCTION f_bigint() RETURNS YEAR LANGUAGE JS AS $$ return BigInt(70) $$; +CREATE FUNCTION f_bool() RETURNS YEAR LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS YEAR LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS YEAR LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_1() RETURNS YEAR LANGUAGE JS AS $$ return "12" $$; +CREATE FUNCTION f_str_2() RETURNS YEAR LANGUAGE JS AS $$ return "75" $$; +CREATE FUNCTION f_str_3() RETURNS YEAR LANGUAGE JS AS $$ return "7.5" $$; +CREATE FUNCTION f_str_o() RETURNS YEAR LANGUAGE JS AS $$ return "100" $$; +CREATE FUNCTION f_str_mi() RETURNS YEAR LANGUAGE JS AS $$ return"1901" $$; +CREATE FUNCTION f_str_mx() RETURNS YEAR LANGUAGE JS AS $$ return"2155" $$; +CREATE FUNCTION f_str_n() RETURNS YEAR LANGUAGE JS AS $$ return "-1" $$; +CREATE FUNCTION f_str_mi1() RETURNS YEAR LANGUAGE JS AS $$ return"1900" $$; +CREATE FUNCTION f_str_mx1() RETURNS YEAR LANGUAGE JS AS $$ return"2156" $$; +CREATE FUNCTION f_array() RETURNS YEAR LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS YEAR LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS YEAR LANGUAGE JS AS $$ return function (a) { return 1} $$; +CREATE FUNCTION f_object_serr() RETURNS YEAR LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_2() AS i2, f_int_3() AS i3; +i0 i1 i2 i3 +0 2007 2069 1970 +SELECT f_int_o() AS o; +ERROR 22003: Out of range value for column 'o' at row 1 +SELECT f_int_mi() AS mi, f_int_mx() AS mx; +mi mx +1901 2155 +SELECT f_int_mi1(); +ERROR 22003: Out of range value for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22003: Out of range value for column 'f_int_mx1()' at row 1 +SELECT f_int_n(); +ERROR 22003: Out of range value for column 'f_int_n()' at row 1 +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4; +n1 n2 n3 n4 +2001 2001 2050 1901 +SELECT f_num_tb(); +ERROR 22003: Out of range value for column 'f_num_tb()' at row 1 +SELECT f_bigint() AS bi; +bi +1970 +SELECT f_bool(); +ERROR HY000: Incorrect integer value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e(); +ERROR HY000: Incorrect integer value: '' for column 'f_str_e()' at row 1 +SELECT f_str_a(); +ERROR HY000: Incorrect integer value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +s1 s2 s3 +2012 1975 2008 +SELECT f_str_o(); +ERROR 22003: Out of range value for column 'f_str_o()' at row 1 +SELECT f_str_mi() AS mi, f_str_mx() AS mx; +mi mx +1901 2155 +SELECT f_str_n(); +ERROR 22003: Out of range value for column 'f_str_n()' at row 1 +SELECT f_str_mi1(); +ERROR 22003: Out of range value for column 'f_str_mi1()' at row 1 +SELECT f_str_mx1(); +ERROR 22003: Out of range value for column 'f_str_mx1()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR HY000: Incorrect integer value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR HY000: Incorrect integer value: 'function (a) { return 1}' for column 'f' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_2; +DROP FUNCTION f_int_3; +DROP FUNCTION f_int_o; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_o; +DROP FUNCTION f_str_mi; +DROP FUNCTION f_str_mx; +DROP FUNCTION f_str_n; +DROP FUNCTION f_str_mi1; +DROP FUNCTION f_str_mx1; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; +# +# For other datetime SQL-types conversion is done through strings +# (by doing JS toString() conversion and trying to store resulting +# string as SQL type value). As usual JS 'null' and 'undefined' +# values which are mapped to SQL NULL. +# +CREATE FUNCTION f_undefined() RETURNS DATE LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS DATE LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS DATE LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS DATE LANGUAGE JS AS $$ return 20200101; $$; +CREATE FUNCTION f_int_mi() RETURNS DATE LANGUAGE JS AS $$ return 10000101; $$; +CREATE FUNCTION f_int_mx() RETURNS DATE LANGUAGE JS AS $$ return 99991231; $$; +CREATE FUNCTION f_int_n() RETURNS DATE LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS DATE LANGUAGE JS AS $$ return 10000101 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS DATE LANGUAGE JS AS $$ return 99991231 + 1; $$; +CREATE FUNCTION f_num() RETURNS DATE LANGUAGE JS AS $$ return 2.0200101e+7; $$; +CREATE FUNCTION f_bigint() RETURNS DATE LANGUAGE JS AS $$ return BigInt(20200101); $$; +CREATE FUNCTION f_bool() RETURNS DATE LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS DATE LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS DATE LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS DATE LANGUAGE JS AS $$ return "2020-01-01"; $$; +CREATE FUNCTION f_str_b() RETURNS DATE LANGUAGE JS AS $$ return "2020-13-01"; $$; +CREATE FUNCTION f_str_ax() RETURNS DATE LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS DATE LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS DATE LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DATE LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DATE LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS DATE LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS DATE LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS DATE LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0(); +ERROR 22007: Incorrect date value: '0' for column 'f_int_0()' at row 1 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +2020-01-01 1000-01-01 9999-12-31 +SELECT f_int_n(); +ERROR 22007: Incorrect date value: '-1' for column 'f_int_n()' at row 1 +SELECT f_int_mi1(); +ERROR 22007: Incorrect date value: '10000100' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect date value: '99991232' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +2020-01-01 +SELECT f_bigint() AS bi; +bi +2020-01-01 +SELECT f_bool(); +ERROR 22007: Incorrect date value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +ERROR 22007: Incorrect date value: '' for column 'se' at row 1 +SELECT f_str_a(); +ERROR 22007: Incorrect date value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +2020-01-01 +SELECT f_str_b(); +ERROR 22007: Incorrect date value: '2020-13-01' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect date value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect date value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect date value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect date value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect date value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TIME LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TIME LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS TIME LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS TIME LANGUAGE JS AS $$ return 11; $$; +CREATE FUNCTION f_int_mi() RETURNS TIME LANGUAGE JS AS $$ return -8385959; $$; +CREATE FUNCTION f_int_mx() RETURNS TIME LANGUAGE JS AS $$ return 8385959; $$; +CREATE FUNCTION f_int_n() RETURNS TIME LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS TIME LANGUAGE JS AS $$ return -8385959 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS TIME LANGUAGE JS AS $$ return 8385959 + 1; $$; +CREATE FUNCTION f_num() RETURNS TIME LANGUAGE JS AS $$ return 11.1; $$; +CREATE FUNCTION f_bigint() RETURNS TIME LANGUAGE JS AS $$ return BigInt(11); $$; +CREATE FUNCTION f_bool() RETURNS TIME LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TIME LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TIME LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TIME LANGUAGE JS AS $$ return "00:11"; $$; +CREATE FUNCTION f_str_b() RETURNS TIME LANGUAGE JS AS $$ return "00:65"; $$; +CREATE FUNCTION f_str_ax() RETURNS TIME LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS TIME LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS TIME LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TIME LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TIME LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TIME LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TIME LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TIME LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +# 0 is valid value for TIME type. +SELECT f_int_0(); +f_int_0() +00:00:00 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +00:00:11 -838:59:59 838:59:59 +SELECT f_int_mi1(); +ERROR 22007: Incorrect time value: '-8385960' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect time value: '8385960' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +00:00:11 +SELECT f_bigint() AS bi; +bi +00:00:11 +SELECT f_bool(); +ERROR 22007: Incorrect time value: 'true' for column 'f_bool()' at row 1 +# Empty string is converted to 0 for TIME type. +SELECT f_str_e() AS se; +se +00:00:00 +SELECT f_str_a(); +ERROR 22007: Incorrect time value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +00:11:00 +SELECT f_str_b(); +ERROR 22007: Incorrect time value: '00:65' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect time value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect time value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_array() AS a; +ERROR 22007: Incorrect time value: '1,2,3' for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect time value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect time value: 'function (a) { return 1;}' for column 'f' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_typed_arr() AS ta; +ERROR 22007: Incorrect time value: '0,1,2,3,5' for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect time value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TIME(1) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TIME(1) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS TIME(1) LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS TIME(1) LANGUAGE JS AS $$ return 11; $$; +CREATE FUNCTION f_int_mi() RETURNS TIME(1) LANGUAGE JS AS $$ return -8385959; $$; +CREATE FUNCTION f_int_mx() RETURNS TIME(1) LANGUAGE JS AS $$ return 8385959; $$; +CREATE FUNCTION f_int_n() RETURNS TIME(1) LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS TIME(1) LANGUAGE JS AS $$ return -8385959 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS TIME(1) LANGUAGE JS AS $$ return 8385959 + 1; $$; +CREATE FUNCTION f_num() RETURNS TIME(1) LANGUAGE JS AS $$ return 11.1; $$; +CREATE FUNCTION f_bigint() RETURNS TIME(1) LANGUAGE JS AS $$ return BigInt(11); $$; +CREATE FUNCTION f_bool() RETURNS TIME(1) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TIME(1) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TIME(1) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TIME(1) LANGUAGE JS AS $$ return "00:11.1"; $$; +CREATE FUNCTION f_str_b() RETURNS TIME(1) LANGUAGE JS AS $$ return "01:71.1"; $$; +CREATE FUNCTION f_str_ax() RETURNS TIME(1) LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS TIME(1) LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS TIME(1) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TIME(1) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TIME(1) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TIME(1) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TIME(1) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TIME(1) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +# 0 is valid value for TIME type. +SELECT f_int_0(); +f_int_0() +00:00:00.0 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +00:00:11.0 -838:59:59.0 838:59:59.0 +SELECT f_int_mi1(); +ERROR 22007: Incorrect time value: '-8385960' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect time value: '8385960' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +00:00:11.1 +SELECT f_bigint() AS bi; +bi +00:00:11.0 +SELECT f_bool(); +ERROR 22007: Incorrect time value: 'true' for column 'f_bool()' at row 1 +# Empty string is converted to 0 for TIME type. +SELECT f_str_e() AS se; +se +00:00:00.0 +SELECT f_str_a(); +ERROR 22007: Incorrect time value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +00:11:00.1 +SELECT f_str_b(); +ERROR 22007: Incorrect time value: '01:71.1' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect time value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect time value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_array() AS a; +ERROR 22007: Incorrect time value: '1,2,3' for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect time value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect time value: 'function (a) { return 1;}' for column 'f' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_typed_arr() AS ta; +ERROR 22007: Incorrect time value: '0,1,2,3,5' for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect time value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS DATETIME LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS DATETIME LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS DATETIME LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS DATETIME LANGUAGE JS AS $$ return 20200102030405; $$; +CREATE FUNCTION f_int_mi() RETURNS DATETIME LANGUAGE JS AS $$ return 10000101000000; $$; +CREATE FUNCTION f_int_mx() RETURNS DATETIME LANGUAGE JS AS $$ return 99991231235959; $$; +CREATE FUNCTION f_int_n() RETURNS DATETIME LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS DATETIME LANGUAGE JS AS $$ return 10000101000000 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS DATETIME LANGUAGE JS AS $$ return 99991231235959 + 1; $$; +CREATE FUNCTION f_num() RETURNS DATETIME LANGUAGE JS AS $$ return 20200102030405.06; $$; +CREATE FUNCTION f_bigint() RETURNS DATETIME LANGUAGE JS AS $$ return BigInt(20200102030405); $$; +CREATE FUNCTION f_bool() RETURNS DATETIME LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS DATETIME LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS DATETIME LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS DATETIME LANGUAGE JS AS $$ return "2020-01-02 03:04:05"; $$; +CREATE FUNCTION f_str_b() RETURNS DATETIME LANGUAGE JS AS $$ return "2020-01-32 03:04:05"; $$; +CREATE FUNCTION f_str_ax() RETURNS DATETIME LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS DATETIME LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS DATETIME LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DATETIME LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DATETIME LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS DATETIME LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS DATETIME LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS DATETIME LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0(); +ERROR 22007: Incorrect datetime value: '0' for column 'f_int_0()' at row 1 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +2020-01-02 03:04:05 1000-01-01 00:00:00 9999-12-31 23:59:59 +SELECT f_int_n(); +ERROR 22007: Incorrect datetime value: '-1' for column 'f_int_n()' at row 1 +SELECT f_int_mi1(); +ERROR 22007: Incorrect datetime value: '10000100999999' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect datetime value: '99991231235960' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +2020-01-02 03:04:05 +SELECT f_bigint() AS bi; +bi +2020-01-02 03:04:05 +SELECT f_bool(); +ERROR 22007: Incorrect datetime value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +ERROR 22007: Incorrect datetime value: '' for column 'se' at row 1 +SELECT f_str_a(); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +2020-01-02 03:04:05 +SELECT f_str_b(); +ERROR 22007: Incorrect datetime value: '2020-01-32 03:04:05' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect datetime value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect datetime value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS DATETIME(2) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS DATETIME(2) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 20200102030405; $$; +CREATE FUNCTION f_int_mi() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 10000101000000; $$; +CREATE FUNCTION f_int_mx() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 99991231235959; $$; +CREATE FUNCTION f_int_n() RETURNS DATETIME(2) LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 10000101000000 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 99991231235959 + 1; $$; +CREATE FUNCTION f_num() RETURNS DATETIME(2) LANGUAGE JS AS $$ return 20200102030405.06; $$; +CREATE FUNCTION f_bigint() RETURNS DATETIME(2) LANGUAGE JS AS $$ return BigInt(20200102030405); $$; +CREATE FUNCTION f_bool() RETURNS DATETIME(2) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS DATETIME(2) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS DATETIME(2) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS DATETIME(2) LANGUAGE JS AS $$ return "2020-01-02 03:04:05.06"; $$; +CREATE FUNCTION f_str_b() RETURNS DATETIME(2) LANGUAGE JS AS $$ return "2020-01-02 25:04:05.06"; $$; +CREATE FUNCTION f_str_ax() RETURNS DATETIME(2) LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS DATETIME(2) LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS DATETIME(2) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DATETIME(2) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DATETIME(2) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS DATETIME(2) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS DATETIME(2) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS DATETIME(2) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0(); +ERROR 22007: Incorrect datetime value: '0' for column 'f_int_0()' at row 1 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +2020-01-02 03:04:05.00 1000-01-01 00:00:00.00 9999-12-31 23:59:59.00 +SELECT f_int_n(); +ERROR 22007: Incorrect datetime value: '-1' for column 'f_int_n()' at row 1 +SELECT f_int_mi1(); +ERROR 22007: Incorrect datetime value: '10000100999999' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect datetime value: '99991231235960' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +2020-01-02 03:04:05.06 +SELECT f_bigint() AS bi; +bi +2020-01-02 03:04:05.00 +SELECT f_bool(); +ERROR 22007: Incorrect datetime value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +ERROR 22007: Incorrect datetime value: '' for column 'se' at row 1 +SELECT f_str_a(); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +2020-01-02 03:04:05.06 +SELECT f_str_b(); +ERROR 22007: Incorrect datetime value: '2020-01-02 25:04:05.06' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect datetime value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect datetime value: 'function (a) { return 1;}' for column 'f' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TIMESTAMP LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TIMESTAMP LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 20231222102000; $$; +CREATE FUNCTION f_int_mi() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 19700101030001; $$; +CREATE FUNCTION f_int_mx() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 20380119061407; $$; +CREATE FUNCTION f_int_n() RETURNS TIMESTAMP LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 19700101030001 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 20380119061407 + 1; $$; +CREATE FUNCTION f_num() RETURNS TIMESTAMP LANGUAGE JS AS $$ return 20231222102000.0123; $$; +CREATE FUNCTION f_bigint() RETURNS TIMESTAMP LANGUAGE JS AS $$ return BigInt(20231222102000); $$; +CREATE FUNCTION f_bool() RETURNS TIMESTAMP LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TIMESTAMP LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TIMESTAMP LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TIMESTAMP LANGUAGE JS AS $$ return "2023-12-22 10:20:00"; $$; +CREATE FUNCTION f_str_b() RETURNS TIMESTAMP LANGUAGE JS AS $$ return "2023-12-22 10:71:00"; $$; +CREATE FUNCTION f_str_ax() RETURNS TIMESTAMP LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS TIMESTAMP LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS TIMESTAMP LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TIMESTAMP LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TIMESTAMP LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TIMESTAMP LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TIMESTAMP LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TIMESTAMP LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0(); +ERROR 22007: Incorrect datetime value: '0' for column 'f_int_0()' at row 1 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +2023-12-22 10:20:00 1970-01-01 03:00:01 2038-01-19 06:14:07 +SELECT f_int_n(); +ERROR 22007: Incorrect datetime value: '-1' for column 'f_int_n()' at row 1 +SELECT f_int_mi1(); +ERROR 22007: Incorrect datetime value: '19700101030000' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect datetime value: '20380119061408' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +2023-12-22 10:20:00 +SELECT f_bigint() AS bi; +bi +2023-12-22 10:20:00 +SELECT f_bool(); +ERROR 22007: Incorrect datetime value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +ERROR 22007: Incorrect datetime value: '' for column 'se' at row 1 +SELECT f_str_a(); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +2023-12-22 10:20:00 +SELECT f_str_b(); +ERROR 22007: Incorrect datetime value: '2023-12-22 10:71:00' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect datetime value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_array() AS a; +ERROR 22007: Incorrect datetime value: '1,2,3' for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect datetime value: 'function (a) { return 1;}' for column 'f' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_typed_arr() AS ta; +ERROR 22007: Incorrect datetime value: '0,1,2,3,5' for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +CREATE FUNCTION f_undefined() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return; $$; +CREATE FUNCTION f_null() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return null; $$; +CREATE FUNCTION f_int_0() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 0; $$; +CREATE FUNCTION f_int_1() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 20231222102000; $$; +CREATE FUNCTION f_int_mi() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 19700101030001; $$; +CREATE FUNCTION f_int_mx() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 20380119061407; $$; +CREATE FUNCTION f_int_n() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return -1; $$; +CREATE FUNCTION f_int_mi1() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 19700101030001 - 1; $$; +CREATE FUNCTION f_int_mx1() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 20380119061407 + 1; $$; +CREATE FUNCTION f_num() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return 20231222102000.0123; $$; +CREATE FUNCTION f_bigint() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return BigInt(20231222102000); $$; +CREATE FUNCTION f_bool() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return true; $$; +CREATE FUNCTION f_str_e() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return ""; $$; +CREATE FUNCTION f_str_a() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return "alpha"; $$; +CREATE FUNCTION f_str_1() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return "2023-12-22 10:20:00.1234"; $$; +CREATE FUNCTION f_str_b() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return "2023-12-22 10:20:63.1234"; $$; +CREATE FUNCTION f_str_ax() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return "\u{1F384}"; $$; +CREATE FUNCTION f_date() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return new Date(2023,11,22,13,0,0,123) $$; +CREATE FUNCTION f_array() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return function (a) { return 1;} $$; +CREATE FUNCTION f_typed_arr() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE FUNCTION f_data_view() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv $$; +CREATE FUNCTION f_object_serr() RETURNS TIMESTAMP(4) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int_0(); +ERROR 22007: Incorrect datetime value: '0' for column 'f_int_0()' at row 1 +SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; +i1 imi imx +2023-12-22 10:20:00.0000 1970-01-01 03:00:01.0000 2038-01-19 06:14:07.0000 +SELECT f_int_n(); +ERROR 22007: Incorrect datetime value: '-1' for column 'f_int_n()' at row 1 +SELECT f_int_mi1(); +ERROR 22007: Incorrect datetime value: '19700101030000' for column 'f_int_mi1()' at row 1 +SELECT f_int_mx1(); +ERROR 22007: Incorrect datetime value: '20380119061408' for column 'f_int_mx1()' at row 1 +SELECT f_num() AS n; +n +2023-12-22 10:20:00.0100 +SELECT f_bigint() AS bi; +bi +2023-12-22 10:20:00.0000 +SELECT f_bool(); +ERROR 22007: Incorrect datetime value: 'true' for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +ERROR 22007: Incorrect datetime value: '' for column 'se' at row 1 +SELECT f_str_a(); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'f_str_a()' at row 1 +SELECT f_str_1() AS s1; +s1 +2023-12-22 10:20:00.1234 +SELECT f_str_b(); +ERROR 22007: Incorrect datetime value: '2023-12-22 10:20:63.1234' for column 'f_str_b()' at row 1 +SELECT f_str_ax(); +ERROR 22007: Incorrect datetime value: '?' for column 'f_str_ax()' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +SELECT f_date() AS d; +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'd' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_array() AS a; +ERROR 22007: Incorrect datetime value: '1,2,3' for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 22007: Incorrect datetime value: 'function (a) { return 1;}' for column 'f' at row 1 +# DATE and DATETIME types accept weird literals. +SELECT f_typed_arr() AS ta; +ERROR 22007: Incorrect datetime value: '0,1,2,3,5' for column 'ta' at row 1 +SELECT f_data_view() AS dv; +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'dv' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_b; +DROP FUNCTION f_str_ax; +DROP FUNCTION f_date; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +DROP FUNCTION f_object_serr; +# +# ENUM return type is handled similarly to string types. +# +CREATE FUNCTION f_undefined() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_bigint() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return BigInt(100) $$; +CREATE FUNCTION f_bool() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_0() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "a" $$; +CREATE FUNCTION f_str_1() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_2() RETURNS ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 +LANGUAGE JS AS $$ return "Додо" $$; +CREATE FUNCTION f_str_cerr() RETURNS ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 +LANGUAGE JS AS $$ return "\u{1F9A4}" $$; +CREATE FUNCTION f_array() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return function (a) { return 1 } $$; +CREATE FUNCTION f_object_serr() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i; +i +a +SELECT f_num() AS num; +ERROR 01000: Data truncated for column 'num' at row 1 +SELECT f_bigint() AS bi; +ERROR 01000: Data truncated for column 'bi' at row 1 +SELECT f_bool() AS bo; +ERROR 01000: Data truncated for column 'bo' at row 1 +SELECT f_str_e() AS se; +ERROR 01000: Data truncated for column 'se' at row 1 +SELECT f_str_0() AS s0; +s0 +a +SELECT f_str_2() = X'c4eee4ee' AS s2; +s2 +1 +SELECT f_str_1() AS s1; +ERROR 01000: Data truncated for column 's1' at row 1 +SELECT f_str_cerr(); +ERROR 01000: Data truncated for column 'f_str_cerr()' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 01000: Data truncated for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 01000: Data truncated for column 'f' at row 1 +SELECT f_object_serr(); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; +# +# SET return type is handled similarly to how SQL core interprets +# integer/floating-point values and strings which are stored in +# SET columns. +# +# Numeric values are converted to integers and treated as bitmaps +# representing sets. Strings are expected to contain comma-separated +# lists of SET elements. Additionally strings containing integer +# values are interpreted as bitmaps. +CREATE FUNCTION f_undefined() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_int_tb() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 10 $$; +CREATE FUNCTION f_int_n() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_num() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 1.6 $$; +CREATE FUNCTION f_bigint() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return BigInt(4) $$; +CREATE FUNCTION f_bool() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_0() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "a" $$; +CREATE FUNCTION f_str_1() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "b,c" $$; +CREATE FUNCTION f_str_2() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "3" $$; +CREATE FUNCTION f_str_3() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "1.5" $$; +CREATE FUNCTION f_str_n() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "-3" $$; +CREATE FUNCTION f_str_w() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_4() RETURNS SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 +LANGUAGE JS AS $$ return "Додо" $$; +CREATE FUNCTION f_str_cerr() RETURNS SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 +LANGUAGE JS AS $$ return "\u{1F9A4}" $$; +CREATE FUNCTION f_array() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return function (a) { return 1 } $$; +CREATE FUNCTION f_object_serr() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i; +i +a +SELECT f_int_tb(); +ERROR 01000: Data truncated for column 'f_int_tb()' at row 1 +SELECT f_int_n(); +ERROR 01000: Data truncated for column 'f_int_n()' at row 1 +SELECT f_num() AS n; +n +a +SELECT f_bigint() AS bi; +bi +c +SELECT f_bool(); +ERROR 01000: Data truncated for column 'f_bool()' at row 1 +SELECT f_str_e() AS se; +se + +SELECT f_str_0() AS s0; +s0 +a +SELECT f_str_1() AS s1; +s1 +b,c +SELECT f_str_2() AS s2; +s2 +a,b +# SQL core doesn't handle non-integer numbers represented as strings +# and doubles stored in SET columns consistently either. +SELECT f_str_3(); +ERROR 01000: Data truncated for column 'f_str_3()' at row 1 +SELECT f_str_n(); +ERROR 01000: Data truncated for column 'f_str_n()' at row 1 +SELECT f_str_w(); +ERROR 01000: Data truncated for column 'f_str_w()' at row 1 +SELECT f_str_4() = X'c4eee4ee' AS s; +s +1 +SELECT f_str_cerr() AS cerr; +ERROR 01000: Data truncated for column 'cerr' at row 1 +SELECT f_array() AS a; +ERROR 01000: Data truncated for column 'a' at row 1 +SELECT f_object() AS o; +ERROR 01000: Data truncated for column 'o' at row 1 +SELECT f_func() AS f; +ERROR 01000: Data truncated for column 'f' at row 1 +SELECT f_object_serr() AS serr; +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_n; +DROP FUNCTION f_str_w; +DROP FUNCTION f_str_4; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; +# +# BIT return type is handled in a special way. +# +# JS values are converted to numbers (ultimately integers) and then +# their binary representation is interpreted as array of bits. +# +# We do not support JS values which are not convertible to numbers to +# avoid confusion caused by different interpretation of 1, "1" and "a". +# +CREATE FUNCTION f_undefined() RETURNS BIT(5) LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS BIT(5) LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int_0() RETURNS BIT(5) LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS BIT(5) LANGUAGE JS AS $$ return 7 $$; +CREATE FUNCTION f_int_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return 33 $$; +CREATE FUNCTION f_int_n() RETURNS BIT(5) LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_num_1() RETURNS BIT(5) LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS BIT(5) LANGUAGE JS AS $$ return 7.8 $$; +CREATE FUNCTION f_num_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return 32.5 $$; +CREATE FUNCTION f_num_n() RETURNS BIT(5) LANGUAGE JS AS $$ return -1.2 $$; +CREATE FUNCTION f_bigint() RETURNS BIT(5) LANGUAGE JS AS $$ return BigInt(5) $$; +CREATE FUNCTION f_bigint_n() RETURNS BIT(5) LANGUAGE JS AS $$ return BigInt(-1) $$; +CREATE FUNCTION f_bool() RETURNS BIT(5) LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS BIT(5) LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS BIT(5) LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_1() RETURNS BIT(7) LANGUAGE JS AS $$ return "0" $$; +CREATE FUNCTION f_str_2() RETURNS BIT(7) LANGUAGE JS AS $$ return "16" $$; +CREATE FUNCTION f_str_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return "33" $$; +CREATE FUNCTION f_array() RETURNS BIT(5) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS BIT(5) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS BIT(5) LANGUAGE JS AS $$ return function (a) { return 1} $$; +CREATE FUNCTION f_object_nerr() RETURNS BIT(5) LANGUAGE JS AS $$ return { valueOf() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT HEX(f_int_0()) AS i0, HEX(f_int_1()) AS i1; +i0 i1 +0 7 +SELECT f_int_tb(); +ERROR 22001: Data too long for column 'f_int_tb()' at row 1 +SELECT f_int_n(); +ERROR 22001: Data too long for column 'f_int_n()' at row 1 +SELECT HEX(f_num_1()) AS n1, HEX(f_num_2()) AS n2; +n1 n2 +1 7 +SELECT f_num_tb(); +ERROR 22001: Data too long for column 'f_num_tb()' at row 1 +SELECT f_num_n(); +ERROR 22001: Data too long for column 'f_num_n()' at row 1 +SELECT HEX(f_bigint()) AS bi; +bi +5 +SELECT f_bigint_n(); +ERROR HY000: Can't convert BigInt value to BIT type (value out of range) +SELECT HEX(f_bool()) AS b; +b +1 +SELECT HEX(f_str_e()); +HEX(f_str_e()) +0 +SELECT f_str_a(); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +SELECT HEX(f_str_1()) AS s1; +s1 +0 +SELECT HEX(f_str_2()) AS s1; +s1 +10 +SELECT f_str_tb(); +ERROR 22001: Data too long for column 'f_str_tb()' at row 1 +SELECT f_array() AS a; +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +SELECT f_object() AS o; +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +SELECT f_func() AS f; +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +SELECT f_object_nerr() AS nerr; +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_num_n; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_tb; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_nerr; +# +# For GEOMETRY return type we only support conversion from +# ArrayBuffer-based JS values. +# +CREATE FUNCTION f_undefined() RETURNS GEOMETRY LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS GEOMETRY LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int() RETURNS GEOMETRY LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS GEOMETRY LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_bigint() RETURNS GEOMETRY LANGUAGE JS AS $$ return BigInt(100) $$; +CREATE FUNCTION f_bool() RETURNS GEOMETRY LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS GEOMETRY LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS GEOMETRY LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_array() RETURNS GEOMETRY LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS GEOMETRY LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS GEOMETRY LANGUAGE JS AS $$ return function (a) { return 1 } $$; +CREATE FUNCTION f_typed_arr() RETURNS GEOMETRY LANGUAGE JS AS $$ return new Uint8Array([0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 64, 0, 0, 0, 0, 0, 0, 52, 64]) $$; +CREATE FUNCTION f_data_view() RETURNS GEOMETRY LANGUAGE JS AS $$ +let dv = new DataView(new ArrayBuffer(25)); +dv.setUint32(0, 0, true); // SRID 0 +dv.setUint8(4, 1); // little-endian +dv.setUint32(5, 1, true); // POINT +dv.setFloat64(9, 15, true); // X +dv.setFloat64(17, 20, true); // Y +return dv; +$$| +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_num(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_bigint(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_bool(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_str_e(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_str_a(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_array(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_object(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT f_func(); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +SELECT ST_AsText(f_typed_arr()) AS g; +g +POINT(15 20) +SELECT ST_AsText(f_data_view()) AS g; +g +POINT(15 20) +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; +# +# For JSON return type we apply JSON.stringify() to JS value and +# then try to store resulting string as return value (of JSON SQL +# type). +# +CREATE FUNCTION f_undefined() RETURNS JSON LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS JSON LANGUAGE JS AS $$ return null $$; +CREATE FUNCTION f_int() RETURNS JSON LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS JSON LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_bigint() RETURNS JSON LANGUAGE JS AS $$ return BigInt(100) $$; +CREATE FUNCTION f_bool() RETURNS JSON LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS JSON LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS JSON LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_array() RETURNS JSON LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS JSON LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS JSON LANGUAGE JS AS $$ return function (a) { return 1 } $$; +CREATE FUNCTION f_object_jerr() RETURNS JSON LANGUAGE JS AS $$ return { toJSON() { throw "Kaboom!" } } $$; +SELECT f_undefined() AS u, f_null() AS nil; +u nil +NULL NULL +SELECT f_int() AS i, f_num() AS n; +i n +1 1.25 +# JSON.stringify() doesn't support BigInt by default. +SELECT f_bigint() AS bi; +ERROR HY000: For JSON return type only values supported by JSON.stringify() are allowed. +SELECT f_bool() AS b; +b +true +SELECT f_str_e() AS se, f_str_a() AS sa; +se sa +"" "alpha" +SELECT f_array() AS arr, f_object() AS obj; +arr obj +[1, 2, 3] {"x": 1, "y": "alpha"} +# SQL JSON type doesn't accept all JSON values produced by V8. +SELECT f_func() AS func; +ERROR 22032: Invalid JSON text: "Invalid value." at position 0 in value for column '.func' at row 1. +SELECT f_object_jerr(); +ERROR HY000: For JSON return type only values supported by JSON.stringify() are allowed. +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_jerr; + +# +# Finally let us test how JS values are converted to SQL types +# for OUT parameters. +# +# These conversion uses the same mechanism as for return values. +# +# OUT parameters with string SQL types. +# +CREATE PROCEDURE p_undefined(OUT r CHAR(50)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r CHAR(50)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r CHAR(50)) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r CHAR(50)) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r CHAR(50)) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r CHAR(50)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r CHAR(50)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r CHAR(50)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r CHAR(50) CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r CHAR(50) CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r CHAR(50) CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r CHAR(50) CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r CHAR(50)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r CHAR(50)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r CHAR(50)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r CHAR(50)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r CHAR(50)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r CHAR(50)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r VARCHAR(60) CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r VARCHAR(60) CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r VARCHAR(60) CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r VARCHAR(60) CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r VARCHAR(60)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r VARCHAR(60)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TINYTEXT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TINYTEXT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r TINYTEXT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r TINYTEXT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r TINYTEXT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r TINYTEXT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TINYTEXT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r TINYTEXT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TINYTEXT CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r TINYTEXT CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r TINYTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r TINYTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r TINYTEXT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TINYTEXT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TINYTEXT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TINYTEXT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TINYTEXT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TINYTEXT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TEXT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TEXT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r TEXT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r TEXT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r TEXT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r TEXT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TEXT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r TEXT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TEXT CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r TEXT CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r TEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r TEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r TEXT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TEXT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TEXT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TEXT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TEXT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TEXT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r MEDIUMTEXT CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r MEDIUMTEXT CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r MEDIUMTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r MEDIUMTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r MEDIUMTEXT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r LONGTEXT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r LONGTEXT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r LONGTEXT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r LONGTEXT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r LONGTEXT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r LONGTEXT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r LONGTEXT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_0(OUT r LONGTEXT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r LONGTEXT CHARACTER SET utf8mb4) LANGUAGE JS AS $$ r = "Far over the misty mountains cold"; $$; +CREATE PROCEDURE p_str_2(OUT r LONGTEXT CHARACTER SET latin1) LANGUAGE JS AS $$ r = "Au-delà des montagnes glaciales et embrumées"; $$; +CREATE PROCEDURE p_str_3(OUT r LONGTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "Там отвъд мъглявите студени планини"; $$; +CREATE PROCEDURE p_str_cerr(OUT r LONGTEXT CHARACTER SET cp1251) LANGUAGE JS AS $$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; $$; +CREATE PROCEDURE p_array(OUT r LONGTEXT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r LONGTEXT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r LONGTEXT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r LONGTEXT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r LONGTEXT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r LONGTEXT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +alpha +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +Far over the misty mountains cold +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +Au-delà des montagnes glaciales et embrumées +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +Там отвъд мъглявите студени планини +CALL p_str_1(@r); +SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; +r1 +1 +CALL p_str_2(@r); +SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; +r2 +1 +CALL p_str_3(@r); +SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; +r3 +1 +CALL p_str_cerr(@r); +ERROR HY000: Incorrect string value: '\xF0\x9F\x90\xB4\xF0\x9F...' for column 'r' at row 1 +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT @r AS ta; +ta +0,1,2,3,5 +CALL p_data_view(@r); +SELECT @r AS dv; +dv +[object DataView] +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; + +# +# OUT parameters of binary string/BLOB SQL-types. +# +CREATE PROCEDURE p_undefined(OUT r BINARY(50)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r BINARY(50)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r BINARY(50)) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r BINARY(50)) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r BINARY(50)) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r BINARY(50)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r BINARY(50)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r BINARY(50)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r BINARY(50)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r BINARY(50)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r BINARY(50)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r BINARY(50)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r BINARY(50)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r BINARY(50)) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r BINARY(50)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +# BINARY type does 0-padding so we use HEX to correctly print value. +CALL p_int(@r); +SELECT HEX(@r) AS i; +i +3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_num(@r); +SELECT HEX(@r) AS num; +num +312E323500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_bigint(@r); +SELECT HEX(@r) AS bi; +bi +3130300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_bool(@r); +SELECT HEX(@r) AS bo; +bo +7472756500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_str_e(@r); +SELECT HEX(@r) AS se; +se +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_str_a(@r); +SELECT HEX(@r) AS sa; +sa +616C706861000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_array(@r); +SELECT HEX(@r) AS a; +a +312C322C33000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_object(@r); +SELECT HEX(@r) AS o; +o +5B6F626A656374204F626A6563745D0000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_func(@r); +SELECT HEX(@r) AS f; +f +66756E6374696F6E20286129207B2072203D20313B7D00000000000000000000000000000000000000000000000000000000 +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +0000000103000700000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +0000000001030007000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r VARBINARY(60)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r VARBINARY(60)) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r VARBINARY(60)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_a(@r); +SELECT @r AS sa; +sa +alpha +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +00000001030007 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +000000000103000700 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TINYBLOB) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TINYBLOB) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r TINYBLOB) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r TINYBLOB) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r TINYBLOB) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r TINYBLOB) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TINYBLOB) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TINYBLOB) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r TINYBLOB) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TINYBLOB) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TINYBLOB) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TINYBLOB) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TINYBLOB) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r TINYBLOB) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r TINYBLOB) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_a(@r); +SELECT @r AS sa; +sa +alpha +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +00000001030007 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +000000000103000700 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r BLOB) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r BLOB) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r BLOB) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r BLOB) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r BLOB) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r BLOB) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r BLOB) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r BLOB) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r BLOB) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r BLOB) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r BLOB) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r BLOB) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r BLOB) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r BLOB) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r BLOB) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_a(@r); +SELECT @r AS sa; +sa +alpha +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +00000001030007 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +000000000103000700 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r MEDIUMBLOB) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_a(@r); +SELECT @r AS sa; +sa +alpha +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +00000001030007 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +000000000103000700 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r LONGBLOB) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r LONGBLOB) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int(OUT r LONGBLOB) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_num(OUT r LONGBLOB) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_bigint(OUT r LONGBLOB) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bool(OUT r LONGBLOB) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r LONGBLOB) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r LONGBLOB) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_array(OUT r LONGBLOB) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r LONGBLOB) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r LONGBLOB) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r LONGBLOB) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r LONGBLOB) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv $$; +CREATE PROCEDURE p_arr_buff(OUT r LONGBLOB) LANGUAGE JS AS $$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab $$; +CREATE PROCEDURE p_object_serr(OUT r LONGBLOB) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS num; +num +1.25 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bool(@r); +SELECT @r AS bo; +bo +true +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_a(@r); +SELECT @r AS sa; +sa +alpha +CALL p_array(@r); +SELECT @r AS a; +a +1,2,3 +CALL p_object(@r); +SELECT @r AS o; +o +[object Object] +CALL p_func(@r); +SELECT @r AS f; +f +function (a) { r = 1;} +CALL p_typed_arr(@r); +SELECT HEX(@r) AS ta; +ta +0001020305 +CALL p_data_view(@r); +SELECT HEX(@r) AS dv; +dv +00000001030007 +CALL p_arr_buff(@r); +SELECT HEX(@r) AS ab; +ab +000000000103000700 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_arr_buff; +DROP PROCEDURE p_object_serr; +# +# Test conversions for OUT parameters of integer SQL-types. +# +# See comments for test coverage for return values. +CREATE PROCEDURE p_undefined(OUT r TINYINT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TINYINT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r TINYINT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r TINYINT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r TINYINT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_m(OUT r TINYINT) LANGUAGE JS AS $$ r = 127; $$; +CREATE PROCEDURE p_int_m1(OUT r TINYINT) LANGUAGE JS AS $$ r = 127 + 1; $$; +CREATE PROCEDURE p_int_nm(OUT r TINYINT) LANGUAGE JS AS $$ r = -127 - 1; $$; +CREATE PROCEDURE p_int_nm1(OUT r TINYINT) LANGUAGE JS AS $$ r = -127 - 2; $$; +CREATE PROCEDURE p_uint_m(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = 2*127 + 1; $$; +CREATE PROCEDURE p_uint_m1(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = 2*127 + 2; $$; +CREATE PROCEDURE p_uint_n(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = - 1; $$; +CREATE PROCEDURE p_num_1(OUT r TINYINT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r TINYINT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r TINYINT) LANGUAGE JS AS $$ r = 5e-2; $$; +CREATE PROCEDURE p_num_4(OUT r TINYINT) LANGUAGE JS AS $$ r = 1.2345e+2; $$; +CREATE PROCEDURE p_num_5(OUT r TINYINT) LANGUAGE JS AS $$ r = -1.2345e+1; $$; +CREATE PROCEDURE p_num_r1(OUT r TINYINT) LANGUAGE JS AS $$ r = 4.5; $$; +CREATE PROCEDURE p_num_r2(OUT r TINYINT) LANGUAGE JS AS $$ r = 4.5e+0; $$; +CREATE PROCEDURE p_num_un(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = -1.5; $$; +CREATE PROCEDURE p_num_tb(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = 1e+70; $$; +CREATE PROCEDURE p_bigint(OUT r TINYINT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_n(OUT r TINYINT) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_u(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(9007199254740991); $$; +CREATE PROCEDURE p_bigint_un(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_tb(OUT r TINYINT) LANGUAGE JS AS $$ r = BigInt(1e+25); $$; +CREATE PROCEDURE p_bool(OUT r TINYINT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TINYINT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TINYINT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r TINYINT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r TINYINT) LANGUAGE JS AS $$ r = "-2"; $$; +CREATE PROCEDURE p_str_n3(OUT r TINYINT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_nr1(OUT r TINYINT) LANGUAGE JS AS $$ r = "4.5"; $$; +CREATE PROCEDURE p_str_nr2(OUT r TINYINT) LANGUAGE JS AS $$ r = "4.5e+0"; $$; +CREATE PROCEDURE p_str_nu(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = "-1"; $$; +CREATE PROCEDURE p_str_tb1(OUT r TINYINT) LANGUAGE JS AS $$ r = "1e+25"; $$; +CREATE PROCEDURE p_str_tb2(OUT r TINYINT) LANGUAGE JS AS $$ r = "18446744073709551616"; $$; +CREATE PROCEDURE p_array(OUT r TINYINT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TINYINT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TINYINT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TINYINT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TINYINT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TINYINT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CREATE PROCEDURE p_object_userr(OUT r TINYINT UNSIGNED) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_int_m(@r); +SELECT @r AS im; +im +127 +CALL p_int_nm(@r); +SELECT @r AS nm; +nm +-128 +CALL p_uint_m(@r); +SELECT @r AS um; +um +255 +CALL p_int_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_nm1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +1 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +0 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +123 +CALL p_num_5(@r); +SELECT @r AS n4; +n4 +-12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +CALL p_num_r1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_num_r2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_num_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bigint_n(@r); +SELECT @r AS bn; +bn +-42 +CALL p_bigint_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +-2 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +13 +CALL p_str_nr1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_str_nr2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_str_nu(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb2(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +CALL p_object_userr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_m; +DROP PROCEDURE p_int_m1; +DROP PROCEDURE p_int_nm; +DROP PROCEDURE p_int_nm1; +DROP PROCEDURE p_uint_m; +DROP PROCEDURE p_uint_m1; +DROP PROCEDURE p_uint_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_5; +DROP PROCEDURE p_num_r1; +DROP PROCEDURE p_num_r2; +DROP PROCEDURE p_num_un; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bigint_u; +DROP PROCEDURE p_bigint_un; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_nr1; +DROP PROCEDURE p_str_nr2; +DROP PROCEDURE p_str_nu; +DROP PROCEDURE p_str_tb1; +DROP PROCEDURE p_str_tb2; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +DROP PROCEDURE p_object_userr; +CREATE PROCEDURE p_undefined(OUT r SMALLINT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r SMALLINT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r SMALLINT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r SMALLINT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r SMALLINT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_m(OUT r SMALLINT) LANGUAGE JS AS $$ r = 32767; $$; +CREATE PROCEDURE p_int_m1(OUT r SMALLINT) LANGUAGE JS AS $$ r = 32767 + 1; $$; +CREATE PROCEDURE p_int_nm(OUT r SMALLINT) LANGUAGE JS AS $$ r = -32767 - 1; $$; +CREATE PROCEDURE p_int_nm1(OUT r SMALLINT) LANGUAGE JS AS $$ r = -32767 - 2; $$; +CREATE PROCEDURE p_uint_m(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = 2*32767 + 1; $$; +CREATE PROCEDURE p_uint_m1(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = 2*32767 + 2; $$; +CREATE PROCEDURE p_uint_n(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = - 1; $$; +CREATE PROCEDURE p_num_1(OUT r SMALLINT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r SMALLINT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r SMALLINT) LANGUAGE JS AS $$ r = 5e-2; $$; +CREATE PROCEDURE p_num_4(OUT r SMALLINT) LANGUAGE JS AS $$ r = 1.2345e+2; $$; +CREATE PROCEDURE p_num_5(OUT r SMALLINT) LANGUAGE JS AS $$ r = -1.2345e+1; $$; +CREATE PROCEDURE p_num_r1(OUT r SMALLINT) LANGUAGE JS AS $$ r = 4.5; $$; +CREATE PROCEDURE p_num_r2(OUT r SMALLINT) LANGUAGE JS AS $$ r = 4.5e+0; $$; +CREATE PROCEDURE p_num_un(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = -1.5; $$; +CREATE PROCEDURE p_num_tb(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = 1e+70; $$; +CREATE PROCEDURE p_bigint(OUT r SMALLINT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_n(OUT r SMALLINT) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_u(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(9007199254740991); $$; +CREATE PROCEDURE p_bigint_un(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_tb(OUT r SMALLINT) LANGUAGE JS AS $$ r = BigInt(1e+25); $$; +CREATE PROCEDURE p_bool(OUT r SMALLINT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r SMALLINT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r SMALLINT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r SMALLINT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r SMALLINT) LANGUAGE JS AS $$ r = "-2"; $$; +CREATE PROCEDURE p_str_n3(OUT r SMALLINT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_nr1(OUT r SMALLINT) LANGUAGE JS AS $$ r = "4.5"; $$; +CREATE PROCEDURE p_str_nr2(OUT r SMALLINT) LANGUAGE JS AS $$ r = "4.5e+0"; $$; +CREATE PROCEDURE p_str_nu(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = "-1"; $$; +CREATE PROCEDURE p_str_tb1(OUT r SMALLINT) LANGUAGE JS AS $$ r = "1e+25"; $$; +CREATE PROCEDURE p_str_tb2(OUT r SMALLINT) LANGUAGE JS AS $$ r = "18446744073709551616"; $$; +CREATE PROCEDURE p_array(OUT r SMALLINT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r SMALLINT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r SMALLINT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r SMALLINT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r SMALLINT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r SMALLINT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CREATE PROCEDURE p_object_userr(OUT r SMALLINT UNSIGNED) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_int_m(@r); +SELECT @r AS im; +im +32767 +CALL p_int_nm(@r); +SELECT @r AS nm; +nm +-32768 +CALL p_uint_m(@r); +SELECT @r AS um; +um +65535 +CALL p_int_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_nm1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +1 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +0 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +123 +CALL p_num_5(@r); +SELECT @r AS n4; +n4 +-12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +CALL p_num_r1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_num_r2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_num_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bigint_n(@r); +SELECT @r AS bn; +bn +-42 +CALL p_bigint_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +-2 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +13 +CALL p_str_nr1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_str_nr2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_str_nu(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb2(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +CALL p_object_userr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_m; +DROP PROCEDURE p_int_m1; +DROP PROCEDURE p_int_nm; +DROP PROCEDURE p_int_nm1; +DROP PROCEDURE p_uint_m; +DROP PROCEDURE p_uint_m1; +DROP PROCEDURE p_uint_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_5; +DROP PROCEDURE p_num_r1; +DROP PROCEDURE p_num_r2; +DROP PROCEDURE p_num_un; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bigint_u; +DROP PROCEDURE p_bigint_un; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_nr1; +DROP PROCEDURE p_str_nr2; +DROP PROCEDURE p_str_nu; +DROP PROCEDURE p_str_tb1; +DROP PROCEDURE p_str_tb2; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +DROP PROCEDURE p_object_userr; +CREATE PROCEDURE p_undefined(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_m(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 8388607; $$; +CREATE PROCEDURE p_int_m1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 8388607 + 1; $$; +CREATE PROCEDURE p_int_nm(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = -8388607 - 1; $$; +CREATE PROCEDURE p_int_nm1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = -8388607 - 2; $$; +CREATE PROCEDURE p_uint_m(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = 2*8388607 + 1; $$; +CREATE PROCEDURE p_uint_m1(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = 2*8388607 + 2; $$; +CREATE PROCEDURE p_uint_n(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = - 1; $$; +CREATE PROCEDURE p_num_1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 5e-2; $$; +CREATE PROCEDURE p_num_4(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 1.2345e+2; $$; +CREATE PROCEDURE p_num_5(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = -1.2345e+1; $$; +CREATE PROCEDURE p_num_r1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 4.5; $$; +CREATE PROCEDURE p_num_r2(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = 4.5e+0; $$; +CREATE PROCEDURE p_num_un(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = -1.5; $$; +CREATE PROCEDURE p_num_tb(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = 1e+70; $$; +CREATE PROCEDURE p_bigint(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_n(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_u(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(9007199254740991); $$; +CREATE PROCEDURE p_bigint_un(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_tb(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = BigInt(1e+25); $$; +CREATE PROCEDURE p_bool(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "-2"; $$; +CREATE PROCEDURE p_str_n3(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_nr1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "4.5"; $$; +CREATE PROCEDURE p_str_nr2(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "4.5e+0"; $$; +CREATE PROCEDURE p_str_nu(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = "-1"; $$; +CREATE PROCEDURE p_str_tb1(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "1e+25"; $$; +CREATE PROCEDURE p_str_tb2(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = "18446744073709551616"; $$; +CREATE PROCEDURE p_array(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r MEDIUMINT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r MEDIUMINT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CREATE PROCEDURE p_object_userr(OUT r MEDIUMINT UNSIGNED) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_int_m(@r); +SELECT @r AS im; +im +8388607 +CALL p_int_nm(@r); +SELECT @r AS nm; +nm +-8388608 +CALL p_uint_m(@r); +SELECT @r AS um; +um +16777215 +CALL p_int_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_nm1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +1 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +0 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +123 +CALL p_num_5(@r); +SELECT @r AS n4; +n4 +-12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +CALL p_num_r1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_num_r2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_num_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bigint_n(@r); +SELECT @r AS bn; +bn +-42 +CALL p_bigint_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +-2 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +13 +CALL p_str_nr1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_str_nr2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_str_nu(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb2(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +CALL p_object_userr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_m; +DROP PROCEDURE p_int_m1; +DROP PROCEDURE p_int_nm; +DROP PROCEDURE p_int_nm1; +DROP PROCEDURE p_uint_m; +DROP PROCEDURE p_uint_m1; +DROP PROCEDURE p_uint_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_5; +DROP PROCEDURE p_num_r1; +DROP PROCEDURE p_num_r2; +DROP PROCEDURE p_num_un; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bigint_u; +DROP PROCEDURE p_bigint_un; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_nr1; +DROP PROCEDURE p_str_nr2; +DROP PROCEDURE p_str_nu; +DROP PROCEDURE p_str_tb1; +DROP PROCEDURE p_str_tb2; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +DROP PROCEDURE p_object_userr; +CREATE PROCEDURE p_undefined(OUT r INT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r INT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r INT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r INT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r INT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_m(OUT r INT) LANGUAGE JS AS $$ r = 2147483647; $$; +CREATE PROCEDURE p_int_m1(OUT r INT) LANGUAGE JS AS $$ r = 2147483647 + 1; $$; +CREATE PROCEDURE p_int_nm(OUT r INT) LANGUAGE JS AS $$ r = -2147483647 - 1; $$; +CREATE PROCEDURE p_int_nm1(OUT r INT) LANGUAGE JS AS $$ r = -2147483647 - 2; $$; +CREATE PROCEDURE p_uint_m(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = 2*2147483647 + 1; $$; +CREATE PROCEDURE p_uint_m1(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = 2*2147483647 + 2; $$; +CREATE PROCEDURE p_uint_n(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = - 1; $$; +CREATE PROCEDURE p_num_1(OUT r INT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r INT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r INT) LANGUAGE JS AS $$ r = 5e-2; $$; +CREATE PROCEDURE p_num_4(OUT r INT) LANGUAGE JS AS $$ r = 1.2345e+2; $$; +CREATE PROCEDURE p_num_5(OUT r INT) LANGUAGE JS AS $$ r = -1.2345e+1; $$; +CREATE PROCEDURE p_num_r1(OUT r INT) LANGUAGE JS AS $$ r = 4.5; $$; +CREATE PROCEDURE p_num_r2(OUT r INT) LANGUAGE JS AS $$ r = 4.5e+0; $$; +CREATE PROCEDURE p_num_un(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = -1.5; $$; +CREATE PROCEDURE p_num_tb(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = 1e+70; $$; +CREATE PROCEDURE p_bigint(OUT r INT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_n(OUT r INT) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_u(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(9007199254740991); $$; +CREATE PROCEDURE p_bigint_un(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_tb(OUT r INT) LANGUAGE JS AS $$ r = BigInt(1e+25); $$; +CREATE PROCEDURE p_bool(OUT r INT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r INT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r INT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r INT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r INT) LANGUAGE JS AS $$ r = "-2"; $$; +CREATE PROCEDURE p_str_n3(OUT r INT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_nr1(OUT r INT) LANGUAGE JS AS $$ r = "4.5"; $$; +CREATE PROCEDURE p_str_nr2(OUT r INT) LANGUAGE JS AS $$ r = "4.5e+0"; $$; +CREATE PROCEDURE p_str_nu(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = "-1"; $$; +CREATE PROCEDURE p_str_tb1(OUT r INT) LANGUAGE JS AS $$ r = "1e+25"; $$; +CREATE PROCEDURE p_str_tb2(OUT r INT) LANGUAGE JS AS $$ r = "18446744073709551616"; $$; +CREATE PROCEDURE p_array(OUT r INT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r INT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r INT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r INT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r INT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r INT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CREATE PROCEDURE p_object_userr(OUT r INT UNSIGNED) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_int_m(@r); +SELECT @r AS im; +im +2147483647 +CALL p_int_nm(@r); +SELECT @r AS nm; +nm +-2147483648 +CALL p_uint_m(@r); +SELECT @r AS um; +um +4294967295 +CALL p_int_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_nm1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_m1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_uint_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +1 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +0 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +123 +CALL p_num_5(@r); +SELECT @r AS n4; +n4 +-12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +CALL p_num_r1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_num_r2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_num_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bigint_n(@r); +SELECT @r AS bn; +bn +-42 +CALL p_bigint_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +-2 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +13 +CALL p_str_nr1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_str_nr2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_str_nu(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb2(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +CALL p_object_userr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_m; +DROP PROCEDURE p_int_m1; +DROP PROCEDURE p_int_nm; +DROP PROCEDURE p_int_nm1; +DROP PROCEDURE p_uint_m; +DROP PROCEDURE p_uint_m1; +DROP PROCEDURE p_uint_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_5; +DROP PROCEDURE p_num_r1; +DROP PROCEDURE p_num_r2; +DROP PROCEDURE p_num_un; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bigint_u; +DROP PROCEDURE p_bigint_un; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_nr1; +DROP PROCEDURE p_str_nr2; +DROP PROCEDURE p_str_nu; +DROP PROCEDURE p_str_tb1; +DROP PROCEDURE p_str_tb2; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +DROP PROCEDURE p_object_userr; +CREATE PROCEDURE p_undefined(OUT r BIGINT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r BIGINT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r BIGINT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r BIGINT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r BIGINT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_m(OUT r BIGINT) LANGUAGE JS AS $$ r = 9007199254740991; $$; +CREATE PROCEDURE p_int_m1(OUT r BIGINT) LANGUAGE JS AS $$ r = 9007199254740991 + 1; $$; +CREATE PROCEDURE p_int_nm(OUT r BIGINT) LANGUAGE JS AS $$ r = -9007199254740991 - 1; $$; +CREATE PROCEDURE p_int_nm1(OUT r BIGINT) LANGUAGE JS AS $$ r = -9007199254740991 - 2; $$; +CREATE PROCEDURE p_uint_m(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = 2*9007199254740991 + 1; $$; +CREATE PROCEDURE p_uint_m1(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = 2*9007199254740991 + 2; $$; +CREATE PROCEDURE p_uint_n(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = - 1; $$; +CREATE PROCEDURE p_num_1(OUT r BIGINT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r BIGINT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r BIGINT) LANGUAGE JS AS $$ r = 5e-2; $$; +CREATE PROCEDURE p_num_4(OUT r BIGINT) LANGUAGE JS AS $$ r = 1.2345e+2; $$; +CREATE PROCEDURE p_num_5(OUT r BIGINT) LANGUAGE JS AS $$ r = -1.2345e+1; $$; +CREATE PROCEDURE p_num_r1(OUT r BIGINT) LANGUAGE JS AS $$ r = 4.5; $$; +CREATE PROCEDURE p_num_r2(OUT r BIGINT) LANGUAGE JS AS $$ r = 4.5e+0; $$; +CREATE PROCEDURE p_num_un(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = -1.5; $$; +CREATE PROCEDURE p_num_tb(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = 1e+70; $$; +CREATE PROCEDURE p_bigint(OUT r BIGINT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_n(OUT r BIGINT) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_u(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(9007199254740991); $$; +CREATE PROCEDURE p_bigint_un(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = BigInt(-42); $$; +CREATE PROCEDURE p_bigint_tb(OUT r BIGINT) LANGUAGE JS AS $$ r = BigInt(1e+25); $$; +CREATE PROCEDURE p_bool(OUT r BIGINT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r BIGINT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r BIGINT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r BIGINT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r BIGINT) LANGUAGE JS AS $$ r = "-2"; $$; +CREATE PROCEDURE p_str_n3(OUT r BIGINT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_nr1(OUT r BIGINT) LANGUAGE JS AS $$ r = "4.5"; $$; +CREATE PROCEDURE p_str_nr2(OUT r BIGINT) LANGUAGE JS AS $$ r = "4.5e+0"; $$; +CREATE PROCEDURE p_str_nu(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = "-1"; $$; +CREATE PROCEDURE p_str_tb1(OUT r BIGINT) LANGUAGE JS AS $$ r = "1e+25"; $$; +CREATE PROCEDURE p_str_tb2(OUT r BIGINT) LANGUAGE JS AS $$ r = "18446744073709551616"; $$; +CREATE PROCEDURE p_array(OUT r BIGINT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r BIGINT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r BIGINT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r BIGINT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r BIGINT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r BIGINT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CREATE PROCEDURE p_object_userr(OUT r BIGINT UNSIGNED) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_int_m(@r); +SELECT @r AS im; +im +9007199254740991 +CALL p_int_nm(@r); +SELECT @r AS nm; +nm +-9007199254740992 +CALL p_uint_m(@r); +SELECT @r AS um; +um +18014398509481984 +CALL p_uint_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +# When MySQL converts string value with a floating point number to +# an integer, it converts string to floating point value first and +# then converts it to integer with rounding. +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +1 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +0 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +123 +CALL p_num_5(@r); +SELECT @r AS n4; +n4 +-12 +# MySQL rounds floating-point values differently than decimal values, +# floating-point values in strings and decimal values as string when +# storing them as integer (for floating-point values rint() rounding +# is used, while other use round() style rounding). +# +# We try to avoid the confusion and stick to round()-style +# rounding in all cases. +CALL p_num_r1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_num_r2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_num_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +CALL p_bigint_n(@r); +SELECT @r AS bn; +bn +-42 +CALL p_bigint_u(@r); +SELECT @r AS bu; +bu +9007199254740991 +CALL p_bigint_un(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +-2 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +13 +CALL p_str_nr1(@r); +SELECT @r AS nr1; +nr1 +5 +CALL p_str_nr2(@r); +SELECT @r AS nr2; +nr2 +5 +CALL p_str_nu(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tb2(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR HY000: Incorrect integer value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +CALL p_object_userr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_m; +DROP PROCEDURE p_int_m1; +DROP PROCEDURE p_int_nm; +DROP PROCEDURE p_int_nm1; +DROP PROCEDURE p_uint_m; +DROP PROCEDURE p_uint_m1; +DROP PROCEDURE p_uint_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_5; +DROP PROCEDURE p_num_r1; +DROP PROCEDURE p_num_r2; +DROP PROCEDURE p_num_un; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bigint_u; +DROP PROCEDURE p_bigint_un; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_nr1; +DROP PROCEDURE p_str_nr2; +DROP PROCEDURE p_str_nu; +DROP PROCEDURE p_str_tb1; +DROP PROCEDURE p_str_tb2; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +DROP PROCEDURE p_object_userr; +# +# OUT parameters of floating point SQL-types. +# +CREATE PROCEDURE p_undefined(OUT r FLOAT) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r FLOAT) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r FLOAT) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r FLOAT) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r FLOAT) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_num_1(OUT r FLOAT) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r FLOAT) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r FLOAT) LANGUAGE JS AS $$ r = -5e-2; $$; +CREATE PROCEDURE p_num_m(OUT r FLOAT) LANGUAGE JS AS $$ r = 3.4028234e+38; $$; +CREATE PROCEDURE p_bigint(OUT r FLOAT) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_pl(OUT r FLOAT) LANGUAGE JS AS $$ r = BigInt("36028797018963967"); $$; +CREATE PROCEDURE p_bool(OUT r FLOAT) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r FLOAT) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r FLOAT) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r FLOAT) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r FLOAT) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_n_pl(OUT r FLOAT) LANGUAGE JS AS $$ r = "36028797018963967"; $$; +CREATE PROCEDURE p_str_n_tb(OUT r FLOAT) LANGUAGE JS AS $$ r = "1.1e+400"; $$; +CREATE PROCEDURE p_array(OUT r FLOAT) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r FLOAT) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r FLOAT) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r FLOAT) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r FLOAT) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r FLOAT) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1.25 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +0.5 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +-0.05000000074505806 +CALL p_num_m(@r); +SELECT @r AS nm; +nm +3.4028234663852886e38 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +# For FLOAT the below call returns different value as compared to +# stored function case. This is due to user-variables internally +# using double precision for storing floating point values. +CALL p_bigint_pl(@r); +SELECT @r AS bipl; +bipl +3.602879701896397e16 +CALL p_bool(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +12.649999618530273 +CALL p_str_n_pl(@r); +SELECT @r AS npl; +npl +3.602879701896397e16 +CALL p_str_n_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_func(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_m; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_pl; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n_pl; +DROP PROCEDURE p_str_n_tb; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r DOUBLE) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r DOUBLE) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r DOUBLE) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r DOUBLE) LANGUAGE JS AS $$ r = 1; $$; +CREATE PROCEDURE p_int_n(OUT r DOUBLE) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_num_1(OUT r DOUBLE) LANGUAGE JS AS $$ r = 1.25; $$; +CREATE PROCEDURE p_num_2(OUT r DOUBLE) LANGUAGE JS AS $$ r = 5e-1; $$; +CREATE PROCEDURE p_num_3(OUT r DOUBLE) LANGUAGE JS AS $$ r = -5e-2; $$; +CREATE PROCEDURE p_num_m(OUT r DOUBLE) LANGUAGE JS AS $$ r = 1.7976931348623157e+308; $$; +CREATE PROCEDURE p_bigint(OUT r DOUBLE) LANGUAGE JS AS $$ r = BigInt(100); $$; +CREATE PROCEDURE p_bigint_pl(OUT r DOUBLE) LANGUAGE JS AS $$ r = BigInt("36028797018963967"); $$; +CREATE PROCEDURE p_bool(OUT r DOUBLE) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r DOUBLE) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r DOUBLE) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_n1(OUT r DOUBLE) LANGUAGE JS AS $$ r = "123"; $$; +CREATE PROCEDURE p_str_n2(OUT r DOUBLE) LANGUAGE JS AS $$ r = "12.65"; $$; +CREATE PROCEDURE p_str_n_pl(OUT r DOUBLE) LANGUAGE JS AS $$ r = "36028797018963967"; $$; +CREATE PROCEDURE p_str_n_tb(OUT r DOUBLE) LANGUAGE JS AS $$ r = "1.1e+400"; $$; +CREATE PROCEDURE p_array(OUT r DOUBLE) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DOUBLE) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DOUBLE) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r DOUBLE) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r DOUBLE) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r DOUBLE) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1 +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1.25 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +0.5 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +-0.05 +CALL p_num_m(@r); +SELECT @r AS nm; +nm +1.7976931348623157e308 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +100 +# For FLOAT the below call returns different value as compared to +# stored function case. This is due to user-variables internally +# using double precision for storing floating point values. +CALL p_bigint_pl(@r); +SELECT @r AS bipl; +bipl +3.602879701896397e16 +CALL p_bool(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +123 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +12.65 +CALL p_str_n_pl(@r); +SELECT @r AS npl; +npl +3.602879701896397e16 +CALL p_str_n_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_func(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_typed_arr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_m; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_pl; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n_pl; +DROP PROCEDURE p_str_n_tb; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +# +# OUT parameters of DECIMAL type. +# +CREATE PROCEDURE p_undefined(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int_0(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_int_n(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r =-1 $$; +CREATE PROCEDURE p_int_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r =100 $$; +CREATE PROCEDURE p_num_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 5e-1 $$; +CREATE PROCEDURE p_num_3(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = -5e-2 $$; +CREATE PROCEDURE p_num_m(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 99.99 $$; +CREATE PROCEDURE p_num_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 100.01 $$; +CREATE PROCEDURE p_num_tl(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 0.0125 $$; +CREATE PROCEDURE p_bigint_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = BigInt(10) $$; +CREATE PROCEDURE p_bigint_2(OUT r DECIMAL(20,2)) LANGUAGE JS AS $$ r = BigInt("10000000000000000") $$; +CREATE PROCEDURE p_bigint_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = BigInt(1000) $$; +CREATE PROCEDURE p_bool(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_n1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12" $$; +CREATE PROCEDURE p_str_n2(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12.65" $$; +CREATE PROCEDURE p_str_n3(OUT r DECIMAL(20,2)) LANGUAGE JS AS $$ r = "10000000000000000.12" $$; +CREATE PROCEDURE p_str_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "123" $$; +CREATE PROCEDURE p_str_tl(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12.324" $$; +CREATE PROCEDURE p_str_api(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "π" $$; +CREATE PROCEDURE p_array(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; +CREATE PROCEDURE p_object_serr(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0.00 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +1.00 +CALL p_int_n(@r); +SELECT @r AS n; +n +-1.00 +CALL p_int_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +1.25 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +0.50 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +-0.05 +CALL p_num_m(@r); +SELECT @r AS nm; +nm +99.99 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_tl(@r); +Warnings: +Note 1265 Data truncated for column 'r' at row 1 +SELECT @r AS ntl; +ntl +0.01 +CALL p_bigint_1(@r); +SELECT @r AS bi1; +bi1 +10.00 +CALL p_bigint_2(@r); +SELECT @r AS bi2; +bi2 +10000000000000000.00 +CALL p_bigint_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bool(@r); +ERROR HY000: Incorrect decimal value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect decimal value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect decimal value: 'alpha' for column 'r' at row 1 +CALL p_str_n1(@r); +SELECT @r AS n1; +n1 +12.00 +CALL p_str_n2(@r); +SELECT @r AS n2; +n2 +12.65 +CALL p_str_n3(@r); +SELECT @r AS n3; +n3 +10000000000000000.12 +CALL p_str_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_tl(@r); +Warnings: +Note 1265 Data truncated for column 'r' at row 1 +SELECT @r AS tl; +tl +12.32 +CALL p_str_api(@r); +ERROR HY000: Incorrect decimal value: 'π' for column 'r' at row 1 +CALL p_array(@r); +ERROR HY000: Incorrect decimal value: '1,2,3' for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect decimal value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect decimal value: 'function (a) { r = 1}' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_m; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_num_tl; +DROP PROCEDURE p_bigint_1; +DROP PROCEDURE p_bigint_2; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_tb; +DROP PROCEDURE p_str_tl; +DROP PROCEDURE p_str_api; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; +# +# YEAR OUT parameters are handled similarly to integer types. +# +CREATE PROCEDURE p_undefined(OUT r YEAR) LANGUAGE JS AS $$ r =undefined $$; +CREATE PROCEDURE p_null(OUT r YEAR) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int_0(OUT r YEAR) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r YEAR) LANGUAGE JS AS $$ r = 7 $$; +CREATE PROCEDURE p_int_2(OUT r YEAR) LANGUAGE JS AS $$ r = 69 $$; +CREATE PROCEDURE p_int_3(OUT r YEAR) LANGUAGE JS AS $$ r = 70 $$; +CREATE PROCEDURE p_int_o(OUT r YEAR) LANGUAGE JS AS $$ r = 123 $$; +CREATE PROCEDURE p_int_mi(OUT r YEAR) LANGUAGE JS AS $$ r = 1901 $$; +CREATE PROCEDURE p_int_mx(OUT r YEAR) LANGUAGE JS AS $$ r = 2155 $$; +CREATE PROCEDURE p_int_mi1(OUT r YEAR) LANGUAGE JS AS $$ r = 1900 $$; +CREATE PROCEDURE p_int_mx1(OUT r YEAR) LANGUAGE JS AS $$ r = 2156 $$; +CREATE PROCEDURE p_int_n(OUT r YEAR) LANGUAGE JS AS $$ r = -1 $$; +CREATE PROCEDURE p_num_1(OUT r YEAR) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r YEAR) LANGUAGE JS AS $$ r = 5e-1 $$; +CREATE PROCEDURE p_num_3(OUT r YEAR) LANGUAGE JS AS $$ r = 5e+1 $$; +CREATE PROCEDURE p_num_4(OUT r YEAR) LANGUAGE JS AS $$ r = 1.901e+3 $$; +CREATE PROCEDURE p_num_tb(OUT r YEAR) LANGUAGE JS AS $$ r = 2.2e+3 $$; +CREATE PROCEDURE p_bigint(OUT r YEAR) LANGUAGE JS AS $$ r = BigInt(70) $$; +CREATE PROCEDURE p_bool(OUT r YEAR) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r YEAR) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r YEAR) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_1(OUT r YEAR) LANGUAGE JS AS $$ r = "12" $$; +CREATE PROCEDURE p_str_2(OUT r YEAR) LANGUAGE JS AS $$ r = "75" $$; +CREATE PROCEDURE p_str_3(OUT r YEAR) LANGUAGE JS AS $$ r = "7.5" $$; +CREATE PROCEDURE p_str_o(OUT r YEAR) LANGUAGE JS AS $$ r = "100" $$; +CREATE PROCEDURE p_str_mi(OUT r YEAR) LANGUAGE JS AS $$ r ="1901" $$; +CREATE PROCEDURE p_str_mx(OUT r YEAR) LANGUAGE JS AS $$ r ="2155" $$; +CREATE PROCEDURE p_str_n(OUT r YEAR) LANGUAGE JS AS $$ r = "-1" $$; +CREATE PROCEDURE p_str_mi1(OUT r YEAR) LANGUAGE JS AS $$ r ="1900" $$; +CREATE PROCEDURE p_str_mx1(OUT r YEAR) LANGUAGE JS AS $$ r ="2156" $$; +CREATE PROCEDURE p_array(OUT r YEAR) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r YEAR) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r YEAR) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; +CREATE PROCEDURE p_object_serr(OUT r YEAR) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT @r AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2007 +CALL p_int_2(@r); +SELECT @r AS i2; +i2 +2069 +CALL p_int_3(@r); +SELECT @r AS i3; +i3 +1970 +CALL p_int_o(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_mi(@r); +SELECT @r AS mi; +mi +1901 +CALL p_int_mx(@r); +SELECT @r AS mx; +mx +2155 +CALL p_int_mi1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_int_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_num_1(@r); +SELECT @r AS n1; +n1 +2001 +CALL p_num_2(@r); +SELECT @r AS n2; +n2 +2001 +CALL p_num_3(@r); +SELECT @r AS n3; +n3 +2050 +CALL p_num_4(@r); +SELECT @r AS n4; +n4 +1901 +CALL p_num_tb(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +1970 +CALL p_bool(@r); +ERROR HY000: Incorrect integer value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR HY000: Incorrect integer value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR HY000: Incorrect integer value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2012 +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +1975 +CALL p_str_3(@r); +SELECT @r AS s3; +s3 +2008 +CALL p_str_o(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_mi(@r); +SELECT @r AS mi; +mi +1901 +CALL p_str_mx(@r); +SELECT @r AS mx; +mx +2155 +CALL p_str_n(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_mi1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_str_mx1(@r); +ERROR 22003: Out of range value for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR HY000: Incorrect integer value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR HY000: Incorrect integer value: 'function (a) { r = 1}' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_2; +DROP PROCEDURE p_int_3; +DROP PROCEDURE p_int_o; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_o; +DROP PROCEDURE p_str_mi; +DROP PROCEDURE p_str_mx; +DROP PROCEDURE p_str_n; +DROP PROCEDURE p_str_mi1; +DROP PROCEDURE p_str_mx1; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; +# +# OUT parameters of other datetime SQL-types. +# +CREATE PROCEDURE p_undefined(OUT r DATE) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r DATE) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r DATE) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r DATE) LANGUAGE JS AS $$ r = 20200101; $$; +CREATE PROCEDURE p_int_mi(OUT r DATE) LANGUAGE JS AS $$ r = 10000101; $$; +CREATE PROCEDURE p_int_mx(OUT r DATE) LANGUAGE JS AS $$ r = 99991231; $$; +CREATE PROCEDURE p_int_n(OUT r DATE) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r DATE) LANGUAGE JS AS $$ r = 10000101 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r DATE) LANGUAGE JS AS $$ r = 99991231 + 1; $$; +CREATE PROCEDURE p_num(OUT r DATE) LANGUAGE JS AS $$ r = 2.0200101e+7; $$; +CREATE PROCEDURE p_bigint(OUT r DATE) LANGUAGE JS AS $$ r = BigInt(20200101); $$; +CREATE PROCEDURE p_bool(OUT r DATE) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r DATE) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r DATE) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r DATE) LANGUAGE JS AS $$ r = "2020-01-01"; $$; +CREATE PROCEDURE p_str_b(OUT r DATE) LANGUAGE JS AS $$ r = "2020-13-01"; $$; +CREATE PROCEDURE p_str_ax(OUT r DATE) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r DATE) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r DATE) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DATE) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DATE) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r DATE) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r DATE) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r DATE) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +ERROR 22007: Incorrect date value: '0' for column 'r' at row 1 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2020-01-01 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +1000-01-01 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +9999-12-31 +CALL p_int_n(@r); +ERROR 22007: Incorrect date value: '-1' for column 'r' at row 1 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect date value: '10000100' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect date value: '99991232' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +2020-01-01 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +2020-01-01 +CALL p_bool(@r); +ERROR 22007: Incorrect date value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 22007: Incorrect date value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 22007: Incorrect date value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2020-01-01 +CALL p_str_b(@r); +ERROR 22007: Incorrect date value: '2020-13-01' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect date value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect date value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect date value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect date value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect date value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TIME) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TIME) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r TIME) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r TIME) LANGUAGE JS AS $$ r = 11; $$; +CREATE PROCEDURE p_int_mi(OUT r TIME) LANGUAGE JS AS $$ r = -8385959; $$; +CREATE PROCEDURE p_int_mx(OUT r TIME) LANGUAGE JS AS $$ r = 8385959; $$; +CREATE PROCEDURE p_int_n(OUT r TIME) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r TIME) LANGUAGE JS AS $$ r = -8385959 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r TIME) LANGUAGE JS AS $$ r = 8385959 + 1; $$; +CREATE PROCEDURE p_num(OUT r TIME) LANGUAGE JS AS $$ r = 11.1; $$; +CREATE PROCEDURE p_bigint(OUT r TIME) LANGUAGE JS AS $$ r = BigInt(11); $$; +CREATE PROCEDURE p_bool(OUT r TIME) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TIME) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TIME) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TIME) LANGUAGE JS AS $$ r = "00:11"; $$; +CREATE PROCEDURE p_str_b(OUT r TIME) LANGUAGE JS AS $$ r = "00:65"; $$; +CREATE PROCEDURE p_str_ax(OUT r TIME) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r TIME) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r TIME) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TIME) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TIME) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TIME) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TIME) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TIME) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +# 0 is valid value for TIME type. +CALL p_int_0(@r); +SELECT @r AS z; +z +00:00:00 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +00:00:11 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +-838:59:59 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +838:59:59 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect time value: '-8385960' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect time value: '8385960' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +00:00:11 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +00:00:11 +CALL p_bool(@r); +ERROR 22007: Incorrect time value: 'true' for column 'r' at row 1 +# Empty string is converted to 0 for TIME type. +CALL p_str_e(@r); +SELECT @r AS se; +se +00:00:00 +CALL p_str_a(@r); +ERROR 22007: Incorrect time value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +00:11:00 +CALL p_str_b(@r); +ERROR 22007: Incorrect time value: '00:65' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect time value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect time value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_array(@r); +ERROR 22007: Incorrect time value: '1,2,3' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect time value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect time value: 'function (a) { r = 1;}' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_typed_arr(@r); +ERROR 22007: Incorrect time value: '0,1,2,3,5' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect time value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TIME(1)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TIME(1)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r TIME(1)) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r TIME(1)) LANGUAGE JS AS $$ r = 11; $$; +CREATE PROCEDURE p_int_mi(OUT r TIME(1)) LANGUAGE JS AS $$ r = -8385959; $$; +CREATE PROCEDURE p_int_mx(OUT r TIME(1)) LANGUAGE JS AS $$ r = 8385959; $$; +CREATE PROCEDURE p_int_n(OUT r TIME(1)) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r TIME(1)) LANGUAGE JS AS $$ r = -8385959 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r TIME(1)) LANGUAGE JS AS $$ r = 8385959 + 1; $$; +CREATE PROCEDURE p_num(OUT r TIME(1)) LANGUAGE JS AS $$ r = 11.1; $$; +CREATE PROCEDURE p_bigint(OUT r TIME(1)) LANGUAGE JS AS $$ r = BigInt(11); $$; +CREATE PROCEDURE p_bool(OUT r TIME(1)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TIME(1)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TIME(1)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TIME(1)) LANGUAGE JS AS $$ r = "00:11.1"; $$; +CREATE PROCEDURE p_str_b(OUT r TIME(1)) LANGUAGE JS AS $$ r = "01:71.1"; $$; +CREATE PROCEDURE p_str_ax(OUT r TIME(1)) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r TIME(1)) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r TIME(1)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TIME(1)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TIME(1)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TIME(1)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TIME(1)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TIME(1)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +# 0 is valid value for TIME type. +CALL p_int_0(@r); +SELECT @r AS z; +z +00:00:00.0 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +00:00:11.0 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +-838:59:59.0 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +838:59:59.0 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect time value: '-8385960' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect time value: '8385960' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +00:00:11.1 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +00:00:11.0 +CALL p_bool(@r); +ERROR 22007: Incorrect time value: 'true' for column 'r' at row 1 +# Empty string is converted to 0 for TIME type. +CALL p_str_e(@r); +SELECT @r AS se; +se +00:00:00.0 +CALL p_str_a(@r); +ERROR 22007: Incorrect time value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +00:11:00.1 +CALL p_str_b(@r); +ERROR 22007: Incorrect time value: '01:71.1' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect time value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect time value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_array(@r); +ERROR 22007: Incorrect time value: '1,2,3' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect time value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect time value: 'function (a) { r = 1;}' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_typed_arr(@r); +ERROR 22007: Incorrect time value: '0,1,2,3,5' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect time value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r DATETIME) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r DATETIME) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r DATETIME) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r DATETIME) LANGUAGE JS AS $$ r = 20200102030405; $$; +CREATE PROCEDURE p_int_mi(OUT r DATETIME) LANGUAGE JS AS $$ r = 10000101000000; $$; +CREATE PROCEDURE p_int_mx(OUT r DATETIME) LANGUAGE JS AS $$ r = 99991231235959; $$; +CREATE PROCEDURE p_int_n(OUT r DATETIME) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r DATETIME) LANGUAGE JS AS $$ r = 10000101000000 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r DATETIME) LANGUAGE JS AS $$ r = 99991231235959 + 1; $$; +CREATE PROCEDURE p_num(OUT r DATETIME) LANGUAGE JS AS $$ r = 20200102030405.06; $$; +CREATE PROCEDURE p_bigint(OUT r DATETIME) LANGUAGE JS AS $$ r = BigInt(20200102030405); $$; +CREATE PROCEDURE p_bool(OUT r DATETIME) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r DATETIME) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r DATETIME) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r DATETIME) LANGUAGE JS AS $$ r = "2020-01-02 03:04:05"; $$; +CREATE PROCEDURE p_str_b(OUT r DATETIME) LANGUAGE JS AS $$ r = "2020-01-32 03:04:05"; $$; +CREATE PROCEDURE p_str_ax(OUT r DATETIME) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r DATETIME) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r DATETIME) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DATETIME) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DATETIME) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r DATETIME) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r DATETIME) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r DATETIME) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +ERROR 22007: Incorrect datetime value: '0' for column 'r' at row 1 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2020-01-02 03:04:05 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +1000-01-01 00:00:00 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +9999-12-31 23:59:59 +CALL p_int_n(@r); +ERROR 22007: Incorrect datetime value: '-1' for column 'r' at row 1 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect datetime value: '10000100999999' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect datetime value: '99991231235960' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +2020-01-02 03:04:05 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +2020-01-02 03:04:05 +CALL p_bool(@r); +ERROR 22007: Incorrect datetime value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 22007: Incorrect datetime value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2020-01-02 03:04:05 +CALL p_str_b(@r); +ERROR 22007: Incorrect datetime value: '2020-01-32 03:04:05' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect datetime value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect datetime value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 20200102030405; $$; +CREATE PROCEDURE p_int_mi(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 10000101000000; $$; +CREATE PROCEDURE p_int_mx(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 99991231235959; $$; +CREATE PROCEDURE p_int_n(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 10000101000000 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 99991231235959 + 1; $$; +CREATE PROCEDURE p_num(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = 20200102030405.06; $$; +CREATE PROCEDURE p_bigint(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = BigInt(20200102030405); $$; +CREATE PROCEDURE p_bool(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = "2020-01-02 03:04:05.06"; $$; +CREATE PROCEDURE p_str_b(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = "2020-01-02 25:04:05.06"; $$; +CREATE PROCEDURE p_str_ax(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r DATETIME(2)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r DATETIME(2)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +ERROR 22007: Incorrect datetime value: '0' for column 'r' at row 1 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2020-01-02 03:04:05.00 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +1000-01-01 00:00:00.00 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +9999-12-31 23:59:59.00 +CALL p_int_n(@r); +ERROR 22007: Incorrect datetime value: '-1' for column 'r' at row 1 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect datetime value: '10000100999999' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect datetime value: '99991231235960' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +2020-01-02 03:04:05.06 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +2020-01-02 03:04:05.00 +CALL p_bool(@r); +ERROR 22007: Incorrect datetime value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 22007: Incorrect datetime value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2020-01-02 03:04:05.06 +CALL p_str_b(@r); +ERROR 22007: Incorrect datetime value: '2020-01-02 25:04:05.06' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect datetime value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect datetime value: 'function (a) { r = 1;}' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 20231222102000; $$; +CREATE PROCEDURE p_int_mi(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 19700101030001; $$; +CREATE PROCEDURE p_int_mx(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 20380119061407; $$; +CREATE PROCEDURE p_int_n(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 19700101030001 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 20380119061407 + 1; $$; +CREATE PROCEDURE p_num(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = 20231222102000.0123; $$; +CREATE PROCEDURE p_bigint(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = BigInt(20231222102000); $$; +CREATE PROCEDURE p_bool(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = "2023-12-22 10:20:00"; $$; +CREATE PROCEDURE p_str_b(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = "2023-12-22 10:71:00"; $$; +CREATE PROCEDURE p_str_ax(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TIMESTAMP) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TIMESTAMP) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +ERROR 22007: Incorrect datetime value: '0' for column 'r' at row 1 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2023-12-22 10:20:00 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +1970-01-01 03:00:01 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +2038-01-19 06:14:07 +CALL p_int_n(@r); +ERROR 22007: Incorrect datetime value: '-1' for column 'r' at row 1 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect datetime value: '19700101030000' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect datetime value: '20380119061408' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +2023-12-22 10:20:00 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +2023-12-22 10:20:00 +CALL p_bool(@r); +ERROR 22007: Incorrect datetime value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 22007: Incorrect datetime value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2023-12-22 10:20:00 +CALL p_str_b(@r); +ERROR 22007: Incorrect datetime value: '2023-12-22 10:71:00' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect datetime value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_array(@r); +ERROR 22007: Incorrect datetime value: '1,2,3' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect datetime value: 'function (a) { r = 1;}' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_typed_arr(@r); +ERROR 22007: Incorrect datetime value: '0,1,2,3,5' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +CREATE PROCEDURE p_undefined(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = undefined; $$; +CREATE PROCEDURE p_null(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = null; $$; +CREATE PROCEDURE p_int_0(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 0; $$; +CREATE PROCEDURE p_int_1(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 20231222102000; $$; +CREATE PROCEDURE p_int_mi(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 19700101030001; $$; +CREATE PROCEDURE p_int_mx(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 20380119061407; $$; +CREATE PROCEDURE p_int_n(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = -1; $$; +CREATE PROCEDURE p_int_mi1(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 19700101030001 - 1; $$; +CREATE PROCEDURE p_int_mx1(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 20380119061407 + 1; $$; +CREATE PROCEDURE p_num(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = 20231222102000.0123; $$; +CREATE PROCEDURE p_bigint(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = BigInt(20231222102000); $$; +CREATE PROCEDURE p_bool(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = true; $$; +CREATE PROCEDURE p_str_e(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = ""; $$; +CREATE PROCEDURE p_str_a(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = "alpha"; $$; +CREATE PROCEDURE p_str_1(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = "2023-12-22 10:20:00.1234"; $$; +CREATE PROCEDURE p_str_b(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = "2023-12-22 10:20:63.1234"; $$; +CREATE PROCEDURE p_str_ax(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = "\u{1F384}"; $$; +CREATE PROCEDURE p_date(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = new Date(2023,11,22,13,0,0,123) $$; +CREATE PROCEDURE p_array(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = function (a) { r = 1;} $$; +CREATE PROCEDURE p_typed_arr(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = new Uint8Array([0, 1, 2, 3, 5]) $$; +CREATE PROCEDURE p_data_view(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv $$; +CREATE PROCEDURE p_object_serr(OUT r TIMESTAMP(4)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +ERROR 22007: Incorrect datetime value: '0' for column 'r' at row 1 +CALL p_int_1(@r); +SELECT @r AS i1; +i1 +2023-12-22 10:20:00.0000 +CALL p_int_mi(@r); +SELECT @r AS imi; +imi +1970-01-01 03:00:01.0000 +CALL p_int_mx(@r); +SELECT @r AS imx; +imx +2038-01-19 06:14:07.0000 +CALL p_int_n(@r); +ERROR 22007: Incorrect datetime value: '-1' for column 'r' at row 1 +CALL p_int_mi1(@r); +ERROR 22007: Incorrect datetime value: '19700101030000' for column 'r' at row 1 +CALL p_int_mx1(@r); +ERROR 22007: Incorrect datetime value: '20380119061408' for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +2023-12-22 10:20:00.0100 +CALL p_bigint(@r); +SELECT @r AS bi; +bi +2023-12-22 10:20:00.0000 +CALL p_bool(@r); +ERROR 22007: Incorrect datetime value: 'true' for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 22007: Incorrect datetime value: '' for column 'r' at row 1 +CALL p_str_a(@r); +ERROR 22007: Incorrect datetime value: 'alpha' for column 'r' at row 1 +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +2023-12-22 10:20:00.1234 +CALL p_str_b(@r); +ERROR 22007: Incorrect datetime value: '2023-12-22 10:20:63.1234' for column 'r' at row 1 +CALL p_str_ax(@r); +ERROR 22007: Incorrect datetime value: '?' for column 'r' at row 1 +# Direct string representation of Date type is not compatible with +# MySQL datetime values. +CALL p_date(@r); +ERROR 22007: Incorrect datetime value: 'Fri Dec 22 2023 13:00:00 GMT+0300 (GMT)' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_array(@r); +ERROR 22007: Incorrect datetime value: '1,2,3' for column 'r' at row 1 +CALL p_object(@r); +ERROR 22007: Incorrect datetime value: '[object Object]' for column 'r' at row 1 +CALL p_func(@r); +ERROR 22007: Incorrect datetime value: 'function (a) { r = 1;}' for column 'r' at row 1 +# DATE and DATETIME types accept weird literals. +CALL p_typed_arr(@r); +ERROR 22007: Incorrect datetime value: '0,1,2,3,5' for column 'r' at row 1 +CALL p_data_view(@r); +ERROR 22007: Incorrect datetime value: '[object DataView]' for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_b; +DROP PROCEDURE p_str_ax; +DROP PROCEDURE p_date; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +DROP PROCEDURE p_object_serr; +# +# ENUM type of OUT parameter is handled similarly to string types. +# +CREATE PROCEDURE p_undefined(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_0(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "a" $$; +CREATE PROCEDURE p_str_1(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_2(OUT r ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) +LANGUAGE JS AS $$ r = "Додо" $$; +CREATE PROCEDURE p_str_cerr(OUT r ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) +LANGUAGE JS AS $$ r = "\u{1F9A4}" $$; +CREATE PROCEDURE p_array(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; +CREATE PROCEDURE p_object_serr(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +a +CALL p_num(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_bigint(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_bool(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_e(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +a +CALL p_str_2(@r); +SELECT @r = X'c4eee4ee' AS s2; +s2 +1 +CALL p_str_1(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_cerr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_func(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; +# +# SET type OUT parameters are handled similarly to how SQL core +# interprets integer/floating-point values and strings which are +# stored in SET columns. +# +# Numeric values are converted to integers and treated as bitmaps +# representing sets. Strings are expected to contain comma-separated +# lists of SET elements. Additionally strings containing integer +# values are interpreted as bitmaps. +CREATE PROCEDURE p_undefined(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_int_tb(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 10 $$; +CREATE PROCEDURE p_int_n(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = -1 $$; +CREATE PROCEDURE p_num(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 1.6 $$; +CREATE PROCEDURE p_bigint(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = BigInt(4) $$; +CREATE PROCEDURE p_bool(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_0(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "a" $$; +CREATE PROCEDURE p_str_1(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "b,c" $$; +CREATE PROCEDURE p_str_2(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "3" $$; +CREATE PROCEDURE p_str_3(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "1.5" $$; +CREATE PROCEDURE p_str_n(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "-3" $$; +CREATE PROCEDURE p_str_w(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_4(OUT r SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) +LANGUAGE JS AS $$ r = "Додо" $$; +CREATE PROCEDURE p_str_cerr(OUT r SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) +LANGUAGE JS AS $$ r = "\u{1F9A4}" $$; +CREATE PROCEDURE p_array(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; +CREATE PROCEDURE p_object_serr(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +a +CALL p_int_tb(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_int_n(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_num(@r); +SELECT @r AS n; +n +a +CALL p_bigint(@r); +SELECT @r AS bi; +bi +c +CALL p_bool(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_e(@r); +SELECT @r AS se; +se + +CALL p_str_0(@r); +SELECT @r AS s0; +s0 +a +CALL p_str_1(@r); +SELECT @r AS s1; +s1 +b,c +CALL p_str_2(@r); +SELECT @r AS s2; +s2 +a,b +# SQL core doesn't handle non-integer numbers represented as strings +# and doubles stored in SET columns consistently either. +CALL p_str_3(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_n(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_w(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_str_4(@r); +SELECT @r = X'c4eee4ee' AS s; +s +1 +CALL p_str_cerr(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_array(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_func(@r); +ERROR 01000: Data truncated for column 'r' at row 1 +CALL p_object_serr(@r); +ERROR HY000: Can't convert JS value to string to be stored in out parameter or return value +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_n; +DROP PROCEDURE p_str_w; +DROP PROCEDURE p_str_4; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; +# +# BIT type of OUT parameter is handled in a special way. +# +# JS values are converted to numbers (ultimately integers) and then +# their binary representation is interpreted as array of bits. +# +# We do not support JS values which are not convertible to numbers to +# avoid confusion caused by different interpretation of 1, "1" and "a". +# +CREATE PROCEDURE p_undefined(OUT r BIT(5)) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r BIT(5)) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int_0(OUT r BIT(5)) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r BIT(5)) LANGUAGE JS AS $$ r = 7 $$; +CREATE PROCEDURE p_int_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = 33 $$; +CREATE PROCEDURE p_int_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = -1 $$; +CREATE PROCEDURE p_num_1(OUT r BIT(5)) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r BIT(5)) LANGUAGE JS AS $$ r = 7.8 $$; +CREATE PROCEDURE p_num_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = 32.5 $$; +CREATE PROCEDURE p_num_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = -1.2 $$; +CREATE PROCEDURE p_bigint(OUT r BIT(5)) LANGUAGE JS AS $$ r = BigInt(5) $$; +CREATE PROCEDURE p_bigint_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = BigInt(-1) $$; +CREATE PROCEDURE p_bool(OUT r BIT(5)) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r BIT(5)) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r BIT(5)) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_1(OUT r BIT(7)) LANGUAGE JS AS $$ r = "0" $$; +CREATE PROCEDURE p_str_2(OUT r BIT(7)) LANGUAGE JS AS $$ r = "16" $$; +CREATE PROCEDURE p_str_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = "33" $$; +CREATE PROCEDURE p_array(OUT r BIT(5)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r BIT(5)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r BIT(5)) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; +CREATE PROCEDURE p_object_nerr(OUT r BIT(5)) LANGUAGE JS AS $$ r = { valueOf() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int_0(@r); +SELECT HEX(@r) AS i0; +i0 +0 +CALL p_int_1(@r); +SELECT HEX(@r) AS i1; +i1 +7 +CALL p_int_tb(@r); +ERROR 22001: Data too long for column 'r' at row 1 +CALL p_int_n(@r); +ERROR 22001: Data too long for column 'r' at row 1 +CALL p_num_1(@r); +SELECT HEX(@r) AS n1; +n1 +1 +CALL p_num_2(@r); +SELECT HEX(@r) AS n2; +n2 +7 +CALL p_num_tb(@r); +ERROR 22001: Data too long for column 'r' at row 1 +CALL p_num_n(@r); +ERROR 22001: Data too long for column 'r' at row 1 +CALL p_bigint(@r); +SELECT HEX(@r) AS bi; +bi +5 +CALL p_bigint_n(@r); +ERROR HY000: Can't convert BigInt value to BIT type (value out of range) +CALL p_bool(@r); +SELECT HEX(@r) AS b; +b +1 +CALL p_str_e(@r); +SELECT HEX(@r) AS se; +se +0 +CALL p_str_a(@r); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +CALL p_str_1(@r); +SELECT HEX(@r) AS s1; +s1 +0 +CALL p_str_2(@r); +SELECT HEX(@r) AS s2; +s2 +10 +CALL p_str_tb(@r); +ERROR 22001: Data too long for column 'r' at row 1 +CALL p_array(@r); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +CALL p_object(@r); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +CALL p_func(@r); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +CALL p_object_nerr(@r); +ERROR HY000: Can't convert JS value to BIT type (possibly non-numeric value) +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_num_n; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_tb; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_nerr; +# +# For GEOMETRY type of OUT parameter we only support conversion from +# ArrayBuffer-based JS values. +# +CREATE PROCEDURE p_undefined(OUT r GEOMETRY) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r GEOMETRY) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int(OUT r GEOMETRY) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r GEOMETRY) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r GEOMETRY) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r GEOMETRY) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r GEOMETRY) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r GEOMETRY) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_array(OUT r GEOMETRY) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r GEOMETRY) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r GEOMETRY) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; +CREATE PROCEDURE p_typed_arr(OUT r GEOMETRY) LANGUAGE JS AS $$ r = new Uint8Array([0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 64, 0, 0, 0, 0, 0, 0, 52, 64]) $$; +CREATE PROCEDURE p_data_view(OUT r GEOMETRY) LANGUAGE JS AS $$ +let dv = new DataView(new ArrayBuffer(25)); +dv.setUint32(0, 0, true); // SRID 0 +dv.setUint8(4, 1); // little-endian +dv.setUint32(5, 1, true); // POINT +dv.setFloat64(9, 15, true); // X +dv.setFloat64(17, 20, true); // Y +r = dv; +$$| +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_num(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_bigint(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_bool(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_str_e(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_str_a(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_array(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_object(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_func(@r); +ERROR HY000: Only ArrayBuffer-based values are supported for GEOMETRY return type. +CALL p_typed_arr(@r); +SELECT ST_AsText(@r) AS g; +g +POINT(15 20) +CALL p_data_view(@r); +SELECT ST_AsText(@r) AS g; +g +POINT(15 20) +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; +# +# For JSON OUT parameters we apply JSON.stringify() to JS value and +# then try to store resulting string in the OUT parameter (of JSON SQL +# type). +# +CREATE PROCEDURE p_undefined(OUT r JSON) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r JSON) LANGUAGE JS AS $$ r = null $$; +CREATE PROCEDURE p_int(OUT r JSON) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r JSON) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r JSON) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r JSON) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r JSON) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r JSON) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_array(OUT r JSON) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r JSON) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r JSON) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; +CREATE PROCEDURE p_object_jerr(OUT r JSON) LANGUAGE JS AS $$ r = { toJSON() { throw "Kaboom!" } } $$; +CALL p_undefined(@r); +SELECT @r AS u; +u +NULL +CALL p_null(@r); +SELECT @r AS nil; +nil +NULL +CALL p_int(@r); +SELECT @r AS i; +i +1 +CALL p_num(@r); +SELECT @r AS n; +n +1.25 +# JSON.stringify() doesn't support BigInt by default. +CALL p_bigint(@r); +ERROR HY000: For JSON return type only values supported by JSON.stringify() are allowed. +CALL p_bool(@r); +SELECT @r AS b; +b +true +CALL p_str_e(@r); +SELECT @r AS se; +se +"" +CALL p_str_a(@r); +SELECT @r AS sa; +sa +"alpha" +CALL p_array(@r); +SELECT @r AS arr; +arr +[1, 2, 3] +CALL p_object(@r); +SELECT @r AS obj; +obj +{"x": 1, "y": "alpha"} +# SQL JSON type doesn't accept all JSON values produced by V8. +CALL p_func(@r); +ERROR 22032: Invalid JSON text: "Invalid value." at position 0 in value for column '.r' at row 1. +CALL p_object_jerr(@r); +ERROR HY000: For JSON return type only values supported by JSON.stringify() are allowed. +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_jerr; +# +# Let us test how JS contexts are handled by our JS routines. +# +# JS contexts are created for each connection and each account under +# which routines are executed. +# +# This means: +# - Different connections get different contexts. +# - Routines which are executed under different accounts get different +# contexts (even if is the same routine, e.g. function with SQL +# SECURITY INVOKER attribute used from view and directly). +# - Any routines which are executed within the same connection under +# the same account get the same context. +# +# We check whether two calls use same JS context by accessing global +# object. +CREATE PROCEDURE p(i INT) LANGUAGE JS AS $$ globalThis.a = i $$; +CREATE FUNCTION f() RETURNS INT LANGUAGE JS AS $$ return globalThis.a $$; +CREATE USER user1@localhost; +GRANT ALL PRIVILEGES ON *.* TO user1@localhost; +CREATE USER user2@localhost; +GRANT ALL PRIVILEGES ON *.* TO user2@localhost; +CREATE DEFINER = user1@localhost FUNCTION g() RETURNS INT LANGUAGE JS AS $$ return globalThis.a $$; +CREATE FUNCTION h() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return globalThis.a $$; +# Create SQL SECURITY DEFINER wrapper over h() +CREATE DEFINER = user2@localhost FUNCTION i() RETURNS INT RETURN h(); +# +# Routine calls within the same connection executed under same +# account share the context. +connection default; +CALL p(1); +SELECT f(); +f() +1 +# +# Calls in different connections get different contexts. +connect con1, localhost, root,,; +SELECT f(); +f() +NULL +CALL p(2); +SELECT f(); +f() +2 +disconnect con1; +connection default; +SELECT f(); +f() +1 +# +# Calls under different accounts get different contexts. +# +# Test this by running SECURITY DEFINER routine with non-root definer. +SELECT g(); +g() +NULL +# And also by running SECURITY INVOKER routine from different security +# contexts. +SELECT h(), i(); +h() i() +1 NULL +DROP PROCEDURE p; +DROP FUNCTION f; +DROP FUNCTION g; +DROP FUNCTION h; +DROP FUNCTION i; +DROP USER user2@localhost; +DROP USER user1@localhost; +# +# Let us test privilege requirements for JS routine creation and +# execution. +# +CREATE DATABASE mysqltest; +CREATE USER u_creator@localhost; +GRANT CREATE ROUTINE ON mysqltest.* TO u_creator@localhost; +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; +CREATE USER u_caller@localhost; +GRANT EXECUTE ON mysqltest.* TO u_caller@localhost; +CREATE USER u_dropper@localhost; +GRANT ALTER ROUTINE ON mysqltest.* TO u_dropper@localhost; +# +# Let us show that JS routine can be created iff user has both CREATE +# ROUTINE privilege on the database and global CREATE_JS_ROUTINE +# privilege. +connect creator, localhost, u_creator,,; +CREATE FUNCTION mysqltest.f_suid() RETURNS INT LANGUAGE JS AS $$ return 2*2 $$; +CREATE FUNCTION mysqltest.f_nosuid() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 2*2 $$; +connection default; +GRANT ALL PRIVILEGES ON *.* TO u_creator@localhost; +REVOKE CREATE_JS_ROUTINE ON *.* FROM u_creator@localhost; +connection creator; +CREATE FUNCTION mysqltest.f_err() RETURNS INT LANGUAGE JS AS $$ return 42 $$; +ERROR 42000: Access denied; you need (at least one of) the CREATE_JS_ROUTINE privilege(s) for this operation +CREATE FUNCTION mysqltest.f_err() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 42 $$; +ERROR 42000: Access denied; you need (at least one of) the CREATE_JS_ROUTINE privilege(s) for this operation +connection default; +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; +REVOKE CREATE ROUTINE ON *.* FROM u_creator@localhost; +REVOKE CREATE ROUTINE ON mysqltest.* FROM u_creator@localhost; +connection creator; +CREATE FUNCTION mysqltest.f_err() RETURNS INT LANGUAGE JS AS $$ return 42 $$; +ERROR 42000: Access denied for user 'u_creator'@'localhost' to database 'mysqltest' +CREATE FUNCTION mysqltest.f_err() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 42 $$; +ERROR 42000: Access denied for user 'u_creator'@'localhost' to database 'mysqltest' +connection default; +# Restore status quo for 'creator' user (ignore +# --sp-automatic-privileges effect for now). +REVOKE ALL PRIVILEGES ON *.* FROM u_creator@localhost; +GRANT CREATE ROUTINE ON mysqltest.* TO u_creator@localhost; +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; +# +# Now show that JS routine can be executed iff user has EXECUTE +# privilege on it. For SQL SECURITY DEFINER routines routine definer +# also needs to have EXECUTE privilege at the time of call. +GRANT EXECUTE ON mysqltest.* TO u_creator@localhost; +connect caller, localhost, u_caller,,; +SELECT mysqltest.f_suid(); +mysqltest.f_suid() +4 +SELECT mysqltest.f_nosuid(); +mysqltest.f_nosuid() +4 +connection default; +REVOKE EXECUTE ON mysqltest.* FROM u_creator@localhost; +connection caller; +SELECT mysqltest.f_suid(); +ERROR 42000: execute command denied to user 'u_creator'@'localhost' for routine 'mysqltest.f_suid' +SELECT mysqltest.f_nosuid(); +mysqltest.f_nosuid() +4 +connection default; +GRANT EXECUTE ON mysqltest.* TO u_creator@localhost; +GRANT ALL PRIVILEGES ON *.* TO u_caller@localhost; +REVOKE EXECUTE ON *.* FROM u_caller@localhost; +REVOKE EXECUTE ON mysqltest.* FROM u_caller@localhost; +connection caller; +SELECT mysqltest.f_suid(); +ERROR 42000: execute command denied to user 'u_caller'@'localhost' for routine 'mysqltest.f_suid' +SELECT mysqltest.f_nosuid(); +ERROR 42000: execute command denied to user 'u_caller'@'localhost' for routine 'mysqltest.f_nosuid' +# +# Show that JS routine can be dropped iff user has ALTER ROUTINE +# privilege on it. +connection default; +GRANT ALL PRIVILEGES ON *.* TO u_dropper@localhost; +REVOKE ALTER ROUTINE ON *.* FROM u_dropper@localhost; +REVOKE ALTER ROUTINE ON mysqltest.* FROM u_dropper@localhost; +connect dropper, localhost, u_dropper,,; +DROP FUNCTION mysqltest.f1_suid; +ERROR 42000: alter routine command denied to user 'u_dropper'@'localhost' for routine 'mysqltest.f1_suid' +DROP FUNCTION mysqltest.f1_nosuid; +ERROR 42000: alter routine command denied to user 'u_dropper'@'localhost' for routine 'mysqltest.f1_nosuid' +connection default; +REVOKE ALL PRIVILEGES ON *.* FROM u_dropper@localhost; +GRANT ALTER ROUTINE ON mysqltest.* TO u_dropper@localhost; +connection dropper; +DROP FUNCTION mysqltest.f_suid; +DROP FUNCTION mysqltest.f_nosuid; +connection creator; +disconnect creator; +connection caller; +disconnect caller; +connection dropper; +disconnect dropper; +connection default; +DROP USER u_creator@localhost; +DROP USER u_caller@localhost; +DROP USER u_dropper@localhost; +DROP DATABASE mysqltest; +REVOKE CREATE_JS_ROUTINE ON *.* FROM root@localhost; +# +# Test of component uninstallation while having outstanding per +# connection context and isolate. +# +UNINSTALL COMPONENT 'file://component_js_lang'; +ERROR HY000: Language component: Cannot uninstall due to connected sessions. Please disconnect all sessions and try again. +# Disconnect of default connection frees the only remaining per +# connection context and isolate. It is safe to uninstall +# component now. +disconnect default; +connect default,localhost,root; +UNINSTALL COMPONENT 'file://component_js_lang'; +# +# UNINSTALL -> INSTALL is not supported without interim restart +# as V8 doesn't support re-initialization. +INSTALL COMPONENT 'file://component_js_lang'; +ERROR HY000: Re-installing the component without server restart is not supported. +# Restart server to let subsequent tests to do INSTALL COMPONENT freely. +# restart diff --git a/mysql-test/suite/component_js_lang/r/js_lang_big.result b/mysql-test/suite/component_js_lang/r/js_lang_big.result new file mode 100644 index 000000000000..9e4581b275be --- /dev/null +++ b/mysql-test/suite/component_js_lang/r/js_lang_big.result @@ -0,0 +1,60 @@ +# Global prepare of playground. +INSTALL COMPONENT 'file://component_js_lang'; +GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; +# +# Test what happens if one tries to create JS routine which body which +# exceeds V8 string length limit (2^29 - 24 on 64-bit systems). +# +# Temporarily raise max_allowed_packet value so we can easily produce +# strings exceeding V8 limit. It should be multiply of 1024. +SET @save_max_allowed_packet = @@global.max_allowed_packet; +SET @@global.max_allowed_packet= V8_MAX_STRING_LENGTH + 24 + 1024; +# Use new connection so new max_allowed_packet value takes effect. +connect con_big_packet, localhost, root,,; +# Try to create function with body exceeding JS string length limit. +# Note that auxiliary huge_string string we use for this still fits +# within the limit. +# Also note that attempt to create routine which is below the limit, +# but is huge enough, might pass but will make InnoDB complain in +# the error log about too big rows/tuples. +# Executing: CREATE FUNCTION f_huge() RETURNS LONGBLOB LANGUAGE JS AS $$ let too_lengthy_body_we_get = 1; return "HUGE_STRING_HERE" $$ +ERROR HY000: Routine body exceeds V8 string length +# +# Test coverage for SQL to JS parameter conversion failures due to +# exceeding V8 string length limit/ 2^29 - 24. +# +# Create functions with parameter types which values can exceed +# the limit. +CREATE FUNCTION f_json(s JSON) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longtext(s LONGTEXT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longtext_cs(s LONGTEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longblob(b LONGBLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return b.byteLength $$; +# Passing too long JSON and LONGTEXT values should cause error with +# appropriate message. +SELECT f_json(JSON_ARRAY(REPEAT('a', V8_MAX_STRING_LENGTH - 100 ), REPEAT('b', 200 ))); +ERROR HY000: Can't create JS value from routine parameter value of JSON type (V8 string length limit excedeed) +SELECT f_longtext(REPEAT('a', V8_MAX_STRING_LENGTH + 200)); +ERROR HY000: Can't create JS value from routine parameter value (V8 string length limit excedeed) +SELECT f_longtext_cs(REPEAT(X'fa', V8_MAX_STRING_LENGTH + 200)); +ERROR HY000: Can't create JS value from routine parameter value (V8 string length limit excedeed) +# This should not apply to LONGBLOB parameters however, as their +# values are directly converted to JS ArrayBuffer/DataView objects. +SELECT f_longblob(REPEAT(X'ff', V8_MAX_STRING_LENGTH + 200)) = V8_MAX_STRING_LENGTH + 200 AS length_is_correct; +length_is_correct +1 +DROP FUNCTION f_json; +DROP FUNCTION f_longtext; +DROP FUNCTION f_longtext_cs; +DROP FUNCTION f_longblob; +disconnect con_big_packet; +connection default; +SET @@global.max_allowed_packet = @save_max_allowed_packet; +# Global clean-up. +REVOKE CREATE_JS_ROUTINE ON *.* FROM root@localhost; +# Disconnect of default connection to free the only remaining +# connection context and isolate, so we can uninstall component. +disconnect default; +connect default,localhost,root; +UNINSTALL COMPONENT 'file://component_js_lang'; +# Restart server to let subsequent tests to do INSTALL COMPONENT freely. +# restart diff --git a/mysql-test/suite/component_js_lang/t/js_lang_basic.test b/mysql-test/suite/component_js_lang/t/js_lang_basic.test new file mode 100644 index 000000000000..fa98654724c9 --- /dev/null +++ b/mysql-test/suite/component_js_lang/t/js_lang_basic.test @@ -0,0 +1,3359 @@ +--source include/have_js_lang_component.inc + +INSTALL COMPONENT 'file://component_js_lang'; + +GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; + +--echo # +--echo # Some basic tests. +--echo # +CREATE FUNCTION f1() RETURNS INT LANGUAGE JS AS $$ return 2*2 $$; +SELECT f1(); +DROP FUNCTION f1; + +DELIMITER |; +CREATE FUNCTION fact(n INT) RETURNS INT LANGUAGE JS AS $$ + let result = 1; + while (n > 1) { + result *= n; + n--; + } + return result; +$$| +DELIMITER ;| +SELECT fact(5); +DROP FUNCTION fact; + +DELIMITER |; +CREATE PROCEDURE p1(a INT, b INT, OUT r INT) LANGUAGE JS AS $$ + r = a * b; +$$| +DELIMITER ;| +CALL p1(7, 11, @r); +SELECT @r; +DROP PROCEDURE p1; + +--error ER_LANGUAGE_COMPONENT +CREATE FUNCTION f2() RETURNS INT LANGUAGE JS AS $$ Syntax error ! $$; + +DELIMITER |; +CREATE PROCEDURE p2(OUT i INT, j INT, INOUT k INT) LANGUAGE JS AS $$ i = 5; k = k * j $$ | +DELIMITER ;| +SET @k := 7; +CALL p2(@i, 11, @k); +SELECT @i, @k; +DROP PROCEDURE p2; + +--echo +--echo # +--echo # Test that creation of routine with parameter name which is +--echo # valid in MySQL but is not valid JS identifier fails. +--echo # +--echo # Error messages might be cryptic at the moment. +--error ER_LANGUAGE_COMPONENT +CREATE FUNCTION f3(1param INT) RETURNS INT LANGUAGE JS AS $$ return 1 $$; +--error ER_LANGUAGE_COMPONENT +CREATE PROCEDURE p3(`123` INT) LANGUAGE JS AS $$ return $$; +--error ER_LANGUAGE_COMPONENT +CREATE FUNCTION f3(`for` INT) RETURNS INT LANGUAGE JS AS $$ return 1 $$; +--error ER_LANGUAGE_COMPONENT +CREATE PROCEDURE p3(throw INT) LANGUAGE JS AS $$ return $$; + + +--echo +--echo # +--echo # Test that returning values from procedures is not allowed. +--echo # +CREATE PROCEDURE p3(a INT) LANGUAGE JS AS $$ return 1 $$; +--error ER_LANGUAGE_COMPONENT +CALL p3(1); +DROP PROCEDURE p3; +--echo # However, return without value should be fine. +CREATE PROCEDURE p3(a INT) LANGUAGE JS AS $$ return $$; +CALL p3(2); +DROP PROCEDURE p3; + +--echo +--echo # +--echo # Test that 'strict' mode is enforced for our routines without +--echo # it being enabled it explicitly. +--echo # +--echo # Assigning to undeclared variables is banned in strict mode. +DELIMITER |; +CREATE FUNCTION f_strict() RETURNS INT LANGUAGE JS AS $$ + no_such_var = 1; + return 1; +$$ | +DELIMITER ;| +--error ER_LANGUAGE_COMPONENT +SELECT f_strict(); +DROP FUNCTION f_strict; + +CREATE PROCEDURE p_strict() LANGUAGE JS AS $$ no_such_var = 1 $$; +--error ER_LANGUAGE_COMPONENT +CALL p_strict(); +DROP PROCEDURE p_strict; + + +--echo +--echo # +--echo # Let us test how stored program parameters are converted to JS values. +--echo # + +--echo # +--echo # Let us start with numeric types. +--echo # + +# Create the unpacking iterator +--let $json_label = num_types +--let $json_keys = type, values, comment +--source include/create_json_unpacking_iterator.inc + +# Reset the iterator to the first element of this array. +let $json_array = [ + { + "type": "TINYINT", + "values": "null, 0, 1, -1, 127, -128" + }, + { + "type": "TINYINT UNSIGNED", + "values": "null, 0, 1, 255" + }, + { + "type": "SMALLINT", + "values": "null, 0, 1, -1, 32767, -32768" + }, + { + "type": "SMALLINT UNSIGNED", + "values": "null, 0, 1, 65535" + }, + { + "type": "MEDIUMINT", + "values": "null, 0, 1, -1, 8388607, -8388608" + }, + { + "type": "MEDIUMINT UNSIGNED", + "values": "null, 0, 1, 16777215" + }, + { + "type": "INT", + "values": "null, 0, 1, -1, 1073741823, -1073741824, 1073741824, -1073741825, -2147483648, 2147483647", + "comment": "-2^30 and 2^30-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit systems, on 64-bit systems these are -2^31 and 2^31-1." + }, + { + "type": "INT UNSIGNED", + "values": "null, 0, 1, 1073741823, 1073741824, 2147483647, 2147483648, 4294967295", + "comment": "2^30-1 is interesting because this is the border of V8 SMall Integer (SMI) optimization on 32-bit systems, on 64-bit systems this is 2^31-1." + }, + { + "type": "BIGINT", + "values": "null, 0, 1, -1, 1073741823, -1073741824, 1073741824, -1073741825, -2147483648, 2147483647, -2147483649, 2147483648, 9007199254740991, -9007199254740991, 9007199254740992,-9007199254740992, 9223372036854775806, -9223372036854775807", + "comment": "-2^30, 2^30-1 and -2^31, 2^31-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit and 64-bit systems. +/-2^53-1 are interesting becauses this is max/min safe integer representable as Numeric type (primitive) in JS." + }, + { + "type": "BIGINT UNSIGNED", + "values": "null, 0, 1, 1073741823, 1073741824, 2147483647, 2147483648, 4294967295, 4294967296, 9007199254740991, 9007199254740992, 18446744073709551615", + "comment": "2^30-1 and 2^31-1 are interesting because these are borders of V8 SMall Integer (SMI) optimization on 32-bit and 64-bit systems. 2^53-1 is interesting becauses this is max safe integer representable as Numeric type (primitive) in JS." + }, + { + "type": "FLOAT", + "values": "null, 0e0, 1e0, -1e0, 1e1, 5e-1, -3.4028234e+38, -1.1754943e-38, 1.1754943e-38, 3.4028234E+38", + "comment": "UNSIGNED attribute doesn't affect floating and fixed point type storage and is deprecated for them." + }, + { + "type": "DOUBLE", + "values": "null, 0e0, 1e0, -1e0, 1e1, 5e-1, -1.7976931348623157e+308, -2.2250738585072014e-308, 2.2250738585072014e-308, 1.7976931348623157e+308", + "comment": "UNSIGNED attribute doesn't affect floating and fixed point type storage and is deprecated for them." + }, + { + "type": "DECIMAL(10,5)", + "values": "null, 0.0, 1.0, -1.0, 10.0, 0.1, 1.23456, -1.23456, 99999.99999, -99999.99999", + "comment": "JS doesn't have primitive fixed precision type. So we convert such parameters to strings to avoid precision loss." + }, + { + "type": "BOOLEAN", + "values": "null, false, true, 0, 1, 100", + "comment": "BOOLEAN type is an alias for TINYINT at the moment and is indistinguishable from it." + } +]; + +--source $json_num_types_start +while (!$json_num_types_done) { + --eval CREATE FUNCTION f(arg $type) RETURNS VARCHAR(100) LANGUAGE JS AS \$\$ return '<' + arg + '> is ' + typeof arg; \$\$ + if ($comment) { + --echo # Note: $comment + } + --eval SELECT f(val) FROM JSON_TABLE('[$values]', '\$[*]' COLUMNS(val $type PATH '\$')) as v + DROP FUNCTION f; + + # Step forward to the next element. + --source $json_num_types_next +} + +# Clean up the generated files +--source include/destroy_json_functions.inc + +--echo +--echo # +--echo # Now let us test string types using different charsets. +--echo # +--echo # They are naturally mapped to JS strings (using Unicode). + +--let $str_types='CHAR(40),VARCHAR(50),TINYTEXT,TEXT,MEDIUMTEXT,LONGTEXT' +--let $charsets='utf8mb4,latin1,cp1251' +--let $literals="'Twas brillig, and the slithy toves'|X'496c20e974616974206772696c6865757265203b206c657320736c6963747565757820746f766573'|X'c1e520f1e3ebe0e4ede520e820f7e5f1f2ebe8edede8f2e520eaeeece1f3f0f1e8'" +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($str_types,',',$i),',',-1)` + --let $j= 1 + while($j <= 3) + { + --let $charset = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($charsets,',',$j),',',-1)` + --let $literal = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($literals,'|',$j),'|',-1)` + + --eval CREATE FUNCTION f(arg $type CHARACTER SET $charset) RETURNS VARCHAR(100) LANGUAGE JS AS \$\$ return '<' + arg + '> is ' + typeof arg; \$\$ + --eval SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(_$charset $literal) AS r; + DROP FUNCTION f; + --inc $j + } + --inc $i +} + +--echo +--echo # +--echo # Binary/BLOB types are mapped to TypedArray objects though. +--echo # +--echo # Notice the padding for BINARY(10)! +--let $bin_types='BINARY(10),VARBINARY(15),TINYBLOB,BLOB,MEDIUMBLOB,LONGBLOB' +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($bin_types,',',$i),',',-1)` + --echo # We do a bit of pretty-printing of DataView contents. + --eval CREATE FUNCTION f(arg $type) RETURNS VARCHAR(100) LANGUAGE JS AS \$\$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg); \$\$ + SELECT f(NULL) AS nil, f("") AS e, f("A") AS a, f(X'0001020304') AS r; + DROP FUNCTION f; + --inc $i +} + +--echo +--echo # +--echo # Test for various datetime types. +--echo # + +--echo +--echo # +--echo # YEAR parameters are mapped to integers. +--echo # +CREATE FUNCTION f(arg YEAR) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f('00') AS a, f('01') AS b, f('99') AS c, f('1901') AS d, f('2155') AS e; +DROP FUNCTION f; + +--echo +--echo # +--echo # TIME parameters are mapped to strings as JS doesn't have +--echo # corresponding type. +--echo # +CREATE FUNCTION f(arg TIME) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('0:0:0') AS z, f('-838:59:59') AS a, f('838:59:59') AS b, f('01:02:03') AS c; +DROP FUNCTION f; +--echo # +--echo # Also test TIME with fractional part. +--echo # +CREATE FUNCTION f(arg TIME(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('0:0:0.0') AS z, f('-838:59:59.000000') AS a, f('838:59:59.000000') AS b, f('01:02:03.999999') AS c; +DROP FUNCTION f; + +--echo +--echo # +--echo # DATETIME and TIMESTAMP parameters are mapped to strings as +--echo # TZ-related and API issues make their mapping to JS Date +--echo # not the best idea. +--echo # +CREATE FUNCTION f(arg DATETIME) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01 00:00:00') AS a, f('9999-12-31 23:59:59') AS b, f('2023-12-07 11:04:42') AS c; +DROP FUNCTION f; +CREATE FUNCTION f(arg DATETIME(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01 00:00:00.000000') AS a, f('9999-12-31 23:59:59.499999') AS b, f('2023-12-07 11:04:42.123456') AS c; +DROP FUNCTION f; +CREATE FUNCTION f(arg TIMESTAMP) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1970-01-01 03:00:01') AS a, f('2038-01-19 06:14:07') AS b, f('2023-12-07 11:04:42') AS c; +DROP FUNCTION f; +CREATE FUNCTION f(arg TIMESTAMP(6)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1970-01-01 03:00:01.000000') AS a, f('2038-01-19 06:14:07.499999') AS b, f('2023-12-07 11:04:42.123456') AS c; +DROP FUNCTION f; + +--echo +--echo # +--echo # DATE parameters are mapped to strings for the same reasons as well. +--echo # +CREATE FUNCTION f(arg DATE) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f('1000-01-01') AS a, f('9999-12-31') AS b, f('2023-12-07') AS c; +DROP FUNCTION f; + +--echo +--echo # +--echo # ENUM parameters are mapped to strings as well. +--echo # +CREATE FUNCTION f(arg ENUM('a', 'b', 'c')) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("a") AS a, f(2) AS t; +DROP FUNCTION f; +--echo # +--echo # We do charset conversions for ENUM as well. +--echo # +CREATE FUNCTION f(arg ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("Додо") AS Dodo, f(2) AS Tweedledum; +DROP FUNCTION f; + +--echo +--echo # +--echo # SET parameters are mapped to strings of comma-separated list of set +--echo # elements. +--echo # +CREATE FUNCTION f(arg SET('a', 'b', 'c')) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("a,b") AS ab, f("c") AS c, f(6) AS bc; +DROP FUNCTION f; +--echo # +--echo # Check SET with non-UTF8 charset as well. +--echo # +CREATE FUNCTION f(arg SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f("Додо") AS D, f("Туидълдъм,Туидълди") AS TT, f(5) AS TD; +DROP FUNCTION f; + +--echo +--echo # +--echo # BIT parameters with size <= 53 bits are mapped to integer JS Number +--echo # values. +--echo # +--echo # BIT parameters with size > 53 bits can't be safely represented as JS +--echo # Number values in generic case. So we map them to BigInt values. +--echo # +CREATE FUNCTION f(arg BIT(11)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS m; +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(53)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'1FFFFFFFFFFFFF') AS m; +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(54)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'3FFFFFFFFFFFFF') AS m; +DROP FUNCTION f; +CREATE FUNCTION f(arg BIT(64)) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil, f(0) AS z, f(b'1000000000') AS f, f(7) AS s, f(b'11111111111') AS b, f(x'FFFFFFFFFFFFFFFF') AS m; +DROP FUNCTION f; + + +--echo +--echo # +--echo # GEOMETRY arguments are mapped to JS DataView objects over +--echo # MySQL internal representation of this type. This internal +--echo # representation is documented and based on standard WKB format. +--echo # +CREATE FUNCTION f(arg GEOMETRY) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return (arg instanceof DataView) ? ('<' + new Uint8Array(arg.buffer) + '> is DataView') : ('<' + arg + '> is ' + typeof arg) $$; +SELECT f(NULL) AS nil, f(ST_GeomFromText('POINT(15 20)')) AS g; +DROP FUNCTION f; + +--echo +--echo # +--echo # JSON arguments are mapped to corresponding JS objects which +--echo # are constructed using JSON.parse() method. +--echo # +CREATE FUNCTION f(arg JSON) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return '<' + arg + '> is ' + typeof arg $$; +SELECT f(NULL) AS nil1, f("null") AS nil2, f("1") AS i, f("1.01") AS n, f('"alpha"') AS s, f("[1, 2, 3]") AS arr, f('{"a": 1, "b": "alpha"}') AS obj; +DROP FUNCTION f; + +--echo +--echo # +--echo # Additional test coverage for scenarios in which SQL to JS value +--echo # conversion fails can be found in js_lang_big.test. +--echo # + +--echo +--echo # +--echo # Now let us test how JS values are converted to SQL types for return +--echo # values. +--echo # + +--echo # +--echo # Let us start with string SQL types. +--echo # +--echo # Values of all JS types are converted to non-binary string SQL-types +--echo # using JS toString() conversion. +--echo # The exception are JS 'null' and 'undefined' values which are mapped +--echo # to SQL NULL. + +--let $str_types='CHAR(50),VARCHAR(60),TINYTEXT,TEXT,MEDIUMTEXT,LONGTEXT' +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($str_types,',',$i),',',-1)` + + --eval CREATE FUNCTION f_undefined() RETURNS $type LANGUAGE JS AS \$\$ return; \$\$ + --eval CREATE FUNCTION f_null() RETURNS $type LANGUAGE JS AS \$\$ return null; \$\$ + + --eval CREATE FUNCTION f_int() RETURNS $type LANGUAGE JS AS \$\$ return 1; \$\$ + --eval CREATE FUNCTION f_num() RETURNS $type LANGUAGE JS AS \$\$ return 1.25; \$\$ + --eval CREATE FUNCTION f_bigint() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(100); \$\$ + --eval CREATE FUNCTION f_bool() RETURNS $type LANGUAGE JS AS \$\$ return true; \$\$ + + --eval CREATE FUNCTION f_str_e() RETURNS $type LANGUAGE JS AS \$\$ return ""; \$\$ + --eval CREATE FUNCTION f_str_0() RETURNS $type LANGUAGE JS AS \$\$ return "alpha"; \$\$ + --eval CREATE FUNCTION f_str_1() RETURNS $type CHARACTER SET utf8mb4 LANGUAGE JS AS \$\$ return "Far over the misty mountains cold"; \$\$ + --eval CREATE FUNCTION f_str_2() RETURNS $type CHARACTER SET latin1 LANGUAGE JS AS \$\$ return "Au-delà des montagnes glaciales et embrumées"; \$\$ + --eval CREATE FUNCTION f_str_3() RETURNS $type CHARACTER SET cp1251 LANGUAGE JS AS \$\$ return "Там отвъд мъглявите студени планини"; \$\$ + --eval CREATE FUNCTION f_str_cerr() RETURNS $type CHARACTER SET cp1251 LANGUAGE JS AS \$\$ return "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; \$\$ + + --eval CREATE FUNCTION f_array() RETURNS $type LANGUAGE JS AS \$\$ return [1, 2, 3] \$\$ + --eval CREATE FUNCTION f_object() RETURNS $type LANGUAGE JS AS \$\$ return { x: 1, y: "alpha" } \$\$ + --eval CREATE FUNCTION f_func() RETURNS $type LANGUAGE JS AS \$\$ return function (a) { return 1;} \$\$ + + # Typed Arrays and DataView objects are interesting because they got + # special handling for binary string types. + --eval CREATE FUNCTION f_typed_arr() RETURNS $type LANGUAGE JS AS \$\$ return new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE FUNCTION f_data_view() RETURNS $type LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(3), 1, 1); dv.setUint8(0, 3); return dv \$\$ + + --eval CREATE FUNCTION f_object_serr() RETURNS $type LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + + SELECT f_undefined() AS u, f_null() AS nil; + SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; + SELECT f_str_e() AS se, f_str_0() AS s0, f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; + # Check that values which are returned are expected ones and in expected charset. + SELECT f_str_1() = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1, + f_str_2() = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2, + f_str_3() = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_str_cerr(); + + SELECT f_array() AS a, f_object() AS o, f_func() AS f; + SELECT f_typed_arr() AS ta, f_data_view() AS dv; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_serr(); + + DROP FUNCTION f_undefined; + DROP FUNCTION f_null; + DROP FUNCTION f_int; + DROP FUNCTION f_num; + DROP FUNCTION f_bigint; + DROP FUNCTION f_bool; + DROP FUNCTION f_str_e; + DROP FUNCTION f_str_0; + DROP FUNCTION f_str_1; + DROP FUNCTION f_str_2; + DROP FUNCTION f_str_3; + DROP FUNCTION f_str_cerr; + DROP FUNCTION f_array; + DROP FUNCTION f_object; + DROP FUNCTION f_func; + DROP FUNCTION f_typed_arr; + DROP FUNCTION f_data_view; + DROP FUNCTION f_object_serr; + + --inc $i +} + + +--echo +--echo # +--echo # For binary string/BLOB SQL-types in addition to 'null' and 'undefined' +--echo # values, ArrayBuffer-based objects are also getting special treatment. +--echo # All other JS values are converted using the same toString() approach +--echo # as for non-binary strings. + +--let $bin_types='BINARY(50),VARBINARY(60),TINYBLOB,BLOB,MEDIUMBLOB,LONGBLOB' +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($bin_types,',',$i),',',-1)` + + --eval CREATE FUNCTION f_undefined() RETURNS $type LANGUAGE JS AS \$\$ return; \$\$ + --eval CREATE FUNCTION f_null() RETURNS $type LANGUAGE JS AS \$\$ return null; \$\$ + + --eval CREATE FUNCTION f_int() RETURNS $type LANGUAGE JS AS \$\$ return 1; \$\$ + --eval CREATE FUNCTION f_num() RETURNS $type LANGUAGE JS AS \$\$ return 1.25; \$\$ + --eval CREATE FUNCTION f_bigint() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(100); \$\$ + --eval CREATE FUNCTION f_bool() RETURNS $type LANGUAGE JS AS \$\$ return true; \$\$ + + --eval CREATE FUNCTION f_str_e() RETURNS $type LANGUAGE JS AS \$\$ return ""; \$\$ + --eval CREATE FUNCTION f_str_a() RETURNS $type LANGUAGE JS AS \$\$ return "alpha"; \$\$ + + --eval CREATE FUNCTION f_array() RETURNS $type LANGUAGE JS AS \$\$ return [1, 2, 3] \$\$ + --eval CREATE FUNCTION f_object() RETURNS $type LANGUAGE JS AS \$\$ return { x: 1, y: "alpha" } \$\$ + --eval CREATE FUNCTION f_func() RETURNS $type LANGUAGE JS AS \$\$ return function (a) { return 1;} \$\$ + + --eval CREATE FUNCTION f_typed_arr() RETURNS $type LANGUAGE JS AS \$\$ return new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE FUNCTION f_data_view() RETURNS $type LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return dv \$\$ + --eval CREATE FUNCTION f_arr_buff() RETURNS $type LANGUAGE JS AS \$\$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); return ab \$\$ + + --eval CREATE FUNCTION f_object_serr() RETURNS $type LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + + SELECT f_undefined() AS u, f_null() AS nil; + if (`SELECT '$type' LIKE 'BINARY%'`) { + --echo # BINARY type does 0-padding so we use HEX to correctly print returned value. + SELECT HEX(f_int()) AS i, HEX(f_num()) AS num, HEX(f_bigint()) AS bi, HEX(f_bool()) AS bo; + SELECT HEX(f_str_e()) AS se, HEX(f_str_a()) AS sa; + SELECT HEX(f_array()) AS a, HEX(f_object()) AS o, HEX(f_func()) AS f; + } + if (!`SELECT '$type' LIKE 'BINARY%'`) { + SELECT f_int() AS i, f_num() AS num, f_bigint() AS bi, f_bool() AS bo; + SELECT f_str_e() AS se, f_str_a() AS sa; + SELECT f_array() AS a, f_object() AS o, f_func() AS f; + } + SELECT HEX(f_typed_arr()) AS ta, HEX(f_data_view()) AS dv, HEX(f_arr_buff()) AS ab; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_serr(); + + DROP FUNCTION f_undefined; + DROP FUNCTION f_null; + DROP FUNCTION f_int; + DROP FUNCTION f_num; + DROP FUNCTION f_bigint; + DROP FUNCTION f_bool; + DROP FUNCTION f_str_e; + DROP FUNCTION f_str_a; + DROP FUNCTION f_array; + DROP FUNCTION f_object; + DROP FUNCTION f_func; + DROP FUNCTION f_typed_arr; + DROP FUNCTION f_data_view; + DROP FUNCTION f_arr_buff; + DROP FUNCTION f_object_serr; + + --inc $i +} + + +--echo # +--echo # For integer SQL-types conversion works in the following way: +--echo # - JS 'null' and 'undefined' values which are mapped to SQL NULL. +--echo # - If JS numeric or BigInt value can be safely converted to SQL-type +--echo # we use direct conversion (for performance reasons). +--echo # - Otherwise, as well as for all other types of JS values conversion +--echo # is done through strings (i.e. by doing JS toString() conversion +--echo # and trying to store resulting string as SQL integer value). + +--echo +--echo # Maximum BIGINT value can't be represented as exact numeric in JS, +--echo # so we resort to using 2^53-1 (max safe in JS) instead and reduce +--echo # testing for it. + +--let $int_types='TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT' +--let $int_max='127,32767,8388607,2147483647,9007199254740991' +--let $i = 1 +while($i <= 5) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_types,',',$i),',',-1)` + --let $max = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_max,',',$i),',',-1)` + + --eval CREATE FUNCTION f_undefined() RETURNS $type LANGUAGE JS AS \$\$ return; \$\$ + --eval CREATE FUNCTION f_null() RETURNS $type LANGUAGE JS AS \$\$ return null; \$\$ + + --eval CREATE FUNCTION f_int_0() RETURNS $type LANGUAGE JS AS \$\$ return 0; \$\$ + --eval CREATE FUNCTION f_int_1() RETURNS $type LANGUAGE JS AS \$\$ return 1; \$\$ + --eval CREATE FUNCTION f_int_n() RETURNS $type LANGUAGE JS AS \$\$ return -1; \$\$ + --eval CREATE FUNCTION f_int_m() RETURNS $type LANGUAGE JS AS \$\$ return $max; \$\$ + --eval CREATE FUNCTION f_int_m1() RETURNS $type LANGUAGE JS AS \$\$ return $max + 1; \$\$ + --eval CREATE FUNCTION f_int_nm() RETURNS $type LANGUAGE JS AS \$\$ return -$max - 1; \$\$ + --eval CREATE FUNCTION f_int_nm1() RETURNS $type LANGUAGE JS AS \$\$ return -$max - 2; \$\$ + --eval CREATE FUNCTION f_uint_m() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return 2*$max + 1; \$\$ + --eval CREATE FUNCTION f_uint_m1() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return 2*$max + 2; \$\$ + --eval CREATE FUNCTION f_uint_n() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return - 1; \$\$ + + --eval CREATE FUNCTION f_num_1() RETURNS $type LANGUAGE JS AS \$\$ return 1.25; \$\$ + --eval CREATE FUNCTION f_num_2() RETURNS $type LANGUAGE JS AS \$\$ return 5e-1; \$\$ + --eval CREATE FUNCTION f_num_3() RETURNS $type LANGUAGE JS AS \$\$ return 5e-2; \$\$ + --eval CREATE FUNCTION f_num_4() RETURNS $type LANGUAGE JS AS \$\$ return 1.2345e+2; \$\$ + --eval CREATE FUNCTION f_num_5() RETURNS $type LANGUAGE JS AS \$\$ return -1.2345e+1; \$\$ + --eval CREATE FUNCTION f_num_r1() RETURNS $type LANGUAGE JS AS \$\$ return 4.5; \$\$ + --eval CREATE FUNCTION f_num_r2() RETURNS $type LANGUAGE JS AS \$\$ return 4.5e+0; \$\$ + --eval CREATE FUNCTION f_num_un() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return -1.5; \$\$ + --eval CREATE FUNCTION f_num_tb() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return 1e+70; \$\$ + + --eval CREATE FUNCTION f_bigint() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(100); \$\$ + --eval CREATE FUNCTION f_bigint_n() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(-42); \$\$ + --eval CREATE FUNCTION f_bigint_u() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return BigInt(9007199254740991); \$\$ + --eval CREATE FUNCTION f_bigint_un() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return BigInt(-42); \$\$ + --eval CREATE FUNCTION f_bigint_tb() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(1e+25); \$\$ + + --eval CREATE FUNCTION f_bool() RETURNS $type LANGUAGE JS AS \$\$ return true; \$\$ + + --eval CREATE FUNCTION f_str_e() RETURNS $type LANGUAGE JS AS \$\$ return ""; \$\$ + --eval CREATE FUNCTION f_str_a() RETURNS $type LANGUAGE JS AS \$\$ return "alpha"; \$\$ + --eval CREATE FUNCTION f_str_n1() RETURNS $type LANGUAGE JS AS \$\$ return "123"; \$\$ + --eval CREATE FUNCTION f_str_n2() RETURNS $type LANGUAGE JS AS \$\$ return "-2"; \$\$ + --eval CREATE FUNCTION f_str_n3() RETURNS $type LANGUAGE JS AS \$\$ return "12.65"; \$\$ + --eval CREATE FUNCTION f_str_nu() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return "-1"; \$\$ + --eval CREATE FUNCTION f_str_nr1() RETURNS $type LANGUAGE JS AS \$\$ return "4.5"; \$\$ + --eval CREATE FUNCTION f_str_nr2() RETURNS $type LANGUAGE JS AS \$\$ return "4.5e+0"; \$\$ + --eval CREATE FUNCTION f_str_tb1() RETURNS $type LANGUAGE JS AS \$\$ return "1e+25"; \$\$ + --eval CREATE FUNCTION f_str_tb2() RETURNS $type LANGUAGE JS AS \$\$ return "18446744073709551616"; \$\$ + + --eval CREATE FUNCTION f_array() RETURNS $type LANGUAGE JS AS \$\$ return [1, 2, 3] \$\$ + --eval CREATE FUNCTION f_object() RETURNS $type LANGUAGE JS AS \$\$ return { x: 1, y: "alpha" } \$\$ + --eval CREATE FUNCTION f_func() RETURNS $type LANGUAGE JS AS \$\$ return function (a) { return 1;} \$\$ + + --eval CREATE FUNCTION f_typed_arr() RETURNS $type LANGUAGE JS AS \$\$ return new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE FUNCTION f_data_view() RETURNS $type LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv \$\$ + + --eval CREATE FUNCTION f_object_serr() RETURNS $type LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + --eval CREATE FUNCTION f_object_userr() RETURNS $type UNSIGNED LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + + SELECT f_undefined() AS u, f_null() AS nil; + SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_int_m() AS im, f_int_nm() AS nm, f_uint_m() AS um; + if (!`SELECT '$type' LIKE 'BIGINT'`) { + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_int_m1(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_int_nm1(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_uint_m1(); + } + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_uint_n(); + + --echo # When MySQL converts string value with a floating point number to + --echo # an integer, it converts string to floating point value first and + --echo # then converts it to integer with rounding. + SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4, f_num_5(); + + --echo # MySQL rounds floating-point values differently than decimal values, + --echo # floating-point values in strings and decimal values as string when + --echo # storing them as integer (for floating-point values rint() rounding + --echo # is used, while other use round() style rounding). + --echo # + --echo # We try to avoid the confusion and stick to round()-style + --echo # rounding in all cases. + SELECT f_num_r1() AS r1, f_num_r2() AS r2; + + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_num_un(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_num_tb(); + + SELECT f_bigint() AS bi, f_bigint_n() AS bn; + if (`SELECT '$type' LIKE 'BIGINT'`) { + SELECT f_bigint_u() AS bu; + } + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_bigint_un(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_bigint_tb(); + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_bool() AS bo; + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_str_e() AS se; + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_str_a() AS sa; + SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; + SELECT f_str_nr1() AS nr1, f_str_nr2() AS nr2; + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_str_nu(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_str_tb1(); + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_str_tb2(); + + --error 1265 + SELECT f_array() AS a; + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_object() AS o; + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_func() AS f; + --error 1265 + SELECT f_typed_arr() AS ta; + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + SELECT f_data_view() AS dv; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_serr() AS serr; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_userr() AS userr; + + DROP FUNCTION f_undefined; + DROP FUNCTION f_null; + DROP FUNCTION f_int_0; + DROP FUNCTION f_int_1; + DROP FUNCTION f_int_n; + DROP FUNCTION f_int_m; + DROP FUNCTION f_int_m1; + DROP FUNCTION f_int_nm; + DROP FUNCTION f_int_nm1; + DROP FUNCTION f_uint_m; + DROP FUNCTION f_uint_m1; + DROP FUNCTION f_uint_n; + DROP FUNCTION f_num_1; + DROP FUNCTION f_num_2; + DROP FUNCTION f_num_3; + DROP FUNCTION f_num_4; + DROP FUNCTION f_num_5; + DROP FUNCTION f_num_r1; + DROP FUNCTION f_num_r2; + DROP FUNCTION f_num_un; + DROP FUNCTION f_num_tb; + DROP FUNCTION f_bigint; + DROP FUNCTION f_bigint_n; + DROP FUNCTION f_bigint_u; + DROP FUNCTION f_bigint_un; + DROP FUNCTION f_bigint_tb; + DROP FUNCTION f_bool; + DROP FUNCTION f_str_e; + DROP FUNCTION f_str_a; + DROP FUNCTION f_str_n1; + DROP FUNCTION f_str_n2; + DROP FUNCTION f_str_n3; + DROP FUNCTION f_str_nu; + DROP FUNCTION f_str_nr1; + DROP FUNCTION f_str_nr2; + DROP FUNCTION f_str_tb1; + DROP FUNCTION f_str_tb2; + DROP FUNCTION f_array; + DROP FUNCTION f_object; + DROP FUNCTION f_func; + DROP FUNCTION f_typed_arr; + DROP FUNCTION f_data_view; + DROP FUNCTION f_object_serr; + DROP FUNCTION f_object_userr; + + --inc $i +} + + +--echo # +--echo # For floating point SQL-types conversion works in similar way: +--echo # - JS 'null' and 'undefined' values which are mapped to SQL NULL. +--echo # - If JS numeric value can be safely converted to SQL-type we use +--echo # direct conversion (for performance reasons). +--echo # - Otherwise, as well as for all other types of JS values conversion +--echo # is done through strings (i.e. by doing JS toString() conversion +--echo # and trying to store resulting string as SQL floating-point value). + +--let $real_types='FLOAT;DOUBLE' +--let $real_max='3.4028234e+38,1.7976931348623157e+308' +--let $i = 1 +while($i <= 2) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($real_types,';',$i),';',-1)` + --let $max = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($real_max,',',$i),',',-1)` + + --eval CREATE FUNCTION f_undefined() RETURNS $type LANGUAGE JS AS \$\$ return; \$\$ + --eval CREATE FUNCTION f_null() RETURNS $type LANGUAGE JS AS \$\$ return null; \$\$ + + --eval CREATE FUNCTION f_int_0() RETURNS $type LANGUAGE JS AS \$\$ return 0; \$\$ + --eval CREATE FUNCTION f_int_1() RETURNS $type LANGUAGE JS AS \$\$ return 1; \$\$ + --eval CREATE FUNCTION f_int_n() RETURNS $type LANGUAGE JS AS \$\$ return -1; \$\$ + + --eval CREATE FUNCTION f_num_1() RETURNS $type LANGUAGE JS AS \$\$ return 1.25; \$\$ + --eval CREATE FUNCTION f_num_2() RETURNS $type LANGUAGE JS AS \$\$ return 5e-1; \$\$ + --eval CREATE FUNCTION f_num_3() RETURNS $type LANGUAGE JS AS \$\$ return -5e-2; \$\$ + --eval CREATE FUNCTION f_num_m() RETURNS $type LANGUAGE JS AS \$\$ return $max; \$\$ + + --eval CREATE FUNCTION f_bigint() RETURNS $type LANGUAGE JS AS \$\$ return BigInt(100); \$\$ + # Value below can't be represented exactly as a single nor as a double precision floating-point + --eval CREATE FUNCTION f_bigint_pl() RETURNS $type LANGUAGE JS AS \$\$ return BigInt("36028797018963967"); \$\$ + + --eval CREATE FUNCTION f_bool() RETURNS $type LANGUAGE JS AS \$\$ return true; \$\$ + + --eval CREATE FUNCTION f_str_e() RETURNS $type LANGUAGE JS AS \$\$ return ""; \$\$ + --eval CREATE FUNCTION f_str_a() RETURNS $type LANGUAGE JS AS \$\$ return "alpha"; \$\$ + --eval CREATE FUNCTION f_str_n1() RETURNS $type LANGUAGE JS AS \$\$ return "123"; \$\$ + --eval CREATE FUNCTION f_str_n2() RETURNS $type LANGUAGE JS AS \$\$ return "12.65"; \$\$ + # Value below can't be represented exactly as a single nor as a double precision floating-point + --eval CREATE FUNCTION f_str_n_pl() RETURNS $type LANGUAGE JS AS \$\$ return "36028797018963967"; \$\$ + --eval CREATE FUNCTION f_str_n_tb() RETURNS $type LANGUAGE JS AS \$\$ return "1.1e+400"; \$\$ + + --eval CREATE FUNCTION f_array() RETURNS $type LANGUAGE JS AS \$\$ return [1, 2, 3] \$\$ + --eval CREATE FUNCTION f_object() RETURNS $type LANGUAGE JS AS \$\$ return { x: 1, y: "alpha" } \$\$ + --eval CREATE FUNCTION f_func() RETURNS $type LANGUAGE JS AS \$\$ return function (a) { return 1;} \$\$ + + --eval CREATE FUNCTION f_typed_arr() RETURNS $type LANGUAGE JS AS \$\$ return new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE FUNCTION f_data_view() RETURNS $type LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv \$\$ + + --eval CREATE FUNCTION f_object_serr() RETURNS $type LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + + SELECT f_undefined() AS u, f_null() AS nil; + + SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n, f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_m() AS nm; + + SELECT f_bigint() AS bi; + SELECT f_bigint_pl() AS bipl; + + --error 1265 + SELECT f_bool() AS bo; + + --error 1265 + SELECT f_str_e() AS se; + --error 1265 + SELECT f_str_a() AS sa; + SELECT f_str_n1() AS n1, f_str_n2() AS n2; + SELECT f_str_n_pl() AS spl; + --error ER_WARN_DATA_OUT_OF_RANGE + SELECT f_str_n_tb() AS stb; + + --error 1265 + SELECT f_array() AS a; + --error 1265 + SELECT f_object() AS o; + --error 1265 + SELECT f_func() AS f; + --error 1265 + SELECT f_typed_arr() AS ta; + --error 1265 + SELECT f_data_view() AS dv; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_serr() AS serr; + + DROP FUNCTION f_undefined; + DROP FUNCTION f_null; + DROP FUNCTION f_int_0; + DROP FUNCTION f_int_1; + DROP FUNCTION f_int_n; + DROP FUNCTION f_num_1; + DROP FUNCTION f_num_2; + DROP FUNCTION f_num_3; + DROP FUNCTION f_num_m; + DROP FUNCTION f_bigint; + DROP FUNCTION f_bigint_pl; + DROP FUNCTION f_bool; + DROP FUNCTION f_str_e; + DROP FUNCTION f_str_a; + DROP FUNCTION f_str_n1; + DROP FUNCTION f_str_n2; + DROP FUNCTION f_str_n_pl; + DROP FUNCTION f_str_n_tb; + DROP FUNCTION f_array; + DROP FUNCTION f_object; + DROP FUNCTION f_func; + DROP FUNCTION f_typed_arr; + DROP FUNCTION f_data_view; + DROP FUNCTION f_object_serr; + + --inc $i +} + +--echo # +--echo # For DECIMAL SQL-type conversion is done through strings (by doing JS +--echo # toString() conversion and trying to store resulting string as SQL +--echo # type value). There is no point in optimizing conversion from Number +--echo # type, like it is done for floating point SQL-types, as SQL core does +--echo # double -> DECIMAL conversions through strings. As usual JS 'null' +--echo # and 'undefined' values are mapped to SQL NULL. +--echo # + +CREATE FUNCTION f_undefined() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int_0() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_int_n() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_int_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 100 $$; + +CREATE FUNCTION f_num_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 5e-1 $$; +CREATE FUNCTION f_num_3() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return -5e-2 $$; +CREATE FUNCTION f_num_m() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 99.99 $$; +CREATE FUNCTION f_num_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 100.01 $$; +CREATE FUNCTION f_num_tl() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return 0.0125 $$; + +CREATE FUNCTION f_bigint_1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return BigInt(10) $$; +CREATE FUNCTION f_bigint_2() RETURNS DECIMAL(20,2) LANGUAGE JS AS $$ return BigInt("10000000000000000") $$; +CREATE FUNCTION f_bigint_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return BigInt(1000) $$; + +CREATE FUNCTION f_bool() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_n1() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12" $$; +CREATE FUNCTION f_str_n2() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12.65" $$; +CREATE FUNCTION f_str_n3() RETURNS DECIMAL(20,2) LANGUAGE JS AS $$ return "10000000000000000.12" $$; +CREATE FUNCTION f_str_tb() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "123" $$; +CREATE FUNCTION f_str_tl() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "12.324" $$; +CREATE FUNCTION f_str_api() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return "π" $$; + +CREATE FUNCTION f_array() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return function (a) { return 1} $$; + +CREATE FUNCTION f_object_serr() RETURNS DECIMAL(4,2) LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; + +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_n() AS n; + +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_int_tb() AS itb; + +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_m() AS nm; + +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_num_tb() AS ntb; + +SELECT f_num_tl() AS ntl; + +SELECT f_bigint_1() AS bi1, f_bigint_2() AS bi2; + +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_bigint_tb() AS bitb; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_bool() AS bo; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_str_e() AS se; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_str_a() AS sa; + +SELECT f_str_n1() AS n1, f_str_n2() AS n2, f_str_n3() AS n3; + +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_str_tb() AS stb; + +SELECT f_str_tl() AS stl; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_str_api() AS sapi; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_array() AS a; +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_object() AS o; +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_func() AS f; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_serr(); + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_m; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_num_tl; +DROP FUNCTION f_bigint_1; +DROP FUNCTION f_bigint_2; +DROP FUNCTION f_bigint_tb; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_n1; +DROP FUNCTION f_str_n2; +DROP FUNCTION f_str_n3; +DROP FUNCTION f_str_tb; +DROP FUNCTION f_str_tl; +DROP FUNCTION f_str_api; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; + + +--echo # +--echo # YEAR return type is handled similarly to integer types. +--echo # +CREATE FUNCTION f_undefined() RETURNS YEAR LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS YEAR LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int_0() RETURNS YEAR LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS YEAR LANGUAGE JS AS $$ return 7 $$; +CREATE FUNCTION f_int_2() RETURNS YEAR LANGUAGE JS AS $$ return 69 $$; +CREATE FUNCTION f_int_3() RETURNS YEAR LANGUAGE JS AS $$ return 70 $$; +CREATE FUNCTION f_int_o() RETURNS YEAR LANGUAGE JS AS $$ return 123 $$; +CREATE FUNCTION f_int_mi() RETURNS YEAR LANGUAGE JS AS $$ return 1901 $$; +CREATE FUNCTION f_int_mx() RETURNS YEAR LANGUAGE JS AS $$ return 2155 $$; +CREATE FUNCTION f_int_mi1() RETURNS YEAR LANGUAGE JS AS $$ return 1900 $$; +CREATE FUNCTION f_int_mx1() RETURNS YEAR LANGUAGE JS AS $$ return 2156 $$; +CREATE FUNCTION f_int_n() RETURNS YEAR LANGUAGE JS AS $$ return -1 $$; + +CREATE FUNCTION f_num_1() RETURNS YEAR LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS YEAR LANGUAGE JS AS $$ return 5e-1 $$; +CREATE FUNCTION f_num_3() RETURNS YEAR LANGUAGE JS AS $$ return 5e+1 $$; +CREATE FUNCTION f_num_4() RETURNS YEAR LANGUAGE JS AS $$ return 1.901e+3 $$; +CREATE FUNCTION f_num_tb() RETURNS YEAR LANGUAGE JS AS $$ return 2.2e+3 $$; + +CREATE FUNCTION f_bigint() RETURNS YEAR LANGUAGE JS AS $$ return BigInt(70) $$; + +CREATE FUNCTION f_bool() RETURNS YEAR LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS YEAR LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS YEAR LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_1() RETURNS YEAR LANGUAGE JS AS $$ return "12" $$; +CREATE FUNCTION f_str_2() RETURNS YEAR LANGUAGE JS AS $$ return "75" $$; +CREATE FUNCTION f_str_3() RETURNS YEAR LANGUAGE JS AS $$ return "7.5" $$; +CREATE FUNCTION f_str_o() RETURNS YEAR LANGUAGE JS AS $$ return "100" $$; +CREATE FUNCTION f_str_mi() RETURNS YEAR LANGUAGE JS AS $$ return"1901" $$; +CREATE FUNCTION f_str_mx() RETURNS YEAR LANGUAGE JS AS $$ return"2155" $$; +CREATE FUNCTION f_str_n() RETURNS YEAR LANGUAGE JS AS $$ return "-1" $$; +CREATE FUNCTION f_str_mi1() RETURNS YEAR LANGUAGE JS AS $$ return"1900" $$; +CREATE FUNCTION f_str_mx1() RETURNS YEAR LANGUAGE JS AS $$ return"2156" $$; + +CREATE FUNCTION f_array() RETURNS YEAR LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS YEAR LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS YEAR LANGUAGE JS AS $$ return function (a) { return 1} $$; + +CREATE FUNCTION f_object_serr() RETURNS YEAR LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; + +SELECT f_int_0() AS i0, f_int_1() AS i1, f_int_2() AS i2, f_int_3() AS i3; +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_int_o() AS o; +SELECT f_int_mi() AS mi, f_int_mx() AS mx; +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_int_mi1(); +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_int_mx1(); +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_int_n(); + +SELECT f_num_1() AS n1, f_num_2() AS n2, f_num_3() AS n3, f_num_4() AS n4; +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_num_tb(); + +SELECT f_bigint() AS bi; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_bool(); + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_str_e(); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_str_a(); +SELECT f_str_1() AS s1, f_str_2() AS s2, f_str_3() AS s3; +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_str_o(); +SELECT f_str_mi() AS mi, f_str_mx() AS mx; +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_str_n(); +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_str_mi1(); +--error ER_WARN_DATA_OUT_OF_RANGE +SELECT f_str_mx1(); + +--error 1265 +SELECT f_array() AS a; +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_object() AS o; +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +SELECT f_func() AS f; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_serr() AS serr; + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_2; +DROP FUNCTION f_int_3; +DROP FUNCTION f_int_o; +DROP FUNCTION f_int_mi; +DROP FUNCTION f_int_mx; +DROP FUNCTION f_int_mi1; +DROP FUNCTION f_int_mx1; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_3; +DROP FUNCTION f_num_4; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_o; +DROP FUNCTION f_str_mi; +DROP FUNCTION f_str_mx; +DROP FUNCTION f_str_n; +DROP FUNCTION f_str_mi1; +DROP FUNCTION f_str_mx1; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; + + +--echo # +--echo # For other datetime SQL-types conversion is done through strings +--echo # (by doing JS toString() conversion and trying to store resulting +--echo # string as SQL type value). As usual JS 'null' and 'undefined' +--echo # values which are mapped to SQL NULL. +--echo # + +--let $dt_types='DATE;TIME;TIME(1);DATETIME;DATETIME(2);TIMESTAMP;TIMESTAMP(4)' +--let $int_vals='20200101,11,11,20200102030405,20200102030405,20231222102000,20231222102000' +--let $int_mis='10000101,-8385959,-8385959,10000101000000,10000101000000,19700101030001,19700101030001' +--let $int_mxs='99991231,8385959,8385959,99991231235959,99991231235959,20380119061407,20380119061407' +--let $num_vals='2.0200101e+7,11.1,11.1,20200102030405.06,20200102030405.06,20231222102000.0123,20231222102000.0123' +--let $str_vals='2020-01-01,00:11,00:11.1,2020-01-02 03:04:05,2020-01-02 03:04:05.06,2023-12-22 10:20:00,2023-12-22 10:20:00.1234' +--let $bad_vals='2020-13-01,00:65,01:71.1,2020-01-32 03:04:05,2020-01-02 25:04:05.06,2023-12-22 10:71:00,2023-12-22 10:20:63.1234' +--let $i = 1 +while($i <= 7) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($dt_types,';',$i),';',-1)` + --let $i_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_vals,',',$i),',',-1)` + --let $i_mi = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_mis,',',$i),',',-1)` + --let $i_mx = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_mxs,',',$i),',',-1)` + --let $n_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($num_vals,',',$i),',',-1)` + --let $s_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($str_vals,',',$i),',',-1)` + --let $b_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($bad_vals,',',$i),',',-1)` + + --eval CREATE FUNCTION f_undefined() RETURNS $type LANGUAGE JS AS \$\$ return; \$\$ + --eval CREATE FUNCTION f_null() RETURNS $type LANGUAGE JS AS \$\$ return null; \$\$ + + --eval CREATE FUNCTION f_int_0() RETURNS $type LANGUAGE JS AS \$\$ return 0; \$\$ + --eval CREATE FUNCTION f_int_1() RETURNS $type LANGUAGE JS AS \$\$ return $i_val; \$\$ + --eval CREATE FUNCTION f_int_mi() RETURNS $type LANGUAGE JS AS \$\$ return $i_mi; \$\$ + --eval CREATE FUNCTION f_int_mx() RETURNS $type LANGUAGE JS AS \$\$ return $i_mx; \$\$ + --eval CREATE FUNCTION f_int_n() RETURNS $type LANGUAGE JS AS \$\$ return -1; \$\$ + --eval CREATE FUNCTION f_int_mi1() RETURNS $type LANGUAGE JS AS \$\$ return $i_mi - 1; \$\$ + --eval CREATE FUNCTION f_int_mx1() RETURNS $type LANGUAGE JS AS \$\$ return $i_mx + 1; \$\$ + + --eval CREATE FUNCTION f_num() RETURNS $type LANGUAGE JS AS \$\$ return $n_val; \$\$ + + --eval CREATE FUNCTION f_bigint() RETURNS $type LANGUAGE JS AS \$\$ return BigInt($i_val); \$\$ + + --eval CREATE FUNCTION f_bool() RETURNS $type LANGUAGE JS AS \$\$ return true; \$\$ + + --eval CREATE FUNCTION f_str_e() RETURNS $type LANGUAGE JS AS \$\$ return ""; \$\$ + --eval CREATE FUNCTION f_str_a() RETURNS $type LANGUAGE JS AS \$\$ return "alpha"; \$\$ + --eval CREATE FUNCTION f_str_1() RETURNS $type LANGUAGE JS AS \$\$ return "$s_val"; \$\$ + --eval CREATE FUNCTION f_str_b() RETURNS $type LANGUAGE JS AS \$\$ return "$b_val"; \$\$ + --eval CREATE FUNCTION f_str_ax() RETURNS $type LANGUAGE JS AS \$\$ return "\u{1F384}"; \$\$ + + --eval CREATE FUNCTION f_date() RETURNS $type LANGUAGE JS AS \$\$ return new Date(2023,11,22,13,0,0,123) \$\$ + + --eval CREATE FUNCTION f_array() RETURNS $type LANGUAGE JS AS \$\$ return [1, 2, 3] \$\$ + --eval CREATE FUNCTION f_object() RETURNS $type LANGUAGE JS AS \$\$ return { x: 1, y: "alpha" } \$\$ + --eval CREATE FUNCTION f_func() RETURNS $type LANGUAGE JS AS \$\$ return function (a) { return 1;} \$\$ + + --eval CREATE FUNCTION f_typed_arr() RETURNS $type LANGUAGE JS AS \$\$ return new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE FUNCTION f_data_view() RETURNS $type LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); return dv \$\$ + + --eval CREATE FUNCTION f_object_serr() RETURNS $type LANGUAGE JS AS \$\$ return { toString() { throw "Kaboom!" } } \$\$ + + SELECT f_undefined() AS u, f_null() AS nil; + + if (`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --echo # 0 is valid value for TIME type. + SELECT f_int_0(); + } + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_int_0(); + } + SELECT f_int_1() AS i1, f_int_mi() AS imi, f_int_mx() AS imx; + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_int_n(); + } + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_int_mi1(); + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_int_mx1(); + + SELECT f_num() AS n; + + SELECT f_bigint() AS bi; + + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_bool(); + + if (`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --echo # Empty string is converted to 0 for TIME type. + SELECT f_str_e() AS se; + } + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_str_e() AS se; + } + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_str_a(); + SELECT f_str_1() AS s1; + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_str_b(); + + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_str_ax(); + + --echo # Direct string representation of Date type is not compatible with + --echo # MySQL datetime values. + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_date() AS d; + + if (!`SELECT '$type' LIKE 'DATE%'`) { + --echo # DATE and DATETIME types accept weird literals. + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_array() AS a; + } + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_object() AS o; + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_func() AS f; + if (!`SELECT '$type' LIKE 'DATE%'`) { + --echo # DATE and DATETIME types accept weird literals. + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_typed_arr() AS ta; + } + --error ER_TRUNCATED_WRONG_VALUE + SELECT f_data_view() AS dv; + + --error ER_LANGUAGE_COMPONENT + SELECT f_object_serr(); + + DROP FUNCTION f_undefined; + DROP FUNCTION f_null; + DROP FUNCTION f_int_0; + DROP FUNCTION f_int_1; + DROP FUNCTION f_int_mi; + DROP FUNCTION f_int_mx; + DROP FUNCTION f_int_n; + DROP FUNCTION f_int_mi1; + DROP FUNCTION f_int_mx1; + DROP FUNCTION f_num; + DROP FUNCTION f_bigint; + DROP FUNCTION f_bool; + DROP FUNCTION f_str_e; + DROP FUNCTION f_str_a; + DROP FUNCTION f_str_1; + DROP FUNCTION f_str_b; + DROP FUNCTION f_str_ax; + DROP FUNCTION f_date; + DROP FUNCTION f_array; + DROP FUNCTION f_object; + DROP FUNCTION f_func; + DROP FUNCTION f_typed_arr; + DROP FUNCTION f_data_view; + DROP FUNCTION f_object_serr; + + --inc $i +} + + +--echo # +--echo # ENUM return type is handled similarly to string types. +--echo # +CREATE FUNCTION f_undefined() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_bigint() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return BigInt(100) $$; +CREATE FUNCTION f_bool() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_0() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "a" $$; +CREATE FUNCTION f_str_1() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_2() RETURNS ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 + LANGUAGE JS AS $$ return "Додо" $$; +CREATE FUNCTION f_str_cerr() RETURNS ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 + LANGUAGE JS AS $$ return "\u{1F9A4}" $$; + +CREATE FUNCTION f_array() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return function (a) { return 1 } $$; + +CREATE FUNCTION f_object_serr() RETURNS ENUM('a','b') LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; + +SELECT f_int() AS i; + +--error 1265 +SELECT f_num() AS num; +--error 1265 +SELECT f_bigint() AS bi; +--error 1265 +SELECT f_bool() AS bo; + +--error 1265 +SELECT f_str_e() AS se; +SELECT f_str_0() AS s0; +# Check that function returns expected value and in correct charset. +SELECT f_str_2() = X'c4eee4ee' AS s2; +--error 1265 +SELECT f_str_1() AS s1; +--error 1265 +SELECT f_str_cerr(); + +--error 1265 +SELECT f_array() AS a; +--error 1265 +SELECT f_object() AS o; +--error 1265 +SELECT f_func() AS f; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_serr(); + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; + + +--echo # +--echo # SET return type is handled similarly to how SQL core interprets +--echo # integer/floating-point values and strings which are stored in +--echo # SET columns. +--echo # +--echo # Numeric values are converted to integers and treated as bitmaps +--echo # representing sets. Strings are expected to contain comma-separated +--echo # lists of SET elements. Additionally strings containing integer +--echo # values are interpreted as bitmaps. +CREATE FUNCTION f_undefined() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_int_tb() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 10 $$; +CREATE FUNCTION f_int_n() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return -1 $$; +CREATE FUNCTION f_num() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return 1.6 $$; + +CREATE FUNCTION f_bigint() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return BigInt(4) $$; + +CREATE FUNCTION f_bool() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_0() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "a" $$; +CREATE FUNCTION f_str_1() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "b,c" $$; +CREATE FUNCTION f_str_2() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "3" $$; +CREATE FUNCTION f_str_3() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "1.5" $$; +CREATE FUNCTION f_str_n() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "-3" $$; +CREATE FUNCTION f_str_w() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_4() RETURNS SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 + LANGUAGE JS AS $$ return "Додо" $$; + +CREATE FUNCTION f_str_cerr() RETURNS SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251 + LANGUAGE JS AS $$ return "\u{1F9A4}" $$; + +CREATE FUNCTION f_array() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return function (a) { return 1 } $$; + +CREATE FUNCTION f_object_serr() RETURNS SET('a','b','c') LANGUAGE JS AS $$ return { toString() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; + +SELECT f_int() AS i; +--error 1265 +SELECT f_int_tb(); +--error 1265 +SELECT f_int_n(); + +SELECT f_num() AS n; + +SELECT f_bigint() AS bi; +--error 1265 +SELECT f_bool(); + +SELECT f_str_e() AS se; +SELECT f_str_0() AS s0; +SELECT f_str_1() AS s1; +SELECT f_str_2() AS s2; +--echo # SQL core doesn't handle non-integer numbers represented as strings +--echo # and doubles stored in SET columns consistently either. +--error 1265 +SELECT f_str_3(); +--error 1265 +SELECT f_str_n(); +--error 1265 +SELECT f_str_w(); +SELECT f_str_4() = X'c4eee4ee' AS s; + +--error 1265 +SELECT f_str_cerr() AS cerr; + +--error 1265 +SELECT f_array() AS a; +--error 1265 +SELECT f_object() AS o; +--error 1265 +SELECT f_func() AS f; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_serr() AS serr; + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_0; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_3; +DROP FUNCTION f_str_n; +DROP FUNCTION f_str_w; +DROP FUNCTION f_str_4; +DROP FUNCTION f_str_cerr; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_serr; + + +--echo # +--echo # BIT return type is handled in a special way. +--echo # +--echo # JS values are converted to numbers (ultimately integers) and then +--echo # their binary representation is interpreted as array of bits. +--echo # +--echo # We do not support JS values which are not convertible to numbers to +--echo # avoid confusion caused by different interpretation of 1, "1" and "a". +--echo # +CREATE FUNCTION f_undefined() RETURNS BIT(5) LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS BIT(5) LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int_0() RETURNS BIT(5) LANGUAGE JS AS $$ return 0 $$; +CREATE FUNCTION f_int_1() RETURNS BIT(5) LANGUAGE JS AS $$ return 7 $$; +CREATE FUNCTION f_int_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return 33 $$; +CREATE FUNCTION f_int_n() RETURNS BIT(5) LANGUAGE JS AS $$ return -1 $$; + +CREATE FUNCTION f_num_1() RETURNS BIT(5) LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_num_2() RETURNS BIT(5) LANGUAGE JS AS $$ return 7.8 $$; +CREATE FUNCTION f_num_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return 32.5 $$; +CREATE FUNCTION f_num_n() RETURNS BIT(5) LANGUAGE JS AS $$ return -1.2 $$; + +CREATE FUNCTION f_bigint() RETURNS BIT(5) LANGUAGE JS AS $$ return BigInt(5) $$; +CREATE FUNCTION f_bigint_n() RETURNS BIT(5) LANGUAGE JS AS $$ return BigInt(-1) $$; + +CREATE FUNCTION f_bool() RETURNS BIT(5) LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS BIT(5) LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS BIT(5) LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_str_1() RETURNS BIT(7) LANGUAGE JS AS $$ return "0" $$; +CREATE FUNCTION f_str_2() RETURNS BIT(7) LANGUAGE JS AS $$ return "16" $$; +CREATE FUNCTION f_str_tb() RETURNS BIT(5) LANGUAGE JS AS $$ return "33" $$; + +CREATE FUNCTION f_array() RETURNS BIT(5) LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS BIT(5) LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS BIT(5) LANGUAGE JS AS $$ return function (a) { return 1} $$; + +CREATE FUNCTION f_object_nerr() RETURNS BIT(5) LANGUAGE JS AS $$ return { valueOf() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; + +SELECT HEX(f_int_0()) AS i0, HEX(f_int_1()) AS i1; +--error ER_DATA_TOO_LONG +SELECT f_int_tb(); +--error ER_DATA_TOO_LONG +SELECT f_int_n(); + +SELECT HEX(f_num_1()) AS n1, HEX(f_num_2()) AS n2; +--error ER_DATA_TOO_LONG +SELECT f_num_tb(); +--error ER_DATA_TOO_LONG +SELECT f_num_n(); + +SELECT HEX(f_bigint()) AS bi; +--error ER_LANGUAGE_COMPONENT +SELECT f_bigint_n(); + +SELECT HEX(f_bool()) AS b; + +SELECT HEX(f_str_e()); +--error ER_LANGUAGE_COMPONENT +SELECT f_str_a(); +SELECT HEX(f_str_1()) AS s1; +SELECT HEX(f_str_2()) AS s1; +--error ER_DATA_TOO_LONG +SELECT f_str_tb(); + +--error ER_LANGUAGE_COMPONENT +SELECT f_array() AS a; +--error ER_LANGUAGE_COMPONENT +SELECT f_object() AS o; +--error ER_LANGUAGE_COMPONENT +SELECT f_func() AS f; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_nerr() AS nerr; + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int_0; +DROP FUNCTION f_int_1; +DROP FUNCTION f_int_tb; +DROP FUNCTION f_int_n; +DROP FUNCTION f_num_1; +DROP FUNCTION f_num_2; +DROP FUNCTION f_num_tb; +DROP FUNCTION f_num_n; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bigint_n; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_str_1; +DROP FUNCTION f_str_2; +DROP FUNCTION f_str_tb; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_nerr; + + +--echo # +--echo # For GEOMETRY return type we only support conversion from +--echo # ArrayBuffer-based JS values. +--echo # +CREATE FUNCTION f_undefined() RETURNS GEOMETRY LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS GEOMETRY LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int() RETURNS GEOMETRY LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS GEOMETRY LANGUAGE JS AS $$ return 1.25 $$; +CREATE FUNCTION f_bigint() RETURNS GEOMETRY LANGUAGE JS AS $$ return BigInt(100) $$; +CREATE FUNCTION f_bool() RETURNS GEOMETRY LANGUAGE JS AS $$ return true $$; +CREATE FUNCTION f_str_e() RETURNS GEOMETRY LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS GEOMETRY LANGUAGE JS AS $$ return "alpha" $$; +CREATE FUNCTION f_array() RETURNS GEOMETRY LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS GEOMETRY LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS GEOMETRY LANGUAGE JS AS $$ return function (a) { return 1 } $$; + +CREATE FUNCTION f_typed_arr() RETURNS GEOMETRY LANGUAGE JS AS $$ return new Uint8Array([0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 64, 0, 0, 0, 0, 0, 0, 52, 64]) $$; +DELIMITER |; +CREATE FUNCTION f_data_view() RETURNS GEOMETRY LANGUAGE JS AS $$ + let dv = new DataView(new ArrayBuffer(25)); + dv.setUint32(0, 0, true); // SRID 0 + dv.setUint8(4, 1); // little-endian + dv.setUint32(5, 1, true); // POINT + dv.setFloat64(9, 15, true); // X + dv.setFloat64(17, 20, true); // Y + return dv; +$$| +DELIMITER ;| + +SELECT f_undefined() AS u, f_null() AS nil; + +--error ER_LANGUAGE_COMPONENT +SELECT f_int(); +--error ER_LANGUAGE_COMPONENT +SELECT f_num(); +--error ER_LANGUAGE_COMPONENT +SELECT f_bigint(); +--error ER_LANGUAGE_COMPONENT +SELECT f_bool(); +--error ER_LANGUAGE_COMPONENT +SELECT f_str_e(); +--error ER_LANGUAGE_COMPONENT +SELECT f_str_a(); +--error ER_LANGUAGE_COMPONENT +SELECT f_array(); +--error ER_LANGUAGE_COMPONENT +SELECT f_object(); +--error ER_LANGUAGE_COMPONENT +SELECT f_func(); + +SELECT ST_AsText(f_typed_arr()) AS g; +SELECT ST_AsText(f_data_view()) AS g; + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_typed_arr; +DROP FUNCTION f_data_view; + + +--echo # +--echo # For JSON return type we apply JSON.stringify() to JS value and +--echo # then try to store resulting string as return value (of JSON SQL +--echo # type). +--echo # +CREATE FUNCTION f_undefined() RETURNS JSON LANGUAGE JS AS $$ return $$; +CREATE FUNCTION f_null() RETURNS JSON LANGUAGE JS AS $$ return null $$; + +CREATE FUNCTION f_int() RETURNS JSON LANGUAGE JS AS $$ return 1 $$; +CREATE FUNCTION f_num() RETURNS JSON LANGUAGE JS AS $$ return 1.25 $$; + +CREATE FUNCTION f_bigint() RETURNS JSON LANGUAGE JS AS $$ return BigInt(100) $$; + +CREATE FUNCTION f_bool() RETURNS JSON LANGUAGE JS AS $$ return true $$; + +CREATE FUNCTION f_str_e() RETURNS JSON LANGUAGE JS AS $$ return "" $$; +CREATE FUNCTION f_str_a() RETURNS JSON LANGUAGE JS AS $$ return "alpha" $$; + +CREATE FUNCTION f_array() RETURNS JSON LANGUAGE JS AS $$ return [1, 2, 3] $$; +CREATE FUNCTION f_object() RETURNS JSON LANGUAGE JS AS $$ return { x: 1, y: "alpha" } $$; +CREATE FUNCTION f_func() RETURNS JSON LANGUAGE JS AS $$ return function (a) { return 1 } $$; + +CREATE FUNCTION f_object_jerr() RETURNS JSON LANGUAGE JS AS $$ return { toJSON() { throw "Kaboom!" } } $$; + +SELECT f_undefined() AS u, f_null() AS nil; +SELECT f_int() AS i, f_num() AS n; + +--echo # JSON.stringify() doesn't support BigInt by default. +--error ER_LANGUAGE_COMPONENT +SELECT f_bigint() AS bi; + +SELECT f_bool() AS b; +SELECT f_str_e() AS se, f_str_a() AS sa; +SELECT f_array() AS arr, f_object() AS obj; + +--echo # SQL JSON type doesn't accept all JSON values produced by V8. +--error ER_INVALID_JSON_TEXT +SELECT f_func() AS func; + +--error ER_LANGUAGE_COMPONENT +SELECT f_object_jerr(); + +DROP FUNCTION f_undefined; +DROP FUNCTION f_null; +DROP FUNCTION f_int; +DROP FUNCTION f_num; +DROP FUNCTION f_bigint; +DROP FUNCTION f_bool; +DROP FUNCTION f_str_e; +DROP FUNCTION f_str_a; +DROP FUNCTION f_array; +DROP FUNCTION f_object; +DROP FUNCTION f_func; +DROP FUNCTION f_object_jerr; + +--echo +--echo # +--echo # Finally let us test how JS values are converted to SQL types +--echo # for OUT parameters. +--echo # +--echo # These conversion uses the same mechanism as for return values. + +--echo # +--echo # OUT parameters with string SQL types. +--echo # +--let $str_types='CHAR(50),VARCHAR(60),TINYTEXT,TEXT,MEDIUMTEXT,LONGTEXT' +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($str_types,',',$i),',',-1)` + + --eval CREATE PROCEDURE p_undefined(OUT r $type) LANGUAGE JS AS \$\$ r = undefined; \$\$ + --eval CREATE PROCEDURE p_null(OUT r $type) LANGUAGE JS AS \$\$ r = null; \$\$ + + --eval CREATE PROCEDURE p_int(OUT r $type) LANGUAGE JS AS \$\$ r = 1; \$\$ + --eval CREATE PROCEDURE p_num(OUT r $type) LANGUAGE JS AS \$\$ r = 1.25; \$\$ + --eval CREATE PROCEDURE p_bigint(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(100); \$\$ + --eval CREATE PROCEDURE p_bool(OUT r $type) LANGUAGE JS AS \$\$ r = true; \$\$ + + --eval CREATE PROCEDURE p_str_e(OUT r $type) LANGUAGE JS AS \$\$ r = ""; \$\$ + --eval CREATE PROCEDURE p_str_0(OUT r $type) LANGUAGE JS AS \$\$ r = "alpha"; \$\$ + --eval CREATE PROCEDURE p_str_1(OUT r $type CHARACTER SET utf8mb4) LANGUAGE JS AS \$\$ r = "Far over the misty mountains cold"; \$\$ + --eval CREATE PROCEDURE p_str_2(OUT r $type CHARACTER SET latin1) LANGUAGE JS AS \$\$ r = "Au-delà des montagnes glaciales et embrumées"; \$\$ + --eval CREATE PROCEDURE p_str_3(OUT r $type CHARACTER SET cp1251) LANGUAGE JS AS \$\$ r = "Там отвъд мъглявите студени планини"; \$\$ + --eval CREATE PROCEDURE p_str_cerr(OUT r $type CHARACTER SET cp1251) LANGUAGE JS AS \$\$ r = "\u{1F434}\u{1F9D9}\u{26F0}\u{FE0F}"; \$\$ + + --eval CREATE PROCEDURE p_array(OUT r $type) LANGUAGE JS AS \$\$ r = [1, 2, 3] \$\$ + --eval CREATE PROCEDURE p_object(OUT r $type) LANGUAGE JS AS \$\$ r = { x: 1, y: "alpha" } \$\$ + --eval CREATE PROCEDURE p_func(OUT r $type) LANGUAGE JS AS \$\$ r = function (a) { r = 1;} \$\$ + + # Typed Arrays and DataView objects are interesting because they got + # special handling for binary string types. + --eval CREATE PROCEDURE p_typed_arr(OUT r $type) LANGUAGE JS AS \$\$ r = new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE PROCEDURE p_data_view(OUT r $type) LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv \$\$ + + --eval CREATE PROCEDURE p_object_serr(OUT r $type) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + + CALL p_undefined(@r); + SELECT @r AS u; + CALL p_null(@r); + SELECT @r AS nil; + + CALL p_int(@r); + SELECT @r AS i; + CALL p_num(@r); + SELECT @r AS num; + CALL p_bigint(@r); + SELECT @r AS bi; + CALL p_bool(@r); + SELECT @r AS bo; + + CALL p_str_e(@r); + SELECT @r AS se; + CALL p_str_0(@r); + SELECT @r AS s0; + CALL p_str_1(@r); + SELECT @r AS s1; + CALL p_str_2(@r); + SELECT @r AS s2; + CALL p_str_3(@r); + SELECT @r AS s3; + # Check that values which are returned are expected ones and in expected charset. + CALL p_str_1(@r); + SELECT @r = X'466172206F76657220746865206D69737479206D6F756E7461696E7320636F6C64' AS r1; + CALL p_str_2(@r); + SELECT @r = X'41752D64656CE020646573206D6F6E7461676E657320676C616369616C657320657420656D6272756DE96573' AS r2; + CALL p_str_3(@r); + SELECT @r = X'D2E0EC20EEF2E2FAE420ECFAE3EBFFE2E8F2E520F1F2F3E4E5EDE820EFEBE0EDE8EDE8' AS r3; + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_str_cerr(@r); + + CALL p_array(@r); + SELECT @r AS a; + CALL p_object(@r); + SELECT @r AS o; + CALL p_func(@r); + SELECT @r AS f; + + CALL p_typed_arr(@r); + SELECT @r AS ta; + CALL p_data_view(@r); + SELECT @r AS dv; + + --error ER_LANGUAGE_COMPONENT + CALL p_object_serr(@r); + + DROP PROCEDURE p_undefined; + DROP PROCEDURE p_null; + DROP PROCEDURE p_int; + DROP PROCEDURE p_num; + DROP PROCEDURE p_bigint; + DROP PROCEDURE p_bool; + DROP PROCEDURE p_str_e; + DROP PROCEDURE p_str_0; + DROP PROCEDURE p_str_1; + DROP PROCEDURE p_str_2; + DROP PROCEDURE p_str_3; + DROP PROCEDURE p_str_cerr; + DROP PROCEDURE p_array; + DROP PROCEDURE p_object; + DROP PROCEDURE p_func; + DROP PROCEDURE p_typed_arr; + DROP PROCEDURE p_data_view; + DROP PROCEDURE p_object_serr; + + --inc $i +} + +--echo +--echo # +--echo # OUT parameters of binary string/BLOB SQL-types. +--echo # + +--let $bin_types='BINARY(50),VARBINARY(60),TINYBLOB,BLOB,MEDIUMBLOB,LONGBLOB' +--let $i = 1 +while($i <= 6) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($bin_types,',',$i),',',-1)` + + --eval CREATE PROCEDURE p_undefined(OUT r $type) LANGUAGE JS AS \$\$ r = undefined; \$\$ + --eval CREATE PROCEDURE p_null(OUT r $type) LANGUAGE JS AS \$\$ r = null; \$\$ + + --eval CREATE PROCEDURE p_int(OUT r $type) LANGUAGE JS AS \$\$ r = 1; \$\$ + --eval CREATE PROCEDURE p_num(OUT r $type) LANGUAGE JS AS \$\$ r = 1.25; \$\$ + --eval CREATE PROCEDURE p_bigint(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(100); \$\$ + --eval CREATE PROCEDURE p_bool(OUT r $type) LANGUAGE JS AS \$\$ r = true; \$\$ + + --eval CREATE PROCEDURE p_str_e(OUT r $type) LANGUAGE JS AS \$\$ r = ""; \$\$ + --eval CREATE PROCEDURE p_str_a(OUT r $type) LANGUAGE JS AS \$\$ r = "alpha"; \$\$ + + --eval CREATE PROCEDURE p_array(OUT r $type) LANGUAGE JS AS \$\$ r = [1, 2, 3] \$\$ + --eval CREATE PROCEDURE p_object(OUT r $type) LANGUAGE JS AS \$\$ r = { x: 1, y: "alpha" } \$\$ + --eval CREATE PROCEDURE p_func(OUT r $type) LANGUAGE JS AS \$\$ r = function (a) { r = 1;} \$\$ + + --eval CREATE PROCEDURE p_typed_arr(OUT r $type) LANGUAGE JS AS \$\$ r = new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE PROCEDURE p_data_view(OUT r $type) LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(9), 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = dv \$\$ + --eval CREATE PROCEDURE p_arr_buff(OUT r $type) LANGUAGE JS AS \$\$ let ab = new ArrayBuffer(9); let dv = new DataView(ab, 1, 7); dv.setUint32(0, 1); dv.setUint8(4, 3); dv.setUint16(5, 7); r = ab \$\$ + + --eval CREATE PROCEDURE p_object_serr(OUT r $type) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + + CALL p_undefined(@r); + SELECT @r AS u; + CALL p_null(@r); + SELECT @r AS nil; + + if (`SELECT '$type' LIKE 'BINARY%'`) { + --echo # BINARY type does 0-padding so we use HEX to correctly print value. + CALL p_int(@r); + SELECT HEX(@r) AS i; + CALL p_num(@r); + SELECT HEX(@r) AS num; + CALL p_bigint(@r); + SELECT HEX(@r) AS bi; + CALL p_bool(@r); + SELECT HEX(@r) AS bo; + + CALL p_str_e(@r); + SELECT HEX(@r) AS se; + CALL p_str_a(@r); + SELECT HEX(@r) AS sa; + + CALL p_array(@r); + SELECT HEX(@r) AS a; + CALL p_object(@r); + SELECT HEX(@r) AS o; + CALL p_func(@r); + SELECT HEX(@r) AS f; + } + if (!`SELECT '$type' LIKE 'BINARY%'`) { + CALL p_int(@r); + SELECT @r AS i; + CALL p_num(@r); + SELECT @r AS num; + CALL p_bigint(@r); + SELECT @r AS bi; + CALL p_bool(@r); + SELECT @r AS bo; + + CALL p_str_e(@r); + SELECT @r AS se; + CALL p_str_a(@r); + SELECT @r AS sa; + + CALL p_array(@r); + SELECT @r AS a; + CALL p_object(@r); + SELECT @r AS o; + CALL p_func(@r); + SELECT @r AS f; + } + + CALL p_typed_arr(@r); + SELECT HEX(@r) AS ta; + CALL p_data_view(@r); + SELECT HEX(@r) AS dv; + CALL p_arr_buff(@r); + SELECT HEX(@r) AS ab; + + --error ER_LANGUAGE_COMPONENT + CALL p_object_serr(@r); + + DROP PROCEDURE p_undefined; + DROP PROCEDURE p_null; + DROP PROCEDURE p_int; + DROP PROCEDURE p_num; + DROP PROCEDURE p_bigint; + DROP PROCEDURE p_bool; + DROP PROCEDURE p_str_e; + DROP PROCEDURE p_str_a; + DROP PROCEDURE p_array; + DROP PROCEDURE p_object; + DROP PROCEDURE p_func; + DROP PROCEDURE p_typed_arr; + DROP PROCEDURE p_data_view; + DROP PROCEDURE p_arr_buff; + DROP PROCEDURE p_object_serr; + + --inc $i +} + +--echo # +--echo # Test conversions for OUT parameters of integer SQL-types. +--echo # +--echo # See comments for test coverage for return values. + +--let $int_types='TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT' +--let $int_max='127,32767,8388607,2147483647,9007199254740991' +--let $i = 1 +while($i <= 5) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_types,',',$i),',',-1)` + --let $max = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_max,',',$i),',',-1)` + + --eval CREATE PROCEDURE p_undefined(OUT r $type) LANGUAGE JS AS \$\$ r = undefined; \$\$ + --eval CREATE PROCEDURE p_null(OUT r $type) LANGUAGE JS AS \$\$ r = null; \$\$ + + --eval CREATE PROCEDURE p_int_0(OUT r $type) LANGUAGE JS AS \$\$ r = 0; \$\$ + --eval CREATE PROCEDURE p_int_1(OUT r $type) LANGUAGE JS AS \$\$ r = 1; \$\$ + --eval CREATE PROCEDURE p_int_n(OUT r $type) LANGUAGE JS AS \$\$ r = -1; \$\$ + --eval CREATE PROCEDURE p_int_m(OUT r $type) LANGUAGE JS AS \$\$ r = $max; \$\$ + --eval CREATE PROCEDURE p_int_m1(OUT r $type) LANGUAGE JS AS \$\$ r = $max + 1; \$\$ + --eval CREATE PROCEDURE p_int_nm(OUT r $type) LANGUAGE JS AS \$\$ r = -$max - 1; \$\$ + --eval CREATE PROCEDURE p_int_nm1(OUT r $type) LANGUAGE JS AS \$\$ r = -$max - 2; \$\$ + --eval CREATE PROCEDURE p_uint_m(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = 2*$max + 1; \$\$ + --eval CREATE PROCEDURE p_uint_m1(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = 2*$max + 2; \$\$ + --eval CREATE PROCEDURE p_uint_n(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = - 1; \$\$ + + --eval CREATE PROCEDURE p_num_1(OUT r $type) LANGUAGE JS AS \$\$ r = 1.25; \$\$ + --eval CREATE PROCEDURE p_num_2(OUT r $type) LANGUAGE JS AS \$\$ r = 5e-1; \$\$ + --eval CREATE PROCEDURE p_num_3(OUT r $type) LANGUAGE JS AS \$\$ r = 5e-2; \$\$ + --eval CREATE PROCEDURE p_num_4(OUT r $type) LANGUAGE JS AS \$\$ r = 1.2345e+2; \$\$ + --eval CREATE PROCEDURE p_num_5(OUT r $type) LANGUAGE JS AS \$\$ r = -1.2345e+1; \$\$ + --eval CREATE PROCEDURE p_num_r1(OUT r $type) LANGUAGE JS AS \$\$ r = 4.5; \$\$ + --eval CREATE PROCEDURE p_num_r2(OUT r $type) LANGUAGE JS AS \$\$ r = 4.5e+0; \$\$ + --eval CREATE PROCEDURE p_num_un(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = -1.5; \$\$ + --eval CREATE PROCEDURE p_num_tb(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = 1e+70; \$\$ + + --eval CREATE PROCEDURE p_bigint(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(100); \$\$ + --eval CREATE PROCEDURE p_bigint_n(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(-42); \$\$ + --eval CREATE PROCEDURE p_bigint_u(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = BigInt(9007199254740991); \$\$ + --eval CREATE PROCEDURE p_bigint_un(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = BigInt(-42); \$\$ + --eval CREATE PROCEDURE p_bigint_tb(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(1e+25); \$\$ + + --eval CREATE PROCEDURE p_bool(OUT r $type) LANGUAGE JS AS \$\$ r = true; \$\$ + + --eval CREATE PROCEDURE p_str_e(OUT r $type) LANGUAGE JS AS \$\$ r = ""; \$\$ + --eval CREATE PROCEDURE p_str_a(OUT r $type) LANGUAGE JS AS \$\$ r = "alpha"; \$\$ + --eval CREATE PROCEDURE p_str_n1(OUT r $type) LANGUAGE JS AS \$\$ r = "123"; \$\$ + --eval CREATE PROCEDURE p_str_n2(OUT r $type) LANGUAGE JS AS \$\$ r = "-2"; \$\$ + --eval CREATE PROCEDURE p_str_n3(OUT r $type) LANGUAGE JS AS \$\$ r = "12.65"; \$\$ + --eval CREATE PROCEDURE p_str_nr1(OUT r $type) LANGUAGE JS AS \$\$ r = "4.5"; \$\$ + --eval CREATE PROCEDURE p_str_nr2(OUT r $type) LANGUAGE JS AS \$\$ r = "4.5e+0"; \$\$ + --eval CREATE PROCEDURE p_str_nu(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = "-1"; \$\$ + --eval CREATE PROCEDURE p_str_tb1(OUT r $type) LANGUAGE JS AS \$\$ r = "1e+25"; \$\$ + --eval CREATE PROCEDURE p_str_tb2(OUT r $type) LANGUAGE JS AS \$\$ r = "18446744073709551616"; \$\$ + + --eval CREATE PROCEDURE p_array(OUT r $type) LANGUAGE JS AS \$\$ r = [1, 2, 3] \$\$ + --eval CREATE PROCEDURE p_object(OUT r $type) LANGUAGE JS AS \$\$ r = { x: 1, y: "alpha" } \$\$ + --eval CREATE PROCEDURE p_func(OUT r $type) LANGUAGE JS AS \$\$ r = function (a) { r = 1;} \$\$ + + --eval CREATE PROCEDURE p_typed_arr(OUT r $type) LANGUAGE JS AS \$\$ r = new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE PROCEDURE p_data_view(OUT r $type) LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv \$\$ + + --eval CREATE PROCEDURE p_object_serr(OUT r $type) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + --eval CREATE PROCEDURE p_object_userr(OUT r $type UNSIGNED) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + + CALL p_undefined(@r); + SELECT @r AS u; + CALL p_null(@r); + SELECT @r AS nil; + + CALL p_int_0(@r); + SELECT @r AS i0; + CALL p_int_1(@r); + SELECT @r AS i1; + CALL p_int_n(@r); + SELECT @r AS n; + CALL p_int_m(@r); + SELECT @r AS im; + CALL p_int_nm(@r); + SELECT @r AS nm; + CALL p_uint_m(@r); + SELECT @r AS um; + if (!`SELECT '$type' LIKE 'BIGINT'`) { + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_int_m1(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_int_nm1(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_uint_m1(@r); + } + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_uint_n(@r); + + --echo # When MySQL converts string value with a floating point number to + --echo # an integer, it converts string to floating point value first and + --echo # then converts it to integer with rounding. + CALL p_num_1(@r); + SELECT @r AS n1; + CALL p_num_2(@r); + SELECT @r AS n2; + CALL p_num_3(@r); + SELECT @r AS n3; + CALL p_num_4(@r); + SELECT @r AS n4; + CALL p_num_5(@r); + SELECT @r AS n4; + + --echo # MySQL rounds floating-point values differently than decimal values, + --echo # floating-point values in strings and decimal values as string when + --echo # storing them as integer (for floating-point values rint() rounding + --echo # is used, while other use round() style rounding). + --echo # + --echo # We try to avoid the confusion and stick to round()-style + --echo # rounding in all cases. + CALL p_num_r1(@r); + SELECT @r AS nr1; + CALL p_num_r2(@r); + SELECT @r AS nr2; + + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_num_un(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_num_tb(@r); + + CALL p_bigint(@r); + SELECT @r AS bi; + CALL p_bigint_n(@r); + SELECT @r AS bn; + if (`SELECT '$type' LIKE 'BIGINT'`) { + CALL p_bigint_u(@r); + SELECT @r AS bu; + } + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_bigint_un(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_bigint_tb(@r); + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_bool(@r); + + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_str_e(@r); + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_str_a(@r); + CALL p_str_n1(@r); + SELECT @r AS n1; + CALL p_str_n2(@r); + SELECT @r AS n2; + CALL p_str_n3(@r); + SELECT @r AS n3; + CALL p_str_nr1(@r); + SELECT @r AS nr1; + CALL p_str_nr2(@r); + SELECT @r AS nr2; + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_str_nu(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_str_tb1(@r); + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_str_tb2(@r); + + --error 1265 + CALL p_array(@r); + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_object(@r); + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_func(@r); + --error 1265 + CALL p_typed_arr(@r); + --error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD + CALL p_data_view(@r); + + --error ER_LANGUAGE_COMPONENT + CALL p_object_serr(@r); + --error ER_LANGUAGE_COMPONENT + CALL p_object_userr(@r); + + DROP PROCEDURE p_undefined; + DROP PROCEDURE p_null; + DROP PROCEDURE p_int_0; + DROP PROCEDURE p_int_1; + DROP PROCEDURE p_int_n; + DROP PROCEDURE p_int_m; + DROP PROCEDURE p_int_m1; + DROP PROCEDURE p_int_nm; + DROP PROCEDURE p_int_nm1; + DROP PROCEDURE p_uint_m; + DROP PROCEDURE p_uint_m1; + DROP PROCEDURE p_uint_n; + DROP PROCEDURE p_num_1; + DROP PROCEDURE p_num_2; + DROP PROCEDURE p_num_3; + DROP PROCEDURE p_num_4; + DROP PROCEDURE p_num_5; + DROP PROCEDURE p_num_r1; + DROP PROCEDURE p_num_r2; + DROP PROCEDURE p_num_un; + DROP PROCEDURE p_num_tb; + DROP PROCEDURE p_bigint; + DROP PROCEDURE p_bigint_n; + DROP PROCEDURE p_bigint_u; + DROP PROCEDURE p_bigint_un; + DROP PROCEDURE p_bigint_tb; + DROP PROCEDURE p_bool; + DROP PROCEDURE p_str_e; + DROP PROCEDURE p_str_a; + DROP PROCEDURE p_str_n1; + DROP PROCEDURE p_str_n2; + DROP PROCEDURE p_str_n3; + DROP PROCEDURE p_str_nr1; + DROP PROCEDURE p_str_nr2; + DROP PROCEDURE p_str_nu; + DROP PROCEDURE p_str_tb1; + DROP PROCEDURE p_str_tb2; + DROP PROCEDURE p_array; + DROP PROCEDURE p_object; + DROP PROCEDURE p_func; + DROP PROCEDURE p_typed_arr; + DROP PROCEDURE p_data_view; + DROP PROCEDURE p_object_serr; + DROP PROCEDURE p_object_userr; + + --inc $i +} + +--echo # +--echo # OUT parameters of floating point SQL-types. +--echo # + +--let $real_types='FLOAT;DOUBLE' +--let $real_max='3.4028234e+38,1.7976931348623157e+308' +--let $i = 1 +while($i <= 2) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($real_types,';',$i),';',-1)` + --let $max = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($real_max,',',$i),',',-1)` + + --eval CREATE PROCEDURE p_undefined(OUT r $type) LANGUAGE JS AS \$\$ r = undefined; \$\$ + --eval CREATE PROCEDURE p_null(OUT r $type) LANGUAGE JS AS \$\$ r = null; \$\$ + + --eval CREATE PROCEDURE p_int_0(OUT r $type) LANGUAGE JS AS \$\$ r = 0; \$\$ + --eval CREATE PROCEDURE p_int_1(OUT r $type) LANGUAGE JS AS \$\$ r = 1; \$\$ + --eval CREATE PROCEDURE p_int_n(OUT r $type) LANGUAGE JS AS \$\$ r = -1; \$\$ + + --eval CREATE PROCEDURE p_num_1(OUT r $type) LANGUAGE JS AS \$\$ r = 1.25; \$\$ + --eval CREATE PROCEDURE p_num_2(OUT r $type) LANGUAGE JS AS \$\$ r = 5e-1; \$\$ + --eval CREATE PROCEDURE p_num_3(OUT r $type) LANGUAGE JS AS \$\$ r = -5e-2; \$\$ + --eval CREATE PROCEDURE p_num_m(OUT r $type) LANGUAGE JS AS \$\$ r = $max; \$\$ + + --eval CREATE PROCEDURE p_bigint(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt(100); \$\$ + # Value below can't be represented exactly as a single nor as a double precision floating-point + --eval CREATE PROCEDURE p_bigint_pl(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt("36028797018963967"); \$\$ + + --eval CREATE PROCEDURE p_bool(OUT r $type) LANGUAGE JS AS \$\$ r = true; \$\$ + + --eval CREATE PROCEDURE p_str_e(OUT r $type) LANGUAGE JS AS \$\$ r = ""; \$\$ + --eval CREATE PROCEDURE p_str_a(OUT r $type) LANGUAGE JS AS \$\$ r = "alpha"; \$\$ + --eval CREATE PROCEDURE p_str_n1(OUT r $type) LANGUAGE JS AS \$\$ r = "123"; \$\$ + --eval CREATE PROCEDURE p_str_n2(OUT r $type) LANGUAGE JS AS \$\$ r = "12.65"; \$\$ + # Value below can't be represented exactly as a single nor as a double precision floating-point + --eval CREATE PROCEDURE p_str_n_pl(OUT r $type) LANGUAGE JS AS \$\$ r = "36028797018963967"; \$\$ + --eval CREATE PROCEDURE p_str_n_tb(OUT r $type) LANGUAGE JS AS \$\$ r = "1.1e+400"; \$\$ + + --eval CREATE PROCEDURE p_array(OUT r $type) LANGUAGE JS AS \$\$ r = [1, 2, 3] \$\$ + --eval CREATE PROCEDURE p_object(OUT r $type) LANGUAGE JS AS \$\$ r = { x: 1, y: "alpha" } \$\$ + --eval CREATE PROCEDURE p_func(OUT r $type) LANGUAGE JS AS \$\$ r = function (a) { r = 1;} \$\$ + + --eval CREATE PROCEDURE p_typed_arr(OUT r $type) LANGUAGE JS AS \$\$ r = new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE PROCEDURE p_data_view(OUT r $type) LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv \$\$ + + --eval CREATE PROCEDURE p_object_serr(OUT r $type) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + + CALL p_undefined(@r); + SELECT @r AS u; + CALL p_null(@r); + SELECT @r AS nil; + + CALL p_int_0(@r); + SELECT @r AS i0; + CALL p_int_1(@r); + SELECT @r AS i1; + CALL p_int_n(@r); + SELECT @r AS n; + CALL p_num_1(@r); + SELECT @r AS n1; + CALL p_num_2(@r); + SELECT @r AS n2; + CALL p_num_3(@r); + SELECT @r AS n3; + CALL p_num_m(@r); + SELECT @r AS nm; + + CALL p_bigint(@r); + SELECT @r AS bi; + --echo # For FLOAT the below call returns different value as compared to + --echo # stored function case. This is due to user-variables internally + --echo # using double precision for storing floating point values. + CALL p_bigint_pl(@r); + SELECT @r AS bipl; + + --error 1265 + CALL p_bool(@r); + + --error 1265 + CALL p_str_e(@r); + --error 1265 + CALL p_str_a(@r); + CALL p_str_n1(@r); + SELECT @r AS n1; + CALL p_str_n2(@r); + SELECT @r AS n2; + CALL p_str_n_pl(@r); + SELECT @r AS npl; + --error ER_WARN_DATA_OUT_OF_RANGE + CALL p_str_n_tb(@r); + + --error 1265 + CALL p_array(@r); + --error 1265 + CALL p_object(@r); + --error 1265 + CALL p_func(@r); + --error 1265 + CALL p_typed_arr(@r); + --error 1265 + CALL p_data_view(@r); + + --error ER_LANGUAGE_COMPONENT + CALL p_object_serr(@r); + + DROP PROCEDURE p_undefined; + DROP PROCEDURE p_null; + DROP PROCEDURE p_int_0; + DROP PROCEDURE p_int_1; + DROP PROCEDURE p_int_n; + DROP PROCEDURE p_num_1; + DROP PROCEDURE p_num_2; + DROP PROCEDURE p_num_3; + DROP PROCEDURE p_num_m; + DROP PROCEDURE p_bigint; + DROP PROCEDURE p_bigint_pl; + DROP PROCEDURE p_bool; + DROP PROCEDURE p_str_e; + DROP PROCEDURE p_str_a; + DROP PROCEDURE p_str_n1; + DROP PROCEDURE p_str_n2; + DROP PROCEDURE p_str_n_pl; + DROP PROCEDURE p_str_n_tb; + DROP PROCEDURE p_array; + DROP PROCEDURE p_object; + DROP PROCEDURE p_func; + DROP PROCEDURE p_typed_arr; + DROP PROCEDURE p_data_view; + DROP PROCEDURE p_object_serr; + + --inc $i +} + + +--echo # +--echo # OUT parameters of DECIMAL type. +--echo # + +CREATE PROCEDURE p_undefined(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int_0(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_int_n(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r =-1 $$; +CREATE PROCEDURE p_int_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r =100 $$; + +CREATE PROCEDURE p_num_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 5e-1 $$; +CREATE PROCEDURE p_num_3(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = -5e-2 $$; +CREATE PROCEDURE p_num_m(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 99.99 $$; +CREATE PROCEDURE p_num_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 100.01 $$; +CREATE PROCEDURE p_num_tl(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = 0.0125 $$; + +CREATE PROCEDURE p_bigint_1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = BigInt(10) $$; +CREATE PROCEDURE p_bigint_2(OUT r DECIMAL(20,2)) LANGUAGE JS AS $$ r = BigInt("10000000000000000") $$; +CREATE PROCEDURE p_bigint_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = BigInt(1000) $$; + +CREATE PROCEDURE p_bool(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = true $$; + +CREATE PROCEDURE p_str_e(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_n1(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12" $$; +CREATE PROCEDURE p_str_n2(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12.65" $$; +CREATE PROCEDURE p_str_n3(OUT r DECIMAL(20,2)) LANGUAGE JS AS $$ r = "10000000000000000.12" $$; +CREATE PROCEDURE p_str_tb(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "123" $$; +CREATE PROCEDURE p_str_tl(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "12.324" $$; +CREATE PROCEDURE p_str_api(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = "π" $$; + +CREATE PROCEDURE p_array(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; + +CREATE PROCEDURE p_object_serr(OUT r DECIMAL(4,2)) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int_0(@r); +SELECT @r AS i0; +CALL p_int_1(@r); +SELECT @r AS i1; +CALL p_int_n(@r); +SELECT @r AS n; + +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_int_tb(@r); + +CALL p_num_1(@r); +SELECT @r AS n1; +CALL p_num_2(@r); +SELECT @r AS n2; +CALL p_num_3(@r); +SELECT @r AS n3; +CALL p_num_m(@r); +SELECT @r AS nm; + +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_num_tb(@r); + +CALL p_num_tl(@r); +SELECT @r AS ntl; + +CALL p_bigint_1(@r); +SELECT @r AS bi1; +CALL p_bigint_2(@r); +SELECT @r AS bi2; + +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_bigint_tb(@r); + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_bool(@r); + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_str_e(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_str_a(@r); + +CALL p_str_n1(@r); +SELECT @r AS n1; +CALL p_str_n2(@r); +SELECT @r AS n2; +CALL p_str_n3(@r); +SELECT @r AS n3; + +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_str_tb(@r); + +CALL p_str_tl(@r); +SELECT @r AS tl; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_str_api(@r); + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_array(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_object(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_serr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_m; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_num_tl; +DROP PROCEDURE p_bigint_1; +DROP PROCEDURE p_bigint_2; +DROP PROCEDURE p_bigint_tb; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_n1; +DROP PROCEDURE p_str_n2; +DROP PROCEDURE p_str_n3; +DROP PROCEDURE p_str_tb; +DROP PROCEDURE p_str_tl; +DROP PROCEDURE p_str_api; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; + + +--echo # +--echo # YEAR OUT parameters are handled similarly to integer types. +--echo # +CREATE PROCEDURE p_undefined(OUT r YEAR) LANGUAGE JS AS $$ r =undefined $$; +CREATE PROCEDURE p_null(OUT r YEAR) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int_0(OUT r YEAR) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r YEAR) LANGUAGE JS AS $$ r = 7 $$; +CREATE PROCEDURE p_int_2(OUT r YEAR) LANGUAGE JS AS $$ r = 69 $$; +CREATE PROCEDURE p_int_3(OUT r YEAR) LANGUAGE JS AS $$ r = 70 $$; +CREATE PROCEDURE p_int_o(OUT r YEAR) LANGUAGE JS AS $$ r = 123 $$; +CREATE PROCEDURE p_int_mi(OUT r YEAR) LANGUAGE JS AS $$ r = 1901 $$; +CREATE PROCEDURE p_int_mx(OUT r YEAR) LANGUAGE JS AS $$ r = 2155 $$; +CREATE PROCEDURE p_int_mi1(OUT r YEAR) LANGUAGE JS AS $$ r = 1900 $$; +CREATE PROCEDURE p_int_mx1(OUT r YEAR) LANGUAGE JS AS $$ r = 2156 $$; +CREATE PROCEDURE p_int_n(OUT r YEAR) LANGUAGE JS AS $$ r = -1 $$; + +CREATE PROCEDURE p_num_1(OUT r YEAR) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r YEAR) LANGUAGE JS AS $$ r = 5e-1 $$; +CREATE PROCEDURE p_num_3(OUT r YEAR) LANGUAGE JS AS $$ r = 5e+1 $$; +CREATE PROCEDURE p_num_4(OUT r YEAR) LANGUAGE JS AS $$ r = 1.901e+3 $$; +CREATE PROCEDURE p_num_tb(OUT r YEAR) LANGUAGE JS AS $$ r = 2.2e+3 $$; + +CREATE PROCEDURE p_bigint(OUT r YEAR) LANGUAGE JS AS $$ r = BigInt(70) $$; + +CREATE PROCEDURE p_bool(OUT r YEAR) LANGUAGE JS AS $$ r = true $$; + +CREATE PROCEDURE p_str_e(OUT r YEAR) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r YEAR) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_1(OUT r YEAR) LANGUAGE JS AS $$ r = "12" $$; +CREATE PROCEDURE p_str_2(OUT r YEAR) LANGUAGE JS AS $$ r = "75" $$; +CREATE PROCEDURE p_str_3(OUT r YEAR) LANGUAGE JS AS $$ r = "7.5" $$; +CREATE PROCEDURE p_str_o(OUT r YEAR) LANGUAGE JS AS $$ r = "100" $$; +CREATE PROCEDURE p_str_mi(OUT r YEAR) LANGUAGE JS AS $$ r ="1901" $$; +CREATE PROCEDURE p_str_mx(OUT r YEAR) LANGUAGE JS AS $$ r ="2155" $$; +CREATE PROCEDURE p_str_n(OUT r YEAR) LANGUAGE JS AS $$ r = "-1" $$; +CREATE PROCEDURE p_str_mi1(OUT r YEAR) LANGUAGE JS AS $$ r ="1900" $$; +CREATE PROCEDURE p_str_mx1(OUT r YEAR) LANGUAGE JS AS $$ r ="2156" $$; + +CREATE PROCEDURE p_array(OUT r YEAR) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r YEAR) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r YEAR) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; + +CREATE PROCEDURE p_object_serr(OUT r YEAR) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int_0(@r); +SELECT @r AS i0; +CALL p_int_1(@r); +SELECT @r AS i1; +CALL p_int_2(@r); +SELECT @r AS i2; +CALL p_int_3(@r); +SELECT @r AS i3; +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_int_o(@r); +CALL p_int_mi(@r); +SELECT @r AS mi; +CALL p_int_mx(@r); +SELECT @r AS mx; +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_int_mi1(@r); +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_int_mx1(@r); +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_int_n(@r); + +CALL p_num_1(@r); +SELECT @r AS n1; +CALL p_num_2(@r); +SELECT @r AS n2; +CALL p_num_3(@r); +SELECT @r AS n3; +CALL p_num_4(@r); +SELECT @r AS n4; +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_num_tb(@r); + +CALL p_bigint(@r); +SELECT @r AS bi; + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_bool(@r); + +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_str_e(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_str_a(@r); +CALL p_str_1(@r); +SELECT @r AS s1; +CALL p_str_2(@r); +SELECT @r AS s2; +CALL p_str_3(@r); +SELECT @r AS s3; +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_str_o(@r); +CALL p_str_mi(@r); +SELECT @r AS mi; +CALL p_str_mx(@r); +SELECT @r AS mx; +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_str_n(@r); +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_str_mi1(@r); +--error ER_WARN_DATA_OUT_OF_RANGE +CALL p_str_mx1(@r); + +--error 1265 +CALL p_array(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_object(@r); +--error ER_TRUNCATED_WRONG_VALUE_FOR_FIELD +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_serr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_2; +DROP PROCEDURE p_int_3; +DROP PROCEDURE p_int_o; +DROP PROCEDURE p_int_mi; +DROP PROCEDURE p_int_mx; +DROP PROCEDURE p_int_mi1; +DROP PROCEDURE p_int_mx1; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_3; +DROP PROCEDURE p_num_4; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_o; +DROP PROCEDURE p_str_mi; +DROP PROCEDURE p_str_mx; +DROP PROCEDURE p_str_n; +DROP PROCEDURE p_str_mi1; +DROP PROCEDURE p_str_mx1; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; + +--echo # +--echo # OUT parameters of other datetime SQL-types. +--echo # + +--let $dt_types='DATE;TIME;TIME(1);DATETIME;DATETIME(2);TIMESTAMP;TIMESTAMP(4)' +--let $int_vals='20200101,11,11,20200102030405,20200102030405,20231222102000,20231222102000' +--let $int_mis='10000101,-8385959,-8385959,10000101000000,10000101000000,19700101030001,19700101030001' +--let $int_mxs='99991231,8385959,8385959,99991231235959,99991231235959,20380119061407,20380119061407' +--let $num_vals='2.0200101e+7,11.1,11.1,20200102030405.06,20200102030405.06,20231222102000.0123,20231222102000.0123' +--let $str_vals='2020-01-01,00:11,00:11.1,2020-01-02 03:04:05,2020-01-02 03:04:05.06,2023-12-22 10:20:00,2023-12-22 10:20:00.1234' +--let $bad_vals='2020-13-01,00:65,01:71.1,2020-01-32 03:04:05,2020-01-02 25:04:05.06,2023-12-22 10:71:00,2023-12-22 10:20:63.1234' +--let $i = 1 +while($i <= 7) +{ + --let $type = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($dt_types,';',$i),';',-1)` + --let $i_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_vals,',',$i),',',-1)` + --let $i_mi = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_mis,',',$i),',',-1)` + --let $i_mx = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($int_mxs,',',$i),',',-1)` + --let $n_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($num_vals,',',$i),',',-1)` + --let $s_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($str_vals,',',$i),',',-1)` + --let $b_val = `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX($bad_vals,',',$i),',',-1)` + + --eval CREATE PROCEDURE p_undefined(OUT r $type) LANGUAGE JS AS \$\$ r = undefined; \$\$ + --eval CREATE PROCEDURE p_null(OUT r $type) LANGUAGE JS AS \$\$ r = null; \$\$ + + --eval CREATE PROCEDURE p_int_0(OUT r $type) LANGUAGE JS AS \$\$ r = 0; \$\$ + --eval CREATE PROCEDURE p_int_1(OUT r $type) LANGUAGE JS AS \$\$ r = $i_val; \$\$ + --eval CREATE PROCEDURE p_int_mi(OUT r $type) LANGUAGE JS AS \$\$ r = $i_mi; \$\$ + --eval CREATE PROCEDURE p_int_mx(OUT r $type) LANGUAGE JS AS \$\$ r = $i_mx; \$\$ + --eval CREATE PROCEDURE p_int_n(OUT r $type) LANGUAGE JS AS \$\$ r = -1; \$\$ + --eval CREATE PROCEDURE p_int_mi1(OUT r $type) LANGUAGE JS AS \$\$ r = $i_mi - 1; \$\$ + --eval CREATE PROCEDURE p_int_mx1(OUT r $type) LANGUAGE JS AS \$\$ r = $i_mx + 1; \$\$ + + --eval CREATE PROCEDURE p_num(OUT r $type) LANGUAGE JS AS \$\$ r = $n_val; \$\$ + + --eval CREATE PROCEDURE p_bigint(OUT r $type) LANGUAGE JS AS \$\$ r = BigInt($i_val); \$\$ + + --eval CREATE PROCEDURE p_bool(OUT r $type) LANGUAGE JS AS \$\$ r = true; \$\$ + + --eval CREATE PROCEDURE p_str_e(OUT r $type) LANGUAGE JS AS \$\$ r = ""; \$\$ + --eval CREATE PROCEDURE p_str_a(OUT r $type) LANGUAGE JS AS \$\$ r = "alpha"; \$\$ + --eval CREATE PROCEDURE p_str_1(OUT r $type) LANGUAGE JS AS \$\$ r = "$s_val"; \$\$ + --eval CREATE PROCEDURE p_str_b(OUT r $type) LANGUAGE JS AS \$\$ r = "$b_val"; \$\$ + --eval CREATE PROCEDURE p_str_ax(OUT r $type) LANGUAGE JS AS \$\$ r = "\u{1F384}"; \$\$ + + --eval CREATE PROCEDURE p_date(OUT r $type) LANGUAGE JS AS \$\$ r = new Date(2023,11,22,13,0,0,123) \$\$ + + --eval CREATE PROCEDURE p_array(OUT r $type) LANGUAGE JS AS \$\$ r = [1, 2, 3] \$\$ + --eval CREATE PROCEDURE p_object(OUT r $type) LANGUAGE JS AS \$\$ r = { x: 1, y: "alpha" } \$\$ + --eval CREATE PROCEDURE p_func(OUT r $type) LANGUAGE JS AS \$\$ r = function (a) { r = 1;} \$\$ + + --eval CREATE PROCEDURE p_typed_arr(OUT r $type) LANGUAGE JS AS \$\$ r = new Uint8Array([0, 1, 2, 3, 5]) \$\$ + --eval CREATE PROCEDURE p_data_view(OUT r $type) LANGUAGE JS AS \$\$ let dv = new DataView(new ArrayBuffer(1)); dv.setUint8(0, 3); r = dv \$\$ + + --eval CREATE PROCEDURE p_object_serr(OUT r $type) LANGUAGE JS AS \$\$ r = { toString() { throw "Kaboom!" } } \$\$ + + CALL p_undefined(@r); + SELECT @r AS u; + CALL p_null(@r); + SELECT @r AS nil; + + if (`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --echo # 0 is valid value for TIME type. + CALL p_int_0(@r); + SELECT @r AS z; + } + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + CALL p_int_0(@r); + } + CALL p_int_1(@r); + SELECT @r AS i1; + CALL p_int_mi(@r); + SELECT @r AS imi; + CALL p_int_mx(@r); + SELECT @r AS imx; + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + CALL p_int_n(@r); + } + --error ER_TRUNCATED_WRONG_VALUE + CALL p_int_mi1(@r); + --error ER_TRUNCATED_WRONG_VALUE + CALL p_int_mx1(@r); + + CALL p_num(@r); + SELECT @r AS n; + + CALL p_bigint(@r); + SELECT @r AS bi; + + --error ER_TRUNCATED_WRONG_VALUE + CALL p_bool(@r); + + if (`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --echo # Empty string is converted to 0 for TIME type. + CALL p_str_e(@r); + SELECT @r AS se; + } + if (!`SELECT '$type' LIKE 'TIME' OR '$type' LIKE 'TIME(1)'`) { + --error ER_TRUNCATED_WRONG_VALUE + CALL p_str_e(@r); + } + --error ER_TRUNCATED_WRONG_VALUE + CALL p_str_a(@r); + CALL p_str_1(@r); + SELECT @r AS s1; + --error ER_TRUNCATED_WRONG_VALUE + CALL p_str_b(@r); + + --error ER_TRUNCATED_WRONG_VALUE + CALL p_str_ax(@r); + + --echo # Direct string representation of Date type is not compatible with + --echo # MySQL datetime values. + --error ER_TRUNCATED_WRONG_VALUE + CALL p_date(@r); + + if (!`SELECT '$type' LIKE 'DATE%'`) { + --echo # DATE and DATETIME types accept weird literals. + --error ER_TRUNCATED_WRONG_VALUE + CALL p_array(@r); + } + --error ER_TRUNCATED_WRONG_VALUE + CALL p_object(@r); + --error ER_TRUNCATED_WRONG_VALUE + CALL p_func(@r); + if (!`SELECT '$type' LIKE 'DATE%'`) { + --echo # DATE and DATETIME types accept weird literals. + --error ER_TRUNCATED_WRONG_VALUE + CALL p_typed_arr(@r); + } + --error ER_TRUNCATED_WRONG_VALUE + CALL p_data_view(@r); + + --error ER_LANGUAGE_COMPONENT + CALL p_object_serr(@r); + + DROP PROCEDURE p_undefined; + DROP PROCEDURE p_null; + DROP PROCEDURE p_int_0; + DROP PROCEDURE p_int_1; + DROP PROCEDURE p_int_mi; + DROP PROCEDURE p_int_mx; + DROP PROCEDURE p_int_n; + DROP PROCEDURE p_int_mi1; + DROP PROCEDURE p_int_mx1; + DROP PROCEDURE p_num; + DROP PROCEDURE p_bigint; + DROP PROCEDURE p_bool; + DROP PROCEDURE p_str_e; + DROP PROCEDURE p_str_a; + DROP PROCEDURE p_str_1; + DROP PROCEDURE p_str_b; + DROP PROCEDURE p_str_ax; + DROP PROCEDURE p_date; + DROP PROCEDURE p_array; + DROP PROCEDURE p_object; + DROP PROCEDURE p_func; + DROP PROCEDURE p_typed_arr; + DROP PROCEDURE p_data_view; + DROP PROCEDURE p_object_serr; + + --inc $i +} + +--echo # +--echo # ENUM type of OUT parameter is handled similarly to string types. +--echo # +CREATE PROCEDURE p_undefined(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = true $$; + +CREATE PROCEDURE p_str_e(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_0(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "a" $$; +CREATE PROCEDURE p_str_1(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_2(OUT r ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) + LANGUAGE JS AS $$ r = "Додо" $$; +CREATE PROCEDURE p_str_cerr(OUT r ENUM(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) + LANGUAGE JS AS $$ r = "\u{1F9A4}" $$; + +CREATE PROCEDURE p_array(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; + +CREATE PROCEDURE p_object_serr(OUT r ENUM('a','b')) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int(@r); +SELECT @r AS i; + +--error 1265 +CALL p_num(@r); +--error 1265 +CALL p_bigint(@r); +--error 1265 +CALL p_bool(@r); + +--error 1265 +CALL p_str_e(@r); +CALL p_str_0(@r); +SELECT @r AS s0; +# Check that routine returns expected value and in correct charset. +CALL p_str_2(@r); +SELECT @r = X'c4eee4ee' AS s2; +--error 1265 +CALL p_str_1(@r); +--error 1265 +CALL p_str_cerr(@r); + +--error 1265 +CALL p_array(@r); +--error 1265 +CALL p_object(@r); +--error 1265 +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_serr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; + + +--echo # +--echo # SET type OUT parameters are handled similarly to how SQL core +--echo # interprets integer/floating-point values and strings which are +--echo # stored in SET columns. +--echo # +--echo # Numeric values are converted to integers and treated as bitmaps +--echo # representing sets. Strings are expected to contain comma-separated +--echo # lists of SET elements. Additionally strings containing integer +--echo # values are interpreted as bitmaps. +CREATE PROCEDURE p_undefined(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_int_tb(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 10 $$; +CREATE PROCEDURE p_int_n(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = -1 $$; +CREATE PROCEDURE p_num(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = 1.6 $$; + +CREATE PROCEDURE p_bigint(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = BigInt(4) $$; + +CREATE PROCEDURE p_bool(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = true $$; + +CREATE PROCEDURE p_str_e(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_0(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "a" $$; +CREATE PROCEDURE p_str_1(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "b,c" $$; +CREATE PROCEDURE p_str_2(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "3" $$; +CREATE PROCEDURE p_str_3(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "1.5" $$; +CREATE PROCEDURE p_str_n(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "-3" $$; +CREATE PROCEDURE p_str_w(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_4(OUT r SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) + LANGUAGE JS AS $$ r = "Додо" $$; +CREATE PROCEDURE p_str_cerr(OUT r SET(X'd2f3e8e4faebe4faec', X'd2f3e8e4faebe4e8', X'c4eee4ee') CHARACTER SET cp1251) + LANGUAGE JS AS $$ r = "\u{1F9A4}" $$; + +CREATE PROCEDURE p_array(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; + +CREATE PROCEDURE p_object_serr(OUT r SET('a','b','c')) LANGUAGE JS AS $$ r = { toString() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int(@r); +SELECT @r AS i; +--error 1265 +CALL p_int_tb(@r); +--error 1265 +CALL p_int_n(@r); + +CALL p_num(@r); +SELECT @r AS n; + +CALL p_bigint(@r); +SELECT @r AS bi; +--error 1265 +CALL p_bool(@r); + +CALL p_str_e(@r); +SELECT @r AS se; +CALL p_str_0(@r); +SELECT @r AS s0; +CALL p_str_1(@r); +SELECT @r AS s1; +CALL p_str_2(@r); +SELECT @r AS s2; +--echo # SQL core doesn't handle non-integer numbers represented as strings +--echo # and doubles stored in SET columns consistently either. +--error 1265 +CALL p_str_3(@r); +--error 1265 +CALL p_str_n(@r); +--error 1265 +CALL p_str_w(@r); +CALL p_str_4(@r); +SELECT @r = X'c4eee4ee' AS s; + +--error 1265 +CALL p_str_cerr(@r); + +--error 1265 +CALL p_array(@r); +--error 1265 +CALL p_object(@r); +--error 1265 +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_serr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_0; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_3; +DROP PROCEDURE p_str_n; +DROP PROCEDURE p_str_w; +DROP PROCEDURE p_str_4; +DROP PROCEDURE p_str_cerr; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_serr; + + +--echo # +--echo # BIT type of OUT parameter is handled in a special way. +--echo # +--echo # JS values are converted to numbers (ultimately integers) and then +--echo # their binary representation is interpreted as array of bits. +--echo # +--echo # We do not support JS values which are not convertible to numbers to +--echo # avoid confusion caused by different interpretation of 1, "1" and "a". +--echo # +CREATE PROCEDURE p_undefined(OUT r BIT(5)) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r BIT(5)) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int_0(OUT r BIT(5)) LANGUAGE JS AS $$ r = 0 $$; +CREATE PROCEDURE p_int_1(OUT r BIT(5)) LANGUAGE JS AS $$ r = 7 $$; +CREATE PROCEDURE p_int_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = 33 $$; +CREATE PROCEDURE p_int_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = -1 $$; + +CREATE PROCEDURE p_num_1(OUT r BIT(5)) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_num_2(OUT r BIT(5)) LANGUAGE JS AS $$ r = 7.8 $$; +CREATE PROCEDURE p_num_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = 32.5 $$; +CREATE PROCEDURE p_num_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = -1.2 $$; + +CREATE PROCEDURE p_bigint(OUT r BIT(5)) LANGUAGE JS AS $$ r = BigInt(5) $$; +CREATE PROCEDURE p_bigint_n(OUT r BIT(5)) LANGUAGE JS AS $$ r = BigInt(-1) $$; + +CREATE PROCEDURE p_bool(OUT r BIT(5)) LANGUAGE JS AS $$ r = true $$; + +CREATE PROCEDURE p_str_e(OUT r BIT(5)) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r BIT(5)) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_str_1(OUT r BIT(7)) LANGUAGE JS AS $$ r = "0" $$; +CREATE PROCEDURE p_str_2(OUT r BIT(7)) LANGUAGE JS AS $$ r = "16" $$; +CREATE PROCEDURE p_str_tb(OUT r BIT(5)) LANGUAGE JS AS $$ r = "33" $$; + +CREATE PROCEDURE p_array(OUT r BIT(5)) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r BIT(5)) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r BIT(5)) LANGUAGE JS AS $$ r = function (a) { r = 1} $$; + +CREATE PROCEDURE p_object_nerr(OUT r BIT(5)) LANGUAGE JS AS $$ r = { valueOf() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int_0(@r); +SELECT HEX(@r) AS i0; +CALL p_int_1(@r); +SELECT HEX(@r) AS i1; +--error ER_DATA_TOO_LONG +CALL p_int_tb(@r); +--error ER_DATA_TOO_LONG +CALL p_int_n(@r); + +CALL p_num_1(@r); +SELECT HEX(@r) AS n1; +CALL p_num_2(@r); +SELECT HEX(@r) AS n2; +--error ER_DATA_TOO_LONG +CALL p_num_tb(@r); +--error ER_DATA_TOO_LONG +CALL p_num_n(@r); + +CALL p_bigint(@r); +SELECT HEX(@r) AS bi; +--error ER_LANGUAGE_COMPONENT +CALL p_bigint_n(@r); + +CALL p_bool(@r); +SELECT HEX(@r) AS b; + +CALL p_str_e(@r); +SELECT HEX(@r) AS se; +--error ER_LANGUAGE_COMPONENT +CALL p_str_a(@r); +CALL p_str_1(@r); +SELECT HEX(@r) AS s1; +CALL p_str_2(@r); +SELECT HEX(@r) AS s2; +--error ER_DATA_TOO_LONG +CALL p_str_tb(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_array(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_object(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_nerr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int_0; +DROP PROCEDURE p_int_1; +DROP PROCEDURE p_int_tb; +DROP PROCEDURE p_int_n; +DROP PROCEDURE p_num_1; +DROP PROCEDURE p_num_2; +DROP PROCEDURE p_num_tb; +DROP PROCEDURE p_num_n; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bigint_n; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_str_1; +DROP PROCEDURE p_str_2; +DROP PROCEDURE p_str_tb; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_nerr; + + +--echo # +--echo # For GEOMETRY type of OUT parameter we only support conversion from +--echo # ArrayBuffer-based JS values. +--echo # +CREATE PROCEDURE p_undefined(OUT r GEOMETRY) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r GEOMETRY) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int(OUT r GEOMETRY) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r GEOMETRY) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r GEOMETRY) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r GEOMETRY) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r GEOMETRY) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r GEOMETRY) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_array(OUT r GEOMETRY) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r GEOMETRY) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r GEOMETRY) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; + +CREATE PROCEDURE p_typed_arr(OUT r GEOMETRY) LANGUAGE JS AS $$ r = new Uint8Array([0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 64, 0, 0, 0, 0, 0, 0, 52, 64]) $$; +DELIMITER |; +CREATE PROCEDURE p_data_view(OUT r GEOMETRY) LANGUAGE JS AS $$ + let dv = new DataView(new ArrayBuffer(25)); + dv.setUint32(0, 0, true); // SRID 0 + dv.setUint8(4, 1); // little-endian + dv.setUint32(5, 1, true); // POINT + dv.setFloat64(9, 15, true); // X + dv.setFloat64(17, 20, true); // Y + r = dv; +$$| +DELIMITER ;| + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +--error ER_LANGUAGE_COMPONENT +CALL p_int(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_num(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_bigint(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_bool(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_str_e(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_str_a(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_array(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_object(@r); +--error ER_LANGUAGE_COMPONENT +CALL p_func(@r); + +CALL p_typed_arr(@r); +SELECT ST_AsText(@r) AS g; +CALL p_data_view(@r); +SELECT ST_AsText(@r) AS g; + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_typed_arr; +DROP PROCEDURE p_data_view; + + +--echo # +--echo # For JSON OUT parameters we apply JSON.stringify() to JS value and +--echo # then try to store resulting string in the OUT parameter (of JSON SQL +--echo # type). +--echo # +CREATE PROCEDURE p_undefined(OUT r JSON) LANGUAGE JS AS $$ r = undefined $$; +CREATE PROCEDURE p_null(OUT r JSON) LANGUAGE JS AS $$ r = null $$; + +CREATE PROCEDURE p_int(OUT r JSON) LANGUAGE JS AS $$ r = 1 $$; +CREATE PROCEDURE p_num(OUT r JSON) LANGUAGE JS AS $$ r = 1.25 $$; +CREATE PROCEDURE p_bigint(OUT r JSON) LANGUAGE JS AS $$ r = BigInt(100) $$; +CREATE PROCEDURE p_bool(OUT r JSON) LANGUAGE JS AS $$ r = true $$; +CREATE PROCEDURE p_str_e(OUT r JSON) LANGUAGE JS AS $$ r = "" $$; +CREATE PROCEDURE p_str_a(OUT r JSON) LANGUAGE JS AS $$ r = "alpha" $$; +CREATE PROCEDURE p_array(OUT r JSON) LANGUAGE JS AS $$ r = [1, 2, 3] $$; +CREATE PROCEDURE p_object(OUT r JSON) LANGUAGE JS AS $$ r = { x: 1, y: "alpha" } $$; +CREATE PROCEDURE p_func(OUT r JSON) LANGUAGE JS AS $$ r = function (a) { r = 1 } $$; +CREATE PROCEDURE p_object_jerr(OUT r JSON) LANGUAGE JS AS $$ r = { toJSON() { throw "Kaboom!" } } $$; + +CALL p_undefined(@r); +SELECT @r AS u; +CALL p_null(@r); +SELECT @r AS nil; + +CALL p_int(@r); +SELECT @r AS i; +CALL p_num(@r); +SELECT @r AS n; + +--echo # JSON.stringify() doesn't support BigInt by default. +--error ER_LANGUAGE_COMPONENT +CALL p_bigint(@r); + +CALL p_bool(@r); +SELECT @r AS b; + +CALL p_str_e(@r); +SELECT @r AS se; +CALL p_str_a(@r); +SELECT @r AS sa; + +CALL p_array(@r); +SELECT @r AS arr; + +CALL p_object(@r); +SELECT @r AS obj; + +--echo # SQL JSON type doesn't accept all JSON values produced by V8. +--error ER_INVALID_JSON_TEXT +CALL p_func(@r); + +--error ER_LANGUAGE_COMPONENT +CALL p_object_jerr(@r); + +DROP PROCEDURE p_undefined; +DROP PROCEDURE p_null; +DROP PROCEDURE p_int; +DROP PROCEDURE p_num; +DROP PROCEDURE p_bigint; +DROP PROCEDURE p_bool; +DROP PROCEDURE p_str_e; +DROP PROCEDURE p_str_a; +DROP PROCEDURE p_array; +DROP PROCEDURE p_object; +DROP PROCEDURE p_func; +DROP PROCEDURE p_object_jerr; + + +--echo # +--echo # Let us test how JS contexts are handled by our JS routines. +--echo # +--echo # JS contexts are created for each connection and each account under +--echo # which routines are executed. +--echo # +--echo # This means: +--echo # - Different connections get different contexts. +--echo # - Routines which are executed under different accounts get different +--echo # contexts (even if is the same routine, e.g. function with SQL +--echo # SECURITY INVOKER attribute used from view and directly). +--echo # - Any routines which are executed within the same connection under +--echo # the same account get the same context. +--echo # +--echo # We check whether two calls use same JS context by accessing global +--echo # object. +--enable_connect_log +CREATE PROCEDURE p(i INT) LANGUAGE JS AS $$ globalThis.a = i $$; +CREATE FUNCTION f() RETURNS INT LANGUAGE JS AS $$ return globalThis.a $$; +CREATE USER user1@localhost; +GRANT ALL PRIVILEGES ON *.* TO user1@localhost; +CREATE USER user2@localhost; +GRANT ALL PRIVILEGES ON *.* TO user2@localhost; +CREATE DEFINER = user1@localhost FUNCTION g() RETURNS INT LANGUAGE JS AS $$ return globalThis.a $$; +CREATE FUNCTION h() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return globalThis.a $$; +--echo # Create SQL SECURITY DEFINER wrapper over h() +CREATE DEFINER = user2@localhost FUNCTION i() RETURNS INT RETURN h(); + +--echo # +--echo # Routine calls within the same connection executed under same +--echo # account share the context. +--connection default +CALL p(1); +SELECT f(); + +--echo # +--echo # Calls in different connections get different contexts. +--connect (con1, localhost, root,,) +SELECT f(); +CALL p(2); +SELECT f(); + +--disconnect con1 +--source include/wait_until_disconnected.inc +--connection default +SELECT f(); + +--echo # +--echo # Calls under different accounts get different contexts. +--echo # +--echo # Test this by running SECURITY DEFINER routine with non-root definer. +SELECT g(); + +--echo # And also by running SECURITY INVOKER routine from different security +--echo # contexts. +SELECT h(), i(); + +DROP PROCEDURE p; +DROP FUNCTION f; +DROP FUNCTION g; +DROP FUNCTION h; +DROP FUNCTION i; +DROP USER user2@localhost; +DROP USER user1@localhost; + + +--echo # +--echo # Let us test privilege requirements for JS routine creation and +--echo # execution. +--echo # +CREATE DATABASE mysqltest; +CREATE USER u_creator@localhost; +GRANT CREATE ROUTINE ON mysqltest.* TO u_creator@localhost; +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; +CREATE USER u_caller@localhost; +GRANT EXECUTE ON mysqltest.* TO u_caller@localhost; +CREATE USER u_dropper@localhost; +GRANT ALTER ROUTINE ON mysqltest.* TO u_dropper@localhost; + +--echo # +--echo # Let us show that JS routine can be created iff user has both CREATE +--echo # ROUTINE privilege on the database and global CREATE_JS_ROUTINE +--echo # privilege. +--connect (creator, localhost, u_creator,,) +CREATE FUNCTION mysqltest.f_suid() RETURNS INT LANGUAGE JS AS $$ return 2*2 $$; +CREATE FUNCTION mysqltest.f_nosuid() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 2*2 $$; + +--connection default +GRANT ALL PRIVILEGES ON *.* TO u_creator@localhost; +REVOKE CREATE_JS_ROUTINE ON *.* FROM u_creator@localhost; + +--connection creator +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE FUNCTION mysqltest.f_err() RETURNS INT LANGUAGE JS AS $$ return 42 $$; +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE FUNCTION mysqltest.f_err() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 42 $$; + +--connection default +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; +REVOKE CREATE ROUTINE ON *.* FROM u_creator@localhost; +REVOKE CREATE ROUTINE ON mysqltest.* FROM u_creator@localhost; + +--connection creator +--error ER_DBACCESS_DENIED_ERROR +CREATE FUNCTION mysqltest.f_err() RETURNS INT LANGUAGE JS AS $$ return 42 $$; +--error ER_DBACCESS_DENIED_ERROR +CREATE FUNCTION mysqltest.f_err() RETURNS INT SQL SECURITY INVOKER LANGUAGE JS AS $$ return 42 $$; + +--connection default +--echo # Restore status quo for 'creator' user (ignore +--echo # --sp-automatic-privileges effect for now). +REVOKE ALL PRIVILEGES ON *.* FROM u_creator@localhost; +GRANT CREATE ROUTINE ON mysqltest.* TO u_creator@localhost; +GRANT CREATE_JS_ROUTINE ON *.* TO u_creator@localhost; + +--echo # +--echo # Now show that JS routine can be executed iff user has EXECUTE +--echo # privilege on it. For SQL SECURITY DEFINER routines routine definer +--echo # also needs to have EXECUTE privilege at the time of call. +GRANT EXECUTE ON mysqltest.* TO u_creator@localhost; +--connect (caller, localhost, u_caller,,) +SELECT mysqltest.f_suid(); +SELECT mysqltest.f_nosuid(); + +--connection default +REVOKE EXECUTE ON mysqltest.* FROM u_creator@localhost; + +--connection caller +--error ER_PROCACCESS_DENIED_ERROR +SELECT mysqltest.f_suid(); +SELECT mysqltest.f_nosuid(); + +--connection default +GRANT EXECUTE ON mysqltest.* TO u_creator@localhost; +GRANT ALL PRIVILEGES ON *.* TO u_caller@localhost; +REVOKE EXECUTE ON *.* FROM u_caller@localhost; +REVOKE EXECUTE ON mysqltest.* FROM u_caller@localhost; + +--connection caller +--error ER_PROCACCESS_DENIED_ERROR +SELECT mysqltest.f_suid(); +--error ER_PROCACCESS_DENIED_ERROR +SELECT mysqltest.f_nosuid(); + + +--echo # +--echo # Show that JS routine can be dropped iff user has ALTER ROUTINE +--echo # privilege on it. +--connection default +GRANT ALL PRIVILEGES ON *.* TO u_dropper@localhost; +REVOKE ALTER ROUTINE ON *.* FROM u_dropper@localhost; +REVOKE ALTER ROUTINE ON mysqltest.* FROM u_dropper@localhost; + +--connect (dropper, localhost, u_dropper,,) +--error ER_PROCACCESS_DENIED_ERROR +DROP FUNCTION mysqltest.f1_suid; +--error ER_PROCACCESS_DENIED_ERROR +DROP FUNCTION mysqltest.f1_nosuid; + +--connection default +REVOKE ALL PRIVILEGES ON *.* FROM u_dropper@localhost; +GRANT ALTER ROUTINE ON mysqltest.* TO u_dropper@localhost; + +--connection dropper +DROP FUNCTION mysqltest.f_suid; +DROP FUNCTION mysqltest.f_nosuid; + +--connection creator +--disconnect creator +--source include/wait_until_disconnected.inc +--connection caller +--disconnect caller +--source include/wait_until_disconnected.inc +--connection dropper +--disconnect dropper +--source include/wait_until_disconnected.inc + +--connection default +DROP USER u_creator@localhost; +DROP USER u_caller@localhost; +DROP USER u_dropper@localhost; +DROP DATABASE mysqltest; + + +REVOKE CREATE_JS_ROUTINE ON *.* FROM root@localhost; + +--echo # +--echo # Test of component uninstallation while having outstanding per +--echo # connection context and isolate. +--echo # +--error ER_LANGUAGE_COMPONENT_CANNOT_UNINSTALL +UNINSTALL COMPONENT 'file://component_js_lang'; + +--echo # Disconnect of default connection frees the only remaining per +--echo # connection context and isolate. It is safe to uninstall +--echo # component now. +--disconnect default +--source include/wait_until_disconnected.inc +--connect(default,localhost,root) +UNINSTALL COMPONENT 'file://component_js_lang'; + +--echo # +--echo # UNINSTALL -> INSTALL is not supported without interim restart +--echo # as V8 doesn't support re-initialization. +--error ER_LANGUAGE_COMPONENT +INSTALL COMPONENT 'file://component_js_lang'; + +--echo # Restart server to let subsequent tests to do INSTALL COMPONENT freely. +--source include/restart_mysqld.inc + +--disable_connect_log diff --git a/mysql-test/suite/component_js_lang/t/js_lang_big.test b/mysql-test/suite/component_js_lang/t/js_lang_big.test new file mode 100644 index 000000000000..a508944f26f5 --- /dev/null +++ b/mysql-test/suite/component_js_lang/t/js_lang_big.test @@ -0,0 +1,98 @@ +# +# JS language component tests which require lot of memory or CPU. +# +--source include/have_js_lang_component.inc +--source include/big_test.inc + +--echo # Global prepare of playground. +--enable_connect_log + +INSTALL COMPONENT 'file://component_js_lang'; + +GRANT CREATE_JS_ROUTINE ON *.* TO root@localhost; + +--echo # +--echo # Test what happens if one tries to create JS routine which body which +--echo # exceeds V8 string length limit (2^29 - 24 on 64-bit systems). +--echo # +--let $v8_max_string_length = `SELECT 512*1024*1024 - 24` + +--echo # Temporarily raise max_allowed_packet value so we can easily produce +--echo # strings exceeding V8 limit. It should be multiply of 1024. +SET @save_max_allowed_packet = @@global.max_allowed_packet; +--replace_result $v8_max_string_length V8_MAX_STRING_LENGTH +--eval SET @@global.max_allowed_packet= $v8_max_string_length + 24 + 1024 + +--echo # Use new connection so new max_allowed_packet value takes effect. +--connect (con_big_packet, localhost, root,,) + +--echo # Try to create function with body exceeding JS string length limit. +--echo # Note that auxiliary huge_string string we use for this still fits +--echo # within the limit. +--echo # Also note that attempt to create routine which is below the limit, +--echo # but is huge enough, might pass but will make InnoDB complain in +--echo # the error log about too big rows/tuples. +--let $huge_string = `SELECT REPEAT('a', $v8_max_string_length - 30)` +--disable_query_log +--echo # Executing: CREATE FUNCTION f_huge() RETURNS LONGBLOB LANGUAGE JS AS \$\$ let too_lengthy_body_we_get = 1; return "HUGE_STRING_HERE" \$\$ +--error ER_LANGUAGE_COMPONENT +--eval CREATE FUNCTION f_huge() RETURNS LONGBLOB LANGUAGE JS AS \$\$ let too_lengthy_body_we_get = 1; return "$huge_string" \$\$ +--enable_query_log + +--echo # +--echo # Test coverage for SQL to JS parameter conversion failures due to +--echo # exceeding V8 string length limit/ 2^29 - 24. +--echo # + +--echo # Create functions with parameter types which values can exceed +--echo # the limit. +CREATE FUNCTION f_json(s JSON) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longtext(s LONGTEXT) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longtext_cs(s LONGTEXT CHARACTER SET cp1251) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return s.length $$; +CREATE FUNCTION f_longblob(b LONGBLOB) RETURNS VARCHAR(100) LANGUAGE JS AS $$ return b.byteLength $$; + + +--echo # Passing too long JSON and LONGTEXT values should cause error with +--echo # appropriate message. +--replace_result $v8_max_string_length V8_MAX_STRING_LENGTH +--error ER_LANGUAGE_COMPONENT +--eval SELECT f_json(JSON_ARRAY(REPEAT('a', $v8_max_string_length - 100 ), REPEAT('b', 200 ))) + +--replace_result $v8_max_string_length V8_MAX_STRING_LENGTH +--error ER_LANGUAGE_COMPONENT +--eval SELECT f_longtext(REPEAT('a', $v8_max_string_length + 200)) + +--replace_result $v8_max_string_length V8_MAX_STRING_LENGTH +--error ER_LANGUAGE_COMPONENT +--eval SELECT f_longtext_cs(REPEAT(X'fa', $v8_max_string_length + 200)) + +--echo # This should not apply to LONGBLOB parameters however, as their +--echo # values are directly converted to JS ArrayBuffer/DataView objects. +--replace_result $v8_max_string_length V8_MAX_STRING_LENGTH +--eval SELECT f_longblob(REPEAT(X'ff', $v8_max_string_length + 200)) = $v8_max_string_length + 200 AS length_is_correct + +DROP FUNCTION f_json; +DROP FUNCTION f_longtext; +DROP FUNCTION f_longtext_cs; +DROP FUNCTION f_longblob; + +--disconnect con_big_packet +--source include/wait_until_disconnected.inc +--connection default +SET @@global.max_allowed_packet = @save_max_allowed_packet; + +--echo # Global clean-up. +REVOKE CREATE_JS_ROUTINE ON *.* FROM root@localhost; + +--echo # Disconnect of default connection to free the only remaining +--echo # connection context and isolate, so we can uninstall component. +--disconnect default +--source include/wait_until_disconnected.inc +--connect(default,localhost,root) + +UNINSTALL COMPONENT 'file://component_js_lang'; + +--echo # Restart server to let subsequent tests to do INSTALL COMPONENT freely. +--source include/restart_mysqld.inc + +--disable_connect_log diff --git a/mysql-test/suite/component_js_lang/t/suite.opt b/mysql-test/suite/component_js_lang/t/suite.opt new file mode 100644 index 000000000000..b93a042dc539 --- /dev/null +++ b/mysql-test/suite/component_js_lang/t/suite.opt @@ -0,0 +1 @@ +$JS_LANG_COMPONENT_OPT diff --git a/sql/server_component/mysql_stored_program_imp.cc b/sql/server_component/mysql_stored_program_imp.cc index ea215546ea25..c7c9ceeb252a 100644 --- a/sql/server_component/mysql_stored_program_imp.cc +++ b/sql/server_component/mysql_stored_program_imp.cc @@ -22,11 +22,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysql_stored_program_imp.h" -#include // strcmp +#include // strcmp +#include "my_sys.h" #include "my_time.h" // check_datetime_range #include "mysql/components/services/bits/stored_program_bits.h" // stored_program_argument_type +#include "mysql/components/services/mysql_string.h" +#include "mysql/strings/m_ctype.h" #include "mysql_time.h" #include "sql/current_thd.h" +#include "sql/item.h" #include "sql/item_timefunc.h" // Item_time_literal #include "sql/sp_cache.h" // sp_cache #include "sql/sp_head.h" // sp_head @@ -114,6 +118,8 @@ DEFINE_BOOL_METHOD(mysql_stored_program_metadata_query_imp::get, "byte_length" -> uint64_t "char_length" -> uint64_t (Applicable to string data types) "charset" -> char const * + "collation" -> char const * + "max_display_length" -> uint64_t @note Have the key at least 7 characters long, with unique first 8 characters. @returns status of get operation @@ -246,8 +252,13 @@ static int get_field_metadata_internal(Create_field &field, bool input, *reinterpret_cast(value) = field.key_length(); else if (strcmp("charset", key) == 0) *reinterpret_cast(value) = field.charset->csname; + else if (strcmp("collation", key) == 0) + *reinterpret_cast(value) = field.charset->m_coll_name; else if (strcmp("decimals", key) == 0) *reinterpret_cast(value) = field.decimals; + else if (strcmp("max_display_length", key) == 0) + *reinterpret_cast(value) = + field.max_display_width_in_codepoints(); else return MYSQL_FAILURE; return MYSQL_SUCCESS; @@ -826,12 +837,16 @@ DEFINE_BOOL_METHOD(mysql_stored_program_runtime_argument_string_imp::get, if (*is_null) return MYSQL_SUCCESS; auto temp = String{}; auto string = item->val_str(&temp); - // HCS-8941: fix the bug when service called for non-string types: in case - // this string owns the buffer, the buffer will be freed when this function - // exits + if (string->is_alloced()) { - *value = nullptr; - return MYSQL_FAILURE; + // During execution, current_thd mem_root points to execute_mem_root (check + // sp_head::execute) and it is reset to caller mem_root after execution. + // Execute_mem_root is deallocated after execution + auto copied_string = + strmake_root(current_thd->mem_root, string->ptr(), string->length()); + *value = copied_string; + *length = string->length(); + return MYSQL_SUCCESS; } *value = string->c_ptr(); *length = string->length(); @@ -855,7 +870,17 @@ DEFINE_BOOL_METHOD(mysql_stored_program_runtime_argument_string_imp::get, DEFINE_BOOL_METHOD(mysql_stored_program_runtime_argument_string_imp::set, (stored_program_runtime_context sp_runtime_context, uint16_t index, char const *string, size_t length)) { - auto item = new Item_string(string, length, &my_charset_bin); + auto item = new Item_string(NAME_STRING(""), string, length, + &my_charset_utf8mb4_0900_ai_ci); + return set_variable(sp_runtime_context, item, index); +} + +DEFINE_BOOL_METHOD( + mysql_stored_program_runtime_argument_string_charset_imp::set, + (stored_program_runtime_context sp_runtime_context, uint16_t index, + char const *string, size_t length, CHARSET_INFO_h charset)) { + const CHARSET_INFO *src_cs = reinterpret_cast(charset); + auto item = new Item_string(NAME_STRING(""), string, length, src_cs); return set_variable(sp_runtime_context, item, index); } @@ -1223,7 +1248,17 @@ DEFINE_BOOL_METHOD(mysql_stored_program_return_value_null_imp::set, DEFINE_BOOL_METHOD(mysql_stored_program_return_value_string_imp::set, (stored_program_runtime_context sp_runtime_context, char const *string, size_t length)) { - auto item = new Item_string(string, length, &my_charset_bin); + auto item = new Item_string(NAME_STRING(""), string, length, + &my_charset_utf8mb4_0900_ai_ci); + return set_return_value(sp_runtime_context, item); +} + +DEFINE_BOOL_METHOD(mysql_stored_program_return_value_string_charset_imp::set, + (stored_program_runtime_context sp_runtime_context, + char const *string, size_t length, + CHARSET_INFO_h charset)) { + const CHARSET_INFO *src_cs = reinterpret_cast(charset); + auto item = new Item_string(NAME_STRING(""), string, length, src_cs); return set_return_value(sp_runtime_context, item); } diff --git a/sql/server_component/mysql_stored_program_imp.h b/sql/server_component/mysql_stored_program_imp.h index d803b87643e8..d7077849eb5b 100644 --- a/sql/server_component/mysql_stored_program_imp.h +++ b/sql/server_component/mysql_stored_program_imp.h @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define MYSQL_STORED_PROGRAM_IMP_H #include +#include "mysql/components/services/mysql_string.h" #include #include @@ -147,6 +148,14 @@ class mysql_stored_program_runtime_argument_string_imp { size_t length)); }; +class mysql_stored_program_runtime_argument_string_charset_imp { + public: + static DEFINE_BOOL_METHOD(set, + (stored_program_runtime_context sp_runtime_context, + uint16_t index, char const *string, size_t length, + CHARSET_INFO_h charset)); +}; + class mysql_stored_program_runtime_argument_int_imp { public: static DEFINE_BOOL_METHOD(get, @@ -232,6 +241,14 @@ class mysql_stored_program_return_value_string_imp { char const *string, size_t length)); }; +class mysql_stored_program_return_value_string_charset_imp { + public: + static DEFINE_BOOL_METHOD(set, + (stored_program_runtime_context sp_runtime_context, + char const *string, size_t length, + CHARSET_INFO_h charset)); +}; + class mysql_stored_program_return_value_int_imp { public: static DEFINE_BOOL_METHOD(set, diff --git a/sql/server_component/mysql_string_service.cc b/sql/server_component/mysql_string_service.cc index 2cedf5adda31..face5a909154 100644 --- a/sql/server_component/mysql_string_service.cc +++ b/sql/server_component/mysql_string_service.cc @@ -238,6 +238,24 @@ DEFINE_BOOL_METHOD(mysql_string_imp::convert_to_buffer_v2, return true; } +DEFINE_BOOL_METHOD(mysql_string_imp::copy_convert, + (my_h_string dest_string, const char *src_buffer, + uint64 src_length, CHARSET_INFO_h src_charset, + CHARSET_INFO_h dest_charset, uint *errors)) { + try { + String *str = from_api(dest_string); + const CHARSET_INFO *src_cs = from_api(src_charset); + const CHARSET_INFO *dest_cs = from_api(dest_charset); + + if (str->copy(src_buffer, src_length, src_cs, dest_cs, errors)) return true; + + return false; + } catch (...) { + mysql_components_handle_std_exception(__func__); + } + return true; +} + DEFINE_METHOD(void, mysql_string_imp::destroy, (my_h_string string)) { try { String *str = reinterpret_cast(string); diff --git a/sql/server_component/mysql_string_service_imp.h b/sql/server_component/mysql_string_service_imp.h index a5633da443bd..14477bd14ba0 100644 --- a/sql/server_component/mysql_string_service_imp.h +++ b/sql/server_component/mysql_string_service_imp.h @@ -145,6 +145,10 @@ class mysql_string_imp { (my_h_string src_string, char *dest_buffer, uint64 dest_length, CHARSET_INFO_h dest_charset)); + static DEFINE_BOOL_METHOD(copy_convert, + (my_h_string dest_string, const char *src_buffer, + uint64 src_length, CHARSET_INFO_h src_charset, + CHARSET_INFO_h dest_charset, uint *errors)); /** Gets character code of character on specified index position in string to a specified buffer. diff --git a/sql/server_component/server_component.cc b/sql/server_component/server_component.cc index ddb4a02a68cd..857eec09f033 100644 --- a/sql/server_component/server_component.cc +++ b/sql/server_component/server_component.cc @@ -173,6 +173,9 @@ BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_string_charset_converter) mysql_string_imp::convert_from_buffer_v2, mysql_string_imp::convert_to_buffer_v2 END_SERVICE_IMPLEMENTATION(); +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_string_copy_converter) +mysql_string_imp::copy_convert END_SERVICE_IMPLEMENTATION(); + BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_string_character_access) mysql_string_imp::get_char, mysql_string_imp::get_char_length END_SERVICE_IMPLEMENTATION(); @@ -724,6 +727,11 @@ mysql_stored_program_runtime_argument_string_imp::get, mysql_stored_program_runtime_argument_string_imp::set END_SERVICE_IMPLEMENTATION(); +BEGIN_SERVICE_IMPLEMENTATION( + mysql_server, mysql_stored_program_runtime_argument_string_charset) +mysql_stored_program_runtime_argument_string_charset_imp::set +END_SERVICE_IMPLEMENTATION(); + BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_stored_program_runtime_argument_int) mysql_stored_program_runtime_argument_int_imp::get, @@ -772,6 +780,11 @@ BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_stored_program_return_value_string) mysql_stored_program_return_value_string_imp::set END_SERVICE_IMPLEMENTATION(); +BEGIN_SERVICE_IMPLEMENTATION(mysql_server, + mysql_stored_program_return_value_string_charset) +mysql_stored_program_return_value_string_charset_imp::set +END_SERVICE_IMPLEMENTATION(); + BEGIN_SERVICE_IMPLEMENTATION(mysql_server, mysql_stored_program_return_value_int) mysql_stored_program_return_value_int_imp::set, END_SERVICE_IMPLEMENTATION(); @@ -873,6 +886,7 @@ PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file), PROVIDES_SERVICE(mysql_server, mysql_string_case), PROVIDES_SERVICE(mysql_server, mysql_string_converter), PROVIDES_SERVICE(mysql_server, mysql_string_charset_converter), + PROVIDES_SERVICE(mysql_server, mysql_string_copy_converter), PROVIDES_SERVICE(mysql_server, mysql_string_character_access), PROVIDES_SERVICE(mysql_server, mysql_string_byte_access), PROVIDES_SERVICE(mysql_server, mysql_string_iterator), @@ -1054,6 +1068,8 @@ PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file), PROVIDES_SERVICE(mysql_server, mysql_stored_program_runtime_argument_null), PROVIDES_SERVICE(mysql_server, mysql_stored_program_runtime_argument_string), + PROVIDES_SERVICE(mysql_server, + mysql_stored_program_runtime_argument_string_charset), PROVIDES_SERVICE(mysql_server, mysql_stored_program_runtime_argument_int), PROVIDES_SERVICE(mysql_server, mysql_stored_program_runtime_argument_unsigned_int), @@ -1065,6 +1081,8 @@ PROVIDES_SERVICE(mysql_server_path_filter, dynamic_loader_scheme_file), PROVIDES_SERVICE(mysql_server, mysql_stored_program_return_value_timestamp), PROVIDES_SERVICE(mysql_server, mysql_stored_program_return_value_null), PROVIDES_SERVICE(mysql_server, mysql_stored_program_return_value_string), + PROVIDES_SERVICE(mysql_server, + mysql_stored_program_return_value_string_charset), PROVIDES_SERVICE(mysql_server, mysql_stored_program_return_value_int), PROVIDES_SERVICE(mysql_server, mysql_stored_program_return_value_unsigned_int),