diff --git a/.gitignore b/.gitignore index 342a31c02ee20..8e780de9bfd2a 100644 --- a/.gitignore +++ b/.gitignore @@ -218,6 +218,7 @@ sql/yy_mariadb.yy sql/yy_oracle.cc sql/yy_oracle.hh sql/yy_oracle.yy +storage/duckdb/duckdb-build storage/heap/hp_test1 storage/heap/hp_test2 storage/maria/aria_chk diff --git a/.gitmodules b/.gitmodules index 18bcb465fa251..9c95bf90e3099 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "storage/columnstore/columnstore"] path = storage/columnstore/columnstore url = https://github.com/mariadb-corporation/mariadb-columnstore-engine.git +[submodule "storage/duckdb/third_parties/duckdb"] + path = storage/duckdb/third_parties/duckdb + url = https://github.com/duckdb/duckdb.git diff --git a/cmake/cpack_rpm.cmake b/cmake/cpack_rpm.cmake index 99ae88ec6b83a..7582ad7f576e5 100644 --- a/cmake/cpack_rpm.cmake +++ b/cmake/cpack_rpm.cmake @@ -7,7 +7,6 @@ INCLUDE(check_linker_flag) SET(CPACK_GENERATOR "RPM") SET(CPACK_RPM_PACKAGE_DEBUG 1) SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.7) SET(CPACK_RPM_COMPONENT_INSTALL ON) diff --git a/debian/autobake-deb.sh b/debian/autobake-deb.sh index 220cf1ab781ef..0e256ab681705 100755 --- a/debian/autobake-deb.sh +++ b/debian/autobake-deb.sh @@ -177,6 +177,12 @@ then fi fi +# Enable DuckDB storage engine plugin packaging +if grep -q "$architecture" storage/duckdb/debian/control +then + cat storage/duckdb/debian/mariadb-plugin-duckdb.install >> debian/mariadb-server.install +fi + if [ -n "${AUTOBAKE_PREP_CONTROL_RULES_ONLY:-}" ] then exit 0 diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 633b65499b578..aefdd6d5d883b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -254,19 +254,19 @@ static int join_ft_read_first(JOIN_TAB *tab); static int join_ft_read_next(READ_RECORD *info); int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); -static COND *make_cond_for_table(THD *thd, Item *cond,table_map table, - table_map used_table, - int join_tab_idx_arg, - bool exclude_expensive_cond, - bool retain_ref_cond); -static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, - Item *cond, - table_map tables, - table_map used_table, - int join_tab_idx_arg, - bool exclude_expensive_cond, - bool retain_ref_cond, - bool is_top_and_level); +COND *make_cond_for_table(THD *thd, Item *cond,table_map table, + table_map used_table, + int join_tab_idx_arg, + bool exclude_expensive_cond, + bool retain_ref_cond); +COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, + Item *cond, + table_map tables, + table_map used_table, + int join_tab_idx_arg, + bool exclude_expensive_cond, + bool retain_ref_cond, + bool is_top_and_level); static Item* part_of_refkey(TABLE *form,Field *field); static bool test_if_cheaper_ordering(bool in_join_optimizer, @@ -26377,7 +26377,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) make_cond_for_info_schema() uses similar algorithm as well. */ -static Item * +Item * make_cond_for_table(THD *thd, Item *cond, table_map tables, table_map used_table, int join_tab_idx_arg, @@ -26391,7 +26391,7 @@ make_cond_for_table(THD *thd, Item *cond, table_map tables, } -static Item * +Item * make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, table_map tables, table_map used_table, int join_tab_idx_arg, diff --git a/sql/sql_select.h b/sql/sql_select.h index 9c224e730269a..ea1f51b51cae5 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -2285,6 +2285,20 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select); bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result); +COND *make_cond_for_table(THD *thd, Item *cond, table_map tables, + table_map used_table, + int join_tab_idx_arg, + bool exclude_expensive_cond, + bool retain_ref_cond); +COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, + Item *cond, + table_map tables, + table_map used_table, + int join_tab_idx_arg, + bool exclude_expensive_cond, + bool retain_ref_cond, + bool is_top_and_level); + /* General routine to change field->ptr of a NULL-terminated array of Field objects. Useful when needed to call val_int, val_str or similar and the diff --git a/storage/duckdb/CLAUDE.md b/storage/duckdb/CLAUDE.md new file mode 100644 index 0000000000000..bccdb4bf83402 --- /dev/null +++ b/storage/duckdb/CLAUDE.md @@ -0,0 +1,77 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +DuckDB storage engine plugin for MariaDB. Creates tables with `ENGINE=DuckDB` that store data in DuckDB's columnar format and execute analytical queries through DuckDB's vectorized engine. Lives under `storage/duckdb/` in the MariaDB server tree. + +## Build + +```bash +# Build + install + start MariaDB (most common during development) +./build.sh -S -t Debug + +# CI mode (build only, no install) +./build.sh -c -t Debug + +# Build with packages +./build.sh -p -t RelWithDebInfo +``` + +The build directory is created as a sibling: `../DuckdbBuildOf_/`. DuckDB itself is built from source at `third_parties/duckdb/` via `ExternalProject_Add` and merged into a single `libduckdb_bundle.a`. + +## Tests (MTR) + +Tests use MariaDB's MTR (MySQL Test Runner) framework. Test files live in `mysql-test/duckdb/t/` with expected results in `mysql-test/duckdb/r/`. + +```bash +# Run a single test +./run_mtr.sh create_table_column + +# Run and record new expected output +./run_mtr.sh -r create_table_column + +# Run all tests +./run_mtr.sh -a + +# Run against an externally running MariaDB +./run_mtr.sh -e create_table_column + +# Run all against extern server +./run_mtr.sh -a -e +``` + +## Architecture + +All engine code is in the `myduck` namespace (except `ha_duckdb` which is in global scope per MariaDB handler convention). + +### Key components + +- **`ha_duckdb`** (`ha_duckdb.cc/h`) — MariaDB `handler` subclass. Entry point for all storage engine operations (open, close, read, write, DDL). Implements row-at-a-time interface for MariaDB, translating to DuckDB batch operations. + +- **`DuckdbManager`** (`duckdb_manager.cc/h`) — Singleton owning the `duckdb::DuckDB` instance. Created once at plugin init, creates connections for each thread. Database file stored as `duckdb.db` in MariaDB data directory. + +- **`DuckdbThdContext`** (`duckdb_context.cc/h`) — Per-thread context holding a `duckdb::Connection`, transaction state, and appenders. Attached to MariaDB's THD. Manages BEGIN/COMMIT/ROLLBACK lifecycle and session variable propagation (timezone, optimizer flags, collation). + +- **DDL/DML Convertors** (`ddl_convertor.cc/h`, `dml_convertor.cc/h`) — Translate MariaDB SQL structures (TABLE, Field, Alter_info) into DuckDB-compatible SQL strings. Handle identifier quoting differences (MariaDB backticks → DuckDB double quotes) and type mapping. + +- **`DeltaAppender`** (`delta_appender.cc/h`) — Batched write path. Accumulates INSERT/UPDATE/DELETE rows using DuckDB's Appender API into a temporary buffer table, then flushes as a single DuckDB DML statement at commit time. `DeltaAppenders` manages per-table appender instances. + +- **Select handler / Query pushdown** (`ha_duckdb_pushdown.cc/h`) — Implements MariaDB's `select_handler` interface to push entire SELECT queries down to DuckDB. Registered via `hton->create_select` and `hton->create_unit`. Supports pure-DuckDB queries and cross-engine joins. + +- **Cross-engine scan** (`cross_engine_scan.cc/h`) — Enables DuckDB to read from non-DuckDB tables (e.g. InnoDB) during cross-engine joins. Registers a `_mdb_scan` table function and a replacement scan callback in DuckDB. Uses a thread-local registry of external tables. + +- **Type mapping** (`duckdb_types.cc/h`) — Converts between MariaDB field types and DuckDB types. `store_duckdb_field_in_mysql_format()` reads DuckDB values back into MariaDB row format. + +### SQL generation conventions + +All generated SQL must use **double quotes** for identifiers (DuckDB follows SQL standard), not backticks. The `SELECT_LEX::print()` output from MariaDB uses backticks and must be post-processed. See `docs/mariadb-duckdb-incompatibilities.md` for known function name rewrites and type mapping issues. + +### DuckDB source and patches + +DuckDB source is at `third_parties/duckdb/` (git submodule). No patches are applied — all compatibility is handled at runtime via `duckdb_mysql_compat.cc`. The build produces a static library; `_GLIBCXX_DEBUG` is explicitly undefined in CMakeLists.txt to avoid ABI mismatch with MariaDB's debug build. + +### Configuration + +`duckdb.cnf` — MariaDB config snippet that loads `ha_duckdb.so`. Installed to `/etc/my.cnf.d/`. diff --git a/storage/duckdb/CMakeLists.txt b/storage/duckdb/CMakeLists.txt new file mode 100644 index 0000000000000..34d58b2cfcff5 --- /dev/null +++ b/storage/duckdb/CMakeLists.txt @@ -0,0 +1,66 @@ +IF("NO" STREQUAL "${PLUGIN_DUCKDB}") + return() +ENDIF() + +IF(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + MESSAGE_ONCE(duckdb "DuckDB: not Linux, skipping") + return() +ENDIF() + +IF(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR + CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")) + MESSAGE_ONCE(duckdb "DuckDB: not x86_64 or aarch64, skipping") + return() +ENDIF() + +# Check C++17 compiler support +MY_CHECK_CXX_COMPILER_FLAG("-std=c++17") +IF(NOT have_CXX__std_c__17) + MESSAGE_ONCE(duckdb "DuckDB: C++ compiler does not support -std=c++17, skipping") + RETURN() +ENDIF() + +# libduckdb_bundle.a is built without debug STL wrappers. +# Mismatched _GLIBCXX_DEBUG changes sizeof(std::vector) → SIGSEGV. +SET(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -U_GLIBCXX_DEBUG -U_GLIBCXX_ASSERTIONS") + +INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb.cmake) +INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb_target_setup.cmake) + +# Provide MariaDB server include paths to sub-libraries. +# MYSQL_ADD_PLUGIN normally does this, but it runs after ADD_SUBDIRECTORY. +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/sql + ${CMAKE_BINARY_DIR}/include + ${PCRE_INCLUDE_DIRS} + ${SSL_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIRS}) + +# Build sub-libraries. +ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(convertor) +ADD_SUBDIRECTORY(runtime) + +# Plugin sources (handler layer + UDFs whose extern "C" symbols must be +# in the final .so for dlsym() loading). +SET(DUCKDB_PLUGIN_SOURCES + ha_duckdb.cc + ha_duckdb_pushdown.cc + duckdb_udf.cc +) + +MYSQL_ADD_PLUGIN(duckdb ${DUCKDB_PLUGIN_SOURCES} + STORAGE_ENGINE + MODULE_ONLY + LINK_LIBRARIES duckdb_runtime duckdb_convertor duckdb_common ${DUCKDB_LIBRARY} +) + +IF(TARGET duckdb) + duckdb_setup_target(duckdb) + TARGET_INCLUDE_DIRECTORIES(duckdb PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/common + ${CMAKE_CURRENT_SOURCE_DIR}/convertor + ${CMAKE_CURRENT_SOURCE_DIR}/runtime + ) +ENDIF() diff --git a/storage/duckdb/COPYING b/storage/duckdb/COPYING new file mode 100644 index 0000000000000..6e475df55260e --- /dev/null +++ b/storage/duckdb/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 of the License, or + (at your option) any later version. + + 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 Street, Fifth Floor, Boston, MA 02110-1335 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/storage/duckdb/README.md b/storage/duckdb/README.md new file mode 100644 index 0000000000000..e96bd96263513 --- /dev/null +++ b/storage/duckdb/README.md @@ -0,0 +1,119 @@ +# DuckDB Storage Engine for MariaDB + +A pluggable storage engine that brings [DuckDB](https://github.com/duckdb/duckdb)'s columnar analytical engine inside [MariaDB Server](https://github.com/MariaDB/server). + +Create a table with `ENGINE=DuckDB` and analytical queries against it are executed through DuckDB's columnar vectorized engine — no ETL pipelines, no separate cluster, no additional protocols. One server, one SQL interface, the familiar `mariadb` client. + +## Use Cases + +- **HTAP (Hybrid Transactional/Analytical Processing)** — InnoDB handles OLTP, DuckDB handles analytics, both in the same database. +- **Ad-hoc analytical queries** — complex joins, aggregations, subqueries, and window functions over large datasets without exporting data to a separate system. +- **Eliminating ETL complexity** — no need for a dedicated analytical cluster or data movement pipelines; the analytical engine runs in-process. + +## Performance + +TPC-H Scale Factor 10 (~10 GB raw data, ~60M rows in `lineitem`): + +| Metric | Result | +|---|---| +| Data loading | 250 seconds | +| All 22 TPC-H queries | **3.7 seconds** total | + +## How It Works + +DuckDB is an in-process analytical database. Its performance rests on three pillars: + +- **Columnar storage** — minimizes unnecessary data reads. +- **Vectorized execution** — processes data in batches for maximum CPU cache efficiency. +- **Parallelism** — leverages all available processor cores. + +Tables created with `ENGINE=DuckDB` store data in DuckDB's native format. Queries are translated and executed by the DuckDB engine. InnoDB and DuckDB tables coexist in the same database. + +## Building + +The engine is built as part of the MariaDB server tree. It lives under `storage/duckdb/` and uses `ExternalProject_Add` to build DuckDB v1.5.2 from source. + +Until the patch is accepted into MariaDB upstream, clone one of the prepared branches from the server fork and then clone the plugin: + +```bash +# Pick the branch matching your target MariaDB version +git clone -b bb-11.4-duckdb https://github.com/drrtuy/mdb-server.git mariadb-server +# or: -b 11.8-duckdb +# or: -b 12.3-duckdb +cd mariadb-server + +# Clone the DuckDB engine plugin +git clone --recurse-submodules https://github.com/MariaDB/duckdb-engine storage/duckdb/duckdb + +# Install build dependencies (requires root) +./storage/duckdb/duckdb/build.sh -D + +# Build and install +./storage/duckdb/duckdb/build.sh +``` + +### Build packages + +**RPM** (Rocky/Fedora/Amazon Linux): + +```bash +./storage/duckdb/duckdb/build.sh -p +``` + +**DEB** (Debian/Ubuntu): + +```bash +./storage/duckdb/duckdb/build.sh -p +``` + +## Cross-Engine Queries + +The engine supports cross-engine joins — a single `SELECT` can combine DuckDB tables with tables from other engines (e.g. InnoDB). When the query planner detects a mix of engines, the `select_handler` does the following: + +1. Opens the non-DuckDB tables via MariaDB's handler API and registers them in a thread-local table registry. +2. Derives a per-table predicate from the query's `WHERE` (via `make_cond_for_table`) and registers it alongside each external table. +3. Pushes the **entire query** — including all `WHERE`, `JOIN`, `GROUP BY`, and `ORDER BY` clauses — down to DuckDB as a single SQL statement. +4. DuckDB's replacement scan callback transparently redirects references to non-DuckDB tables to the `_mdb_scan` table function. + +The `_mdb_scan` table function does **not** loop over the handler directly. On its first call it lazily spawns a **cooperative fiber** (stack-switching runtime borrowed from `libmariadb`) running on a dedicated background `THD`. Inside the fiber it builds a synthetic, projection- and predicate-pushed +`SELECT FROM . [WHERE ]` +and executes it through the **full MariaDB pipeline** (`lex_start` → `parse_sql` → `mysql_execute_command`). A custom `select_result_interceptor` (`select_to_duckdb`) converts each result row's `Item`s into a DuckDB `DataChunk`; when a chunk fills (`STANDARD_VECTOR_SIZE`) the fiber **yields** back to DuckDB, which consumes the chunk and resumes the fiber for the next batch. + +Because the external scan runs through `mysql_execute_command`, it uses the **MariaDB optimizer and all available access paths** (index range/ref access, etc.), and the pushed `WHERE` predicate is evaluated by MariaDB *before* rows reach DuckDB. On the DuckDB side, the full query text lets DuckDB's optimizer reorder joins, build hash tables, and run all aggregation/sorting over the streamed rows. A direct `ha_rnd_next` loop is retained only as a fallback when no fiber is used. + +This means queries like the following just work: + +```sql +SELECT d.id, d.amount, i.name + FROM analytics.orders d -- ENGINE=DuckDB + JOIN inventory.products i -- ENGINE=InnoDB + ON d.product_id = i.id + WHERE d.amount > 1000; +``` + +DuckDB handles the join, aggregation, and sorting; InnoDB rows are produced on demand by a fiber-driven MariaDB query. No data copying or ETL is required. + +## Current Limitations + +- **DECIMAL precision > 38** — DuckDB supports up to 38 digits; wider MariaDB DECIMALs will fail on DDL conversion. +- **Some MariaDB functions are yet not pushdown-compatible** — `GROUP_CONCAT()`, `DATE_FORMAT()`, `JSON_CONTAINS()`, `FOUND_ROWS()`, `LAST_INSERT_ID()`, and a few others have no DuckDB equivalent or differ in syntax. Such queries fall back to MariaDB execution. +- **Strict GROUP BY** — DuckDB rejects `SELECT` columns not in `GROUP BY` and not aggregated, even when MariaDB's `sql_mode` allows it. +- **XA transactions** — `XA PREPARE` is not supported by the engine. +- **Collations** — MariaDB UCA-based collation rules are approximated via DuckDB's built-in `NOCASE`/`NOACCENT` collations for UTF-8 charsets; non-UTF8 charsets fall back to binary comparison. See [`docs/collation-mapping.md`](docs/collation-mapping.md) for the full mapping and known gaps. +- **Cross-engine scan is yet single-threaded** — each external (non-DuckDB) table is produced by a single fiber-driven MariaDB query (`_mdb_scan` reports `MaxThreads() == 1`); only the DuckDB side of the query is parallelized. +- **ALTER COLUMN DROP DEFAULT** — not propagated to DuckDB catalog. +- **Timezone propagation** — `TIMESTAMP` columns are stored as `TIMESTAMPTZ`; timezone must be set consistently between MariaDB and DuckDB contexts to avoid shifts. + +See [`docs/mariadb-duckdb-incompatibilities.md`](docs/mariadb-duckdb-incompatibilities.md) for a detailed compatibility matrix. + +## License + +This project is licensed under the GNU General Public License v2. See [COPYING](COPYING) for details. + +DuckDB itself is licensed under the MIT License. + +## Acknowledgments + +**Alibaba and the AliSQL Project** — A special thank you to Alibaba and their engineering team for open-sourcing [AliSQL](https://github.com/alibaba/AliSQL). AliSQL is a MySQL branch developed at Alibaba Group and extensively used in their production infrastructure. The December 2025 open-source release of AliSQL 8.0 included integration of DuckDB as a native storage engine, providing a valuable reference implementation and validating the viability of embedding DuckDB inside a MySQL-compatible server. The DuckDB Engine for MariaDB draws heavily on this experience. + +**The DuckDB Project** — None of this would be possible without the remarkable work of the [DuckDB team](https://github.com/duckdb/duckdb). DuckDB has grown from an academic research project into one of the most impressive analytical engines available today — fast, embeddable, dependency-free, and released under the permissive MIT License. Its clean C++ codebase and well-defined embedding API are what make integrations like this one feasible. diff --git a/storage/duckdb/build.sh b/storage/duckdb/build.sh new file mode 100755 index 0000000000000..c6e4f08e2d00a --- /dev/null +++ b/storage/duckdb/build.sh @@ -0,0 +1,391 @@ +#!/bin/bash + +set -e +set -o pipefail + +SCRIPT_LOCATION=$(dirname "$0") +source "$SCRIPT_LOCATION/utils.sh" +MDB_SOURCE_PATH=$(realpath "$SCRIPT_LOCATION"/../../../) +DUCKDB_SOURCE_PATH=$(realpath "$SCRIPT_LOCATION") +BUILD_PATH=$(realpath "$MDB_SOURCE_PATH"/../DuckdbBuildOf_$(basename "$MDB_SOURCE_PATH")) +CPUS=$(getconf _NPROCESSORS_ONLN) +BUILD_TYPE_OPTIONS=("Debug" "RelWithDebInfo") +BUILD_TYPE="${BUILD_TYPE:-}" +DISTRO_OPTIONS=("ubuntu:22.04" "ubuntu:24.04" "debian:12" "rockylinux:8" "rockylinux:9") +DEFAULT_MDB_DATADIR="/var/lib/mysql" +USER="mysql" +GROUP="mysql" +INSTALL_PREFIX="/usr/" +GCC_VERSION="12" + +usage() { + echo "Usage: $0 [options]" + echo " -t Build type: ${BUILD_TYPE_OPTIONS[*]} (interactive if omitted)" + echo " -d Distro: ${DISTRO_OPTIONS[*]} (auto-detected if omitted)" + echo " -j Number of parallel jobs (default: $CPUS)" + echo " -c CI mode: only build, skip install" + echo " -p Build packages (DEB or RPM)" + echo " -S Start MariaDB after build" + echo " -n No clean: keep existing data files" + echo " -D Install build prerequisites (requires root/sudo)" + echo " -u Build unit tests" + echo " -R Use gcc-toolset-\${GCC_VERSION} on Rocky 8" + echo " -h Show this help" + exit 0 +} + +CI_MODE=false +START_MDB=false +NO_CLEAN=false +BUILD_PACKAGES=false +INSTALL_DEPS=false +GCC_TOOLSET=false +UNIT_TESTS=false +OS="" + +while getopts "t:d:j:cpSnDRuh" opt; do + case $opt in + t) BUILD_TYPE="$OPTARG" ;; + d) OS="$OPTARG" ;; + j) CPUS="$OPTARG" ;; + c) CI_MODE=true ;; + p) BUILD_PACKAGES=true ;; + S) START_MDB=true ;; + n) NO_CLEAN=true ;; + D) INSTALL_DEPS=true ;; + R) GCC_TOOLSET=true ;; + u) UNIT_TESTS=true ;; + h) usage ;; + *) usage ;; + esac +done + +if [[ ! " ${BUILD_TYPE_OPTIONS[*]} " =~ " ${BUILD_TYPE} " ]]; then + menu_choice "Select build type:" BUILD_TYPE_OPTIONS + BUILD_TYPE="$MENU_RESULT" +fi + +detect_distro() { + if [ -f /etc/os-release ]; then + . /etc/os-release + local os_name=$(echo "$NAME" | cut -f1 -d" " | tr '[:upper:]' '[:lower:]') + OS="${os_name}:${VERSION_ID}" + elif [ -f /etc/lsb-release ]; then + . /etc/lsb-release + OS=$(echo "$DISTRIB_ID" | tr '[:upper:]' '[:lower:]'):"$DISTRIB_RELEASE" + else + fail "Cannot detect distro, specify with -d" + fi + info "Detected distro: ${_CLR_YELLOW}$OS" +} + +select_pkg_format() { + if [[ "$1" == *rocky* ]]; then + PKG_FORMAT="rpm" + else + PKG_FORMAT="deb" + fi +} + +if [[ $BUILD_PACKAGES = true ]]; then + if [[ ! " ${DISTRO_OPTIONS[*]} " =~ " ${OS} " ]]; then + if [[ -z "$OS" ]]; then + warn "Distro not specified, detecting..." + detect_distro + fi + if [[ ! " ${DISTRO_OPTIONS[*]} " =~ " ${OS} " ]]; then + menu_choice "Select distro:" DISTRO_OPTIONS + OS="$MENU_RESULT" + fi + fi + select_pkg_format "$OS" +fi + +header "DuckDB Storage Engine Build" +info "Source: ${_CLR_YELLOW}$MDB_SOURCE_PATH" +info "Build dir: ${_CLR_YELLOW}$BUILD_PATH" +info "Build type: ${_CLR_YELLOW}$BUILD_TYPE" +info "Jobs: ${_CLR_YELLOW}$CPUS" +if [[ $BUILD_PACKAGES = true ]]; then + info "Packages: ${_CLR_YELLOW}$PKG_FORMAT ($OS)" +fi +echo "" + +check_user_and_group() { + local user=$1 + if [ -z "$(grep "$user" /etc/passwd)" ]; then + info "Adding user $user" + useradd -r -U "$user" -d /var/lib/mysql + fi + if [ -z "$(grep "$user" /etc/group)" ]; then + local gid=$(awk -F: '{uid[$3]=1}END{for(x=100; x<=999; x++) {if(uid[x] != ""){}else{print x; exit;}}}' /etc/group) + info "Adding group $user with id $gid" + groupadd -g "$gid" "$user" + fi +} + +clean_old_installation() { + if [[ $NO_CLEAN = true ]]; then + return + fi + rm -rf "${DEFAULT_MDB_DATADIR}" + rm -rf /var/run/mysqld +} + +bootstrap_mdb() { + info "Bootstrap MariaDB" + "$INSTALL_PREFIX/bin/mariadb-install-db" \ + --datadir="$DEFAULT_MDB_DATADIR" \ + --user="$USER" --group="$GROUP" > /dev/null +} + +stop_mdb() { + if "$INSTALL_PREFIX/bin/mariadb-admin" ping --silent 2>/dev/null; then + warn "Stopping MariaDB" + "$INSTALL_PREFIX/bin/mariadb-admin" shutdown || true + fi +} + +start_mdb() { + info "Starting MariaDB" + mkdir -p /run/mysqld + chown "$USER:$GROUP" /run/mysqld + "$INSTALL_PREFIX/bin/mariadbd-safe" --datadir="$DEFAULT_MDB_DATADIR" & + + local max_attempts=30 + local attempt=0 + while ! "$INSTALL_PREFIX/bin/mariadb-admin" ping --silent 2>/dev/null; do + attempt=$((attempt + 1)) + if [[ $attempt -ge $max_attempts ]]; then + local err_log="${DEFAULT_MDB_DATADIR}/$(hostname).err" + if [[ -f "$err_log" ]]; then + error "Last 50 lines of $err_log:" + tail -50 "$err_log" + fi + fail "MariaDB failed to start within ${max_attempts} seconds" + fi + sleep 1 + done + success "MariaDB is ready" +} + +setup_dev_user() { + local current_user=$(logname 2>/dev/null || echo "$SUDO_USER") + if [[ -n "$current_user" && "$current_user" != "root" ]]; then + info "Creating dev user '${_CLR_YELLOW}$current_user${_CLR_CYAN}'" + "$INSTALL_PREFIX/bin/mariadb" -e \ + "CREATE USER IF NOT EXISTS '$current_user'@'localhost' IDENTIFIED VIA unix_socket; + GRANT ALL PRIVILEGES ON *.* TO '$current_user'@'localhost';" + fi +} + +create_config() { + # Put config in /etc/my.cnf.d/ which is included by /etc/my.cnf + mkdir -p /etc/my.cnf.d + cp "$DUCKDB_SOURCE_PATH/duckdb.cnf" /etc/my.cnf.d/duckdb.cnf +} + +construct_cmake_flags() { + MDB_CMAKE_FLAGS=( + -DCMAKE_BUILD_TYPE=$BUILD_TYPE + -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX + -DCMAKE_EXPORT_COMPILE_COMMANDS=1 + -DPLUGIN_ARCHIVE=NO + -DPLUGIN_BLACKHOLE=NO + -DPLUGIN_FEDERATED=NO + -DPLUGIN_FEDERATEDX=NO + -DPLUGIN_CONNECT=NO + -DPLUGIN_MROONGA=NO + -DPLUGIN_OQGRAPH=NO + -DPLUGIN_ROCKSDB=NO + -DPLUGIN_SPHINX=NO + -DPLUGIN_SPIDER=NO + -DPLUGIN_TOKUDB=NO + -DPLUGIN_COLUMNSTORE=NO + -DWITH_EMBEDDED_SERVER=NO + -DWITH_WSREP=NO + -DWITH_SSL=system + -DWITH_SAFEMALLOC=OFF + -DMYSQL_MAINTAINER_MODE=OFF + -DPLUGIN_DUCKDB=YES + -DWITH_SBOM=0 + -DDBUG_ON=1 + ) + + if [[ "$BUILD_TYPE" == "Debug" ]]; then + MDB_CMAKE_FLAGS+=(-DDUCKDB_WERROR=ON) + fi + + if [[ $UNIT_TESTS = true ]]; then + MDB_CMAKE_FLAGS+=(-DDUCKDB_UNIT_TESTS=ON) + fi + + if [[ $BUILD_PACKAGES = true ]]; then + if [[ "$PKG_FORMAT" == "rpm" ]]; then + local os_version=${OS//[^0-9]/} + if [[ "$OS" == *rocky* ]]; then + MDB_CMAKE_FLAGS+=(-DRPM=rockylinux${os_version}) + fi + else + local codename="" + case "$OS" in + debian:12*) codename="bookworm" ;; + ubuntu:22.04) codename="jammy" ;; + ubuntu:24.04) codename="noble" ;; + *) fail "Unknown DEB codename for $OS" ;; + esac + MDB_CMAKE_FLAGS+=(-DDEB=${codename} -DINSTALL_LAYOUT=DEB) + fi + else + MDB_CMAKE_FLAGS+=(-DDEB=noble -DINSTALL_LAYOUT=DEB) + fi +} + +install_deps() { + if [[ $INSTALL_DEPS = false ]]; then + return + fi + + if [[ -z "$OS" ]]; then + detect_distro + fi + + local SUDO="" + if [[ $EUID -ne 0 ]]; then + SUDO="sudo" + fi + + # MariaDB server + DuckDB build prerequisites + local RPM_DEPS="git make cmake ninja-build bison flex \ + ncurses-devel readline-devel openssl-devel zlib-devel bzip2-devel \ + libzstd-devel libcurl-devel libaio-devel libxml2-devel pcre2-devel \ + libxcrypt-devel xz-devel pam-devel perl-DBI python3 python3-devel \ + ccache rpm-build" + + local DEB_DEPS="build-essential git cmake ninja-build bison flex \ + libncurses-dev libreadline-dev libssl-dev zlib1g-dev libbz2-dev \ + libzstd-dev libcurl4-openssl-dev libaio-dev libxml2-dev libpcre2-dev \ + libxcrypt-dev liblzma-dev libpam0g-dev libperl-dev python3 python3-dev \ + ccache devscripts equivs debhelper libdistro-info-perl" + + local command="" + case "$OS" in + rockylinux:8|rocky:8) + command="$SUDO dnf install -y 'dnf-command(config-manager)' epel-release && \ + $SUDO dnf config-manager --set-enabled powertools && \ + $SUDO dnf install -y ${RPM_DEPS}" + if [[ $GCC_TOOLSET = true ]]; then + command="$command && $SUDO dnf install -y gcc-toolset-${GCC_VERSION} gcc-toolset-${GCC_VERSION}-gcc-c++" + warn "Activate toolchain before rebuilding: . /opt/rh/gcc-toolset-${GCC_VERSION}/enable" + else + command="$command && $SUDO dnf groupinstall -y \"Development Tools\"" + warn "Rocky 8 default gcc 8 lacks C++20 -- consider re-running with -R" + fi + ;; + rockylinux:9|rocky:9|rocky:10) + command="$SUDO dnf install -y 'dnf-command(config-manager)' epel-release && \ + $SUDO dnf config-manager --set-enabled crb && \ + $SUDO dnf install -y gcc gcc-c++ ${RPM_DEPS}" + ;; + ubuntu:*|debian:*) + command="$SUDO apt-get update && \ + DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y ${DEB_DEPS}" + ;; + *) + fail "Unsupported distro for -D: $OS" + ;; + esac + + separator + info "Installing prerequisites for ${_CLR_YELLOW}$OS" + set +e + eval "$command" | one_liner + local rc=${PIPESTATUS[0]} + set -e + [[ $rc -ne 0 ]] && fail "DEPENDENCY INSTALL FAILED (exit code $rc)" + success "Prerequisites installed" +} + +construct_cmake_flags +install_deps + +build_binary() { + separator + info "Configuring" + set +e + cmake "${MDB_CMAKE_FLAGS[@]}" -S"$MDB_SOURCE_PATH" -B"$BUILD_PATH" | one_liner + local rc=${PIPESTATUS[0]} + set -e + [[ $rc -ne 0 ]] && fail "CONFIGURE FAILED (exit code $rc)" + + separator + info "Building with ${_CLR_YELLOW}$CPUS${_CLR_CYAN} jobs" + set +e + cmake --build "$BUILD_PATH" -j "$CPUS" | one_liner + rc=${PIPESTATUS[0]} + set -e + [[ $rc -ne 0 ]] && fail "BUILD FAILED (exit code $rc)" + + success "Build complete" + info "Adding compile_commands.json symlink" + ln -sf "$BUILD_PATH/compile_commands.json" "$MDB_SOURCE_PATH" +} + +build_package() { + separator + info "Building ${_CLR_YELLOW}$PKG_FORMAT${_CLR_CYAN} package for ${_CLR_YELLOW}$OS" + + set +e + if [[ "$PKG_FORMAT" == "rpm" ]]; then + cd "$BUILD_PATH" + make -j "$CPUS" package | one_liner + else + cd "$MDB_SOURCE_PATH" + export DEBIAN_FRONTEND="noninteractive" + export DEB_BUILD_OPTIONS="parallel=$CPUS" + export BUILDPACKAGE_FLAGS="-b" + CMAKEFLAGS="${MDB_CMAKE_FLAGS[*]}" debian/autobake-deb.sh | one_liner + fi + local rc=${PIPESTATUS[0]} + set -e + [[ $rc -ne 0 ]] && fail "PACKAGE BUILD FAILED (exit code $rc)" + success "Packages ready" +} + +build_binary + +if [[ $BUILD_PACKAGES = true ]]; then + build_package + header "BUILD FINISHED" + exit 0 +fi + +if [[ $CI_MODE = false ]]; then + check_user_and_group "$USER" + stop_mdb + clean_old_installation + + separator + info "Installing" + set +e + cmake --install "$BUILD_PATH" | one_liner + rc=${PIPESTATUS[0]} + set -e + [[ $rc -ne 0 ]] && fail "INSTALL FAILED (exit code $rc)" + + create_config + if [[ $NO_CLEAN = false ]]; then + bootstrap_mdb + else + warn "Skipping bootstrap (--no-clean mode, keeping existing data)" + fi +fi + +if [[ $START_MDB = true ]]; then + stop_mdb + start_mdb + setup_dev_user + +fi + +header "BUILD FINISHED" diff --git a/storage/duckdb/cmake/cpack_overrides.cmake b/storage/duckdb/cmake/cpack_overrides.cmake new file mode 100644 index 0000000000000..bf031caa30ac2 --- /dev/null +++ b/storage/duckdb/cmake/cpack_overrides.cmake @@ -0,0 +1,24 @@ +# DuckDB plugin-specific CPack overrides applied at package time. +# Referenced via CPACK_PROJECT_CONFIG_FILE and included by CPack after +# CPackConfig.cmake, so these settings win over the main project's. + +# Faster payload compression. +set(CPACK_RPM_COMPRESSION_TYPE "zstd") + +# Disable debuginfo/debugsource for the DuckDB plugin package. +set(CPACK_RPM_DEBUGINFO_PACKAGE OFF) +set(CPACK_RPM_PACKAGE_DEBUG 0) +set(CPACK_STRIP_FILES OFF) + +# Prevent rpmbuild itself from running brp-strip / find-debuginfo. +# CPACK_STRIP_FILES only affects CPack's own stripping. +if(DEFINED CPACK_RPM_SPEC_MORE_DEFINE) + set(CPACK_RPM_SPEC_MORE_DEFINE "${CPACK_RPM_SPEC_MORE_DEFINE}\n%define __strip /bin/true\n%define __objdump /bin/true\n%define __os_install_post %nil\n%define __debug_install_post %nil") +else() + set(CPACK_RPM_SPEC_MORE_DEFINE "%define __strip /bin/true\n%define __objdump /bin/true\n%define __os_install_post %nil\n%define __debug_install_post %nil") +endif() + +# Disable debugsource mapping. Without this, CPackRPM requires the source +# directory path to be strictly longer than /usr/src/debug/-, +# which fails for short source paths like /home/rocky/mdb-server. +unset(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX) diff --git a/storage/duckdb/cmake/duckdb.cmake b/storage/duckdb/cmake/duckdb.cmake new file mode 100644 index 0000000000000..1cd6fca5bac2e --- /dev/null +++ b/storage/duckdb/cmake/duckdb.cmake @@ -0,0 +1,96 @@ +# Copyright (c) 2026, MariaDB Foundation. +# +# 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-1335 USA + +# +# Build DuckDB static libraries from submodule source. +# +# The upstream DuckDB repo lives at storage/duckdb/third_parties/duckdb/ +# as a git submodule. We build it via ExternalProject so its CMake targets +# (including one named "duckdb") don't clash with the MariaDB plugin target +# of the same name created by MYSQL_ADD_PLUGIN(). +# + +SET(DUCKDB_SUBMODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_parties/duckdb") +SET(DUCKDB_INCLUDE_DIR "${DUCKDB_SUBMODULE_DIR}/src/include") + +IF(NOT EXISTS "${DUCKDB_SUBMODULE_DIR}/CMakeLists.txt") + MESSAGE(FATAL_ERROR + "DuckDB submodule not found at ${DUCKDB_SUBMODULE_DIR}\n" + "Run: git submodule update --init ${DUCKDB_SUBMODULE_DIR}" + ) +ENDIF() + +INCLUDE(ExternalProject) + +# Map MariaDB build type to a DuckDB-friendly one. +IF(CMAKE_BUILD_TYPE MATCHES "[Dd]ebug") + SET(_DUCKDB_BUILD_TYPE "Debug") +ELSE() + SET(_DUCKDB_BUILD_TYPE "Release") +ENDIF() + +SET(_DUCKDB_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/duckdb-build") + +# The individual static archives DuckDB produces (for the three extensions +# listed in cmake/duckdb_extensions.cmake: core_functions, icu, json). +SET(_DUCKDB_STATIC_LIBS + "${_DUCKDB_BUILD_DIR}/src/libduckdb_static.a" + "${_DUCKDB_BUILD_DIR}/extension/libduckdb_generated_extension_loader.a" + "${_DUCKDB_BUILD_DIR}/extension/core_functions/libcore_functions_extension.a" + "${_DUCKDB_BUILD_DIR}/extension/icu/libicu_extension.a" + "${_DUCKDB_BUILD_DIR}/extension/jemalloc/libjemalloc_extension.a" + "${_DUCKDB_BUILD_DIR}/extension/parquet/libparquet_extension.a" + "${_DUCKDB_BUILD_DIR}/extension/json/libjson_extension.a" +) + +MESSAGE(STATUS "=== Building DuckDB from submodule (${DUCKDB_SUBMODULE_DIR}) ===") + +ExternalProject_Add(duckdb_build + PREFIX "${_DUCKDB_BUILD_DIR}" + SOURCE_DIR "${DUCKDB_SUBMODULE_DIR}" + BINARY_DIR "${_DUCKDB_BUILD_DIR}" + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${_DUCKDB_BUILD_TYPE} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DBUILD_SHELL=OFF + -DBUILD_UNITTESTS=OFF + -DENABLE_UNITTEST_CPP_TESTS=OFF + -DBUILD_PYTHON=OFF + -DBUILD_BENCHMARKS=OFF + -DBUILD_TPCE=OFF + -DEXTENSION_STATIC_BUILD=1 + "-DDUCKDB_EXTENSION_CONFIGS=${CMAKE_CURRENT_SOURCE_DIR}/cmake/duckdb_extensions.cmake" + -DENABLE_SANITIZER=FALSE + -DENABLE_UBSAN=OFF + -DOVERRIDE_GIT_DESCRIBE=v1.5.2-0-g0000000000 + INSTALL_COMMAND "" + BUILD_BYPRODUCTS ${_DUCKDB_STATIC_LIBS} + USES_TERMINAL_BUILD ON +) + +# Expose all DuckDB archives as a single INTERFACE target so the rest of the +# cmake tree links against "libduckdb" unchanged. +ADD_LIBRARY(libduckdb INTERFACE) +TARGET_LINK_LIBRARIES(libduckdb INTERFACE -Wl,--start-group ${_DUCKDB_STATIC_LIBS} -Wl,--end-group) +ADD_DEPENDENCIES(libduckdb duckdb_build) + +MESSAGE(STATUS "DuckDB include: ${DUCKDB_INCLUDE_DIR}") +MESSAGE(STATUS "DuckDB libs: ${_DUCKDB_STATIC_LIBS}") + +INCLUDE_DIRECTORIES(BEFORE SYSTEM "${DUCKDB_INCLUDE_DIR}") +INCLUDE_DIRECTORIES(BEFORE SYSTEM "${DUCKDB_SUBMODULE_DIR}/third_party/re2") +SET(DUCKDB_LIBRARY libduckdb) diff --git a/storage/duckdb/cmake/duckdb_extensions.cmake b/storage/duckdb/cmake/duckdb_extensions.cmake new file mode 100644 index 0000000000000..b831f89cc0a5d --- /dev/null +++ b/storage/duckdb/cmake/duckdb_extensions.cmake @@ -0,0 +1,6 @@ +# Extensions required by the DuckDB storage engine plugin for MariaDB. +# This config is passed to DuckDB via DUCKDB_EXTENSION_CONFIGS. + +duckdb_extension_load(core_functions) +duckdb_extension_load(icu) +duckdb_extension_load(json) diff --git a/storage/duckdb/cmake/duckdb_target_setup.cmake b/storage/duckdb/cmake/duckdb_target_setup.cmake new file mode 100644 index 0000000000000..5a79dc85fcf4d --- /dev/null +++ b/storage/duckdb/cmake/duckdb_target_setup.cmake @@ -0,0 +1,23 @@ +# +# Common compile settings for all DuckDB plugin sub-libraries. +# Usage: duckdb_setup_target() +# +MACRO(duckdb_setup_target _target) + # DuckDB headers require C++17. + SET_TARGET_PROPERTIES(${_target} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ON + ) + # libduckdb_bundle.a is built without debug STL wrappers. + # -U unconditionally undefines the macro no matter how it was inherited + # (CMAKE_CXX_FLAGS_DEBUG, directory properties, generator expressions, etc.). + # Mismatched _GLIBCXX_DEBUG changes sizeof(std::vector) and friends -> SIGSEGV. + TARGET_COMPILE_OPTIONS(${_target} PRIVATE + -U_GLIBCXX_DEBUG -U_GLIBCXX_ASSERTIONS + ) + IF(DUCKDB_WERROR) + TARGET_COMPILE_OPTIONS(${_target} PRIVATE -Werror) + ENDIF() + ADD_DEPENDENCIES(${_target} GenError) +ENDMACRO() diff --git a/storage/duckdb/common/CMakeLists.txt b/storage/duckdb/common/CMakeLists.txt new file mode 100644 index 0000000000000..2fca4a7b28880 --- /dev/null +++ b/storage/duckdb/common/CMakeLists.txt @@ -0,0 +1,13 @@ +INCLUDE(${CMAKE_SOURCE_DIR}/storage/duckdb/cmake/duckdb_target_setup.cmake) + +SET(DUCKDB_COMMON_SOURCES + duckdb_types.cc + duckdb_log.cc + duckdb_charset_collation.cc + duckdb_timezone.cc +) + +ADD_LIBRARY(duckdb_common STATIC ${DUCKDB_COMMON_SOURCES}) +duckdb_setup_target(duckdb_common) + +TARGET_INCLUDE_DIRECTORIES(duckdb_common PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/storage/duckdb/common/duckdb_charset_collation.cc b/storage/duckdb/common/duckdb_charset_collation.cc new file mode 100644 index 0000000000000..67787fc47fb12 --- /dev/null +++ b/storage/duckdb/common/duckdb_charset_collation.cc @@ -0,0 +1,69 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "m_ctype.h" + +#include "duckdb_charset_collation.h" + +namespace myduck +{ + +std::string get_duckdb_collation(const CHARSET_INFO *cs, std::string &warn_msg) +{ + /* DuckDB stores all strings as UTF-8 internally and uses ICU for + collation support. The source MariaDB charset does not matter — + only the collation behavior (case/accent sensitivity) is relevant. + Map using CHARSET_INFO flags which work for any charset. */ + + if (strcmp(cs->cs_name.str, "utf8mb3") && + strcmp(cs->cs_name.str, "utf8mb4") && strcmp(cs->cs_name.str, "ascii")) + { + return COLLATION_BINARY; + } + + /* _bin Collation */ + if (cs->state & MY_CS_BINSORT) + return COLLATION_BINARY; + + /* utf8mb3_tolower_ci is _as_ci actually */ + if (cs->state & MY_CS_LOWER_SORT) + return COLLATION_NOCASE; + + /* In MariaDB levels_for_order is a bitmask of weight levels: + bit 0 (primary) - always set + bit 1 (secondary) - accent sensitivity + bit 2 (tertiary) - case sensitivity + DuckDB only distinguishes case and accent, so map accordingly. */ + bool accent_sensitive= + cs->levels_for_order & (1 << MY_CS_LEVEL_BIT_SECONDARY); + bool case_sensitive= cs->levels_for_order & (1 << MY_CS_LEVEL_BIT_TERTIARY); + + /* _ai_ci Collation (e.g. utf8mb4_0900_ai_ci, latin1_swedish_ci) */ + if (!case_sensitive && !accent_sensitive) + return COLLATION_NOCASE_NOACCENT; + + /* _as_ci Collation (e.g. utf8mb4_0900_as_ci) */ + if (!case_sensitive) + return COLLATION_NOCASE; + + /* _as_cs / _ai_cs Collation */ + return COLLATION_BINARY; +} + +} // namespace myduck diff --git a/storage/duckdb/common/duckdb_charset_collation.h b/storage/duckdb/common/duckdb_charset_collation.h new file mode 100644 index 0000000000000..0914f0068e8b4 --- /dev/null +++ b/storage/duckdb/common/duckdb_charset_collation.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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-1335 USA +*/ + +#pragma once + +#include + +#include +#include "m_ctype.h" + +namespace myduck +{ + +/** Get the corresponding DuckDB collation according to MariaDB CHARSET_INFO. + DuckDB's collation is not completely consistent with MariaDB. + We only focus on the two behaviors of NOCASE and NOACCENT. + @param[in] cs Pointer to MariaDB CHARSET_INFO structure + @param[out] warn_msg Warning message if there is any warning + @return DuckDB collation string +*/ +std::string get_duckdb_collation(const CHARSET_INFO *cs, + std::string &warn_msg); + +/* Charsets other than utf8mb3 and utf8mb4 use POSIX Collation directly. + DuckDB treats POSIX same as binary. We cannot use "binary" because it is + a keyword, so we use POSIX instead. */ +static const std::string COLLATION_BINARY= "POSIX"; +static const std::string COLLATION_NOCASE= "NOCASE"; +static const std::string COLLATION_NOCASE_NOACCENT= "NOCASE.NOACCENT"; + +} // namespace myduck diff --git a/storage/duckdb/common/duckdb_config.h b/storage/duckdb/common/duckdb_config.h new file mode 100644 index 0000000000000..3ca06a0083015 --- /dev/null +++ b/storage/duckdb/common/duckdb_config.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#ifndef DUCKDB_CONFIG_H +#define DUCKDB_CONFIG_H + +#include "duckdb/common/types.hpp" +#include +#include "typelib.h" + +class THD; + +namespace myduck +{ + +/* Global proxy variables */ +extern ulonglong global_memory_limit; +extern char *global_duckdb_temp_directory; +extern ulonglong global_max_temp_directory_size; +extern ulonglong global_max_threads; +extern ulonglong appender_allocator_flush_threshold; +extern ulonglong checkpoint_threshold; +extern my_bool global_use_dio; +extern my_bool global_scheduler_process_partial; +extern my_bool use_double_for_decimal; +extern my_bool require_primary_key; + +std::string BytesToHumanReadableString(uint64_t bytes, + uint64_t multiplier= 1024); + +/* ON_UPDATE callbacks for global proxy variables */ +void update_memory_limit_cb(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +void update_max_temp_directory_size_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +void update_threads_cb(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +void update_scheduler_process_partial_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +void update_appender_flush_threshold_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +void update_checkpoint_threshold_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save); + +/* Accessor functions for session THDVARs (defined in ha_duckdb.cc) */ +ulonglong get_thd_merge_join_threshold(THD *thd); +my_bool get_thd_force_no_collation(THD *thd); +ulong get_thd_explain_output(THD *thd); +ulonglong get_thd_disabled_optimizers(THD *thd); + +/* Explain output types */ +enum enum_explain_output +{ + EXPLAIN_ALL= 0, + EXPLAIN_OPTIMIZED_ONLY, + EXPLAIN_PHYSICAL_ONLY +}; + +extern const char *explain_output_names[]; +extern TYPELIB explain_output_typelib; + +/* Disabled optimizers names */ +extern const char *disabled_optimizers_names[]; +extern TYPELIB disabled_optimizers_typelib; + +} // namespace myduck + +#endif // DUCKDB_CONFIG_H diff --git a/storage/duckdb/common/duckdb_handler_errors.h b/storage/duckdb/common/duckdb_handler_errors.h new file mode 100644 index 0000000000000..8f1e36a4cc34a --- /dev/null +++ b/storage/duckdb/common/duckdb_handler_errors.h @@ -0,0 +1,19 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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. +*/ + +#pragma once + +#include "my_base.h" + +/* Handler-level error codes for DuckDB operations */ +#define HA_DUCKDB_DML_ERROR HA_ERR_GENERIC +#define HA_DUCKDB_APPEND_ERROR HA_ERR_GENERIC +#define HA_DUCKDB_CREATE_ERROR HA_WRONG_CREATE_OPTION +#define HA_DUCKDB_DROP_TABLE_ERROR HA_ERR_GENERIC +#define HA_DUCKDB_RENAME_ERROR HA_ERR_GENERIC +#define HA_DUCKDB_TRUNCATE_TABLE_ERROR HA_ERR_GENERIC diff --git a/storage/duckdb/common/duckdb_log.cc b/storage/duckdb/common/duckdb_log.cc new file mode 100644 index 0000000000000..a6e467c181508 --- /dev/null +++ b/storage/duckdb/common/duckdb_log.cc @@ -0,0 +1,35 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "duckdb_log.h" +#include "typelib.h" + +#include + +namespace myduck +{ + +ulonglong duckdb_log_options= 0; + +const char *duckdb_log_types[]= {"DUCKDB_QUERY", "DUCKDB_QUERY_RESULT", + nullptr}; + +TYPELIB log_options_typelib= CREATE_TYPELIB_FOR(duckdb_log_types); + +} // namespace myduck diff --git a/storage/duckdb/common/duckdb_log.h b/storage/duckdb/common/duckdb_log.h new file mode 100644 index 0000000000000..06976d87725be --- /dev/null +++ b/storage/duckdb/common/duckdb_log.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#ifndef DUCKDB_LOG_H +#define DUCKDB_LOG_H + +#include + +namespace myduck +{ +extern ulonglong duckdb_log_options; + +enum enum_duckdb_log_types +{ + DUCKDB_QUERY, + DUCKDB_QUERY_RESULT +}; + +extern const char *duckdb_log_types[]; +extern TYPELIB log_options_typelib; + +#define LOG_DUCKDB_QUERY (1ULL << myduck::enum_duckdb_log_types::DUCKDB_QUERY) +#define LOG_DUCKDB_QUERY_RESULT \ + (1ULL << myduck::enum_duckdb_log_types::DUCKDB_QUERY_RESULT) +} // namespace myduck + +#endif // DUCKDB_LOG_H diff --git a/storage/duckdb/common/duckdb_timezone.cc b/storage/duckdb/common/duckdb_timezone.cc new file mode 100644 index 0000000000000..c3b1f51f9d8b3 --- /dev/null +++ b/storage/duckdb/common/duckdb_timezone.cc @@ -0,0 +1,157 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "tztime.h" +#include "my_time.h" + +#include "duckdb_timezone.h" + +#include +#include + +namespace myduck +{ + +std::map TimeZoneOffsetHelper::timezone_offset_map; + +void TimeZoneOffsetHelper::init_timezone() +{ + add_timezone(50400, "Etc/GMT-14"); + add_timezone(46800, "Etc/GMT-13"); + add_timezone(43200, "Etc/GMT-12"); + add_timezone(39600, "Etc/GMT-11"); + add_timezone(36000, "Etc/GMT-10"); + add_timezone(32400, "Etc/GMT-9"); + add_timezone(28800, "Etc/GMT-8"); + add_timezone(25200, "Etc/GMT-7"); + add_timezone(21600, "Etc/GMT-6"); + add_timezone(18000, "Etc/GMT-5"); + add_timezone(14400, "Etc/GMT-4"); + add_timezone(10800, "Etc/GMT-3"); + add_timezone(7200, "Etc/GMT-2"); + add_timezone(3600, "Etc/GMT-1"); + add_timezone(0, "Etc/GMT"); + add_timezone(-3600, "Etc/GMT+1"); + add_timezone(-7200, "Etc/GMT+2"); + add_timezone(-10800, "Etc/GMT+3"); + add_timezone(-14400, "Etc/GMT+4"); + add_timezone(-18000, "Etc/GMT+5"); + add_timezone(-21600, "Etc/GMT+6"); + add_timezone(-25200, "Etc/GMT+7"); + add_timezone(-28800, "Etc/GMT+8"); + add_timezone(-32400, "Etc/GMT+9"); + add_timezone(-36000, "Etc/GMT+10"); + add_timezone(-39600, "Etc/GMT+11"); + add_timezone(-43200, "Etc/GMT+12"); +} + +std::string TimeZoneOffsetHelper::get_name_by_offset(int64_t offset, + std::string &warn_msg) +{ + auto it= timezone_offset_map.find(offset); + if (it != timezone_offset_map.end()) + { + return it->second; + } + else + { + std::ostringstream osst; + osst << "Can't find corresponding duckdb time_zone, using Etc/GMT."; + warn_msg= osst.str(); + return "Etc/GMT"; + } +} + +void TimeZoneOffsetHelper::add_timezone(int64_t offset, + const std::string &name) +{ + timezone_offset_map[offset]= name; +} + +/** + Compute system timezone offset in seconds using localtime. +*/ +static my_time_t get_system_timezone_offset() +{ + time_t now= time(nullptr); + struct tm l_time; + localtime_r(&now, &l_time); + + MYSQL_TIME t; + t.year= (uint) l_time.tm_year + 1900; + t.month= (uint) l_time.tm_mon + 1; + t.day= (uint) l_time.tm_mday; + t.hour= (uint) l_time.tm_hour; + t.minute= (uint) l_time.tm_min; + t.second= (uint) l_time.tm_sec; + t.time_type= MYSQL_TIMESTAMP_DATETIME; + t.neg= false; + t.second_part= 0; + + /* Compute seconds since 1970-01-01 00:00:00 */ + my_time_t days= calc_daynr((uint) t.year, (uint) t.month, (uint) t.day) - + (my_time_t) days_at_timestart; + my_time_t seconds= + days * SECONDS_IN_24H + + ((int64_t) t.hour * 3600 + (int64_t) (t.minute * 60 + t.second)); + + /* Get my_time_t via system time zone. */ + long not_used_tz; + uint not_used_err; + my_system_gmt_sec(&t, ¬_used_tz, ¬_used_err); + + return (seconds - now); +} + +std::string get_timezone_according_thd(THD *thd, std::string &warn_msg) +{ + Time_zone *tz= thd->variables.time_zone; + const String *tz_name= tz->get_name(); + std::string name_str(tz_name->ptr(), tz_name->length()); + + /* SYSTEM timezone */ + if (tz == my_tz_SYSTEM || name_str == "SYSTEM") + { + my_time_t offset= get_system_timezone_offset(); + return TimeZoneOffsetHelper::get_name_by_offset(offset, warn_msg); + } + + /* Offset timezone like +08:00 */ + if (name_str.size() >= 3 && (name_str[0] == '+' || name_str[0] == '-')) + { + /* Parse offset from "+HH:MM" format */ + int hours= 0, minutes= 0; + char sign= name_str[0]; + if (sscanf(name_str.c_str() + 1, "%d:%d", &hours, &minutes) >= 1) + { + int64_t offset= (int64_t) hours * 3600 + (int64_t) minutes * 60; + if (sign == '-') + offset= -offset; + return TimeZoneOffsetHelper::get_name_by_offset(offset, warn_msg); + } + } + + /* Named timezone — pass through directly to DuckDB */ + return name_str; +} + +} // namespace myduck diff --git a/storage/duckdb/common/duckdb_timezone.h b/storage/duckdb/common/duckdb_timezone.h new file mode 100644 index 0000000000000..9fc1bba2b18dc --- /dev/null +++ b/storage/duckdb/common/duckdb_timezone.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#ifndef DUCKDB_TIMEZONE_H +#define DUCKDB_TIMEZONE_H + +#include +#include + +class THD; + +namespace myduck +{ +constexpr long days_at_timestart= 719528; + +class TimeZoneOffsetHelper +{ +public: + static void init_timezone(); + + static std::string get_name_by_offset(int64_t offset, std::string &warn_msg); + +private: + static void add_timezone(int64_t offset, const std::string &name); + static std::map timezone_offset_map; +}; + +/** Get duckdb timezone name for current thread. + @param thd THD + @param warn_msg output warning message + @return timezone name suitable for DuckDB +*/ +std::string get_timezone_according_thd(THD *thd, std::string &warn_msg); + +} // namespace myduck + +#endif // DUCKDB_TIMEZONE_H diff --git a/storage/duckdb/common/duckdb_types.cc b/storage/duckdb/common/duckdb_types.cc new file mode 100644 index 0000000000000..625e1c0b168dc --- /dev/null +++ b/storage/duckdb/common/duckdb_types.cc @@ -0,0 +1,116 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "duckdb_types.h" +#include +#include +#include + +#include +#include "m_string.h" +#include "sql_table.h" + +/* Refers to create_table_info_t::normalize_table_name */ +DatabaseTableNames::DatabaseTableNames(const char *name) +{ + const char *name_ptr; + size_t name_len; + const char *db_ptr; + size_t db_len; + const char *ptr; + + /* Scan name from the end */ + ptr= strend(name) - 1; + + /* seek to the last path separator */ + while (ptr >= name && *ptr != '\\' && *ptr != '/') + ptr--; + + name_ptr= ptr + 1; + name_len= strlen(name_ptr); + + /* skip any number of path separators */ + while (ptr >= name && (*ptr == '\\' || *ptr == '/')) + ptr--; + + assert(ptr >= name); + + /* seek to the last but one path separator */ + db_len= 0; + while (ptr >= name && *ptr != '\\' && *ptr != '/') + { + ptr--; + db_len++; + } + + db_ptr= ptr + 1; + + std::string raw_table_name= std::string(name_ptr, name_len); + std::string raw_db_name= std::string(db_ptr, db_len); + + /* + When there are escape characters in the table name or database name + (such as '-' is converted to '@002d'), we need to restore it to the + original characters. + + Skip conversion for #sql- prefixed names (internal temp tables used + during ALTER TABLE). filename_to_tablename() cannot handle the '#' + prefix and would mangle the name. In AliSQL this is not an issue + because dd::Table provides the clean name directly. + */ + if (strncmp(raw_table_name.c_str(), "#sql", 4) == 0) + { + table_name= raw_table_name; + } + else + { + char ori_table_name[NAME_LEN + 1]; + size_t tbl_name_length= filename_to_tablename( + raw_table_name.c_str(), ori_table_name, sizeof(ori_table_name)); + table_name= std::string(ori_table_name, tbl_name_length); + } + + char ori_db_name[NAME_LEN + 1]; + size_t db_name_length= filename_to_tablename( + raw_db_name.c_str(), ori_db_name, sizeof(ori_db_name)); + db_name= std::string(ori_db_name, db_name_length); +} + +Databasename::Databasename(const char *path_name) +{ + char dbname[FN_REFLEN]; + const char *end, *ptr; + char tmp_buff[FN_REFLEN + 1]; + + char *tmp_name= tmp_buff; + /* Scan name from the end */ + ptr= strend(path_name) - 1; + while (ptr >= path_name && *ptr != '\\' && *ptr != '/') + ptr--; + ptr--; + end= ptr; + while (ptr >= path_name && *ptr != '\\' && *ptr != '/') + ptr--; + uint name_len= (uint) (end - ptr); + memcpy(tmp_name, ptr + 1, name_len); + tmp_name[name_len]= '\0'; + + filename_to_tablename(tmp_name, dbname, sizeof(tmp_buff) - 1); + name= std::string(dbname); +} diff --git a/storage/duckdb/common/duckdb_types.h b/storage/duckdb/common/duckdb_types.h new file mode 100644 index 0000000000000..2e39c1aeb1150 --- /dev/null +++ b/storage/duckdb/common/duckdb_types.h @@ -0,0 +1,44 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include + +/** + Utility class to parse a full path like "./db/table" into db_name and + table_name components. Handles escape characters in names. +*/ +class DatabaseTableNames +{ +public: + DatabaseTableNames(const char *name); + std::string db_name; + std::string table_name; +}; + +/** + Utility class to extract the database name from a path like "./db/". +*/ +class Databasename +{ +public: + Databasename(const char *path_name); + std::string name; +}; diff --git a/storage/duckdb/common/row_helpers.h b/storage/duckdb/common/row_helpers.h new file mode 100644 index 0000000000000..a8b7ae5c84737 --- /dev/null +++ b/storage/duckdb/common/row_helpers.h @@ -0,0 +1,107 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include +#include + +/** The following function is used to fetch data from one byte. +@param[in] b pointer to a byte to read +@return ulint integer, >= 0, < 256 */ +static inline uchar mach_read_from_1(const uchar *b) +{ + return ((uchar) (b[0])); +} + +/** Reads a ulint stored in the little-endian format. + @return unsigned long int */ +static inline ulong +mach_read_from_2_little_endian(const uchar *buf) /*!< in: from where to read */ +{ + return ((ulong) (buf[0]) | ((ulong) (buf[1]) << 8)); +} + +/** Reads a ulint stored in the little-endian format. +@param[in] buf From where to read. +@param[in] buf_size From how many bytes to read. +@return unsigned long int */ +static inline ulong mach_read_from_n_little_endian(const uchar *buf, + ulong buf_size) +{ + ulong n= 0; + const uchar *ptr; + + ptr= buf + buf_size; + + for (;;) + { + ptr--; + + n= n << 8; + + n+= (ulong) (*ptr); + + if (ptr == buf) + { + break; + } + } + + return (n); +} + +/** Reads a >= 5.0.3 format true VARCHAR length, in the MySQL row format, and + returns a pointer to the data. + @return pointer to the data, we skip the 1 or 2 bytes at the start + that are used to store the len */ +static inline const uchar *row_mysql_read_true_varchar( + ulong *len, /*!< out: variable-length field length */ + const uchar *field, /*!< in: field in the MySQL format */ + ulong lenlen) /*!< in: storage length of len: either 1 + or 2 bytes */ +{ + if (lenlen == 2) + { + *len= mach_read_from_2_little_endian(field); + + return (field + 2); + } + + *len= mach_read_from_1(field); + + return (field + 1); +} + +/** Reads a reference to a BLOB in the MySQL format. +@param[out] len BLOB length. +@param[in] ref BLOB reference in the MySQL format. +@param[in] col_len BLOB reference length (not BLOB length). +@return pointer to BLOB data */ +static inline const uchar * +row_mysql_read_blob_ref(ulong *len, const uchar *ref, ulong col_len) +{ + uchar *data; + + *len= mach_read_from_n_little_endian(ref, col_len - 8); + + memcpy(&data, ref + col_len - 8, sizeof data); + + return (data); +} diff --git a/storage/duckdb/convertor/CMakeLists.txt b/storage/duckdb/convertor/CMakeLists.txt new file mode 100644 index 0000000000000..6a709e00377c7 --- /dev/null +++ b/storage/duckdb/convertor/CMakeLists.txt @@ -0,0 +1,13 @@ +INCLUDE(${CMAKE_SOURCE_DIR}/storage/duckdb/cmake/duckdb_target_setup.cmake) + +SET(DUCKDB_CONVERTOR_SOURCES + ddl_convertor.cc + dml_convertor.cc + duckdb_select.cc +) + +ADD_LIBRARY(duckdb_convertor STATIC ${DUCKDB_CONVERTOR_SOURCES}) +duckdb_setup_target(duckdb_convertor) + +TARGET_INCLUDE_DIRECTORIES(duckdb_convertor PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +TARGET_LINK_LIBRARIES(duckdb_convertor PUBLIC duckdb_common) diff --git a/storage/duckdb/convertor/ddl_convertor.cc b/storage/duckdb/convertor/ddl_convertor.cc new file mode 100644 index 0000000000000..16f281fa9f310 --- /dev/null +++ b/storage/duckdb/convertor/ddl_convertor.cc @@ -0,0 +1,1129 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "ddl_convertor.h" + +#include +#include +#include +#include +#include + +#include +#undef UNKNOWN +#include "duckdb_charset_collation.h" +#include "duckdb_config.h" +#include "sql_class.h" +#include "sql_alter.h" +#include "sql_table.h" /* primary_key_name */ + +/* ----- Helpers ----- */ + +bool report_duckdb_table_struct_error(const char *not_supported, + const char *try_instead, + const char *column, + ddl_error_context ctx) +{ + if (ctx == ddl_error_context::CREATE) + { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), "DuckDB", not_supported); + } + else + { + char buf[512]; + snprintf(buf, sizeof(buf), "%s is not supported. Try %s '%s'", + not_supported, try_instead, column); + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_GENERIC, buf, "DuckDB"); + } + return true; +} + +/** Check if two types differ between old and new Create_field / Field */ +static bool is_type_changed(const Create_field *new_field, + const Field *old_field) +{ + std::string old_type= FieldConvertor::convert_type(old_field); + + /* + Build a temporary Field-like check via the new Create_field. + For simplicity compare the DuckDB type strings. + */ + if (new_field->field) + { + /* new_field->field points to the original old field for CHANGE COLUMN */ + /* The actual new type comes from the altered_table's field */ + } + return false; /* Will be refined once we have the new table's field */ +} + +/** Check if nullable changed */ +static bool is_nullable_change(const Create_field *new_field, + const Field *old_field) +{ + bool old_not_null= (old_field->flags & NOT_NULL_FLAG) != 0; + bool new_not_null= (new_field->flags & NOT_NULL_FLAG) != 0; + return old_not_null != new_not_null; +} + +/** Check if name changed */ +static bool is_name_changed(const Create_field *new_field, + const Field *old_field) +{ + if (new_field->change.str == nullptr) + return false; + return strcasecmp(new_field->field_name.str, new_field->change.str) != 0; +} + +/** Find the associated field in the new table. */ +static Field *find_field(const Create_field *new_field, const TABLE *new_table) +{ + Field **first_field= new_table->field; + Field **ptr, *cur_field; + for (ptr= first_field; (cur_field= *ptr); ptr++) + { + if (strcasecmp(cur_field->field_name.str, new_field->field_name.str) == 0) + break; + } + return cur_field; +} + +/** Check if the key is primary key. */ +static bool is_primary_key(const KEY *key) __attribute__((unused)); +static bool is_primary_key(const KEY *key) +{ + return ((key->flags & HA_NOSAME) != 0) && + (strcasecmp(key->name.str, primary_key_name.str) == 0); +} + +/** + Extract default expression string for a field from TABLE_SHARE::vcol_defs. + + The Item tree in field->default_value->expr may be corrupted at + ha_duckdb::create() time (the mem_root it was allocated on gets reset + between pack_vcols and ha_create_table). Instead, read the original + expression text directly from the .frm binary blob (vcol_defs). + + vcol_defs format per entry (FRM_VER_EXPRESSSIONS): + byte 0 : type (VCOL_DEFAULT=2) + bytes 1-2 : field_nr + bytes 3-4 : expr_length + byte 5 : name_length + bytes 6.. : name (name_length bytes) + then : expression text (expr_length bytes) +*/ +static std::string get_default_expr_from_vcol_defs(const TABLE_SHARE *share, + uint target_field_nr) +{ + if (!share->vcol_defs.length || !share->vcol_defs.str) + return ""; + + const uchar *pos= share->vcol_defs.str; + const uchar *end= pos + share->vcol_defs.length; + + while (pos < end) + { + uint type= pos[0]; + uint field_nr= uint2korr(pos + 1); + uint expr_len= uint2korr(pos + 3); + uint name_len= pos[5]; + pos+= 6 + name_len; /* FRM_VCOL_NEW_HEADER_SIZE + name */ + + if (type == 2 /* VCOL_DEFAULT */ && field_nr == target_field_nr) + return std::string((const char *) pos, expr_len); + + pos+= expr_len; + } + return ""; +} + +/** + Read the literal default value of a field from the default record and + return it as a string suitable for DuckDB SQL. + + BIT fields are converted to DuckDB blob literal format: '\xHH...'::BLOB. + Other fields use standard quoted literal format: 'value'. + + @param field Field whose default value to read (must not be at offset) + @param offset Offset from record[0] to default_values (s->default_values - + record[0]) + @return Default value string, or "NULL" if field is null at default + record +*/ +static std::string get_field_default_for_duckdb(Field *field, + my_ptrdiff_t offset) +{ + field->move_field_offset(offset); + + std::string default_value; + if (field->is_null()) + { + default_value= "NULL"; + } + else if (field->type() == MYSQL_TYPE_BIT) + { + /* BIT maps to blob in DuckDB: '\xHH\xHH...'::BLOB */ + char vbuf[MAX_FIELD_WIDTH]; + String vstr(vbuf, sizeof(vbuf), &my_charset_bin); + String *val= field->val_str(&vstr); + std::ostringstream ss; + ss << "'"; + if (val) + { + for (uint i= 0; i < val->length(); i++) + { + char hx[8]; + snprintf(hx, sizeof(hx), "\\x%02X", (unsigned char) val->ptr()[i]); + ss << hx; + } + } + ss << "'::BLOB"; + default_value= ss.str(); + } + else + { + char buf[MAX_FIELD_WIDTH]; + String str(buf, sizeof(buf), system_charset_info); + String *val= field->val_str(&str); + if (val && val->length() > 0) + default_value= "'" + std::string(val->ptr(), val->length()) + "'"; + else + default_value= "NULL"; + } + + field->move_field_offset(-offset); + return default_value; +} + +/* ----- String constants ----- */ + +static constexpr char CREATE_TABLE_STR[]= "CREATE TABLE "; +static constexpr char IF_NOT_EXISTS_STR[]= "IF NOT EXISTS "; +static constexpr char ALTER_TABLE_OP_STR[]= "ALTER TABLE "; +static constexpr char RENAME_TABLE_OP_STR[]= " RENAME TO "; +static constexpr char ALTER_COLUMN_OP_STR[]= " ALTER COLUMN "; +static constexpr char ADD_COLUMN_OP_STR[]= " ADD COLUMN "; +static constexpr char DROP_COLUMN_OP_STR[]= " DROP COLUMN "; +static constexpr char RENAME_COLUMN_OP_STR[]= " RENAME COLUMN "; +static constexpr char DEFINE_DEFAULT_STR[]= " DEFAULT "; +static constexpr char SET_DATA_TYPE_STR[]= " SET DATA TYPE "; +static constexpr char SET_DEFAULT_STR[]= " SET DEFAULT "; +static constexpr char DROP_DEFAULT_STR[]= " DROP DEFAULT"; +static constexpr char SET_NOT_NULL_STR[]= " SET NOT NULL"; +static constexpr char DROP_NOT_NULL_STR[]= " DROP NOT NULL"; + +/* ----- Statement builders ----- */ + +static void append_stmt_alter_table(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name) +{ + output << "USE \"" << schema_name << "\";"; + output << ALTER_TABLE_OP_STR << '"' << table_name << '"'; +} + +static void append_stmt_column_add(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name, + const std::string &column_type, + bool has_default, + const std::string &default_value) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty() && + !column_type.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ADD_COLUMN_OP_STR << '"' << column_name << '"' << " " + << column_type; + if (has_default) + output << DEFINE_DEFAULT_STR << default_value; + output << ";"; +} + +static void append_stmt_column_drop(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << DROP_COLUMN_OP_STR << '"' << column_name << '"' << ";"; +} + +static void append_stmt_column_change_type(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name, + const std::string &column_type) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty() && + !column_type.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ALTER_COLUMN_OP_STR << '"' << column_name << '"' + << SET_DATA_TYPE_STR << column_type << ";"; +} + +static void append_stmt_column_rename(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &old_column_name, + const std::string &new_column_name) +{ + assert(!schema_name.empty() && !table_name.empty() && + !old_column_name.empty() && !new_column_name.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << RENAME_COLUMN_OP_STR << '"' << old_column_name << '"' << " TO " + << '"' << new_column_name << '"' << ";"; +} + +static void append_stmt_column_set_default(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name, + const std::string &default_value) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty() && + !default_value.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ALTER_COLUMN_OP_STR << '"' << column_name << '"' << SET_DEFAULT_STR + << default_value << ";"; +} + +static void append_stmt_column_drop_default(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ALTER_COLUMN_OP_STR << '"' << column_name << '"' + << DROP_DEFAULT_STR << ";"; +} + +static void append_stmt_column_set_not_null(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ALTER_COLUMN_OP_STR << '"' << column_name << '"' + << SET_NOT_NULL_STR << ";"; +} + +static void append_stmt_column_drop_not_null(std::ostringstream &output, + const std::string &schema_name, + const std::string &table_name, + const std::string &column_name) +{ + assert(!schema_name.empty() && !table_name.empty() && !column_name.empty()); + append_stmt_alter_table(output, schema_name, table_name); + output << ALTER_COLUMN_OP_STR << '"' << column_name << '"' + << DROP_NOT_NULL_STR << ";"; +} + +static void append_stmt_table_rename(std::ostringstream &output, + const std::string &old_schema_name, + const std::string &old_table_name, + const std::string &new_schema_name + __attribute__((unused)), + const std::string &new_table_name) +{ + assert(!old_schema_name.empty() && !old_table_name.empty() && + !new_schema_name.empty() && !new_table_name.empty()); + assert(old_schema_name == new_schema_name); + append_stmt_alter_table(output, old_schema_name, old_table_name); + output << RENAME_TABLE_OP_STR << '"' << new_table_name << '"' << ";"; +} + +/* ----- FieldConvertor ----- */ + +bool FieldConvertor::check() +{ + /* not support auto_increment */ + if (m_field->flags & AUTO_INCREMENT_FLAG) + return report_duckdb_table_struct_error( + "AUTO_INCREMENT", "removing AUTO_INCREMENT from column", + m_field->field_name.str, m_ctx); + + /* No support for INVISIBLE columns. */ + if (m_field->invisible >= INVISIBLE_USER) + return report_duckdb_table_struct_error("INVISIBLE column", + "removing INVISIBLE from column", + m_field->field_name.str, m_ctx); + + /* No support for non-utf8 charset. */ + if (m_field->has_charset()) + { + const CHARSET_INFO *cs= m_field->charset(); + if (strcmp(cs->cs_name.str, "utf8") && + strcmp(cs->cs_name.str, "utf8mb3") && + strcmp(cs->cs_name.str, "utf8mb4") && strcmp(cs->cs_name.str, "ascii")) + { + return report_duckdb_table_struct_error( + "non-utf8 charset", "using utf8mb4 charset for column", + m_field->field_name.str, m_ctx); + } + } + + /* No support for generated column. */ + assert(!m_field->vcol_info); + + return false; +} + +std::string FieldConvertor::translate() +{ + Field *field= m_field; + + /* Skip system-invisible columns (e.g. row versioning) */ + if (field->invisible >= INVISIBLE_SYSTEM) + return ""; + + std::ostringstream result; + + result << '"' << field->field_name.str << '"' << " "; + result << convert_type(m_field); + + if (field->flags & NOT_NULL_FLAG) + result << " NOT NULL"; + + /* + Get default value from Field directly (no dd::Column in MariaDB). + In MariaDB, default expressions are stored in field->default_value + (Virtual_column_info*), and simple defaults are stored in record[1]. + */ + if (!(field->flags & NO_DEFAULT_VALUE_FLAG)) + { + if (field->default_value) + { + /* + Expression default. The Item tree in field->default_value->expr + is corrupted at ha_duckdb::create() time. Read the expression + string directly from TABLE_SHARE::vcol_defs (.frm binary blob). + */ + std::string expr_str= + get_default_expr_from_vcol_defs(field->table->s, field->field_index); + if (!expr_str.empty()) + result << " DEFAULT (" << expr_str << ")"; + } + else if (field->table->s->default_values && field->table->record[0]) + { + /* + Simple literal default — extract from s->default_values. + At ha_create() time these pointers may not be initialised yet + (e.g. nullable column without explicit DEFAULT), so guard. + */ + my_ptrdiff_t offset= + field->table->s->default_values - field->table->record[0]; + std::string def= get_field_default_for_duckdb(field, offset); + if (def != "NULL") + result << " DEFAULT " << def; + } + } + + assert(!(field->flags & AUTO_INCREMENT_FLAG)); + + return result.str(); +} + +std::string FieldConvertor::convert_type(const Field *field) +{ + std::string ret; + + enum_field_types field_type= field->real_type(); + bool is_unsigned= (field->flags & UNSIGNED_FLAG) != 0; + bool has_charset= field->has_charset(); + + switch (field_type) + { + case MYSQL_TYPE_TINY: + ret= is_unsigned ? "utinyint" : "tinyint"; + break; + case MYSQL_TYPE_SHORT: + ret= is_unsigned ? "usmallint" : "smallint"; + break; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + ret= is_unsigned ? "uinteger" : "integer"; + break; + case MYSQL_TYPE_LONGLONG: + ret= is_unsigned ? "ubigint" : "bigint"; + break; + case MYSQL_TYPE_FLOAT: + ret= "float"; + break; + case MYSQL_TYPE_DOUBLE: + ret= "double"; + break; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: { + const Field_new_decimal *decimal_field= + static_cast(field); + uint precision= decimal_field->precision; + uint dec= decimal_field->dec; + if (precision <= 38) + { + ret= "decimal(" + std::to_string(precision) + "," + std::to_string(dec) + + ")"; + } + else if (myduck::use_double_for_decimal) + { + ret= "double"; + } + else + { + /* Clamp to max DuckDB precision */ + ret= "decimal(38," + std::to_string(std::min((uint) 38, dec)) + ")"; + } + break; + } + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: + ret= "timestamptz"; + break; + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATE: + ret= "date"; + break; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + ret= "time"; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: + ret= "datetime"; + break; + case MYSQL_TYPE_YEAR: + ret= "integer"; + break; + case MYSQL_TYPE_BIT: + ret= "blob"; + break; + case MYSQL_TYPE_GEOMETRY: + ret= "blob"; + break; + case MYSQL_TYPE_NULL: + break; + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + ret= "varchar"; + break; + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + ret= has_charset ? "varchar" : "blob"; + break; + default: + ret= "__unknown_type"; + break; + } + + /* Append DuckDB collation for varchar columns */ + if (ret == "varchar" && field->has_charset()) + { + std::string warn_msg; + std::string co= myduck::get_duckdb_collation(field->charset(), warn_msg); + ret.append(" COLLATE ").append(co); + } + + /* MariaDB does not have MYSQL_TYPE_JSON; JSON is stored as LONG_BLOB with + a special charset. This is handled by the blob/varchar path above. */ + + std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); + + return ret; +} + +/* ----- CreateTableConvertor ----- */ + +bool CreateTableConvertor::check() +{ + Field **first_field= m_table->field; + Field **ptr, *field; + + for (ptr= first_field; (field= *ptr); ptr++) + { + if (FieldConvertor(field, ddl_error_context::CREATE).check()) + return true; + } + + /* Check PK. */ + TABLE_SHARE *share= m_table->s; + + bool has_pk= false; + KEY *key_info= m_table->key_info; + + /* + TABLE_SHARE::primary_key is supposed to be either MAX_KEY or an index into + key_info[]. However during CREATE (especially via ALGORITHM=COPY shadow + tables) it can be uninitialized/garbage, so validate the range and + fall back to scanning keys. + */ + if (share->primary_key != MAX_KEY && share->primary_key < share->keys) + has_pk= is_primary_key(key_info + share->primary_key); + + if (!has_pk) + { + for (uint i= 0; i < share->keys; i++) + { + if (is_primary_key(key_info + i)) + { + has_pk= true; + break; + } + } + } + + if (myduck::require_primary_key && !has_pk) + { + my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0)); + return true; + } + + return false; +} + +std::string CreateTableConvertor::translate() +{ + std::ostringstream result; + assert((m_create_info->options & HA_LEX_CREATE_TMP_TABLE) == 0); + + result << "CREATE SCHEMA IF NOT EXISTS " << '"' << m_schema_name << '"' + << ";"; + + result << "USE " << '"' << m_schema_name << '"' << ";"; + + result << CREATE_TABLE_STR; + /* MariaDB: IF NOT EXISTS is handled at the SQL layer, not in HA_CREATE_INFO. + Always use IF NOT EXISTS for safety in DuckDB. */ + result << IF_NOT_EXISTS_STR; + + result << '"' << m_table_name << '"'; + result << " ("; + + append_column_definition(result); + result << ");"; + + return result.str(); +} + +void CreateTableConvertor::append_column_definition(std::ostringstream &output) +{ + Field *field= nullptr; + Field **first_field= m_table->field; + for (Field **ptr= first_field; (field= *ptr); ptr++) + { + if (ptr != first_field) + output << ","; + output << FieldConvertor(field).translate(); + } +} + +/* ----- RenameTableConvertor ----- */ + +bool RenameTableConvertor::check() +{ + if (m_new_schema_name != m_schema_name) + { + return report_duckdb_table_struct_error( + "cross-schema rename", "renaming within the same schema, not to", + m_new_schema_name.c_str()); + } + return false; +} + +std::string RenameTableConvertor::translate() +{ + std::ostringstream result; + append_stmt_table_rename(result, m_schema_name, m_table_name, + m_new_schema_name, m_new_table_name); + return result.str(); +} + +/* ----- AddColumnConvertor ----- */ + +void AddColumnConvertor::prepare_columns() +{ + List_iterator new_field_it(m_alter_info->create_list); + Create_field *new_field; + + while ((new_field= new_field_it++)) + { + if (new_field->field != nullptr) + continue; + + Field *field= find_field(new_field, m_new_table); + m_columns_to_add.emplace_back(new_field, field); + + if ((new_field->flags & NOT_NULL_FLAG) != 0) + m_columns_to_set_not_null.emplace_back(new_field, field); + } +} + +bool AddColumnConvertor::check() +{ + for (auto &pair : m_columns_to_add) + { + if (FieldConvertor(pair.second).check()) + return true; + } + return false; +} + +std::string AddColumnConvertor::translate() +{ + std::ostringstream result; + + for (auto &pair : m_columns_to_add) + { + Create_field *new_field= pair.first; + Field *field= pair.second; + assert(field != nullptr); + + std::string type= FieldConvertor::convert_type(field); + + bool has_default= false; + std::string default_value= "NULL"; + + if (new_field->on_update != nullptr) + { + has_default= true; + default_value= "CURRENT_TIMESTAMP"; + } + else if (!(field->flags & NO_DEFAULT_VALUE_FLAG)) + { + my_ptrdiff_t offset= + field->table->s->default_values - field->table->record[0]; + has_default= true; + default_value= get_field_default_for_duckdb(field, offset); + } + + append_stmt_column_add(result, m_schema_name, m_table_name, + new_field->field_name.str, type, has_default, + default_value); + } + + for (auto &pair : m_columns_to_set_not_null) + { + Create_field *new_field= pair.first; + assert((new_field->flags & NOT_NULL_FLAG) != 0); + append_stmt_column_set_not_null(result, m_schema_name, m_table_name, + new_field->field_name.str); + } + + return result.str(); +} + +/* ----- DropColumnConvertor ----- */ + +void DropColumnConvertor::prepare_columns() +{ + /* + Prefer Alter_info::drop_list as authoritative (if still populated). + Fallback to old/new table diff when drop_list is unavailable at commit + time. + + Important: exclude renamed/changed columns from diff-based drop detection. + In MariaDB, rename/change is represented in Alter_info::create_list via + Create_field::change (old name). + */ + if (m_alter_info && m_alter_info->drop_list.elements) + { + List_iterator drop_it(m_alter_info->drop_list); + Alter_drop *drop; + while ((drop= drop_it++)) + { + if (drop->type != Alter_drop::COLUMN) + continue; + + for (Field **old_ptr= m_old_table->field; *old_ptr; old_ptr++) + { + Field *old_field= *old_ptr; +#if MYSQL_VERSION_ID >= 110501 + if (strcasecmp(old_field->field_name.str, drop->name.str) == 0) +#else + if (strcasecmp(old_field->field_name.str, drop->name) == 0) +#endif + { + m_columns_to_drop.emplace_back(nullptr, old_field); + break; + } + } + } + return; + } + + if (!m_new_table) + return; + + /* Build a set of old names that are being renamed/changed. */ + std::unordered_set renamed_old_names; + if (m_alter_info) + { + List_iterator def_it(m_alter_info->create_list); + Create_field *def; + while ((def= def_it++)) + { + if (def->change.str) + renamed_old_names.emplace(def->change.str); + } + } + + for (Field **old_ptr= m_old_table->field; *old_ptr; old_ptr++) + { + Field *old_field= *old_ptr; + + if (renamed_old_names.find(old_field->field_name.str) != + renamed_old_names.end()) + continue; + + bool found_in_new= false; + for (Field **new_ptr= m_new_table->field; *new_ptr; new_ptr++) + { + if (strcasecmp((*new_ptr)->field_name.str, old_field->field_name.str) == + 0) + { + found_in_new= true; + break; + } + } + + if (!found_in_new) + m_columns_to_drop.emplace_back(nullptr, old_field); + } +} + +bool DropColumnConvertor::check() +{ + if (!myduck::require_primary_key) + return false; + + for (auto &pair : m_columns_to_drop) + { + Field *field= pair.second; + if (field && (field->flags & PRI_KEY_FLAG)) + { + my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0)); + return true; + } + } + + return false; +} + +std::string DropColumnConvertor::translate() +{ + std::ostringstream result; + + for (auto &pair : m_columns_to_drop) + { + Field *field= pair.second; + append_stmt_column_drop(result, m_schema_name, m_table_name, + field->field_name.str); + } + + return result.str(); +} + +/* ----- ChangeColumnDefaultConvertor ----- */ + +void ChangeColumnDefaultConvertor::prepare_columns() +{ + /* + Compare old and new table fields to detect default value changes. + We cannot use alter_info->alter_list because it is consumed + (emptied) by mysql_prepare_alter_table() before commit. + */ + for (Field **new_ptr= m_new_table->field; *new_ptr; new_ptr++) + { + Field *new_field= *new_ptr; + + /* Find the same column in the old table by name */ + Field *old_field= nullptr; + for (Field **old_ptr= m_old_table->field; *old_ptr; old_ptr++) + { + if (strcasecmp((*old_ptr)->field_name.str, new_field->field_name.str) == + 0) + { + old_field= *old_ptr; + break; + } + } + + if (!old_field) + continue; + + bool old_has_default= !(old_field->flags & NO_DEFAULT_VALUE_FLAG); + bool new_has_default= !(new_field->flags & NO_DEFAULT_VALUE_FLAG); + + if (old_has_default && !new_has_default) + { + /* Default was dropped */ + m_columns_to_drop_default.emplace_back(nullptr, new_field); + } + else if (new_has_default && !old_has_default) + { + /* Default was added */ + m_columns_to_set_default.emplace_back(nullptr, new_field); + } + else if (old_has_default && new_has_default) + { + /* Both have default — check if value changed */ + my_ptrdiff_t old_off= + old_field->table->s->default_values - old_field->table->record[0]; + my_ptrdiff_t new_off= + new_field->table->s->default_values - new_field->table->record[0]; + + old_field->move_field_offset(old_off); + new_field->move_field_offset(new_off); + + bool changed= false; + bool new_is_null= new_field->is_null(); + if (old_field->is_null() != new_is_null) + changed= true; + else if (!old_field->is_null()) + { + char buf1[MAX_FIELD_WIDTH], buf2[MAX_FIELD_WIDTH]; + String s1(buf1, sizeof(buf1), system_charset_info); + String s2(buf2, sizeof(buf2), system_charset_info); + String *v1= old_field->val_str(&s1); + String *v2= new_field->val_str(&s2); + if (v1 && v2) + changed= sortcmp(v1, v2, system_charset_info) != 0; + else + changed= (v1 != v2); + } + + old_field->move_field_offset(-old_off); + new_field->move_field_offset(-new_off); + + if (changed) + { + /* + If the new default is NULL in the default record, treat it + as DROP DEFAULT for DuckDB. MariaDB does not set + NO_DEFAULT_VALUE_FLAG for nullable columns on DROP DEFAULT, + but the semantic is "no explicit default". + */ + if (new_is_null) + m_columns_to_drop_default.emplace_back(nullptr, new_field); + else + m_columns_to_set_default.emplace_back(nullptr, new_field); + } + } + } +} + +std::string ChangeColumnDefaultConvertor::translate() +{ + std::ostringstream result; + + /* Drop default value. */ + for (auto &pair : m_columns_to_drop_default) + { + Field *field= pair.second; + append_stmt_column_drop_default(result, m_schema_name, m_table_name, + field->field_name.str); + } + + /* Set default value. */ + for (auto &pair : m_columns_to_set_default) + { + Field *field= pair.second; + + my_ptrdiff_t offset= + field->table->s->default_values - field->table->record[0]; + std::string default_value= get_field_default_for_duckdb(field, offset); + + append_stmt_column_set_default(result, m_schema_name, m_table_name, + field->field_name.str, default_value); + } + + return result.str(); +} + +/* ----- ChangeColumnConvertor ----- */ + +void ChangeColumnConvertor::prepare_columns() +{ + List_iterator new_field_it(m_alter_info->create_list); + Create_field *new_field; + Field *field; + + while ((new_field= new_field_it++)) + { + if (new_field->change.str == nullptr) + continue; + + field= new_field->field; + Field *cur_field= find_field(new_field, m_new_table); + + bool type_changed= is_type_changed(new_field, field); + bool nullable_changed= is_nullable_change(new_field, field); + bool name_changed= is_name_changed(new_field, field); + + /* Change type. */ + if (type_changed) + m_columns_to_change_type.emplace_back(new_field, cur_field); + + /* Change nullable. */ + if (nullable_changed) + { + if ((new_field->flags & NOT_NULL_FLAG) != 0) + m_columns_to_set_not_null.emplace_back(new_field, cur_field); + else + m_columns_to_drop_not_null.emplace_back(new_field, cur_field); + } + + /* Change name. */ + if (name_changed) + { + assert(field->flags & FIELD_IS_RENAMED); + m_columns_to_rename.emplace_back(new_field, cur_field); + } + + /* All columns will be saved here. */ + m_columns.emplace_back(new_field, cur_field); + } +} + +bool ChangeColumnConvertor::check() +{ + for (auto &pair : m_columns) + { + Field *new_field= pair.second; + if (FieldConvertor(new_field).check()) + return true; + } + return false; +} + +std::string ChangeColumnConvertor::translate() +{ + std::ostringstream result; + + /* Rename column. */ + for (auto &pair : m_columns_to_rename) + { + Create_field *new_field= pair.first; + Field *old_field= new_field->field; + assert(old_field->flags & FIELD_IS_RENAMED); + append_stmt_column_rename(result, m_schema_name, m_table_name, + old_field->field_name.str, + new_field->field_name.str); + } + + /* Change type. */ + for (auto &pair : m_columns_to_change_type) + { + Field *field= pair.second; + std::string new_type= FieldConvertor::convert_type(field); + append_stmt_column_change_type(result, m_schema_name, m_table_name, + field->field_name.str, new_type); + } + + /* Change default value. All columns should be processed. */ + for (auto &pair : m_columns) + { + Create_field *new_field= pair.first; + Field *field= pair.second; + bool drop_default= ((new_field->flags & NO_DEFAULT_VALUE_FLAG) != 0); + + /* Drop default value. */ + if (drop_default) + { + append_stmt_column_drop_default(result, m_schema_name, m_table_name, + new_field->field_name.str); + continue; + } + + if (!field || (field->flags & NO_DEFAULT_VALUE_FLAG)) + continue; + + my_ptrdiff_t offset= + field->table->s->default_values - field->table->record[0]; + std::string default_value= get_field_default_for_duckdb(field, offset); + + append_stmt_column_set_default(result, m_schema_name, m_table_name, + new_field->field_name.str, default_value); + } + + for (auto &pair : m_columns_to_drop_not_null) + { + Create_field *new_field= pair.first; + append_stmt_column_drop_not_null(result, m_schema_name, m_table_name, + new_field->field_name.str); + } + + for (auto &pair : m_columns_to_set_not_null) + { + Create_field *new_field= pair.first; + assert((new_field->flags & NOT_NULL_FLAG) != 0); + append_stmt_column_set_not_null(result, m_schema_name, m_table_name, + new_field->field_name.str); + } + + return result.str(); +} + +/* ----- ChangeColumnForPrimaryKeyConvertor ----- */ + +std::string ChangeColumnForPrimaryKeyConvertor::translate() +{ + std::ostringstream result; + for (auto field : m_columns_to_set_not_null) + { + assert(field->flags & PRI_KEY_FLAG); + assert(field->flags & NOT_NULL_FLAG); + append_stmt_column_set_not_null(result, m_schema_name, m_table_name, + field->field_name.str); + } + return result.str(); +} + +void ChangeColumnForPrimaryKeyConvertor::prepare_columns() +{ + Field **first_field= m_new_table->field; + Field **ptr, *cur_field; + for (ptr= first_field; (cur_field= *ptr); ptr++) + { + if (!(cur_field->flags & PRI_KEY_FLAG)) + continue; + if (!(cur_field->flags & NOT_NULL_FLAG)) + continue; + m_columns_to_set_not_null.push_back(cur_field); + } +} + +/* ----- Utility ----- */ + +std::string toHex(const char *data, size_t length) +{ + std::stringstream ss; + ss << "'"; + for (size_t i= 0; i < length; ++i) + { + ss << "\\x" << std::hex << std::uppercase << std::setw(2) + << std::setfill('0') + << static_cast(static_cast(data[i])); + } + ss << "'::BLOB"; + return ss.str(); +} diff --git a/storage/duckdb/convertor/ddl_convertor.h b/storage/duckdb/convertor/ddl_convertor.h new file mode 100644 index 0000000000000..a9908592ca490 --- /dev/null +++ b/storage/duckdb/convertor/ddl_convertor.h @@ -0,0 +1,371 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include +#include + +#include "duckdb_types.h" + +#define MYSQL_SERVER 1 +#include "my_global.h" +#include "sql_class.h" +#include "field.h" + +class BaseConvertor +{ +public: + /** Check if query can be executed by duckdb */ + virtual bool check()= 0; + + /** Get the query under duckdb syntax. */ + virtual std::string translate()= 0; + + virtual ~BaseConvertor()= default; +}; + +enum enum_ddl_convertor_type +{ + /* Do nothing */ + NONE_OP= 0, + + /* Drop column */ + DROP_COLUMN, + + /* Add column */ + ADD_COLUMN, + + /* Alter column */ + ALTER_COLUMN, + + /* Add index */ + ADD_INDEX, + + /* Rename table */ + RENAME_TABLE, + + /* This should be the last ! */ + DDL_CONVERTOR_TYPE_END +}; + +enum class ddl_error_context +{ + ALTER, + CREATE +}; + +class FieldConvertor : public BaseConvertor +{ +public: + FieldConvertor(Field *field, ddl_error_context ctx= ddl_error_context::ALTER) + : m_field(field), m_ctx(ctx) + { + } + + bool check() override; + + std::string translate() override; + + static std::string convert_type(const Field *field); + +private: + Field *m_field; + ddl_error_context m_ctx; +}; + +/** Convertor to translate "ALTER TABLE ..." */ +class AlterTableConvertor : public BaseConvertor +{ +public: + AlterTableConvertor(const std::string &schema_name, + const std::string &table_name, + const enum_ddl_convertor_type type) + : m_schema_name(schema_name), m_table_name(table_name), m_type(type) + { + } + ~AlterTableConvertor() override= default; + + bool check() override { return false; } + + std::string translate() override { return ""; } + + std::string m_schema_name; + std::string m_table_name; + const enum_ddl_convertor_type m_type; +}; + +/** Convert create table from mysql to duckdb. */ +class CreateTableConvertor : public BaseConvertor +{ +public: + CreateTableConvertor(THD *thd, const TABLE *table, + const HA_CREATE_INFO *create_info, + const std::string &schema_name, + const std::string &table_name) + : m_schema_name(schema_name), m_table_name(table_name), m_thd(thd), + m_table(table), m_create_info(create_info) + { + } + + ~CreateTableConvertor() override= default; + + bool check() override; + + std::string translate() override; + +private: + std::string m_schema_name; + std::string m_table_name; + + /** Thread context */ + THD *m_thd; + + /** Table to create */ + const TABLE *m_table; + + /** Create info */ + const HA_CREATE_INFO *m_create_info; + + /** Append column definition to output. + @param[in, out] output output stream */ + void append_column_definition(std::ostringstream &output); +}; + +/** Convertor to translate "RENAME TABLE ... to ..." or +"ALTER TABLE ... RENAME TO ..." */ +class RenameTableConvertor : public AlterTableConvertor +{ +public: + RenameTableConvertor(const std::string &old_schema_name, + const std::string &old_table_name, + const std::string &new_schema_name, + const std::string &new_table_name) + : AlterTableConvertor(old_schema_name, old_table_name, RENAME_TABLE), + m_new_schema_name(new_schema_name), m_new_table_name(new_table_name) + { + } + + ~RenameTableConvertor() override= default; + + bool check() override; + + /** ALTER TABLE ... RENAME TO ... */ + std::string translate() override; + +private: + /** New schema name */ + std::string m_new_schema_name; + + /** New table name */ + std::string m_new_table_name; +}; + +/** Pair of Create_field(new) and Field(new). */ +using Column= std::pair; + +/** Vector of columns to alter. */ +using Columns= std::vector; + +/** Convertor to translate "ALTER TABLE ... ADD COLUMN ..." */ +class AddColumnConvertor : public AlterTableConvertor +{ +public: + AddColumnConvertor(const std::string &schema_name, + const std::string &table_name, const TABLE *altered_table, + Alter_info *alter_info) + : AlterTableConvertor(schema_name, table_name, ADD_COLUMN), + m_new_table(altered_table), m_alter_info(alter_info) + { + prepare_columns(); + } + + ~AddColumnConvertor() override= default; + + bool check() override; + + std::string translate() override; + +private: + /** new TABLE */ + const TABLE *m_new_table; + + /** Alter options, fields and keys for the new version of table. */ + Alter_info *m_alter_info; + + /** Columns to add */ + Columns m_columns_to_add; + + /** Columns to set not null */ + Columns m_columns_to_set_not_null; + + /** Prepare columns to add and set not null. */ + void prepare_columns(); +}; + +/** Convertor to translate "ALTER TABLE ... DROP COLUMN ..." */ +class DropColumnConvertor : public AlterTableConvertor +{ +public: + DropColumnConvertor(const std::string &schema_name, + const std::string &table_name, const TABLE *old_table, + const TABLE *new_table, Alter_info *alter_info) + : AlterTableConvertor(schema_name, table_name, DROP_COLUMN), + m_old_table(old_table), m_new_table(new_table), + m_alter_info(alter_info) + { + prepare_columns(); + } + ~DropColumnConvertor() override= default; + + bool check() override; + + std::string translate() override; + +private: + /** old TABLE */ + const TABLE *m_old_table; + + /** new TABLE (altered) */ + const TABLE *m_new_table; + + /** Alter options, fields and keys for the new version of table. */ + Alter_info *m_alter_info; + + /** Columns to drop */ + Columns m_columns_to_drop; + + /** Prepare columns to drop. */ + void prepare_columns(); +}; + +/** Convertor to translate change column default value. */ +class ChangeColumnDefaultConvertor : public AlterTableConvertor +{ +public: + ChangeColumnDefaultConvertor(const std::string &schema_name, + const std::string &table_name, + const TABLE *old_table, const TABLE *new_table) + : AlterTableConvertor(schema_name, table_name, ALTER_COLUMN), + m_old_table(old_table), m_new_table(new_table) + { + prepare_columns(); + } + + ~ChangeColumnDefaultConvertor() override= default; + + std::string translate() override; + +private: + /** old TABLE */ + const TABLE *m_old_table; + + /** new TABLE (altered) */ + const TABLE *m_new_table; + + /** Columns to set default */ + Columns m_columns_to_set_default; + + /** Columns to drop default */ + Columns m_columns_to_drop_default; + + /** Prepare columns by comparing old and new table defaults. */ + void prepare_columns(); +}; + +/** Convertor to translate "ALTER TABLE ... [ CHANGE | MODIFY | RENAME ] +COLUMN ..." */ +class ChangeColumnConvertor : public AlterTableConvertor +{ +public: + ChangeColumnConvertor(const std::string &schema_name, + const std::string &table_name, const TABLE *new_table, + Alter_info *alter_info) + : AlterTableConvertor(schema_name, table_name, ALTER_COLUMN), + m_new_table(new_table), m_alter_info(alter_info) + { + prepare_columns(); + } + + ~ChangeColumnConvertor() override= default; + + bool check() override; + + std::string translate() override; + +private: + /** new TABLE */ + const TABLE *m_new_table; + + /** Alter options, fields and keys for the new version of table. */ + Alter_info *m_alter_info; + + /** Columns to change */ + Columns m_columns; + + /** Columns to change type */ + Columns m_columns_to_change_type; + + /** Columns to set not null */ + Columns m_columns_to_set_not_null; + + /** Columns to drop not null */ + Columns m_columns_to_drop_not_null; + + /** Columns to rename */ + Columns m_columns_to_rename; + + /** Prepare columns to change. */ + void prepare_columns(); +}; + +/** Convertor to set primary key column not null. */ +class ChangeColumnForPrimaryKeyConvertor : public AlterTableConvertor +{ +public: + ChangeColumnForPrimaryKeyConvertor(const std::string &schema_name, + const std::string &table_name, + const TABLE *new_table) + : AlterTableConvertor(schema_name, table_name, ALTER_COLUMN), + m_new_table(new_table) + { + prepare_columns(); + } + + std::string translate() override; + +private: + /** new TABLE */ + const TABLE *m_new_table; + + /** Columns to set not null */ + std::vector m_columns_to_set_not_null; + + /** Prepare columns to set not null. */ + void prepare_columns(); +}; + +/* Convert a binary string to hexadecimal representation '\x01\x0A\xAC'::BLOB + */ +std::string toHex(const char *data, size_t length); + +/* Report DuckDB table structure error */ +bool report_duckdb_table_struct_error( + const char *err_msg, const char *try_instead, const char *column, + ddl_error_context ctx= ddl_error_context::ALTER); diff --git a/storage/duckdb/convertor/dml_convertor.cc b/storage/duckdb/convertor/dml_convertor.cc new file mode 100644 index 0000000000000..f0eb0f8a0ad30 --- /dev/null +++ b/storage/duckdb/convertor/dml_convertor.cc @@ -0,0 +1,302 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "dml_convertor.h" + +#include +#include +#include +#include + +#include "sql_table.h" /* primary_key_name */ +#include "my_decimal.h" + +namespace myduck { extern my_bool use_double_for_decimal; } + +static const uint sizeof_trailing_comma= sizeof(", ") - 1; +static const uint sizeof_trailing_and= sizeof(" AND ") - 1; + +void append_field_value_to_sql(String &target_str, Field *field) +{ + if (field->is_null()) + { + target_str.append(STRING_WITH_LEN("NULL")); + return; + } + + char field_value_buffer[128]; + String field_value(field_value_buffer, sizeof(field_value_buffer), + &my_charset_bin); + field_value.length(0); + + enum_field_types type= field->real_type(); + switch (type) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: { + field->val_str(&field_value); + target_str.append(field_value); + break; + } + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: { + double value= field->val_real(); + std::stringstream ss; + ss << std::scientific + << std::setprecision(std::numeric_limits::max_digits10) + << value; + std::string d= ss.str(); + target_str.append(d.c_str(), d.length()); + break; + } + case MYSQL_TYPE_NEWDECIMAL: { + my_decimal value; + Field_new_decimal *decimal_field= static_cast(field); + uint precision= decimal_field->precision; + uint8 dec= decimal_field->dec; + if (precision <= 38) + { + decimal_field->val_decimal(&value); + char buff[DECIMAL_MAX_STR_LENGTH + 1]; + int string_length= DECIMAL_MAX_STR_LENGTH + 1; + decimal2string(&value, buff, &string_length, precision, dec, '0'); + target_str.append(buff, string_length); + } + else if (myduck::use_double_for_decimal) + { + /* DuckDB column is DOUBLE for precision >38 — emit as double literal */ + char buff[64]; + snprintf(buff, sizeof(buff), "%.17e", field->val_real()); + target_str.append(buff, strlen(buff)); + } + else + { + field->val_str(&field_value); + target_str.append(field_value); + } + break; + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: { + target_str.append(STRING_WITH_LEN("'")); + field->val_str(&field_value); + target_str.append(field_value); + target_str.append(STRING_WITH_LEN("'")); + break; + } + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: { + target_str.append(STRING_WITH_LEN("'")); + field->val_str(&field_value); + target_str.append(field_value); + target_str.append(STRING_WITH_LEN("'")); + break; + } + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: { + field->val_str(&field_value); + std::string hex_str= toHex(field_value.c_ptr_safe(), field_value.length()); + + if (FieldConvertor::convert_type(field) == "BLOB") + { + target_str.append(hex_str.c_str(), hex_str.size()); + } + else + { + target_str.append(STRING_WITH_LEN("DECODE(")); + target_str.append(hex_str.c_str(), hex_str.size()); + target_str.append(STRING_WITH_LEN(")::VARCHAR")); + } + break; + } + default: + target_str.append(STRING_WITH_LEN("__ERROR__")); + } +} + +static inline void append_table_name(TABLE *table, String &query) +{ + /* + Use normalized_path to resolve the actual DuckDB table name. + During ALTER TABLE, table->s->table_name is the final name (e.g. + "t_tinyint") but the DuckDB table is the temp "#sql-alter-..." table + created by ha_duckdb::create(). The normalized_path correctly contains + the temp name. + */ + DatabaseTableNames dt(table->s->normalized_path.str); + query.append(STRING_WITH_LEN("\"")); + query.append(dt.db_name.c_str(), dt.db_name.length()); + query.append(STRING_WITH_LEN("\"")); + query.append(STRING_WITH_LEN(".")); + query.append(STRING_WITH_LEN("\"")); + query.append(dt.table_name.c_str(), dt.table_name.length()); + query.append(STRING_WITH_LEN("\"")); +} + +static inline void get_write_fields(TABLE *table, std::vector &fields) +{ + for (uint i= 0; i < table->s->fields; i++) + { + Field *field= table->field[i]; + if (bitmap_is_set(table->write_set, field->field_index)) + { + fields.push_back(field); + } + } +} + +std::string DMLConvertor::translate() +{ + char query_buffer[128]; + String query(query_buffer, sizeof(query_buffer), &my_charset_bin); + query.length(0); + + generate_prefix(query); + generate_fields_and_values(query); + generate_where_clause(query); + + return (std::string(query.c_ptr_safe(), query.length())); +} + +void DMLConvertor::fill_index_fields_for_where(std::vector &fields) +{ + KEY *key_info= m_table->key_info; + if (key_info) + { + KEY_PART_INFO *key_part= key_info->key_part; + for (uint j= 0; j < key_info->user_defined_key_parts; j++, key_part++) + { + fields.push_back(key_part->field); + } + } + else + { + for (uint j= 0; j < m_table->s->fields; j++) + { + fields.push_back(m_table->field[j]); + } + } +} + +void DMLConvertor::generate_where_clause(String &query) +{ + std::vector fields; + fill_index_fields_for_where(fields); + assert(fields.size()); + + if (!fields.size()) + return; + + query.append(STRING_WITH_LEN(" WHERE ")); + + for (auto field : fields) + { + query.append(STRING_WITH_LEN("\"")); + query.append(field->field_name.str, field->field_name.length); + query.append(STRING_WITH_LEN("\"")); + query.append(STRING_WITH_LEN(" = ")); + + append_where_value(query, field); + + query.append(STRING_WITH_LEN(" AND ")); + } + query.length(query.length() - sizeof_trailing_and); +} + +void InsertConvertor::generate_prefix(String &query) +{ + query.append(STRING_WITH_LEN("INSERT INTO ")); + append_table_name(m_table, query); +} + +void InsertConvertor::generate_fields_and_values(String &query) +{ + std::vector fields; + get_write_fields(m_table, fields); + + if (fields.size()) + { + query.append(STRING_WITH_LEN(" (")); + for (auto field : fields) + { + query.append(STRING_WITH_LEN("\"")); + query.append(field->field_name.str, field->field_name.length); + query.append(STRING_WITH_LEN("\"")); + query.append(STRING_WITH_LEN(", ")); + } + query.length(query.length() - sizeof_trailing_comma); + query.append(STRING_WITH_LEN(")")); + } + + query.append(STRING_WITH_LEN(" VALUES (")); + for (auto field : fields) + { + append_field_value_to_sql(query, field); + query.append(STRING_WITH_LEN(", ")); + } + query.length(query.length() - sizeof_trailing_comma); + query.append(STRING_WITH_LEN(")")); +} + +void UpdateConvertor::generate_prefix(String &query) +{ + query.append(STRING_WITH_LEN("UPDATE ")); + append_table_name(m_table, query); + query.append(STRING_WITH_LEN(" SET ")); +} + +void UpdateConvertor::generate_fields_and_values(String &query) +{ + std::vector fields; + get_write_fields(m_table, fields); + + for (auto field : fields) + { + query.append(STRING_WITH_LEN("\"")); + query.append(field->field_name.str, field->field_name.length); + query.append(STRING_WITH_LEN("\"")); + query.append(STRING_WITH_LEN(" = ")); + + append_field_value_to_sql(query, field); + query.append(STRING_WITH_LEN(", ")); + } + query.length(query.length() - sizeof_trailing_comma); +} + +void DeleteConvertor::generate_prefix(String &query) +{ + query.append(STRING_WITH_LEN("DELETE FROM ")); + append_table_name(m_table, query); +} diff --git a/storage/duckdb/convertor/dml_convertor.h b/storage/duckdb/convertor/dml_convertor.h new file mode 100644 index 0000000000000..99262aed211e5 --- /dev/null +++ b/storage/duckdb/convertor/dml_convertor.h @@ -0,0 +1,124 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include "ddl_convertor.h" + +void append_field_value_to_sql(String &target_str, Field *field); + +class DMLConvertor : public BaseConvertor +{ +public: + DMLConvertor(TABLE *table) : m_table(table) {} + + bool check() override { return false; } + + std::string translate() override; + +protected: + virtual void generate_prefix(String &query)= 0; + + virtual void generate_fields_and_values(String &query) {} + + virtual void generate_where_clause(String &query); + + virtual void append_where_value(String &query, Field *field) {} + + TABLE *m_table; + +private: + void fill_index_fields_for_where(std::vector &fields); +}; + +class InsertConvertor : public DMLConvertor +{ +public: + InsertConvertor(TABLE *table, bool flag) + : DMLConvertor(table), idempotent_flag(flag) + { + } + +protected: + void generate_prefix(String &query) override; + + void generate_fields_and_values(String &query) override; + + void generate_where_clause(String &query) override {} + +private: + bool idempotent_flag; +}; + +class UpdateConvertor : public DMLConvertor +{ +public: + UpdateConvertor(TABLE *table, const uchar *old_row) + : DMLConvertor(table), m_old_row(old_row) + { + } + +protected: + void generate_prefix(String &query) override; + + void generate_fields_and_values(String &query) override; + + void append_where_value(String &query, Field *field) override + { + uchar *saved_ptr= field->ptr; + field->ptr= + const_cast(m_old_row + field->offset(m_table->record[0])); + append_field_value_to_sql(query, field); + field->ptr= saved_ptr; + } + +private: + const uchar *m_old_row; +}; + +class DeleteConvertor : public DMLConvertor +{ +public: + DeleteConvertor(TABLE *table, const uchar *old_row= nullptr) + : DMLConvertor(table), m_old_row(old_row) + { + } + +protected: + void generate_prefix(String &query) override; + + void append_where_value(String &query, Field *field) override + { + if (!m_old_row) + { + append_field_value_to_sql(query, field); + } + else + { + uchar *saved_ptr= field->ptr; + field->ptr= + const_cast(m_old_row + field->offset(m_table->record[0])); + append_field_value_to_sql(query, field); + field->ptr= saved_ptr; + } + } + +private: + const uchar *m_old_row; +}; diff --git a/storage/duckdb/convertor/duckdb_select.cc b/storage/duckdb/convertor/duckdb_select.cc new file mode 100644 index 0000000000000..b8fe0eba90c06 --- /dev/null +++ b/storage/duckdb/convertor/duckdb_select.cc @@ -0,0 +1,198 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "duckdb_select.h" + +#include +#include + +#include +#include "my_time.h" +#include "sql_class.h" +#include "sql_time.h" +#include "tztime.h" + +#include "ddl_convertor.h" + +/** + Store a temporal value, used for temporal type field like DATE, DATETIME, + TIMESTAMP, TIME. + @param field the field to store value + @param ltime the time value to be stored in field +*/ +static void store_field_temporal_value(Field *field, MYSQL_TIME *ltime) +{ + field->store_time(ltime); +} + +/** + Store a duckdb value in a field of mysql format. + @param field the field to store value + @param value the value to be stored + @param thd the thread handle +*/ +void store_duckdb_field_in_mysql_format(Field *field, duckdb::Value &value, + THD *thd) +{ + if (value.IsNull()) + { + field->set_default(); + if (field->real_maybe_null()) + field->set_null(); + } + else + { + field->set_notnull(); + switch (field->type()) + { + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_BIT: { + auto str= value.GetValueUnsafe(); + auto varchar= str.c_str(); + auto varchar_len= str.size(); + field->store(varchar, varchar_len, &my_charset_bin); + break; + } + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: { + if (field->has_charset()) + { + DBUG_ASSERT(field->charset() != &my_charset_bin); + auto str= value.GetValue(); + auto varchar= str.c_str(); + auto varchar_len= str.size(); + field->store(varchar, varchar_len, field->charset()); + break; + } + else + { + auto str= value.GetValueUnsafe(); + auto varchar= str.c_str(); + auto varchar_len= str.size(); + field->store(varchar, varchar_len, &my_charset_bin); + break; + } + } + case MYSQL_TYPE_NULL: + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_NEWDECIMAL: { + auto str= value.GetValue(); + auto varchar= str.c_str(); + auto varchar_len= str.size(); + field->store(varchar, varchar_len, system_charset_info); + break; + } + case MYSQL_TYPE_TINY: { + int64_t v= value.GetValue(); + field->store(v, field->is_unsigned()); + break; + } + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: { + int64_t v= value.GetValue(); + field->store(v, field->is_unsigned()); + break; + } + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: { + int64_t v= value.GetValue(); + field->store(v, field->is_unsigned()); + break; + } + case MYSQL_TYPE_LONGLONG: { + int64_t v; + if (field->is_unsigned()) + { + v= value.GetValue(); + } + else + { + v= value.GetValue(); + } + field->store(v, field->is_unsigned()); + break; + } + case MYSQL_TYPE_FLOAT: { + float v= value.GetValue(); + field->store(v); + break; + } + case MYSQL_TYPE_DOUBLE: { + double v= value.GetValue(); + field->store(v); + break; + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: { + /* + DuckDB date_t stores days since 1970-01-01. + Convert to MYSQL_TIME via val_str and store_time. + */ + auto str= value.GetValue(); + MYSQL_TIME tm; + MYSQL_TIME_STATUS status; + my_time_status_init(&status); + str_to_datetime_or_date(str.c_str(), str.size(), &tm, 0, &status); + store_field_temporal_value(field, &tm); + break; + } + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: { + auto str= value.GetValue(); + MYSQL_TIME tm; + MYSQL_TIME_STATUS status; + my_time_status_init(&status); + str_to_datetime_or_date(str.c_str(), str.size(), &tm, 0, &status); + store_field_temporal_value(field, &tm); + break; + } + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: { + auto str= value.GetValue(); + MYSQL_TIME tm; + MYSQL_TIME_STATUS status; + my_time_status_init(&status); + str_to_datetime_or_date(str.c_str(), str.size(), &tm, 0, &status); + store_field_temporal_value(field, &tm); + break; + } + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: { + auto str= value.GetValue(); + MYSQL_TIME tm; + MYSQL_TIME_STATUS status; + my_time_status_init(&status); + str_to_DDhhmmssff(str.c_str(), str.size(), &tm, TIME_MAX_HOUR, &status); + store_field_temporal_value(field, &tm); + break; + } + default: + /* TODO: no support */ + DBUG_ASSERT(0); + break; + } + } +} diff --git a/storage/duckdb/convertor/duckdb_select.h b/storage/duckdb/convertor/duckdb_select.h new file mode 100644 index 0000000000000..6f8b0e14da502 --- /dev/null +++ b/storage/duckdb/convertor/duckdb_select.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include + +#include +#include "my_base.h" +#include "field.h" +#include "sql_class.h" + +#undef UNKNOWN + +#include "duckdb.hpp" + +extern handlerton *duckdb_hton; + +void store_duckdb_field_in_mysql_format(Field *field, duckdb::Value &value, + THD *thd); diff --git a/storage/duckdb/debian/control b/storage/duckdb/debian/control new file mode 100644 index 0000000000000..d4817df2783c6 --- /dev/null +++ b/storage/duckdb/debian/control @@ -0,0 +1,9 @@ +Package: mariadb-plugin-duckdb +Architecture: amd64 arm64 +Depends: mariadb-server (= ${server:Version}), + ${misc:Depends}, + ${shlibs:Depends} +Description: MariaDB DuckDB storage engine + The MariaDB DuckDB storage engine embeds DuckDB as a pluggable storage + engine, enabling fast analytical (OLAP) query processing directly + inside MariaDB. diff --git a/storage/duckdb/debian/mariadb-plugin-duckdb.install b/storage/duckdb/debian/mariadb-plugin-duckdb.install new file mode 100644 index 0000000000000..248ff92bd02e8 --- /dev/null +++ b/storage/duckdb/debian/mariadb-plugin-duckdb.install @@ -0,0 +1 @@ +usr/lib/mysql/plugin/ha_duckdb.so diff --git a/storage/duckdb/docs/collation-mapping.md b/storage/duckdb/docs/collation-mapping.md new file mode 100644 index 0000000000000..2786c08d4982d --- /dev/null +++ b/storage/duckdb/docs/collation-mapping.md @@ -0,0 +1,82 @@ +# Collation Mapping: MariaDB → DuckDB + +## Overview + +MariaDB and DuckDB have fundamentally different collation systems. MariaDB uses per-charset collations based on UCA (Unicode Collation Algorithm) weight tables at specific Unicode versions. DuckDB stores all strings as UTF-8 internally and provides collation via two mechanisms: + +- **Built-in collations** — `NOCASE` (applies `lower()`), `NOACCENT` (strips diacritics), `NFC` (Unicode normalization). These are combinable: `NOCASE.NOACCENT`. +- **ICU locale collations** (from the ICU extension) — one per ICU locale (e.g. `de`, `en_us`, `fr`, `zh`). These use proper UCA via ICU but are not combinable with the built-in collations. + +Additionally, DuckDB registers `icu_noaccent` with the ICU tag `und-u-ks-level1-kc-true` (primary strength + case level), providing accent-insensitive, case-insensitive comparison via the full UCA algorithm. + +## Current Mapping + +The mapping is implemented in `duckdb_charset_collation.cc` (`get_duckdb_collation()`). It is applied in two places: + +1. **DDL** (`ddl_convertor.cc`) — VARCHAR columns get `COLLATE ` appended based on the column's `CHARSET_INFO`. +2. **Session context** (`duckdb_context.cc`) — each DuckDB connection's `default_collation` is set from the MariaDB session's `collation_connection`. + +### Supported charsets + +Only `utf8mb3`, `utf8mb4`, and `ascii` charsets are mapped to ICU-aware collations. All other charsets (e.g. `latin1`, `cp1251`) fall back to `POSIX` (binary comparison) because DuckDB cannot replicate their byte-level sorting rules. + +### Mapping table + +The mapping uses `CHARSET_INFO` flags — specifically `MY_CS_BINSORT`, `MY_CS_LOWER_SORT`, and `levels_for_order` bits — to determine the collation behavior. + +| MariaDB collation type | `levels_for_order` bits | DuckDB collation | Example MariaDB collation | +|---|---|---|---| +| `_bin` | (binsort flag) | `POSIX` | `utf8mb4_bin` | +| `_ai_ci` | primary only | `NOCASE.NOACCENT` | `utf8mb4_general_ci`, `utf8mb4_0900_ai_ci` | +| `_as_ci` | primary + secondary | `NOCASE` | `utf8mb4_0900_as_ci` | +| `_as_cs` | primary + secondary + tertiary | `POSIX` | `utf8mb4_0900_as_cs` | +| `_tolower_ci` | (lower_sort flag) | `NOCASE` | `utf8mb3_tolower_ci` | +| Non-UTF8 charset | — | `POSIX` | `latin1_swedish_ci` | + +### Constants + +Defined in `duckdb_charset_collation.h`: + +``` +COLLATION_BINARY = "POSIX" +COLLATION_NOCASE = "NOCASE" +COLLATION_NOCASE_NOACCENT = "NOCASE.NOACCENT" +``` + +`POSIX` is used instead of `binary` because `binary` is a reserved keyword in DuckDB. + +## Known Gaps + +### 1. Built-in `NOCASE` vs UCA case folding + +DuckDB's `NOCASE` applies `lower()` — simple Unicode case folding. MariaDB's UCA-based `_ci` collations use weight tables that handle complex cases (e.g. `ß` = `ss` in German, `İ` ≠ `I` in Turkish). This means comparison results can differ for non-ASCII characters. + +### 2. Built-in `NOACCENT` vs UCA accent handling + +DuckDB's `NOACCENT` uses `strip_accents()` which removes combining diacritical marks. MariaDB's `_ai` collations use UCA primary weights which may group characters differently (e.g. ligatures like `æ`). + +### 3. No `_as_cs` UCA equivalent + +MariaDB's `_as_cs` collations use full UCA tertiary weights for ordering. DuckDB's `POSIX` (binary) preserves case and accent sensitivity but produces a different sort order — it sorts by UTF-8 byte values, not UCA weights. This affects `ORDER BY` results. + +### 4. Non-UTF8 charsets lose collation semantics + +Any charset other than `utf8mb3`/`utf8mb4`/`ascii` is mapped to binary comparison. MariaDB's `latin1_swedish_ci` ordering (which treats `ä` = `a`, `ö` = `o`, etc.) is lost. + +## Possible Improvements + +### Use ICU locale collations for better UCA fidelity + +DuckDB's ICU extension registers collations for all available ICU locales. The relevant ICU collation tags for MariaDB equivalence are: + +| UCA behavior | ICU tag | DuckDB collation | +|---|---|---| +| accent-insensitive, case-insensitive (`_ai_ci`) | `und-u-ks-level1-kc-true` | `icu_noaccent` (already registered) | +| accent-sensitive, case-insensitive (`_as_ci`) | `und-u-ks-level2` | Not yet registered | +| accent-sensitive, case-sensitive (`_as_cs`) | `und-u-ks-level3` | Not yet registered | + +Registering `und-u-ks-level2` and `und-u-ks-level3` as custom DuckDB collations and mapping to them would provide much closer UCA equivalence for UTF-8 collations. + +### Register MariaDB-named collations + +An alternative approach is to register collations with MariaDB-compatible names (e.g. `utf8mb4_0900_ai_ci`) that delegate to the corresponding ICU collator. This would allow the DDL converter to emit the original collation name and avoid translation entirely. diff --git a/storage/duckdb/docs/disabled-tests-plan.md b/storage/duckdb/docs/disabled-tests-plan.md new file mode 100644 index 0000000000000..74d3d73e461ff --- /dev/null +++ b/storage/duckdb/docs/disabled-tests-plan.md @@ -0,0 +1,205 @@ +# Disabled Tests Analysis & Work Plan + +Status as of 2026-04-14. **Enabled: 24/47 tests. Disabled: 23. DuckDB: v1.5.2.** + +### Done this session (12 → 24) + +| Test | Fix | +|------|-----| +| `duckdb_set_operation` | Already passing after charset fix | +| `truncate_and_maintenance_duckdb_table` | Re-record for MariaDB ANALYZE output | +| `duckdb_db_table_strconvert` | Re-record for UDF API | +| `duckdb_monitor` | Fix `direct_delete/update_rows` counters + rewrite test | +| `charset_and_collation` | Fix collation in master.opt + error codes | +| `duckdb_require_primary_key` | Add PK check in `check_if_supported_inplace_alter` | +| `create_table_constraint` | Add `have_duckdb.inc` for charset (non-unique indexes already handled) | +| `duckdb_appender_allocator_flush_threshold` | Propagate to DuckDB v1.5 `allocator_flush_threshold` | +| `decimal_high_precision` | DDL: DECIMAL(>38) → DOUBLE, DML: emit as `%.17e`, default `use_double_for_decimal=TRUE` | + +### Engine fixes done + +- **Build**: `CREATE_TYPELIB_FOR` → manual TYPELIB init; `HA_EXTRA_*_COPY` → `HA_EXTRA_*_ALTER_COPY` +- **DuckDB upgrade**: v1.3.2 → v1.5.2 (`ExtensionUtil` → `Catalog`, `core_functions` extension, autoload) +- **Compound ALTER**: execute each DDL convertor on separate auto-commit connection (v1.5 regression fix) +- **Error codes**: `ER_DUCKDB_*` (4206–4213) with codegen from `duckdb_errors.txt` via cmake +- **Error classification**: `ER_DUCKDB_TABLE_STRUCT_INVALID` for ALTER structural errors, `ER_ILLEGAL_HA_CREATE_OPTION` for CREATE, `ER_DUCKDB_QUERY_ERROR` for DML execution failures +- **Monitoring**: `direct_delete_rows` / `direct_update_rows` increment `Duckdb_rows_delete` / `Duckdb_rows_update` +- **XA**: reject DML on DuckDB tables inside XA transactions (`ER_XAER_RMFAIL`) +- **DDL**: reject ALTER TABLE without PK when `duckdb_require_primary_key=ON` +- **Config**: propagate `appender_allocator_flush_threshold` to DuckDB `allocator_flush_threshold` +- **Test infra**: `have_duckdb.inc` (engine check + utf8mb4 setup), `cleanup_duckdb.inc` (restore latin1), `have_mysqld_safe.inc`, `character-set-server=utf8mb4` in suite `my.cnf` + +--- + +## Group A: Missing SQL functions in DuckDB pushdown (6 tests) + +Primary blockers **FIXED**: `adddate()`, `insert()`, `oct()` registered as DuckDB macros; `WITH ROLLUP` rewritten to `GROUP BY ROLLUP(...)`. + +Tests now hit secondary errors: + +| Test | New blocker after fix | +|------|---------------------| +| `duckdb_sql_syntax` | line 16: `SELECT * FROM t1 JOIN t2` — conditionless JOIN not supported by DuckDB parser | +| `duckdb_time_func` | line 45: `addtime()` not in DuckDB | +| `duckdb_string_func` | line 152: `LENGTH(BLOB)` — no BLOB overload in DuckDB | +| `duckdb_fix_sql` | line 28: `oct('123.123a')` — string-to-BIGINT cast fails on non-numeric suffix | +| `duckdb_numeric_func` | line 27: `ACOS` domain error — DuckDB is stricter on [-1,1] | +| `duckdb_agg_func` | line 53: `AVG(VARCHAR)` — no string overload | + +**Fix:** Implement SQL rewrite in `ha_duckdb_pushdown.cc` — intercept `SELECT_LEX::print()` output and rewrite: +- `adddate(x, interval)` → `x + interval` or `date_add(x, interval)` +- `insert(s,p,l,n)` → `overlay(s placing n from p for l)` +- `oct(x)` → implement via `printf('%o', x)` or refuse pushdown +- `WITH ROLLUP` → refuse pushdown (return NULL from `create_duckdb_select_handler`) +- `ACOS` domain error — wrap in `CASE WHEN ... BETWEEN -1 AND 1` or refuse pushdown +- `AVG(VARCHAR)` — refuse pushdown when argument is non-numeric + +## Group B: Decimal precision >38 (3 tests) + +DuckDB max: DECIMAL(38,x). MariaDB supports up to DECIMAL(65,30). + +| Test | Line | Error | +|------|------|-------| +| `decimal_high_precision` | 35 | `Could not cast value ... to DECIMAL(38,30)` | +| `decimal_precision_all_possibilities` | 48 | `Could not cast value ... to DECIMAL(38,5)` | +| `feature_duckdb_data_type` | 74 | Same — extreme decimal insert | + +**Fix:** In `ddl_convertor.cc` map `DECIMAL(>38, scale)` to `DOUBLE` (when `duckdb_use_double_for_decimal=ON`) or `DECIMAL(38, min(scale, 38-intg))` with truncation. In `delta_appender.cc` — analogous fallback on append. Test `decimal_high_precision` also needs PK (already added in working tree). + +## Group C: Wrong error code (3 remaining tests) + +Error code fixes done: `ER_DUCKDB_TABLE_STRUCT_INVALID` for ALTER structural errors, `ER_DUCKDB_QUERY_ERROR` for DML, XA DML rejection. + +| Test | Status | Remaining issue | +|------|--------|-----------------| +| `rename_duckdb_table` | Error codes fixed, result updated | Server log warnings during cross-schema rename test | +| `bugfix_temp_and_system_database` | Error code `ER_DUCKDB_QUERY_ERROR` fixed | `DROP TABLE t1` in DuckDB "temp" schema fails — DuckDB internal schema conflict | +| `duckdb_refuse_xa` | XA DML rejection implemented | INSERT after `XA COMMIT` in PREPARED state fails — need to handle XA lifecycle correctly | + +## Group D: Engine features not implemented (3 remaining tests) + +| Test | Line | Error | What's needed | +|------|------|-------|---------------| +| `alter_default_debug` | 23 | INSERT after `ALTER COLUMN DROP DEFAULT` succeeds — should fail with `ER_NO_DEFAULT_FOR_FIELD` | Implement `ALTER COLUMN DROP DEFAULT` in `ChangeColumnDefaultConvertor` | +| `duckdb_ddl_during_transaction` | 43 | INSERT after DDL in transaction succeeds — should fail with `ER_DUCKDB_APPENDER_ERROR` | Invalidate appender after DDL within a transaction | +| `supported_copy_ddl` | 10 | `cross-schema rename is not supported` | Implement cross-schema rename via COPY (CREATE + INSERT + DROP) | + +## Group E: Result mismatch / UDF issues (3 tests) + +| Test | Problem | +|------|---------| +| `duckdb_add_backticks` | UDF `duckdb_query_udf` returns `[Rows: 0]` for digit-name schemas (`09898141`) — DuckDB information_schema can't find them because schema names starting with digits need quoting | +| `duckdb_appender_allocator_flush_threshold` | `appender_allocator_flush_threshold` setting doesn't exist in upstream DuckDB v1.3.2 (AliSQL fork only). May exist as `allocator_flush_threshold` in DuckDB v1.5.2 | +| `duckdb_bit_string` | `WHERE col = x'41'` returns empty result — hex/binary literal comparison via pushdown is broken, likely `SELECT_LEX::print()` outputs `x'41'` which DuckDB doesn't understand | + +**Fix:** +- `duckdb_add_backticks`: quote schema/table names in DuckDB DDL queries (e.g. `CREATE SCHEMA IF NOT EXISTS "09898141"`) +- `duckdb_appender_allocator_flush_threshold`: consider after DuckDB submodule upgrade (see below) +- `duckdb_bit_string`: rewrite hex literals in pushdown SQL or handle in `SELECT_LEX::print()` post-processing + +## Group F: Server / external issues (5 tests) + +| Test | Problem | Complexity | +|------|---------|------------| +| `duckdb_allow_encryption` | MySQL `keyring_file` plugin, `default-table-encryption` don't exist in MariaDB | Rewrite for MariaDB encryption API or N/A | +| `system_timezone` | Hangs on `mariadbd-safe` restart | Adapt restart mechanism for MariaDB | +| `duckdb_alter_table_engine` | Server crash: `Assertion 'len > alloc_length' failed` on 64MB JSON | MariaDB server bug, not duck | +| `duckdb_kill` | Timeout 900s | `simulate_interrupt_duckdb_row/chunk` DEBUG sync points not implemented | +| `bugfix_crash_after_commit_error` | `--skip TODO` in the test itself | Test needs to be written | + +## Group G: SQL mode / index handling (2 tests) + +| Test | Line | Error | +|------|------|-------| +| `duckdb_sql_mode` | 13 | `column "id" must appear in the GROUP BY clause` — DuckDB is stricter than MariaDB without `ONLY_FULL_GROUP_BY` | +| `alter_duckdb_index` | 18 | `Duplicate key name 'uk_b'` — DuckDB ignores indexes but MariaDB remembers their names | + +**Fix:** +- `duckdb_sql_mode`: pass permissive GROUP BY setting to DuckDB on pushdown, or refuse pushdown when `ONLY_FULL_GROUP_BY` is off +- `alter_duckdb_index`: don't register ignored index names in MariaDB metadata + +## Group H: Timestamp/timezone (1 test) + +| Test | Problem | +|------|---------| +| `create_table_column_timestamp` | Checksums InnoDB vs DuckDB don't match — timezone offset applied incorrectly | + +**Fix:** Fix timezone propagation in `config_duckdb_session` — DuckDB sees wrong timezone on INSERT/SELECT timestamps. + +--- + +## Priority work plan + +### Completed + +| # | Task | Status | +|---|------|--------| +| 3 | Error code fixes | **DONE** — `ER_DUCKDB_*` codegen, error classification | +| 6 | Index handling / CREATE TABLE constraint | **DONE** — `create_table_constraint` enabled | +| 11 | require_primary_key on ALTER | **DONE** | +| 14 | appender_allocator_flush_threshold | **DONE** — maps to `allocator_flush_threshold` in v1.5.2 | +| 20 | Upgrade DuckDB v1.3.2 → v1.5.2 | **DONE** | +| 21 | Compound ALTER regression | **DONE** — separate auto-commit connection per DDL | +| 1a | SQL macros (`adddate`, `insert`, `oct`) + `WITH ROLLUP` rewrite | **DONE** — primary blockers fixed | + +### Remaining (27 disabled tests, grouped by root cause) + +| # | Task | Tests | Complexity | +|---|------|-------|------------| +| 1b | **More SQL macros**: `addtime`, `json_contains` | `duckdb_time_func`, `duckdb_json` | low — same pattern as adddate/oct | +| 1c | **Conditionless JOIN**: `SELECT * FROM t1 JOIN t2` — DuckDB requires ON clause | `duckdb_sql_syntax` | medium — SQL rewrite or refuse pushdown | +| 1d | **oct(string)**: `oct('123.123a')` — MariaDB truncates to number, DuckDB casts strictly | `duckdb_fix_sql` | low — improve macro to handle strings | +| 1e | **LENGTH(BLOB)**: DuckDB has no BLOB overload for `length()` | `duckdb_string_func` | low — add `octet_length` macro alias | +| 2 | **Decimal >38**: `decimal_high_precision` **DONE**. Remaining: `decimal_precision_all_possibilities` (appender incomplete row), `feature_duckdb_data_type` (ENUM/SET insert), `alter_engine_duckdb` (server crash on ALTER) | medium | IN PROGRESS | +| 4 | **ALTER COLUMN DROP DEFAULT** — MariaDB metadata-only, handler not called | `alter_default_debug` | hard | +| 5 | **Appender invalidation** after DDL in transaction | `duckdb_ddl_during_transaction` | medium | +| 7 | **UDF digit-name schemas** — quote in DuckDB queries | `duckdb_add_backticks` | medium | +| 8 | **Hex/binary literal** in WHERE via pushdown | `duckdb_bit_string` | medium | +| 9a | **AVG(VARCHAR)** — no string overload in DuckDB | `duckdb_agg_func` | medium | +| 9b | **Strict GROUP BY** — DuckDB requires ONLY_FULL_GROUP_BY | `duckdb_sql_mode` | medium | +| 10 | **ACOS domain** [-1,1] — DuckDB stricter than MariaDB | `duckdb_numeric_func` | medium | +| 12 | **Timezone propagation** — checksum mismatch | `create_table_column_timestamp` | medium | +| 22 | **DuckDB "temp"/"system" schema conflict** — reserved schema names | `bugfix_temp_and_system_database` | medium | +| 23 | **XA PREPARED lifecycle** — INSERT after XA COMMIT fails | `duckdb_refuse_xa` | medium | +| 24 | **Cross-schema rename error code** — test expects `ER_ALTER_OPERATION_NOT_SUPPORTED` for INPLACE | `supported_copy_ddl` | low — update test | +| 25 | **Server log warnings** on rename test | `rename_duckdb_table` | low — suppress or fix warnings | +| 26 | **Duplicate index names** — DuckDB ignores indexes but MariaDB remembers names | `alter_duckdb_index` | medium | +| 13 | **Cross-schema rename via COPY** | `supported_copy_ddl` | high | +| 15 | **KILL/interrupt** — DEBUG sync points | `duckdb_kill` | high | +| 16 | **bugfix_crash_after_commit_error** — test TODO | `bugfix_crash_after_commit_error` | unknown | +| 17 | **Encryption** — MySQL→MariaDB | `duckdb_allow_encryption` | high | +| 18 | **system_timezone** — mariadbd-safe restart hangs | `system_timezone` | high | +| 19 | **Server crash** on 64MB JSON | `duckdb_alter_table_engine` | out of scope | + +### Next priorities + +**Done**: `addtime`, `subdate`, `subtime`, `oct(string)`, `bin` macros added. `supported_copy_ddl` error code fixed. `json_contains` 3-arg macro dropped (DuckDB native 2-arg works, 3-arg needs SQL rewrite in test). + +**Remaining quick wins**: +- `duckdb_time_func`: `addtime('1 1:1:1.000002')` — MariaDB time interval format differs from DuckDB INTERVAL +- `duckdb_fix_sql`: progressed past oct/bin, check next blocker +- `duckdb_string_func`: `LENGTH(BLOB)` — can't override builtin with macro (infinite recursion) +- `duckdb_json`: `json_contains(json, val, path)` 3-arg — needs SQL rewrite, not macro +- `rename_duckdb_table`: server log warnings + +**Medium impact (2, 9a, 9b)** — decimal fallback + type/mode pushdown issues, 6 tests. +**Hard/external (4, 13, 15, 17, 18, 19)** — architecture limits or external deps. + +### DuckDB upstream upgrade — DONE + +Upgraded: **v1.3.2 → v1.5.2**. + +Changes made: +- `ExtensionUtil` → `Catalog::GetSystemCatalog` + `CatalogTransaction` + `CreateFunction` +- `scheduler_process_partial` removed (gone in v1.5) +- `core_functions` added to `duckdb_extensions.cmake` (was built-in in v1.3, separate extension in v1.5) +- `autoload_known_extensions` + `autoinstall_known_extensions` enabled at DB init +- `DUCKDB_EXTENSION_AUTOLOAD_DEFAULT` + `DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT` cmake flags +- `duckdb_error_h` target dependency on `GenError` fixed for clean builds + +Regression: `alter_duckdb_column` / `alter_duckdb_column_copy` fail on compound ALTER (ADD COLUMN + SET DEFAULT) with `TransactionContext Error: Cannot create index with outstanding updates`. This is new v1.5.2 behavior — see item 21. + +--- + +Items 1–2 are highest priority (unblock 7 tests). +Items 5–10 add 6 more. Items 4, 12–19 are complex or external. diff --git a/storage/duckdb/docs/mariadb-duckdb-incompatibilities.md b/storage/duckdb/docs/mariadb-duckdb-incompatibilities.md new file mode 100644 index 0000000000000..f1222900bdafb --- /dev/null +++ b/storage/duckdb/docs/mariadb-duckdb-incompatibilities.md @@ -0,0 +1,173 @@ +# MariaDB - DuckDB Incompatibilities + +Discovered during porting the DuckDB storage engine plugin to MariaDB 12. +DuckDB upstream version: **v1.5.2** (submodule at `third_parties/duckdb/`). + +--- + +## 1. Identifier Quoting + +| MariaDB | DuckDB | +|---|---| +| `` `backticks` `` | `"double quotes"` (SQL standard) | + +MariaDB uses backticks by default for quoting identifiers. DuckDB follows the SQL standard and uses double quotes. + +**Fix applied**: `backticks_to_double_quotes()` in `duckdb_query.cc` converts every backtick to a double quote in all SQL sent to DuckDB via `duckdb_query()`. DDL/DML codegen in `ddl_convertor.cc`, `dml_convertor.cc`, `ha_duckdb.cc`, `delta_appender.cc` uses double quotes directly. + +SELECT pushdown (`ha_duckdb_pushdown.cc`) uses the original SQL text from `THD::query()` (which contains backticks), so the conversion happens inside `duckdb_query()`. + +--- + +## 2. Function Compatibility + +MariaDB function semantics differ from DuckDB in several areas. These are handled by **runtime function overrides** registered at startup via `register_mysql_compat_functions()` in `duckdb_mysql_compat.cc`. No DuckDB source patches are used. + +### Overridden functions (registered in `duckdb_mysql_compat.cc`) + +| Function | Issue | Fix | +|---|---|---| +| `octet_length(VARCHAR)` | DuckDB builtin only has BLOB overload | Added VARCHAR→BIGINT overload (byte count) | +| `length(VARCHAR)` | DuckDB returns character count; MariaDB returns byte count | Overridden to return byte count | +| `length(BLOB)` | DuckDB builtin `length()` only works on VARCHAR | Added BLOB→BIGINT overload | +| `ascii(VARCHAR)` | DuckDB returns Unicode codepoint; MariaDB returns first byte value | Overridden to return first byte | +| `ord(VARCHAR)` | DuckDB returns Unicode codepoint; MariaDB returns multibyte byte-value | Overridden for MariaDB semantics | +| `hex()` | DuckDB builtin missing several type overloads | Full overload set (VARCHAR, BLOB, BIGINT, UBIGINT, HUGEINT, UHUGEINT, DOUBLE, FLOAT) | +| `oct()` | Not in DuckDB | Full implementation with all numeric + string overloads | +| `bin()` | Not in DuckDB | Full implementation with all numeric + string overloads | +| `locate(substr, str [, pos])` | DuckDB has `instr(str, substr)` with reversed arg order | Custom 2-arg and 3-arg implementations | +| `mid(s, p [, n])` | Not in DuckDB | SQL macro delegating to `substr()` | +| `addtime(TIMESTAMP/TIME, VARCHAR)` | DuckDB INTERVAL doesn't parse MariaDB `'D H:M:S.us'` format | Custom implementation parsing MariaDB interval strings | +| `subtime(TIMESTAMP/TIME, VARCHAR)` | Same as addtime | Custom implementation | +| `rtrim(VARCHAR, VARCHAR)` | DuckDB removes individual chars from set; MariaDB removes substring | Overridden for substring semantics | +| `ltrim(VARCHAR, VARCHAR)` | Same as rtrim | Overridden for substring semantics | +| `regexp_instr(VARCHAR, VARCHAR)` | Not in DuckDB | Custom implementation using RE2 | +| `regexp_replace(VARCHAR, VARCHAR, VARCHAR)` | DuckDB has it but with different overload set | Custom 3-arg implementation using RE2 | +| `regexp_substr(VARCHAR, VARCHAR)` | Not in DuckDB | Custom implementation using RE2 | +| `json_unquote(VARCHAR)` | Not in DuckDB | Custom implementation (strip quotes + unescape) | +| `json_contains(VARCHAR, VARCHAR, VARCHAR)` | DuckDB only has 2-arg form | 3-arg placeholder (returns false — needs proper implementation) | + +### Compatible aliases (already work in DuckDB) + +| User writes | MariaDB canonical | DuckDB status | +|---|---|---| +| `LOWER()` | `lcase()` | Alias exists | +| `UPPER()` | `ucase()` | Alias exists | +| `IFNULL()` / `NVL()` | `ifnull()` | Rewritten to `COALESCE` | +| `CEIL()` | `ceiling()` | Supported natively | +| `POWER()` | `pow()` | Supported natively | +| `SUBSTRING()` | `substr()` | Supported natively | + +### Potentially incompatible (not yet triggered) + +| User writes | MariaDB canonical | DuckDB status | +|---|---|---| +| `SCHEMA()` | `database()` | No `database()` function in DuckDB | +| `ATAN2(x,y)` | `atan(x,y)` | Arity may differ | + +--- + +## 3. SQL Syntax Rewriting (SELECT pushdown) + +SELECT pushdown uses the original SQL text from `THD::query()`. MariaDB-specific syntax is rewritten in `ha_duckdb_pushdown.cc` (`init_scan()`) before sending to DuckDB: + +| MariaDB syntax | DuckDB equivalent | Status | +|---|---|---| +| `GROUP BY ... WITH ROLLUP` | `GROUP BY ROLLUP(...)` | Rewritten | +| `CONVERT(expr, TYPE)` | `CAST(expr AS TYPE)` | Rewritten | +| `CURRENT_TIME()` / `CURRENT_DATE()` / `CURRENT_TIMESTAMP()` | `current_time` / `current_date` / `current_timestamp` (keywords) | Rewritten (parens removed) | +| `STRAIGHT_JOIN` | `CROSS JOIN` | Rewritten | +| Conditionless `JOIN` (no ON/USING) | `CROSS JOIN` | Rewritten | +| `REGEXP` / `NOT REGEXP` / `RLIKE` / `NOT RLIKE` | `~` / `!~` | Rewritten | +| `LIMIT offset, count` | `LIMIT count OFFSET offset` | Rewritten | +| `HIGH_PRIORITY`, `SQL_NO_CACHE`, `SQL_CACHE`, `SQL_BUFFER_RESULT`, `SQL_SMALL_RESULT`, `SQL_BIG_RESULT`, `SQL_CALC_FOUND_ROWS` | -- | Stripped | +| `FORCE INDEX(...)`, `USE INDEX(...)`, `IGNORE INDEX(...)` | -- | Stripped | + +### Known unhandled cases (currently cause query failures) + +These MariaDB constructs are **not yet rewritten** and fail when pushed down. Because pushdown forwards the original `THD::query()` text (only backticks are converted to double quotes), MariaDB-specific token semantics survive into DuckDB. Discovered while running an analytical query set (402 queries) against DuckDB-engine tables. + +| MariaDB construct | Sent to DuckDB as | DuckDB result | Root cause | +|---|---|---|---| +| Double-quoted **string literal**, e.g. `JSON_OBJECT("month", ...)` | `"month"` (verbatim) | `Binder Error: Referenced column "month" not found` | MariaDB without `ANSI_QUOTES` treats `"x"` as a string literal; DuckDB treats `"x"` as an identifier. The forwarded literal is read as a column reference. | +| Unquoted column **alias equal to a DuckDB reserved keyword**, e.g. `SELECT expr name` / `SELECT expr year` | `... name` / `... year` (verbatim) | `Parser Error: syntax error at or near "name"` | DuckDB forbids reserved keywords as unquoted identifiers. `AS name` or `"name"` work; bare `name` / `year` / `month` do not. This is why most implicit aliases pass but keyword aliases fail. | + +Reproductions (against any DuckDB-engine table `t`): + +```sql +SELECT JSON_OBJECT("k", 1) FROM t; -- Binder Error: column "k" not found +SELECT JSON_OBJECT('k', 1) FROM t; -- OK +SELECT col name FROM t; -- Parser Error at "name" +SELECT col AS name FROM t; -- OK +``` + +**Fix direction**: in `ha_duckdb_pushdown.cc`, convert double-quoted string literals to single-quoted form and quote (or `AS`-prefix) aliases that are DuckDB reserved keywords. Both require lexer-aware handling of the query text, not naive replacement — `backticks_to_double_quotes()` already produces legitimate double-quoted identifiers that must not be altered. + +--- + +## 4. Data Type Handling + +### DECIMAL Appender + +DuckDB upstream Appender API does not accept raw `Append()` for DECIMAL columns -- it interprets them as plain integers and fails the type cast. The fix is to use `duckdb::Value::DECIMAL(value, width, scale)` which tells DuckDB the value is already scaled. + +**Affected file**: `delta_appender.cc` + +### Text types + +MariaDB `MEDIUMTEXT`, `LONGTEXT`, `TEXT`, `TINYTEXT` all map to DuckDB `VARCHAR`. + +--- + +## 5. DuckDB API Differences (AliSQL fork vs upstream v1.5.2) + +The AliSQL fork of DuckDB has custom extensions to the API that do not exist in upstream DuckDB: + +| Feature | AliSQL fork | Upstream v1.5.2 | +|---|---|---| +| `scheduler_process_partial` config option | YES | NO - Does not exist | +| `appender_allocator_flush_threshold` config option | YES | NO - Does not exist | +| `Appender(conn, schema, table, AppenderType)` constructor | YES | NO - No `AppenderType` parameter | +| `LengthFun` for VARCHAR | Uses `StrLenOperator` (byte count) | Uses `StringLengthOperator` (codepoint count) — overridden at runtime via `duckdb_mysql_compat.cc` | + +--- + +## 6. DuckDB Extensions + +Loaded via `cmake/duckdb_extensions.cmake`: + +| Extension | Required for | Explicitly loaded? | +|---|---|---| +| **ICU** | `SET TimeZone = ...`, locale-aware collations | YES | +| **JSON** | JSON functions | YES | +| **core_functions** | Basic scalar/aggregate functions | YES | + +Build flags in `cmake/duckdb.cmake`: `EXTENSION_STATIC_BUILD=1`, `DUCKDB_EXTENSION_AUTOLOAD_DEFAULT=1`, `DUCKDB_EXTENSION_AUTOINSTALL_DEFAULT=1`. + +--- + +## 7. ABI Compatibility + +DuckDB is built as a static library (`libduckdb_bundle.a`) via `ExternalProject_Add` in `cmake/duckdb.cmake`. + +| Concern | Detail | +|---|---| +| **`_GLIBCXX_DEBUG`** | MariaDB debug builds define `_GLIBCXX_DEBUG` which changes `sizeof(std::vector)` etc. The plugin target strips this via `-U_GLIBCXX_DEBUG -U_GLIBCXX_ASSERTIONS` in `CMakeLists.txt`. Mismatch causes SIGSEGV in `~DuckDB()`. | +| **C++ standard** | Plugin is built with C++17 (`CXX_STANDARD 17`). DuckDB v1.5.2 requires C++17. | +| **`_GLIBCXX_USE_CXX11_ABI`** | Not explicitly set — uses the compiler default (ABI=1). Both DuckDB and the plugin use the same default. | + +--- + +## 8. Potential Future Incompatibilities + +These have not been triggered yet but are likely to cause issues when more complex queries are pushed down: + +| MariaDB function | DuckDB equivalent | Notes | +|---|---|---| +| `GROUP_CONCAT()` | `string_agg()` / `list_aggr()` | Different syntax and separator handling | +| `DATE_FORMAT()` | `strftime()` | Different format specifiers | +| `UNIX_TIMESTAMP()` | `epoch()` | -- | +| `FORMAT(number, decimals)` | -- | MariaDB: locale-aware number formatting; DuckDB: printf-style | +| `FOUND_ROWS()` | -- | No equivalent | +| `LAST_INSERT_ID()` | -- | No equivalent | +| Collations | -- | MariaDB collation rules do not transfer to DuckDB | diff --git a/storage/duckdb/duckdb.cnf b/storage/duckdb/duckdb.cnf new file mode 100644 index 0000000000000..f3643b4c49704 --- /dev/null +++ b/storage/duckdb/duckdb.cnf @@ -0,0 +1,3 @@ +[mysqld] +plugin-load-add=ha_duckdb.so +#loose-duckdb-memory-limit=1073741824 \ No newline at end of file diff --git a/storage/duckdb/duckdb_udf.cc b/storage/duckdb/duckdb_udf.cc new file mode 100644 index 0000000000000..3123be60371b5 --- /dev/null +++ b/storage/duckdb/duckdb_udf.cc @@ -0,0 +1,110 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +/* + Function plugin: run_in_duckdb(sql_string) + + Executes a SQL query directly in DuckDB and returns the result as a string. + Registered as a MariaDB_FUNCTION_PLUGIN alongside the storage engine plugin + in ha_duckdb.cc's maria_declare_plugin block. +*/ + +#define MYSQL_SERVER +#include "mariadb.h" +#include "item.h" +#include + +#undef UNKNOWN + +#include "duckdb_query.h" +#include "duckdb_manager.h" + +class Item_func_run_in_duckdb : public Item_str_func +{ +public: + Item_func_run_in_duckdb(THD *thd, Item *a) : Item_str_func(thd, a) {} + + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("run_in_duckdb")}; + return name; + } + + bool fix_length_and_dec(THD *) override + { + collation.set(&my_charset_bin); + max_length= 65535; + set_maybe_null(); + return FALSE; + } + + String *val_str(String *str) override + { + DBUG_ASSERT(fixed()); + String *sql_arg= args[0]->val_str(str); + if ((null_value= args[0]->null_value)) + return NULL; + + std::string sql(sql_arg->ptr(), sql_arg->length()); + + auto conn= myduck::DuckdbManager::CreateConnection(); + if (!conn) + { + null_value= 1; + return NULL; + } + + auto res= myduck::duckdb_query(*conn, sql); + + if (res->type == duckdb::QueryResultType::STREAM_RESULT) + { + auto &stream= res->Cast(); + res= stream.Materialize(); + } + + std::string output= res->HasError() ? res->GetError() : res->ToString(); + + if (str->copy(output.c_str(), output.length(), &my_charset_bin)) + { + null_value= 1; + return NULL; + } + + null_value= 0; + return str; + } + + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; + + +class Create_func_run_in_duckdb : public Create_func_arg1 +{ +public: + Item *create_1_arg(THD *thd, Item *arg1) override + { return new (thd->mem_root) Item_func_run_in_duckdb(thd, arg1); } + + static Create_func_run_in_duckdb s_singleton; +}; + +Create_func_run_in_duckdb Create_func_run_in_duckdb::s_singleton; + +Plugin_function plugin_descriptor_function_run_in_duckdb( + &Create_func_run_in_duckdb::s_singleton); diff --git a/storage/duckdb/ha_duckdb.cc b/storage/duckdb/ha_duckdb.cc new file mode 100644 index 0000000000000..8d4635ff706a5 --- /dev/null +++ b/storage/duckdb/ha_duckdb.cc @@ -0,0 +1,1386 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "ha_duckdb.h" + +#include +#include + +#include +#include +#include +#include "sql_class.h" +#include "sql_table.h" + +#include "duckdb_manager.h" +#include "duckdb_context.h" +#include "duckdb_query.h" +#include "duckdb_config.h" +#include "duckdb_timezone.h" +#include "duckdb_types.h" +#include "duckdb_select.h" +#include "ddl_convertor.h" +#include "dml_convertor.h" +#include "delta_appender.h" +#include "row_helpers.h" +#include "ha_duckdb_pushdown.h" +#include "duckdb_log.h" + +/* Global status counters */ +struct duckdb_status_t +{ + ulonglong duckdb_rows_insert; + ulonglong duckdb_rows_update; + ulonglong duckdb_rows_delete; + ulonglong duckdb_rows_insert_in_batch; + ulonglong duckdb_rows_update_in_batch; + ulonglong duckdb_rows_delete_in_batch; + ulonglong duckdb_commit; + ulonglong duckdb_rollback; +}; + +static duckdb_status_t srv_duckdb_status; + +/* Plugin variables */ +static my_bool copy_ddl_in_batch= TRUE; +static my_bool dml_in_batch= TRUE; +static my_bool update_modified_column_only= TRUE; + +static handler *duckdb_create_handler(handlerton *hton, TABLE_SHARE *table, + MEM_ROOT *mem_root); + +handlerton *duckdb_hton; + +/* ----- Per-thread context via thd_get_ha_data / thd_set_ha_data ----- */ + +static myduck::DuckdbThdContext *get_duckdb_context(THD *thd) +{ + auto *ctx= static_cast( + thd_get_ha_data(thd, duckdb_hton)); + if (!ctx) + { + ctx= new myduck::DuckdbThdContext(); + thd_set_ha_data(thd, duckdb_hton, ctx); + } + return ctx; +} + +/* ----- Transaction callbacks ----- */ + +#if MYSQL_VERSION_ID >= 110800 +static int duckdb_prepare(THD *thd, bool all) +#else +static int duckdb_prepare(handlerton *hton, THD *thd, bool all) +#endif +{ + if (all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + { + std::string error_msg; + auto *ctx= get_duckdb_context(thd); + if (ctx->flush_appenders(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, error_msg.c_str(), "DuckDB"); + return 1; + } + } + return 0; +} + +static void push_duckdb_query_error(const std::string &err) +{ + if (err.find("Parser Error") != std::string::npos || + err.find("syntax error") != std::string::npos) + { + my_error(ER_PARSE_ERROR, MYF(0), err.c_str()); + return; + } + + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_GENERIC, err.c_str(), "DuckDB"); +} + +#if MYSQL_VERSION_ID >= 110800 +static int duckdb_commit(THD *thd, bool commit_trx) +#else +static int duckdb_commit(handlerton *hton, THD *thd, bool commit_trx) +#endif +{ + if (commit_trx || + (!thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + { + srv_duckdb_status.duckdb_commit++; + + std::string error_msg; + auto *ctx= get_duckdb_context(thd); + + /* Safety net: flush if prepare() was not called (no 2PC). + This is a no-op when appenders were already flushed. */ + if (ctx->flush_appenders(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, error_msg.c_str(), "DuckDB"); + ctx->duckdb_trans_rollback(error_msg); + return 1; + } + + if (ctx->duckdb_trans_commit(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_GENERIC, error_msg.c_str(), "DuckDB"); + ctx->duckdb_trans_rollback(error_msg); + return 1; + } + } + return 0; +} + +#if MYSQL_VERSION_ID >= 110800 +static int duckdb_rollback(THD *thd, bool rollback_trx) +#else +static int duckdb_rollback(handlerton *hton, THD *thd, bool rollback_trx) +#endif +{ + if (rollback_trx || + !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + { + srv_duckdb_status.duckdb_rollback++; + + std::string error_msg; + auto *ctx= get_duckdb_context(thd); + if (ctx->duckdb_trans_rollback(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_GENERIC, error_msg.c_str(), "DuckDB"); + return 1; + } + } + return 0; +} + +#if MYSQL_VERSION_ID >= 110800 +static int duckdb_close_connection(THD *thd) +#else +static int duckdb_close_connection(handlerton *hton, THD *thd) +#endif +{ + auto *ctx= static_cast( + thd_get_ha_data(thd, duckdb_hton)); + if (ctx) + { + delete ctx; + thd_set_ha_data(thd, duckdb_hton, nullptr); + } + return 0; +} + +static int duckdb_register_trx(THD *thd) +{ + trans_register_ha(thd, false, duckdb_hton, 0); + + if (thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) + trans_register_ha(thd, true, duckdb_hton, 0); + + auto *ctx= get_duckdb_context(thd); + if (!ctx->has_transaction()) + ctx->duckdb_trans_begin(); + return 0; +} + +static void duckdb_drop_database(handlerton *hton, char *path) +{ + THD *thd= current_thd; + DBUG_ENTER("duckdb_drop_database"); + + Databasename db(path); + + std::string query= "DROP SCHEMA IF EXISTS \""; + query.append(db.name); + query.append("\""); + + duckdb_register_trx(thd); + auto *ctx= get_duckdb_context(thd); + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + DBUG_VOID_RETURN; +} + +/* ----- Handlerton init ----- */ + +static int duckdb_init_func(void *p) +{ + DBUG_ENTER("duckdb_init_func"); + + duckdb_hton= (handlerton *) p; + duckdb_hton->db_type= DB_TYPE_AUTOASSIGN; + duckdb_hton->create= duckdb_create_handler; + duckdb_hton->flags= HTON_NO_FLAGS; + duckdb_hton->prepare= duckdb_prepare; + duckdb_hton->commit= duckdb_commit; + duckdb_hton->rollback= duckdb_rollback; + duckdb_hton->close_connection= duckdb_close_connection; + duckdb_hton->drop_database= duckdb_drop_database; + + duckdb_hton->create_select= create_duckdb_select_handler; + duckdb_hton->create_unit= create_duckdb_unit_handler; + + myduck::TimeZoneOffsetHelper::init_timezone(); + + if (myduck::DuckdbManager::CreateInstance()) + { + sql_print_error("DuckDB: failed to create DuckdbManager instance"); + DBUG_RETURN(1); + } + + sql_print_information("DuckDB storage engine initialized"); + DBUG_RETURN(0); +} + +static int duckdb_deinit_func(void *p) +{ + DBUG_ENTER("duckdb_deinit_func"); + myduck::DuckdbManager::Cleanup(); + DBUG_RETURN(0); +} + +/* ----- Share management ----- */ + +Duckdb_share::Duckdb_share() { thr_lock_init(&lock); } + +Duckdb_share *ha_duckdb::get_share() +{ + Duckdb_share *tmp_share; + DBUG_ENTER("ha_duckdb::get_share()"); + + lock_shared_ha_data(); + if (!(tmp_share= static_cast(get_ha_share_ptr()))) + { + tmp_share= new Duckdb_share; + if (!tmp_share) + goto err; + set_ha_share_ptr(static_cast(tmp_share)); + } +err: + unlock_shared_ha_data(); + DBUG_RETURN(tmp_share); +} + +/* ----- Handler creation ----- */ + +static handler *duckdb_create_handler(handlerton *hton, TABLE_SHARE *table, + MEM_ROOT *mem_root) +{ + return new (mem_root) ha_duckdb(hton, table); +} + +ha_duckdb::ha_duckdb(handlerton *hton, TABLE_SHARE *table_arg) + : handler(hton, table_arg) +{ + my_bitmap_init(&m_blob_map, nullptr, MAX_FIELDS); +} + +ha_duckdb::~ha_duckdb() { my_bitmap_free(&m_blob_map); } + +/* ----- Basic handler methods ----- */ + +int ha_duckdb::open(const char *, int, uint) +{ + DBUG_ENTER("ha_duckdb::open"); + + if (!(share= get_share())) + DBUG_RETURN(1); + thr_lock_data_init(&share->lock, &lock, nullptr); + + DBUG_RETURN(0); +} + +int ha_duckdb::close(void) +{ + DBUG_ENTER("ha_duckdb::close"); + DBUG_RETURN(0); +} + +/* ----- DML helpers ----- */ + +static int execute_dml(THD *thd, DMLConvertor *convertor) +{ + if (convertor->check()) + return HA_DUCKDB_DML_ERROR; + + auto query= convertor->translate(); + auto *ctx= get_duckdb_context(thd); + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + + if (query_result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_DML_ERROR, query_result->GetError().c_str(), "DuckDB"); + return HA_DUCKDB_DML_ERROR; + } + + return 0; +} + +/* check whether field is modified */ +static bool calc_field_difference(const uchar *old_row, const uchar *new_row, + TABLE *table, Field *field) +{ + ulong o_len; + ulong n_len; + const uchar *o_ptr; + const uchar *n_ptr; + + o_ptr= (const uchar *) old_row + field->offset(table->record[0]); + n_ptr= (const uchar *) new_row + field->offset(table->record[0]); + + o_len= n_len= field->pack_length(); + + switch (field->type()) + { + case MYSQL_TYPE_VARCHAR: + o_ptr= row_mysql_read_true_varchar( + &o_len, o_ptr, (ulong) ((Field_varstring *) field)->length_bytes); + n_ptr= row_mysql_read_true_varchar( + &n_len, n_ptr, (ulong) ((Field_varstring *) field)->length_bytes); + break; + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_LONG_BLOB: + o_ptr= row_mysql_read_blob_ref(&o_len, o_ptr, o_len); + n_ptr= row_mysql_read_blob_ref(&n_len, n_ptr, n_len); + break; + default:; + } + + if (field->real_maybe_null()) + { + if (field->is_null_in_record(old_row)) + o_len= ~0U; + if (field->is_null_in_record(new_row)) + n_len= ~0U; + } + + return o_len != n_len || + (o_len != ~0U && o_len != 0 && 0 != memcmp(o_ptr, n_ptr, o_len)); +} + +/* calculate row difference, set bit for modified columns in table->tmp_set */ +static bool calc_row_difference(const uchar *old_row, const uchar *new_row, + TABLE *table) +{ + bool res= false; + bitmap_clear_all(&table->tmp_set); + + for (uint i= 0; i < table->s->fields; i++) + { + Field *field= table->field[i]; + if (calc_field_difference(old_row, new_row, table, field)) + { + bitmap_set_bit(&table->tmp_set, field->field_index); + res= true; + } + } + return res; +} + +/* check whether PK is modified */ +static bool calc_pk_difference(const uchar *old_row, const uchar *new_row, + TABLE *table) __attribute__((unused)); +static bool calc_pk_difference(const uchar *old_row, const uchar *new_row, + TABLE *table) +{ + KEY *key_info= table->key_info; + if (!key_info) + return false; + + KEY_PART_INFO *key_part= table->key_info->key_part; + for (uint j= 0; j < key_info->user_defined_key_parts; j++, key_part++) + { + if (calc_field_difference(old_row, new_row, table, key_part->field)) + return true; + } + return false; +} + +static myduck::BatchState get_batch_state(THD *thd) +{ + auto *ctx= get_duckdb_context(thd); + myduck::BatchState batch_state= ctx->get_batch_state(); + + if (batch_state == myduck::BatchState::UNDEFINED) + { + if (dml_in_batch) + batch_state= myduck::BatchState::IN_INSERT_ONLY_BATCH; + else + batch_state= myduck::BatchState::NOT_IN_BATCH; + ctx->set_batch_state(batch_state); + } + return batch_state; +} + +/* Build duckdb type map of blob type */ +static void build_duckdb_blob_map(Field **field_list, MY_BITMAP *map) +{ + for (Field **f_ptr= field_list; *f_ptr != nullptr; f_ptr++) + { + Field *field= *f_ptr; + enum_field_types type= field->real_type(); + + if (type == MYSQL_TYPE_SET || type == MYSQL_TYPE_ENUM || + type == MYSQL_TYPE_BIT || type == MYSQL_TYPE_GEOMETRY || + type == MYSQL_TYPE_VARCHAR || type == MYSQL_TYPE_STRING || + type == MYSQL_TYPE_TINY_BLOB || type == MYSQL_TYPE_BLOB || + type == MYSQL_TYPE_MEDIUM_BLOB || type == MYSQL_TYPE_LONG_BLOB) + { + if (FieldConvertor::convert_type(field) == "BLOB") + bitmap_set_bit(map, field->field_index); + } + } +} + +/* ----- DML operations ----- */ + +int ha_duckdb::write_row(const uchar *) +{ + DBUG_ENTER("ha_duckdb::write_row"); + int ret= 0; + THD *thd= ha_thd(); + + DBUG_ASSERT(table_share != nullptr && table != nullptr); + MY_BITMAP *org_bitmap= dbug_tmp_use_all_columns(table, &table->read_set); + + ret= duckdb_register_trx(thd); + if (ret) + { + dbug_tmp_restore_column_map(&table->read_set, org_bitmap); + DBUG_RETURN(ret); + } + + myduck::BatchState batch_state= get_batch_state(thd); + + if (batch_state == myduck::BatchState::NOT_IN_BATCH) + { + InsertConvertor convertor(table, false); + ret= execute_dml(thd, &convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_insert++; + } + else + { + if (m_first_write) + { + build_duckdb_blob_map(table->field, &m_blob_map); + m_first_write= false; + } + auto *ctx= get_duckdb_context(thd); + ret= ctx->append_row_insert(table, &m_blob_map); + if (ret == 0) + srv_duckdb_status.duckdb_rows_insert_in_batch++; + else + { + /* Appender failed (e.g. table not yet created during ALTER TABLE). + Fall back to non-batch SQL insert. */ + ctx->set_batch_state(myduck::BatchState::NOT_IN_BATCH); + InsertConvertor convertor(table, false); + ret= execute_dml(thd, &convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_insert++; + } + } + + dbug_tmp_restore_column_map(&table->read_set, org_bitmap); + DBUG_RETURN(ret); +} + +int ha_duckdb::update_row(const uchar *old_row, const uchar *new_row) +{ + DBUG_ENTER("ha_duckdb::update_row"); + int ret= 0; + THD *thd= ha_thd(); + + ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + myduck::BatchState batch_state= get_batch_state(thd); + + if (batch_state == myduck::BatchState::NOT_IN_BATCH) + { + if (update_modified_column_only && + calc_row_difference(old_row, new_row, table)) + { + bitmap_copy(table->write_set, &table->tmp_set); + } + bitmap_clear_all(&table->tmp_set); + + UpdateConvertor update_convertor(table, old_row); + ret= execute_dml(thd, &update_convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_update++; + } + else + { + auto *ctx= get_duckdb_context(thd); + ret= ctx->append_row_update(table, old_row); + if (ret == 0) + srv_duckdb_status.duckdb_rows_update_in_batch++; + else + { + ctx->set_batch_state(myduck::BatchState::NOT_IN_BATCH); + UpdateConvertor update_convertor(table, old_row); + ret= execute_dml(thd, &update_convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_update++; + } + } + + DBUG_RETURN(ret); +} + +int ha_duckdb::delete_row(const uchar *) +{ + DBUG_ENTER("ha_duckdb::delete_row"); + int ret= 0; + THD *thd= ha_thd(); + + ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + myduck::BatchState batch_state= get_batch_state(thd); + + if (batch_state == myduck::BatchState::NOT_IN_BATCH) + { + DeleteConvertor convertor(table); + ret= execute_dml(thd, &convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_delete++; + } + else + { + auto *ctx= get_duckdb_context(thd); + ret= ctx->append_row_delete(table); + if (ret == 0) + srv_duckdb_status.duckdb_rows_delete_in_batch++; + else + { + ctx->set_batch_state(myduck::BatchState::NOT_IN_BATCH); + DeleteConvertor convertor(table); + ret= execute_dml(thd, &convertor); + if (ret == 0) + srv_duckdb_status.duckdb_rows_delete++; + } + } + + DBUG_RETURN(ret); +} + +/* ----- Index stubs (not supported) ----- */ + +int ha_duckdb::index_read_map(uchar *, const uchar *, key_part_map, + enum ha_rkey_function) +{ + DBUG_ENTER("ha_duckdb::index_read_map"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_duckdb::index_next(uchar *) +{ + DBUG_ENTER("ha_duckdb::index_next"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_duckdb::index_prev(uchar *) +{ + DBUG_ENTER("ha_duckdb::index_prev"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_duckdb::index_first(uchar *) +{ + DBUG_ENTER("ha_duckdb::index_first"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_duckdb::index_last(uchar *) +{ + DBUG_ENTER("ha_duckdb::index_last"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +/* ----- Table scan (rnd_*) ----- */ + +int ha_duckdb::rnd_init(bool) +{ + DBUG_ENTER("ha_duckdb::rnd_init"); + THD *thd= ha_thd(); + std::string schema_name; + std::string table_name; + + if (table && table->s) + { + schema_name.assign(table->s->db.str, table->s->db.length); + table_name.assign(table->s->table_name.str, table->s->table_name.length); + } + else + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + std::string query= + "SELECT * FROM \"" + schema_name + "\".\"" + table_name + "\""; + + auto *ctx= get_duckdb_context(thd); + query_result= myduck::duckdb_query(ctx->get_connection(), query); + if (query_result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_INTERNAL_ERROR, query_result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + for (auto *field= table->field; *field; ++field) + bitmap_set_bit(table->write_set, (*field)->field_index); + + DBUG_RETURN(0); +} + +int ha_duckdb::rnd_end() +{ + DBUG_ENTER("ha_duckdb::rnd_end"); + query_result.reset(); + current_chunk.reset(); + DBUG_RETURN(0); +} + +int ha_duckdb::rnd_next(uchar *buf) +{ + DBUG_ENTER("ha_duckdb::rnd_next"); + THD *thd= ha_thd(); + + if (!query_result) + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + memset(buf, 0, table->s->reclength); + + /* fetch new chunk when current chunk is empty */ + if (!current_chunk || current_row_index >= current_chunk->size()) + { + current_chunk.reset(); + current_chunk= query_result->Fetch(); + + if (!current_chunk) + DBUG_RETURN(HA_ERR_END_OF_FILE); + current_row_index= 0; + } + + /* store the fields of a tuple */ + for (size_t col_idx= 0; col_idx < current_chunk->ColumnCount(); ++col_idx) + { + duckdb::Value value= current_chunk->GetValue(col_idx, current_row_index); + Field *field= table->field[col_idx]; + store_duckdb_field_in_mysql_format(field, value, thd); + } + + /* update NULL field tag */ + if (table->s->null_bytes > 0) + { + if (table->null_flags) + memcpy(buf, table->null_flags, table->s->null_bytes); + else + memset(buf, 0, table->s->null_bytes); + } + + current_row_index++; + DBUG_RETURN(0); +} + +void ha_duckdb::position(const uchar *) +{ + DBUG_ENTER("ha_duckdb::position"); + DBUG_VOID_RETURN; +} + +int ha_duckdb::rnd_pos(uchar *, uchar *) +{ + DBUG_ENTER("ha_duckdb::rnd_pos"); + DBUG_RETURN(HA_ERR_WRONG_COMMAND); +} + +int ha_duckdb::info(uint flag) +{ + DBUG_ENTER("ha_duckdb::info"); + if (flag & HA_STATUS_VARIABLE) + { + /* Retrieve variable info, such as row counts and file lengths */ + stats.records= records(); + stats.deleted= 0; + // stats.data_file_length = + // stats.index_file_length = + // stats.delete_length = + stats.check_time= 0; + // stats.mrr_length_per_rec = + + // stats.data_file_length may be unset for TIAMAT; avoid division by + // garbage. + if (stats.records == 0 || stats.data_file_length == 0) + stats.mean_rec_length= 0; + else + stats.mean_rec_length= (ulong) (stats.data_file_length / stats.records); + } + + DBUG_RETURN(0); +} + +ha_rows ha_duckdb::records() +{ + DBUG_ENTER("ha_tiamat::records"); + // Optimizer may call records()/info() in contexts where ha_share isn't + // initialized for this handler instance. Return a conservative estimate. + if (stats.records) + DBUG_RETURN(stats.records); + DBUG_RETURN(10); +} + +int ha_duckdb::extra(enum ha_extra_function operation) +{ + DBUG_ENTER("ha_duckdb::extra"); + THD *thd= ha_thd(); + auto *ctx= get_duckdb_context(thd); + + switch (operation) + { + case HA_EXTRA_BEGIN_COPY: + ctx->set_in_copy_ddl(true); + break; + case HA_EXTRA_END_COPY: + case HA_EXTRA_ABORT_COPY: + ctx->set_in_copy_ddl(false); + break; + default: + break; + } + DBUG_RETURN(0); +} + +int ha_duckdb::delete_all_rows() +{ + DBUG_ENTER("ha_duckdb::delete_all_rows"); + int ret= 0; + THD *thd= ha_thd(); + + ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + auto *ctx= get_duckdb_context(thd); + + /* Discard any pending batch rows for this table */ + DatabaseTableNames dt(table->s->normalized_path.str); + ctx->delete_appender(dt.db_name, dt.table_name); + + /* Execute DELETE FROM "schema"."table" */ + std::string query= + "DELETE FROM \"" + dt.db_name + "\".\"" + dt.table_name + "\""; + + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + if (query_result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_DML_ERROR, query_result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_DML_ERROR); + } + + DBUG_RETURN(0); +} + +const COND *ha_duckdb::cond_push(const COND *cond) +{ + DBUG_ENTER("ha_duckdb::cond_push"); + /* + Accept all conditions — DuckDB will evaluate the WHERE clause + from the original SQL query in direct_delete_rows(). + */ + DBUG_RETURN(NULL); +} + +int ha_duckdb::direct_delete_rows_init() +{ + DBUG_ENTER("ha_duckdb::direct_delete_rows_init"); + DBUG_RETURN(0); +} + +int ha_duckdb::direct_delete_rows(ha_rows *delete_rows) +{ + DBUG_ENTER("ha_duckdb::direct_delete_rows"); + int ret= 0; + THD *thd= ha_thd(); + + ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + auto *ctx= get_duckdb_context(thd); + + /* Flush any pending batch rows so DuckDB sees consistent data */ + std::string error_msg; + if (ctx->flush_appenders(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, error_msg.c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_DML_ERROR); + } + + /* Execute the original DELETE statement in DuckDB */ + LEX_STRING *qs= thd_query_string(thd); + std::string query(qs->str, qs->length); + auto result= myduck::duckdb_query(thd, query, true); + if (result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_DML_ERROR, result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_DML_ERROR); + } + + /* DuckDB returns a single row with the count of affected rows */ + auto chunk= result->Fetch(); + if (chunk && chunk->size() > 0) + *delete_rows= chunk->GetValue(0, 0).GetValue(); + else + *delete_rows= 0; + + srv_duckdb_status.duckdb_rows_delete+= *delete_rows; + + DBUG_RETURN(0); +} + +int ha_duckdb::direct_update_rows_init(List *update_fields + __attribute__((unused))) +{ + DBUG_ENTER("ha_duckdb::direct_update_rows_init"); + DBUG_RETURN(0); +} + +int ha_duckdb::direct_update_rows(ha_rows *update_rows, ha_rows *found_rows) +{ + DBUG_ENTER("ha_duckdb::direct_update_rows"); + int ret= 0; + THD *thd= ha_thd(); + + ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + auto *ctx= get_duckdb_context(thd); + + /* Flush any pending batch rows so DuckDB sees consistent data */ + std::string error_msg; + if (ctx->flush_appenders(error_msg)) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, error_msg.c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_DML_ERROR); + } + + /* Execute the original UPDATE statement in DuckDB */ + LEX_STRING *qs= thd_query_string(thd); + std::string query(qs->str, qs->length); + auto result= myduck::duckdb_query(thd, query, true); + if (result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_DML_ERROR, result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_DML_ERROR); + } + + /* DuckDB returns a single row with the count of affected rows */ + auto chunk= result->Fetch(); + ha_rows affected= 0; + if (chunk && chunk->size() > 0) + affected= chunk->GetValue(0, 0).GetValue(); + + *update_rows= affected; + *found_rows= affected; + + srv_duckdb_status.duckdb_rows_update+= affected; + + DBUG_RETURN(0); +} + +int ha_duckdb::external_lock(THD *thd, int lock_type) +{ + DBUG_ENTER("ha_duckdb::external_lock"); + if (lock_type != F_UNLCK) + { + /* DuckDB does not support XA transactions. Reject DML early. */ + if (myduck::reject_xa_if_active(thd)) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + } + DBUG_RETURN(0); +} + +uint ha_duckdb::lock_count(void) const { return 0; } + +THR_LOCK_DATA **ha_duckdb::store_lock(THD *, THR_LOCK_DATA **to, + enum thr_lock_type) +{ + return to; +} + +ha_rows ha_duckdb::records_in_range(uint, const key_range *, const key_range *, + page_range *) +{ + DBUG_ENTER("ha_duckdb::records_in_range"); + DBUG_RETURN(10); +} + +/* ----- DDL operations ----- */ + +int ha_duckdb::create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) +{ + DBUG_ENTER("ha_duckdb::create"); + THD *thd= ha_thd(); + + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + DatabaseTableNames dt(name); + CreateTableConvertor convertor(thd, form, create_info, dt.db_name, + dt.table_name); + + if (convertor.check()) + DBUG_RETURN(HA_DUCKDB_CREATE_ERROR); + + std::string query= convertor.translate(); + + auto *ctx= get_duckdb_context(thd); + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + + if (query_result->HasError()) + { + push_duckdb_query_error(query_result->GetError()); + DBUG_RETURN(HA_DUCKDB_CREATE_ERROR); + } + + DBUG_RETURN(0); +} + +int ha_duckdb::delete_table(const char *name) +{ + DBUG_ENTER("ha_duckdb::delete_table"); + THD *thd= ha_thd(); + + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + DatabaseTableNames dt(name); + + std::string query= + "DROP TABLE IF EXISTS \"" + dt.db_name + "\".\"" + dt.table_name + "\""; + + auto *ctx= get_duckdb_context(thd); + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + + if (query_result == nullptr || query_result->HasError()) + DBUG_RETURN(HA_DUCKDB_DROP_TABLE_ERROR); + + DBUG_RETURN(0); +} + +int ha_duckdb::rename_table(const char *from, const char *to) +{ + DBUG_ENTER("ha_duckdb::rename_table"); + THD *thd= ha_thd(); + + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + DatabaseTableNames old_t(from); + DatabaseTableNames new_t(to); + + auto convertor= std::make_unique( + old_t.db_name, old_t.table_name, new_t.db_name, new_t.table_name); + + if (convertor->check()) + DBUG_RETURN(HA_DUCKDB_RENAME_ERROR); + + std::string query= convertor->translate(); + + auto *ctx= get_duckdb_context(thd); + std::string error_msg; + ret= ctx->flush_appenders(error_msg); + if (ret) + DBUG_RETURN(ret); + + auto query_result= myduck::duckdb_query(ctx->get_connection(), query); + + if (query_result->HasError()) + DBUG_RETURN(HA_DUCKDB_RENAME_ERROR); + + DBUG_RETURN(0); +} + +int ha_duckdb::truncate() +{ + DBUG_ENTER("ha_duckdb::truncate"); + THD *thd= ha_thd(); + + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(ret); + + std::string schema_name(table->s->db.str, table->s->db.length); + std::string table_name(table->s->table_name.str, + table->s->table_name.length); + + std::ostringstream query; + query << "USE \"" << schema_name << "\";"; + query << "TRUNCATE TABLE \"" << table_name << "\";"; + + auto *ctx= get_duckdb_context(thd); + auto query_result= myduck::duckdb_query(ctx->get_connection(), query.str()); + + if (query_result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_TRUNCATE_TABLE_ERROR, query_result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(HA_DUCKDB_TRUNCATE_TABLE_ERROR); + } + + DBUG_RETURN(0); +} + +/* ----- ALTER TABLE (inplace) ----- */ + +static inline bool database_changed(const char *old_schema, + const char *new_schema) +{ + return strcasecmp(old_schema, new_schema) != 0; +} + +enum_alter_inplace_result +ha_duckdb::check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) +{ + DBUG_ENTER("ha_duckdb::check_if_supported_inplace_alter"); + + if (database_changed(table->s->db.str, altered_table->s->db.str)) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + + if (ha_alter_info->alter_info->flags & ALTER_COLUMN_ORDER) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + + /* Reject ALTER on tables without PK when require_primary_key is ON */ + if (myduck::require_primary_key && table->s->primary_key == MAX_KEY) + { + my_error(ER_REQUIRES_PRIMARY_KEY, MYF(0)); + DBUG_RETURN(HA_ALTER_ERROR); + } + + DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK); +} + +bool ha_duckdb::commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit) +{ + DBUG_ENTER("ha_duckdb::commit_inplace_alter_table"); + + if (!commit) + DBUG_RETURN(false); + + THD *thd= ha_thd(); + int ret= duckdb_register_trx(thd); + if (ret) + DBUG_RETURN(true); + + ulonglong handler_flags= ha_alter_info->handler_flags; + ulonglong sql_flags= ha_alter_info->alter_info->flags; + + using DDL_convertor= std::unique_ptr; + using DDL_convertors= std::vector; + DDL_convertor convertor; + DDL_convertors convertors; + + std::string schema_name(table->s->db.str, table->s->db.length); + std::string table_name(table->s->table_name.str, + table->s->table_name.length); + + if (handler_flags & ALTER_ADD_COLUMN) + { + convertor= std::make_unique( + schema_name, table_name, altered_table, ha_alter_info->alter_info); + convertors.push_back(std::move(convertor)); + } + + if (handler_flags & ALTER_DROP_COLUMN) + { + convertor= std::make_unique( + schema_name, table_name, table, altered_table, + ha_alter_info->alter_info); + convertors.push_back(std::move(convertor)); + } + + if ((sql_flags & ALTER_CHANGE_COLUMN) || (handler_flags & ALTER_COLUMN_NAME)) + { + convertor= std::make_unique( + schema_name, table_name, altered_table, ha_alter_info->alter_info); + convertors.push_back(std::move(convertor)); + } + + if (sql_flags & ALTER_CHANGE_COLUMN_DEFAULT) + { + convertor= std::make_unique( + schema_name, table_name, table, altered_table); + convertors.push_back(std::move(convertor)); + } + + /* + When adding a primary key, set NOT NULL on the corresponding columns + in DuckDB (DuckDB doesn't have indexes, but needs the constraint). + */ + if (sql_flags & ALTER_ADD_INDEX) + { + convertor= std::make_unique( + schema_name, table_name, altered_table); + convertors.push_back(std::move(convertor)); + } + + if (convertors.empty()) + DBUG_RETURN(false); + + /* Execute each ALTER operation in its own auto-commit context. + DuckDB v1.5+ does not allow compound DDL that mixes structural + changes (ADD COLUMN) with constraint updates (SET DEFAULT) + within the same transaction. */ + auto con= myduck::DuckdbManager::CreateConnection(); + + for (auto &conv : convertors) + { + if (!conv || conv->check()) + DBUG_RETURN(true); + + std::string sql= conv->translate(); + if (sql.empty()) + continue; + + auto query_result= myduck::duckdb_query(*con, sql); + if (query_result->HasError()) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_GENERIC, query_result->GetError().c_str(), "DuckDB"); + DBUG_RETURN(true); + } + } + + DBUG_RETURN(false); +} + +/* ----- Plugin declaration ----- */ + +/* ---- AliSQL-specific global variables (no DuckDB push) ---- */ + +static MYSQL_SYSVAR_BOOL(copy_ddl_in_batch, copy_ddl_in_batch, + PLUGIN_VAR_RQCMDARG, + "Use batch insert to speed up copy ddl", NULL, NULL, + TRUE); + +static MYSQL_SYSVAR_BOOL(dml_in_batch, dml_in_batch, PLUGIN_VAR_RQCMDARG, + "Use batch to speed up INSERT/UPDATE/DELETE", NULL, + NULL, TRUE); + +static MYSQL_SYSVAR_BOOL(update_modified_column_only, + update_modified_column_only, PLUGIN_VAR_RQCMDARG, + "Whether to only update modified columns", NULL, NULL, + TRUE); + +/* ---- Global proxy variables (pushed into DuckDB) ---- */ + +static MYSQL_SYSVAR_ULONGLONG(memory_limit, myduck::global_memory_limit, + PLUGIN_VAR_RQCMDARG, + "DuckDB memory limit in bytes (0 = default)", + NULL, myduck::update_memory_limit_cb, 0, 0, + ULONGLONG_MAX, 0); + +static MYSQL_SYSVAR_STR(temp_directory, myduck::global_duckdb_temp_directory, + PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG, + "Directory for DuckDB temporary files", NULL, NULL, + NULL); + +static MYSQL_SYSVAR_ULONGLONG(max_temp_directory_size, + myduck::global_max_temp_directory_size, + PLUGIN_VAR_RQCMDARG, + "Max disk space for DuckDB temp directory " + "(0 = 90%% of available)", + NULL, myduck::update_max_temp_directory_size_cb, + 0, 0, ULONGLONG_MAX, 1024); + +static MYSQL_SYSVAR_ULONGLONG(max_threads, myduck::global_max_threads, + PLUGIN_VAR_RQCMDARG, + "DuckDB max threads (0 = default)", NULL, + myduck::update_threads_cb, 0, 0, 1048576, 0); + +static MYSQL_SYSVAR_BOOL(use_direct_io, myduck::global_use_dio, + PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG, + "Use Direct I/O for DuckDB data files", NULL, NULL, + FALSE); + +static MYSQL_SYSVAR_BOOL(scheduler_process_partial, + myduck::global_scheduler_process_partial, + PLUGIN_VAR_RQCMDARG, + "Partially process tasks before rescheduling", NULL, + myduck::update_scheduler_process_partial_cb, TRUE); + +static MYSQL_SYSVAR_ULONGLONG(checkpoint_threshold, + myduck::checkpoint_threshold, + PLUGIN_VAR_RQCMDARG, + "DuckDB WAL checkpoint threshold in bytes", NULL, + myduck::update_checkpoint_threshold_cb, + 268435456, 0, ULONGLONG_MAX, 1024); + +static MYSQL_SYSVAR_BOOL(use_double_for_decimal, + myduck::use_double_for_decimal, PLUGIN_VAR_RQCMDARG, + "Use DOUBLE for DECIMAL precision > 38", NULL, NULL, + TRUE); + +static MYSQL_SYSVAR_BOOL(require_primary_key, myduck::require_primary_key, + PLUGIN_VAR_RQCMDARG, + "Require primary key for DuckDB tables", NULL, NULL, + TRUE); + +static MYSQL_SYSVAR_ULONGLONG(appender_allocator_flush_threshold, + myduck::appender_allocator_flush_threshold, + PLUGIN_VAR_RQCMDARG, + "Flush appender allocator when batch memory " + "reaches this threshold", + NULL, myduck::update_appender_flush_threshold_cb, + 67108864, 0, ULONGLONG_MAX, 1024); + +static MYSQL_SYSVAR_SET(log_options, myduck::duckdb_log_options, + PLUGIN_VAR_RQCMDARG, "DuckDB operation types to log", + NULL, NULL, 0, &myduck::log_options_typelib); + +/* ---- Session variables (propagated per-connection) ---- */ + +static MYSQL_THDVAR_ULONGLONG(merge_join_threshold, PLUGIN_VAR_RQCMDARG, + "Row count threshold to prefer merge join", NULL, + NULL, 4611686018427387904ULL, 0, + 4611686018427387904ULL, 0); + +static MYSQL_THDVAR_BOOL(force_no_collation, PLUGIN_VAR_RQCMDARG, + "Disable collation pushdown, use binary comparison", + NULL, NULL, FALSE); + +static MYSQL_THDVAR_ENUM(explain_output, PLUGIN_VAR_RQCMDARG, + "DuckDB EXPLAIN output format", NULL, NULL, + myduck::EXPLAIN_PHYSICAL_ONLY, + &myduck::explain_output_typelib); + +static MYSQL_THDVAR_SET(disabled_optimizers, PLUGIN_VAR_RQCMDARG, + "Disable specific DuckDB optimizer rules", NULL, NULL, + 0, &myduck::disabled_optimizers_typelib); + +/* ---- THDVAR accessor functions (used from duckdb_context.cc) ---- */ + +namespace myduck +{ + +ulonglong get_thd_merge_join_threshold(THD *thd) +{ + return THDVAR(thd, merge_join_threshold); +} + +my_bool get_thd_force_no_collation(THD *thd) +{ + return THDVAR(thd, force_no_collation); +} + +ulong get_thd_explain_output(THD *thd) { return THDVAR(thd, explain_output); } + +ulonglong get_thd_disabled_optimizers(THD *thd) +{ + return THDVAR(thd, disabled_optimizers); +} + +} // namespace myduck + +static struct st_mysql_sys_var *duckdb_system_variables[]= { + MYSQL_SYSVAR(copy_ddl_in_batch), MYSQL_SYSVAR(dml_in_batch), + MYSQL_SYSVAR(update_modified_column_only), + /* Global proxy */ + MYSQL_SYSVAR(memory_limit), MYSQL_SYSVAR(temp_directory), + MYSQL_SYSVAR(max_temp_directory_size), MYSQL_SYSVAR(max_threads), + MYSQL_SYSVAR(use_direct_io), MYSQL_SYSVAR(scheduler_process_partial), + MYSQL_SYSVAR(checkpoint_threshold), MYSQL_SYSVAR(use_double_for_decimal), + MYSQL_SYSVAR(require_primary_key), + MYSQL_SYSVAR(appender_allocator_flush_threshold), + MYSQL_SYSVAR(log_options), + /* Session proxy */ + MYSQL_SYSVAR(merge_join_threshold), MYSQL_SYSVAR(force_no_collation), + MYSQL_SYSVAR(explain_output), MYSQL_SYSVAR(disabled_optimizers), NULL}; + +static struct st_mysql_show_var duckdb_status_variables[]= { + {"Duckdb_rows_insert", (char *) &srv_duckdb_status.duckdb_rows_insert, + SHOW_LONGLONG}, + {"Duckdb_rows_update", (char *) &srv_duckdb_status.duckdb_rows_update, + SHOW_LONGLONG}, + {"Duckdb_rows_delete", (char *) &srv_duckdb_status.duckdb_rows_delete, + SHOW_LONGLONG}, + {"Duckdb_rows_insert_in_batch", + (char *) &srv_duckdb_status.duckdb_rows_insert_in_batch, SHOW_LONGLONG}, + {"Duckdb_rows_update_in_batch", + (char *) &srv_duckdb_status.duckdb_rows_update_in_batch, SHOW_LONGLONG}, + {"Duckdb_rows_delete_in_batch", + (char *) &srv_duckdb_status.duckdb_rows_delete_in_batch, SHOW_LONGLONG}, + {"Duckdb_commit", (char *) &srv_duckdb_status.duckdb_commit, + SHOW_LONGLONG}, + {"Duckdb_rollback", (char *) &srv_duckdb_status.duckdb_rollback, + SHOW_LONGLONG}, + {NullS, NullS, SHOW_LONG}}; + +static struct st_mysql_storage_engine duckdb_storage_engine= { + MYSQL_HANDLERTON_INTERFACE_VERSION}; + +extern Plugin_function plugin_descriptor_function_run_in_duckdb; + +maria_declare_plugin(duckdb) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &duckdb_storage_engine, + "DUCKDB", + "drrtuy,lfedorov", + "DuckDB storage engine", + PLUGIN_LICENSE_GPL, + duckdb_init_func, /* Plugin Init */ + duckdb_deinit_func, /* Plugin Deinit */ + 0x0100, /* version number (1.0) */ + duckdb_status_variables, /* status variables */ + duckdb_system_variables, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */ +}, +{ + MariaDB_FUNCTION_PLUGIN, + &plugin_descriptor_function_run_in_duckdb, + "RUN_IN_DUCKDB", + "drrtuy,lfedorov", + "Execute a SQL query in DuckDB and return the result as a string", + PLUGIN_LICENSE_GPL, + NULL, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100, /* version number (1.0) */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */ +} +maria_declare_plugin_end; diff --git a/storage/duckdb/ha_duckdb.h b/storage/duckdb/ha_duckdb.h new file mode 100644 index 0000000000000..b0ff93d247185 --- /dev/null +++ b/storage/duckdb/ha_duckdb.h @@ -0,0 +1,167 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#ifndef HA_DUCKDB_H +#define HA_DUCKDB_H + +#include +#include + +#include "my_global.h" +#include "my_base.h" +#include "my_compiler.h" +#include "handler.h" +#include "thr_lock.h" +#include "sql_class.h" +#include "sql_show.h" + +#undef UNKNOWN + +#include "duckdb.hpp" +#include "duckdb/common/types.hpp" + +#include "duckdb_handler_errors.h" + +extern handlerton *duckdb_hton; + +/** @brief + Duckdb_share is a class that will be shared among all open handlers. +*/ +class Duckdb_share : public Handler_share +{ +public: + THR_LOCK lock; + Duckdb_share(); + ~Duckdb_share() { thr_lock_delete(&lock); } +}; + +/** @brief + Class definition for the DuckDB storage engine (MariaDB port) +*/ +class ha_duckdb : public handler +{ + THR_LOCK_DATA lock; ///< MariaDB lock + Duckdb_share *share; ///< Shared lock info + Duckdb_share *get_share(); ///< Get the share + +public: + ha_duckdb(handlerton *hton, TABLE_SHARE *table_arg); + ~ha_duckdb(); + + const char *table_type() const override { return "DUCKDB"; } + + ulonglong table_flags() const override + { + return (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE | + HA_NO_AUTO_INCREMENT | HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | + HA_CAN_DIRECT_UPDATE_AND_DELETE); + } + + ulong index_flags(uint inx, uint part, bool all_parts) const override + { + return 0; + } + + uint max_supported_record_length() const override + { + return HA_MAX_REC_LENGTH; + } + + uint max_supported_keys() const override { return MAX_KEY; } + + uint max_supported_key_parts() const override { return MAX_REF_PARTS; } + + uint max_supported_key_length() const override { return 10240; } + + IO_AND_CPU_COST scan_time() override + { + IO_AND_CPU_COST cost; + cost.io= (double) (stats.records + stats.deleted) * DISK_READ_COST; + cost.cpu= 0; + return cost; + } + + IO_AND_CPU_COST keyread_time(uint, ulong, ha_rows rows, + ulonglong blocks) override + { + IO_AND_CPU_COST cost; + cost.io= blocks * DISK_READ_COST; + cost.cpu= (double) rows * 0.001; + return cost; + } + + /* Methods implemented in ha_duckdb.cc */ + int open(const char *name, int mode, uint test_if_locked) override; + int close(void) override; + + int write_row(const uchar *buf) override; + int update_row(const uchar *old_data, const uchar *new_data) override; + int delete_row(const uchar *buf) override; + + int index_read_map(uchar *buf, const uchar *key, key_part_map keypart_map, + enum ha_rkey_function find_flag) override; + int index_next(uchar *buf) override; + int index_prev(uchar *buf) override; + int index_first(uchar *buf) override; + int index_last(uchar *buf) override; + + int rnd_init(bool scan) override; + int rnd_end() override; + int rnd_next(uchar *buf) override; + int rnd_pos(uchar *buf, uchar *pos) override; + void position(const uchar *record) override; + int info(uint) override; + int extra(enum ha_extra_function operation) override; + int external_lock(THD *thd, int lock_type) override; + int delete_all_rows(void) override; + const COND *cond_push(const COND *cond) override; + int direct_delete_rows_init() override; + int direct_delete_rows(ha_rows *delete_rows) override; + int direct_update_rows_init(List *update_fields) override; + int direct_update_rows(ha_rows *update_rows, ha_rows *found_rows) override; + ha_rows records() override; + ha_rows records_in_range(uint inx, const key_range *min_key, + const key_range *max_key, + page_range *pages) override; + int delete_table(const char *from) override; + int rename_table(const char *from, const char *to) override; + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) override; + int truncate() override; + + uint lock_count(void) const override; + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type) override; + + enum_alter_inplace_result + check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) override; + bool commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit) override; + +private: + std::unique_ptr query_result; + std::unique_ptr current_chunk; + size_t current_row_index= 0; + MY_BITMAP m_blob_map; + bool m_first_write{true}; +}; + +#endif // HA_DUCKDB_H diff --git a/storage/duckdb/ha_duckdb_pushdown.cc b/storage/duckdb/ha_duckdb_pushdown.cc new file mode 100644 index 0000000000000..49514d51bb812 --- /dev/null +++ b/storage/duckdb/ha_duckdb_pushdown.cc @@ -0,0 +1,789 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "sql_select.h" +#include "log.h" + +#undef UNKNOWN + +#include "ha_duckdb_pushdown.h" +#include "duckdb_select.h" +#include "duckdb_query.h" +#include "duckdb_context.h" +#include "cross_engine_scan.h" +#include "duckdb_log.h" + +extern handlerton *duckdb_hton; + +/** + Check whether a SELECT_LEX can be pushed down to DuckDB. + + Returns true if at least one table is DuckDB. Non-DuckDB tables are + collected in @p external_tables so they can be registered with the + replacement-scan mechanism before executing the DuckDB query. +*/ + +static bool can_pushdown_to_duckdb(SELECT_LEX *sel_lex, + std::vector &external_tables, + bool &has_duckdb_table) +{ + for (TABLE_LIST *tbl= sel_lex->get_table_list(); tbl; tbl= tbl->next_global) + { + if (tbl->derived || !tbl->table) + continue; + + if (tbl->table->file->ht == duckdb_hton) + has_duckdb_table= true; + else + external_tables.emplace_back(tbl->table_name.str); + } + + return has_duckdb_table; +} + +/** + Check whether a SELECT_LEX_UNIT (UNION/EXCEPT/INTERSECT) can be + pushed down to DuckDB. Walks every SELECT_LEX in the unit. +*/ + +static bool +can_pushdown_unit_to_duckdb(SELECT_LEX_UNIT *unit, + std::vector &external_tables, + bool &has_duckdb_table) +{ + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + for (TABLE_LIST *tbl= sl->get_table_list(); tbl; tbl= tbl->next_global) + { + if (tbl->derived || !tbl->table) + continue; + + if (tbl->table->file->ht == duckdb_hton) + has_duckdb_table= true; + else + external_tables.emplace_back(tbl->table_name.str); + } + } + + return has_duckdb_table; +} + +/* ----- Factory functions ----- */ + +select_handler *create_duckdb_select_handler(THD *thd, SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit) +{ + /* + Only handle plain SELECT and INSERT ... SELECT for now. + */ + if (thd->lex->sql_command != SQLCOM_SELECT && + thd->lex->sql_command != SQLCOM_INSERT_SELECT) + return nullptr; + + if (!sel_lex) + return nullptr; + + std::vector external_tables; + bool has_duckdb_table= false; + + if (!can_pushdown_to_duckdb(sel_lex, external_tables, has_duckdb_table)) + return nullptr; + + /* At least one DuckDB table must participate */ + if (!has_duckdb_table) + return nullptr; + + /* Do not push down queries with side-effects (e.g. user variables) */ + if (sel_lex->uncacheable & UNCACHEABLE_SIDEEFFECT) + return nullptr; + + auto *handler= new ha_duckdb_select_handler(thd, sel_lex, sel_unit); + if (!external_tables.empty()) + { + handler->set_cross_engine(std::move(external_tables)); + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information("DuckDB: cross-engine pushdown with %zu " + "external table(s)", + handler->external_table_count()); + } + return handler; +} + +select_handler *create_duckdb_unit_handler(THD *thd, SELECT_LEX_UNIT *sel_unit) +{ + if (thd->lex->sql_command == SQLCOM_CREATE_VIEW) + return nullptr; + + if (thd->stmt_arena && thd->stmt_arena->is_stmt_prepare()) + return nullptr; + + if (!sel_unit) + return nullptr; + + std::vector external_tables; + bool has_duckdb_table= false; + + if (!can_pushdown_unit_to_duckdb(sel_unit, external_tables, + has_duckdb_table)) + return nullptr; + + if (!has_duckdb_table) + return nullptr; + + auto *handler= new ha_duckdb_select_handler(thd, sel_unit); + if (!external_tables.empty()) + { + handler->set_cross_engine(std::move(external_tables)); + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information("DuckDB: cross-engine UNION pushdown with %zu " + "external table(s)", + handler->external_table_count()); + } + return handler; +} + +/* ----- ha_duckdb_select_handler implementation ----- */ + +ha_duckdb_select_handler::ha_duckdb_select_handler(THD *thd_arg, + SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit) + : select_handler(thd_arg, duckdb_hton, sel_lex, sel_unit), + current_row_index(0), query_string(thd_arg->charset()) +{ + query_string.length(0); + + /* + Use the original SQL text from THD instead of SELECT_LEX::print(). + SELECT_LEX::print() converts implicit (comma) joins into explicit + "JOIN" without ON clauses, which DuckDB's parser rejects. + The select_handler intercepts the full query in all cases + (simple SELECT and UNION), so the original text is always usable. + */ + query_string.append(thd_arg->query(), thd_arg->query_length()); +} + +ha_duckdb_select_handler::ha_duckdb_select_handler(THD *thd_arg, + SELECT_LEX_UNIT *sel_unit) + : select_handler(thd_arg, duckdb_hton, sel_unit), current_row_index(0), + query_string(thd_arg->charset()) +{ + query_string.length(0); + query_string.append(thd_arg->query(), thd_arg->query_length()); +} + +ha_duckdb_select_handler::~ha_duckdb_select_handler()= default; + +void ha_duckdb_select_handler::set_cross_engine( + std::vector &&tables) +{ + has_cross_engine= true; + external_table_names= std::move(tables); +} + +size_t ha_duckdb_select_handler::external_table_count() const +{ + return external_table_names.size(); +} + +/** + Case-insensitive find that skips single-quoted strings ('...'), + double-quoted strings ("..."), backtick identifiers (`...`), + line comments (-- ...) and C-style comments. + Returns position of the first match at or after @p start, + or std::string::npos. +*/ +static size_t find_in_code(const std::string &sql, const char *pattern, + size_t start= 0) +{ + size_t plen= strlen(pattern); + if (plen == 0 || sql.size() < plen) + return std::string::npos; + + size_t i= start; + while (i + plen <= sql.size()) + { + unsigned char c= sql[i]; + + /* Skip single-quoted strings: handle \' and '' escapes */ + if (c == '\'') + { + for (i++; i < sql.size(); i++) + { + if (sql[i] == '\\') { i++; continue; } + if (sql[i] == '\'') + { + if (i + 1 < sql.size() && sql[i + 1] == '\'') { i++; continue; } + break; + } + } + i++; + continue; + } + + /* Skip double-quoted strings */ + if (c == '"') + { + for (i++; i < sql.size(); i++) + { + if (sql[i] == '\\') { i++; continue; } + if (sql[i] == '"') + { + if (i + 1 < sql.size() && sql[i + 1] == '"') { i++; continue; } + break; + } + } + i++; + continue; + } + + /* Skip backtick identifiers */ + if (c == '`') + { + for (i++; i < sql.size(); i++) + { + if (sql[i] == '`') + { + if (i + 1 < sql.size() && sql[i + 1] == '`') { i++; continue; } + break; + } + } + i++; + continue; + } + + /* Skip -- line comments */ + if (c == '-' && i + 1 < sql.size() && sql[i + 1] == '-') + { + for (i += 2; i < sql.size() && sql[i] != '\n'; i++) {} + continue; + } + + /* Skip C-style comments */ + if (c == '/' && i + 1 < sql.size() && sql[i + 1] == '*') + { + for (i += 2; i + 1 < sql.size(); i++) + { + if (sql[i] == '*' && sql[i + 1] == '/') { i += 2; break; } + } + continue; + } + + /* Case-insensitive match at position i */ + bool match= true; + for (size_t j= 0; j < plen; j++) + { + if (toupper((unsigned char) sql[i + j]) != + toupper((unsigned char) pattern[j])) + { + match= false; + break; + } + } + if (match) + return i; + + i++; + } + + return std::string::npos; +} + +/** + Reverse find_in_code: returns position of the last match at or before + @p before, or std::string::npos. +*/ +static size_t rfind_in_code(const std::string &sql, const char *pattern, + size_t before= std::string::npos) +{ + size_t last= std::string::npos; + size_t pos= 0; + for (;;) + { + pos= find_in_code(sql, pattern, pos); + if (pos == std::string::npos || pos > before) + break; + last= pos; + pos++; + } + return last; +} + +/** + Case-insensitive prefix match: does @p sql at position @p pos start + with @p prefix? +*/ +static bool ci_starts_with(const std::string &sql, size_t pos, + const char *prefix) +{ + for (size_t i= 0; prefix[i]; i++) + { + if (pos + i >= sql.size()) + return false; + if (toupper((unsigned char) sql[pos + i]) != + toupper((unsigned char) prefix[i])) + return false; + } + return true; +} + +int ha_duckdb_select_handler::init_scan() +{ + DBUG_ENTER("ha_duckdb_select_handler::init_scan"); + + /* Register external tables with the thread-local replacement scan registry + */ + if (has_cross_engine) + { + auto register_tables_from_sel= [this](SELECT_LEX *sl) { + for (TABLE_LIST *tbl= sl->get_table_list(); tbl; tbl= tbl->next_global) + { + if (tbl->derived || !tbl->table) + continue; + if (tbl->table->file->ht != duckdb_hton) + { + myduck::register_external_table(tbl->table_name.str, tbl->table); + + if (sl->where) + { + COND *table_cond= + make_cond_for_table(thd, sl->where, tbl->table->map, + tbl->table->map, -1, false, false); + if (table_cond) + { + StringBuffer<1024> buf; + table_cond->print(&buf, + QT_ITEM_IDENT_DISABLE_DB_TABLE_NAMES); + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information( + "CROSS-ENGINE: WHERE for '%s': %.*s", + tbl->table_name.str, (int) buf.length(), buf.ptr()); + myduck::register_external_where( + tbl->table_name.str, + std::string(buf.ptr(), buf.length())); + } + } + } + } + }; + + if (select_lex) + { + register_tables_from_sel(select_lex); + } + else if (lex_unit) + { + for (SELECT_LEX *sl= lex_unit->first_select(); sl; sl= sl->next_select()) + register_tables_from_sel(sl); + } + } + + std::string sql(query_string.ptr(), query_string.length()); + + /* + Rewrite MariaDB-specific SQL syntax that DuckDB does not understand. + GROUP BY ... WITH ROLLUP → GROUP BY ROLLUP(...) + */ + { + size_t search_from= 0; + size_t rollup_pos; + while ((rollup_pos= find_in_code(sql, " WITH ROLLUP", search_from)) + != std::string::npos) + { + size_t group_pos= rfind_in_code(sql, "GROUP BY ", rollup_pos); + if (group_pos != std::string::npos && group_pos >= search_from) + { + size_t cols_start= group_pos + 9; + std::string cols= sql.substr(cols_start, rollup_pos - cols_start); + std::string replacement= "GROUP BY ROLLUP(" + cols + ")"; + sql.replace(group_pos, rollup_pos + 12 - group_pos, replacement); + search_from= group_pos + replacement.size(); + } + else + search_from= rollup_pos + 12; + } + } + + /* + Rewrite CONVERT(expr, TYPE) → CAST(expr AS TYPE) + MariaDB uses CONVERT(expr, type) syntax, DuckDB uses CAST(expr AS type). + */ + { + size_t pos= 0; + while ((pos= find_in_code(sql, "CONVERT(", pos)) != std::string::npos) + { + /* Word boundary: skip if preceded by an identifier character */ + if (pos > 0 && + (isalnum((unsigned char) sql[pos - 1]) || sql[pos - 1] == '_')) + { + pos++; + continue; + } + /* Find matching closing paren, handling nested parens */ + size_t start= pos + 8; /* after "CONVERT(" */ + int depth= 1; + size_t i= start; + size_t comma= std::string::npos; + for (; i < sql.size() && depth > 0; i++) + { + if (sql[i] == '(') + depth++; + else if (sql[i] == ')') + depth--; + else if (sql[i] == ',' && depth == 1 && comma == std::string::npos) + comma= i; + } + if (comma != std::string::npos && depth == 0) + { + std::string expr= sql.substr(start, comma - start); + std::string type= sql.substr(comma + 1, i - 1 - (comma + 1)); + /* Trim whitespace from type */ + size_t ts= type.find_first_not_of(" \t"); + size_t te= type.find_last_not_of(" \t"); + if (ts != std::string::npos) + type= type.substr(ts, te - ts + 1); + std::string replacement= "CAST(" + expr + " AS " + type + ")"; + sql.replace(pos, i - pos, replacement); + } + else + pos++; + } + } + + /* + Rewrite CURRENT_TIME() → current_time, CURRENT_DATE() → current_date, + CURRENT_TIMESTAMP() → current_timestamp. + MariaDB outputs these with parens; DuckDB treats them as keywords. + */ + { + static const char *time_kws[]= { + "CURRENT_TIME(", "CURRENT_DATE(", "CURRENT_TIMESTAMP("}; + static const char *time_repls[]= { + "current_time", "current_date", "current_timestamp"}; + for (int ki= 0; ki < 3; ki++) + { + size_t klen= strlen(time_kws[ki]); + size_t pos= 0; + while ((pos= find_in_code(sql, time_kws[ki], pos)) != std::string::npos) + { + /* Find closing paren */ + size_t end= sql.find(')', pos + klen); + if (end == std::string::npos) { pos++; continue; } + sql.replace(pos, end + 1 - pos, time_repls[ki]); + } + } + } + + /* + Handle STRAIGHT_JOIN: as a SELECT hint — remove; as a join keyword — + replace with JOIN (preserves ON clause). + Then rewrite remaining conditionless JOIN → CROSS JOIN. + */ + { + size_t pos= 0; + while ((pos= find_in_code(sql, "STRAIGHT_JOIN", pos)) != std::string::npos) + { + /* + Determine if STRAIGHT_JOIN is a SELECT modifier (hint) or a join + keyword. As a hint it follows SELECT/ALL/DISTINCT/DISTINCTROW. + As a join it follows a table name, alias, or closing paren. + */ + size_t p= pos; + while (p > 0 && sql[p - 1] == ' ') p--; + size_t word_end= p; + while (p > 0 && + (isalnum((unsigned char) sql[p - 1]) || sql[p - 1] == '_')) + p--; + size_t wlen= word_end - p; + bool is_hint= + wlen > 0 && + ((wlen == 6 && ci_starts_with(sql, p, "SELECT")) || + (wlen == 3 && ci_starts_with(sql, p, "ALL")) || + (wlen == 8 && ci_starts_with(sql, p, "DISTINCT")) || + (wlen == 11 && ci_starts_with(sql, p, "DISTINCTROW"))); + + if (is_hint) + { + size_t elen= 13; + if (pos + elen < sql.size() && sql[pos + elen] == ' ') + elen++; + sql.erase(pos, elen); + } + else + sql.replace(pos, 13, "JOIN"); + } + /* + Scan for remaining conditionless JOIN (no ON/USING). + */ + pos= 0; + while ((pos= find_in_code(sql, " JOIN ", pos)) != std::string::npos) + { + /* Skip qualified JOINs: LEFT/RIGHT/INNER/CROSS/NATURAL/FULL/OUTER */ + { + size_t p= pos; + while (p > 0 && sql[p - 1] == ' ') p--; + size_t word_end= p; + while (p > 0 && isalpha((unsigned char) sql[p - 1])) p--; + size_t wlen= word_end - p; + if (wlen > 0 && + ((wlen == 5 && ci_starts_with(sql, p, "CROSS")) || + (wlen == 7 && ci_starts_with(sql, p, "NATURAL")) || + (wlen == 4 && ci_starts_with(sql, p, "LEFT")) || + (wlen == 5 && ci_starts_with(sql, p, "RIGHT")) || + (wlen == 5 && ci_starts_with(sql, p, "INNER")) || + (wlen == 4 && ci_starts_with(sql, p, "FULL")) || + (wlen == 5 && ci_starts_with(sql, p, "OUTER")))) + { + pos+= 6; + continue; + } + } + + /* Scan forward to find ON/USING or a clause boundary */ + size_t scan= pos + 6; + bool has_condition= false; + while (scan < sql.size()) + { + /* Check for ON (as keyword, followed by space) */ + if (ci_starts_with(sql, scan, "ON ") || + ci_starts_with(sql, scan, "USING ") || + ci_starts_with(sql, scan, "USING(")) + { + has_condition= true; + break; + } + /* Clause boundary — no ON found, this is conditionless */ + if (ci_starts_with(sql, scan, "JOIN ") || + ci_starts_with(sql, scan, "WHERE ") || + ci_starts_with(sql, scan, "GROUP ") || + ci_starts_with(sql, scan, "ORDER ") || + ci_starts_with(sql, scan, "LIMIT ") || + ci_starts_with(sql, scan, "HAVING ") || + sql[scan] == ';') + break; + scan++; + } + if (!has_condition) + { + sql.replace(pos, 6, " CROSS JOIN "); + pos+= 12; + } + else + pos+= 6; + } + } + + /* + Rewrite REGEXP / NOT REGEXP → regexp_matches(). + MariaDB: expr REGEXP pattern / expr NOT REGEXP pattern + DuckDB: regexp_matches(expr, pattern) / NOT regexp_matches(expr, pattern) + */ + { + /* Replace NOT REGEXP first (longer), then REGEXP */ + size_t pos= 0; + while ((pos= find_in_code(sql, " NOT REGEXP ", pos)) != std::string::npos) + sql.replace(pos, 12, " !~ "); + pos= 0; + while ((pos= find_in_code(sql, " REGEXP ", pos)) != std::string::npos) + sql.replace(pos, 8, " ~ "); + /* RLIKE is a synonym for REGEXP in MariaDB */ + pos= 0; + while ((pos= find_in_code(sql, " NOT RLIKE ", pos)) != std::string::npos) + sql.replace(pos, 11, " !~ "); + pos= 0; + while ((pos= find_in_code(sql, " RLIKE ", pos)) != std::string::npos) + sql.replace(pos, 7, " ~ "); + } + + /* + Remove MariaDB-specific SELECT hints that DuckDB doesn't understand. + HIGH_PRIORITY, SQL_NO_CACHE, SQL_CACHE, STRAIGHT_JOIN (as hint). + */ + { + static const char *hints[]= { + "HIGH_PRIORITY ", "SQL_NO_CACHE ", "SQL_CACHE ", + "SQL_BUFFER_RESULT ", "SQL_SMALL_RESULT ", "SQL_BIG_RESULT ", + "SQL_CALC_FOUND_ROWS "}; + for (auto hint : hints) + { + size_t hlen= strlen(hint); + size_t pos; + while ((pos= find_in_code(sql, hint)) != std::string::npos) + sql.erase(pos, hlen); + } + } + + /* + Remove MariaDB index hints: FORCE INDEX(...), USE INDEX(...), + IGNORE INDEX(...). + */ + { + static const char *idx_hints[]= { + "FORCE INDEX(", "USE INDEX(", "IGNORE INDEX("}; + for (auto hint : idx_hints) + { + size_t hlen= strlen(hint); + size_t pos= 0; + while ((pos= find_in_code(sql, hint, pos)) != std::string::npos) + { + /* Find matching closing paren */ + size_t end= sql.find(')', pos + hlen); + if (end == std::string::npos) + { + pos++; + continue; + } + /* Remove including surrounding spaces */ + size_t erase_start= pos; + size_t erase_end= end + 1; + while (erase_end < sql.size() && sql[erase_end] == ' ') + erase_end++; + sql.erase(erase_start, erase_end - erase_start); + } + } + } + + /* + Rewrite LIMIT offset,count → LIMIT count OFFSET offset. + MariaDB: LIMIT 2,5 DuckDB: LIMIT 5 OFFSET 2 + */ + { + size_t lpos= 0; + while ((lpos= find_in_code(sql, "LIMIT ", lpos)) != std::string::npos) + { + size_t after_limit= lpos + 6; + /* Skip whitespace */ + while (after_limit < sql.size() && sql[after_limit] == ' ') + after_limit++; + /* Read first number */ + size_t num1_start= after_limit; + while (after_limit < sql.size() && isdigit(sql[after_limit])) + after_limit++; + if (after_limit == num1_start) + { + lpos= after_limit; + continue; + } + /* Check for comma */ + size_t comma= after_limit; + while (comma < sql.size() && sql[comma] == ' ') + comma++; + if (comma < sql.size() && sql[comma] == ',') + { + std::string offset_str= sql.substr(num1_start, + after_limit - num1_start); + size_t num2_start= comma + 1; + while (num2_start < sql.size() && sql[num2_start] == ' ') + num2_start++; + size_t num2_end= num2_start; + while (num2_end < sql.size() && isdigit(sql[num2_end])) + num2_end++; + std::string count_str= sql.substr(num2_start, num2_end - num2_start); + std::string replacement= "LIMIT " + count_str + " OFFSET " + + offset_str; + sql.replace(lpos, num2_end - lpos, replacement); + lpos+= replacement.size(); + } + else + lpos= after_limit; + } + } + + query_result= myduck::duckdb_query(thd, sql, true); + + if (!query_result || query_result->HasError()) + { + if (query_result) + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_INTERNAL_ERROR, + query_result->GetError().c_str(), "DuckDB"); + else + my_error(ER_GET_ERRMSG, MYF(0), HA_ERR_INTERNAL_ERROR, + "DuckDB query returned null result", "DuckDB"); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + current_chunk.reset(); + current_row_index= 0; + + DBUG_RETURN(0); +} + +int ha_duckdb_select_handler::next_row() +{ + DBUG_ENTER("ha_duckdb_select_handler::next_row"); + + if (!query_result) + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + + /* Fetch a new chunk when the current one is exhausted */ + if (!current_chunk || current_row_index >= current_chunk->size()) + { + current_chunk.reset(); + current_chunk= query_result->Fetch(); + + if (!current_chunk || current_chunk->size() == 0) + DBUG_RETURN(HA_ERR_END_OF_FILE); + + current_row_index= 0; + } + + /* + Store the fields into table->record[0]. + The select_handler framework provides `table` which is a temporary + table with one Field per item in the select list. + */ + size_t col_count= current_chunk->ColumnCount(); + size_t field_count= 0; + + for (Field **f= table->field; *f; f++) + field_count++; + + size_t ncols= (col_count < field_count) ? col_count : field_count; + + for (size_t col_idx= 0; col_idx < ncols; col_idx++) + { + duckdb::Value value= current_chunk->GetValue(col_idx, current_row_index); + Field *field= table->field[col_idx]; + store_duckdb_field_in_mysql_format(field, value, thd); + } + + current_row_index++; + DBUG_RETURN(0); +} + +int ha_duckdb_select_handler::end_scan() +{ + DBUG_ENTER("ha_duckdb_select_handler::end_scan"); + + if (has_cross_engine) + myduck::clear_external_tables(); + + current_chunk.reset(); + query_result.reset(); + current_row_index= 0; + + free_tmp_table(thd, table); + table= 0; + + DBUG_RETURN(0); +} diff --git a/storage/duckdb/ha_duckdb_pushdown.h b/storage/duckdb/ha_duckdb_pushdown.h new file mode 100644 index 0000000000000..2ccf963b256af --- /dev/null +++ b/storage/duckdb/ha_duckdb_pushdown.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#ifndef HA_DUCKDB_PUSHDOWN_H +#define HA_DUCKDB_PUSHDOWN_H + +#include "my_global.h" +#include "sql_class.h" +#include "select_handler.h" + +#undef UNKNOWN + +#include "duckdb.hpp" + +#include +#include + +extern handlerton *duckdb_hton; + +/** + select_handler implementation for DuckDB. + + Pushes SELECT queries down to the DuckDB engine. Supports both + pure-DuckDB queries and cross-engine joins where some tables belong + to other engines (e.g. InnoDB). External tables are exposed to + DuckDB via the _mdb_scan table function and replacement scan. +*/ +class ha_duckdb_select_handler : public select_handler +{ +public: + ha_duckdb_select_handler(THD *thd_arg, SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit); + ha_duckdb_select_handler(THD *thd_arg, SELECT_LEX_UNIT *sel_unit); + ~ha_duckdb_select_handler() override; + + void set_cross_engine(std::vector &&tables); + size_t external_table_count() const; + +protected: + int init_scan() override; + int next_row() override; + int end_scan() override; + +private: + std::unique_ptr query_result; + std::unique_ptr current_chunk; + size_t current_row_index; + + StringBuffer<4096> query_string; + + /** true when the query mixes DuckDB and non-DuckDB tables */ + bool has_cross_engine= false; + + /** Names of external tables registered for the current query */ + std::vector external_table_names; +}; + +/** + Factory function registered in hton->create_select. + Returns a new ha_duckdb_select_handler if the query can be pushed + down to DuckDB — either all tables are DuckDB, or at least one is + DuckDB and the rest can be scanned via the cross-engine mechanism. +*/ +select_handler *create_duckdb_select_handler(THD *thd, SELECT_LEX *sel_lex, + SELECT_LEX_UNIT *sel_unit); + +/** + Factory function registered in hton->create_unit. + Handles UNION/EXCEPT/INTERSECT queries pushed down to DuckDB. +*/ +select_handler *create_duckdb_unit_handler(THD *thd, + SELECT_LEX_UNIT *sel_unit); + +#endif /* HA_DUCKDB_PUSHDOWN_H */ diff --git a/storage/duckdb/mysql-test/duckdb/disabled.def b/storage/duckdb/mysql-test/duckdb/disabled.def new file mode 100644 index 0000000000000..f6f4680f3e6d8 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/disabled.def @@ -0,0 +1,24 @@ +alter_default_debug : ALTER COLUMN DROP DEFAULT not propagated to DuckDB +duckdb_ddl_during_transaction : appender not invalidated after DDL in transaction +alter_duckdb_index : duplicate key name on ignored indexes +alter_engine_duckdb : decimal >38 conversion fails +bugfix_crash_after_commit_error : TODO +bugfix_temp_and_system_database : DuckDB temp/system schema conflict +create_table_column_timestamp : timezone propagation mismatch +decimal_precision_all_possibilities : decimal >38 precision +duckdb_add_backticks : UDF digit-name schema quoting +duckdb_agg_func : AVG(VARCHAR) not supported in pushdown +duckdb_allow_encryption : MySQL keyring_file not available in MariaDB +duckdb_alter_table_engine : server crash on 64MB JSON +duckdb_bit_string : hex/binary literal comparison broken in pushdown +duckdb_fix_sql : oct() not in DuckDB +duckdb_json : json_contains() not in DuckDB +duckdb_kill : DEBUG sync points not implemented, timeout +duckdb_numeric_func : ACOS domain error in DuckDB +duckdb_refuse_xa : XA PREPARED state handling +duckdb_sql_mode : strict GROUP BY in DuckDB +duckdb_time_func : ADDDATE() not in DuckDB +rename_duckdb_table : server log warnings on cross-schema rename +supported_copy_ddl : cross-schema rename not supported +system_timezone : hangs on mariadbd-safe restart +truncate_and_maintenance_duckdb_table: different output b/w major MariaDB versions diff --git a/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_column.inc b/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_column.inc new file mode 100644 index 0000000000000..0883ef3745453 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_column.inc @@ -0,0 +1,417 @@ +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_alter_col CHARACTER SET utf8mb4; +USE db_alter_col; +--enable_query_log + +--perl +unlink "$ENV{MYSQL_TMP_DIR}/duckdb_column_meta.inc"; +EOF + +--write_file $MYSQL_TMP_DIR/duckdb_column_meta.inc + SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); + --sorted_result + SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; + --echo +EOF + + +--echo # +--echo # 1) ADD AND DROP +--echo # +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ADD COLUMN d INT NOT NULL DEFAULT 100 COMMENT "col d", + ADD COLUMN e INT NULL DEFAULT 1000 COMMENT "", + ADD COLUMN f INT, ALGORITHM = $algorithm; +INSERT INTO t VALUES(3, 3, 3, 3, 3, 3, 3); +INSERT INTO t(id, a, b, c) VALUES(4, 4, 4, 4); +INSERT INTO t VALUES(5, 5, 5, 5, 5, NULL, NULL); +--error ER_BAD_NULL_ERROR +INSERT INTO t VALUES(6, 6, 6, 6, NULL, NULL, NULL); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t DROP COLUMN a, DROP COLUMN d, ALGORITHM = $algorithm; +INSERT INTO t VALUES(6, 6, 6, 6, 6); +INSERT INTO t(id, c) VALUES(7, 7); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +--echo # +--echo # 3) SET AND DROP DEFAULT VALUE +--echo # + +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ALTER COLUMN b SET DEFAULT 100, + ALTER COLUMN c SET DEFAULT 100, ALGORITHM = $algorithm; +INSERT INTO t VALUES(3, 3, 3, 3); +INSERT INTO t(id) VALUES(4); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ALTER COLUMN b DROP DEFAULT, + ALTER COLUMN c SET DEFAULT 1000, ALGORITHM = $algorithm; +INSERT INTO t VALUES(5, 5, 5, 5); +INSERT INTO t(id) VALUES(6); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ALTER COLUMN b SET DEFAULT 10000, + ALTER COLUMN c DROP DEFAULT, ALGORITHM = $algorithm; +INSERT INTO t VALUES(7, 7, 7, 7); +INSERT INTO t(id) VALUES(8); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 3) RENAME, MODIFY AND CHANGE +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t RENAME COLUMN a TO a1, + RENAME COLUMN b TO b1, + RENAME COLUMN c TO c1, ALGORITHM = $algorithm; +INSERT INTO t(id, a1, b1, c1) VALUES(3, 3, 3, 3); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +# DELETE FROM t WHERE c1 IS NULL; +--disable_result_log +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c1 IS NULL"); +--enable_result_log +eval ALTER TABLE t MODIFY COLUMN a1 BIGINT COMMENT "col a1", + MODIFY COLUMN b1 BIGINT DEFAULT 100 COMMENT "", + MODIFY COLUMN c1 BIGINT NOT NULL DEFAULT 100, ALGORITHM = $algorithm; +INSERT INTO t VALUES(4, 4, 4, 4); +INSERT INTO t(id) VALUES(5); +INSERT INTO t VALUES(6, 6, NULL, 6); +--error ER_BAD_NULL_ERROR +INSERT INTO t VALUES(7, 7, 7, NULL); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +# DELETE FROM t WHERE b1 IS NULL; +--disable_result_log +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE b1 IS NULL"); +--enable_result_log +eval ALTER TABLE t CHANGE a1 a INT COMMENT "col a", + CHANGE b1 b INT NOT NULL DEFAULT 1000, + CHANGE c1 c INT COMMENT "", ALGORITHM = $algorithm; +INSERT INTO t VALUES(8, 8, 8, 8); +INSERT INTO t(id) VALUES(9); +INSERT INTO t VALUES(10, 10, 10, NULL); +--error ER_BAD_NULL_ERROR +INSERT INTO t VALUES(11, 11, NULL, 11); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +# DELETE FROM t WHERE c IS NULL; +--disable_result_log +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c IS NULL"); +--enable_result_log +eval ALTER TABLE t RENAME COLUMN a TO a1, + MODIFY COLUMN b BIGINT DEFAULT 10000 COMMENT "col b", + CHANGE c c1 BIGINT NOT NULL DEFAULT 10000 COMMENT "col c1", ALGORITHM = $algorithm; +INSERT INTO t VALUES(12, 12, 12, 12); +INSERT INTO t(id) VALUES(13); +INSERT INTO t VALUES(14, 14, NULL, 14); +--error ER_BAD_NULL_ERROR +INSERT INTO t VALUES(15, 15, 15, NULL); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 4) DEFAULT NULL +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, + a INT DEFAULT 1 COMMENT 'col a', + b INT DEFAULT 1 COMMENT 'col b', + c INT DEFAULT 1 COMMENT 'col c', + d INT DEFAULT 1 COMMENT 'col d') ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ALTER COLUMN a SET DEFAULT NULL, + ALTER COLUMN b DROP DEFAULT, + ALTER COLUMN c SET DEFAULT 10, + ALTER COLUMN d SET DEFAULT 10, ALGORITHM = $algorithm; +INSERT INTO t(id, b) VALUES(3, 3); +INSERT INTO t(id, b) VALUES(4, NULL); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t MODIFY a BIGINT, + CHANGE b b BIGINT DEFAULT NULL, + MODIFY COLUMN c BIGINT, + CHANGE d d BIGINT DEFAULT NULL, ALGORITHM = $algorithm; +INSERT INTO t(id) VALUES(5); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 5) DEFAULT TIMESTAMP +--echo # +SET @saved_time_zone = @@session.time_zone; +SET time_zone = '+00:00'; +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, + a TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00', + b TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00') ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +INSERT INTO t VALUES(2, '2020-01-01 12:00:00', '2020-01-01 12:00:00'); +eval ALTER TABLE t MODIFY COLUMN a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + CHANGE COLUMN b b TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM = $algorithm; +SET TIMESTAMP = 1303197722.534231; +INSERT INTO t(id) VALUES(3); +SELECT * FROM t; + +# 'DEFAULT CURRENT_TIMESTAMP' AND 'ON UPDATE CURRENT_TIMESTAMP' +DROP TABLE t; +SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 00:00:00'); +CREATE TABLE t(id INT PRIMARY KEY, a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, b TIMESTAMP DEFAULT NOW()) ENGINE = InnoDB; +INSERT INTO t(id) VALUES (1); +SELECT * FROM t; +# TODO: After DuckDB fix CURRENT_TIMESTAMP +# SET TIMESTAMP=UNIX_TIMESTAMP('2001-02-01 00:00:00'); +# eval ALTER TABLE t ADD COLUMN c TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, ALGORITHM = $algorithm; +# INSERT INTO t(id) VALUES (2); +# SELECT * FROM t; +SET TIMESTAMP=UNIX_TIMESTAMP('2001-03-01 00:00:00'); +eval ALTER TABLE t MODIFY COLUMN b TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; +INSERT INTO t(id) VALUES (3); +SELECT * FROM t; + + +SET time_zone = @saved_time_zone; + +--echo # +--echo # 6) DEFAULT VALUE EXPRESSION +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT DEFAULT (1+1), b VARCHAR(100) DEFAULT ('1+1'), c VARCHAR(100) DEFAULT (1+1)) ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); + +eval ALTER TABLE t ADD COLUMN d INT DEFAULT (2+2), + ADD COLUMN e VARCHAR(100) DEFAULT ('2+2'), + ADD COLUMN f VARCHAR(100) DEFAULT (2+2), ALGORITHM = $algorithm; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); + +eval ALTER TABLE t MODIFY COLUMN a INT DEFAULT (3+3), + MODIFY COLUMN b VARCHAR(100) DEFAULT ('3+3'), + MODIFY COLUMN c VARCHAR(100) DEFAULT (3+3), ALGORITHM = $algorithm; +INSERT INTO t(id) VALUES(5); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)"); + +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 7) DEFAULT VALUE OF BIT +--echo # +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, B0 BIT(1) NOT NULL DEFAULT 0, B1 BIT(32) NOT NULL DEFAULT b'1111') ENGINE=DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); + +eval ALTER TABLE t ADD COLUMN B2 BIT(64) NOT NULL DEFAULT b'11111', + ALTER COLUMN B1 SET DEFAULT 3333, ALGORITHM = $algorithm; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); + +SELECT id, hex(B0), hex(B1), hex(B2) FROM t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 8) MULTIPLE DDL ABOUT COLUMNS +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, + a INT DEFAULT 1 COMMENT 'col a', + b INT DEFAULT 1 COMMENT 'col b', + c INT DEFAULT 1 COMMENT 'col c', + d INT DEFAULT 1 COMMENT 'col d', + e INT DEFAULT 1 COMMENT 'col e', + f INT DEFAULT 1 COMMENT 'col f', + g INT DEFAULT 1 COMMENT 'col g') ENGINE = DuckDB; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + +eval ALTER TABLE t ADD COLUMN h INT NOT NULL DEFAULT 2 COMMENT 'col h1', + DROP COLUMN g, + ALTER COLUMN f SET DEFAULT 2, + ALTER COLUMN e SET DEFAULT NULL, + ALTER COLUMN d DROP DEFAULT, + RENAME COLUMN a TO a1, + MODIFY COLUMN b BIGINT DEFAULT 2 COMMENT "col b1", + CHANGE c c1 BIGINT NOT NULL DEFAULT 2 COMMENT "col c1", ALGORITHM = $algorithm; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc + + +--echo # +--echo # 9) Not supported +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +# INVISIBLE +--error ER_ILLEGAL_HA_CREATE_OPTION +CREATE TABLE t1(id INT PRIMARY KEY, a INT INVISIBLE) ENGINE = DuckDB; +if ($copy_ddl == 0) +{ +--error ER_GET_ERRMSG +eval ALTER TABLE t ADD COLUMN d INT INVISIBLE, ALGORITHM = $algorithm; +} +if ($copy_ddl == 1) +{ +--error ER_ILLEGAL_HA_CREATE_OPTION +eval ALTER TABLE t ADD COLUMN d INT INVISIBLE, ALGORITHM = $algorithm; +} +--error ER_PARSE_ERROR +eval ALTER TABLE t ALTER COLUMN b SET INVISIBLE, ALGORITHM = $algorithm; + +# AUTO_INCREMENT +--error ER_TABLE_CANT_HANDLE_AUTO_INCREMENT +eval ALTER TABLE t MODIFY COLUMN c INT AUTO_INCREMENT KEY, ALGORITHM = $algorithm; +--error ER_TABLE_CANT_HANDLE_AUTO_INCREMENT +eval ALTER TABLE t ADD COLUMN d INT AUTO_INCREMENT, ALGORITHM = $algorithm; + +# ENGINE_ATTRIBUTE +--error ER_UNKNOWN_OPTION +CREATE TABLE t1(id INT PRIMARY KEY, a INT) ENGINE = DuckDB ENGINE_ATTRIBUTE='{"KEY":"VALUE"}'; +--error ER_UNKNOWN_OPTION +eval ALTER TABLE t ADD COLUMN d INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = $algorithm; +--error ER_UNKNOWN_OPTION +eval ALTER TABLE t MODIFY COLUMN c INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = $algorithm; + +if ($copy_ddl == 0) +{ + --error ER_ALTER_OPERATION_NOT_SUPPORTED + eval ALTER TABLE t ADD COLUMN d INT AFTER a, ALGORITHM = $algorithm; + --error ER_ALTER_OPERATION_NOT_SUPPORTED + eval ALTER TABLE t ADD COLUMN d INT FIRST, ALGORITHM = $algorithm; + --error ER_ALTER_OPERATION_NOT_SUPPORTED + eval ALTER TABLE t CHANGE b b INT AFTER c, ALGORITHM = $algorithm; + --error ER_ALTER_OPERATION_NOT_SUPPORTED + eval ALTER TABLE t MODIFY COLUMN a INT FIRST, ALGORITHM = $algorithm; +} + +# GENERATED COLUMN +--error ER_UNSUPPORTED_ENGINE_FOR_GENERATED_COLUMNS +CREATE TABLE t1(id INT PRIMARY KEY, a INT, b INT GENERATED ALWAYS AS (a - 1) STORED) ENGINE = DuckDB; +--error ER_UNSUPPORTED_ENGINE_FOR_GENERATED_COLUMNS +eval ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (a - 1) STORED, ALGORITHM = $algorithm; +--error ER_UNSUPPORTED_ENGINE_FOR_GENERATED_COLUMNS +eval ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (b + 1) VIRTUAL, ALGORITHM = $algorithm; +--error ER_UNSUPPORTED_ENGINE_FOR_GENERATED_COLUMNS +eval ALTER TABLE t MODIFY COLUMN b INT GENERATED ALWAYS AS (a+1) STORED, ALGORITHM = $algorithm; + + +--echo # +--echo # 10) Ignore column format, secondary engine attribute, storage, they can be found in DD. +--echo # +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +--error ER_PARSE_ERROR +eval ALTER TABLE t ADD COLUMN d INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', + ADD COLUMN e INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = $algorithm; + +--error ER_PARSE_ERROR +eval ALTER TABLE t MODIFY COLUMN b INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', + CHANGE c c INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = $algorithm; + +SHOW CREATE TABLE t; + + +--echo # +--echo # 11) Drop primary key column which leads to drop primary key. +--echo # +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; + +--error ER_REQUIRES_PRIMARY_KEY +eval ALTER TABLE t DROP COLUMN a, ALGORITHM = $algorithm; + + +--echo # +--echo # 12) BUG#117725 +--echo # +# Error occurs when modifying the same column twice in a DDL +# If MySQL server fix this bug, the behavior of duckdb may change, so we add test cases here +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; + +--error ER_BAD_FIELD_ERROR +eval ALTER TABLE t MODIFY COLUMN a INT, MODIFY COLUMN a INT, ALGORITHM = $algorithm; +--error ER_BAD_FIELD_ERROR +eval ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, ALTER COLUMN a DROP DEFAULT, ALGORITHM = $algorithm; +--error ER_BAD_FIELD_ERROR +eval ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, RENAME COLUMN a to c, ALGORITHM = $algorithm; +DROP TABLE t; + +# Currently, DuckDB has no index, so we can drop/change columns which are included or before index. +--echo # +--echo # 13) DROP COLUMN IN PRIMARY KEY +--echo # +CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b, c)) ENGINE = DuckDB; +--error ER_KEY_COLUMN_DOES_NOT_EXIST +eval ALTER TABLE t DROP COLUMN a, ALGORITHM = $algorithm; +--error ER_KEY_COLUMN_DOES_NOT_EXIST +eval ALTER TABLE t DROP COLUMN c, ALGORITHM = $algorithm; +SHOW CREATE TABLE t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc +DROP TABLE t; + + +--echo # +--echo # 14) DROP COLUMN BEFORE PRIMARY KEY +--echo # +CREATE TABLE t(a INT, b INT, c INT, d INT, PRIMARY KEY(d, b)) ENGINE = DuckDB; +eval ALTER TABLE t DROP COLUMN a, ALGORITHM = $algorithm; +eval ALTER TABLE t DROP COLUMN c, ALGORITHM = $algorithm; +SHOW CREATE TABLE t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc +DROP TABLE t; + + +--echo # +--echo # 15) CHANGE COLUMN TYPE WITH INDEX +--echo # +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +eval ALTER TABLE t MODIFY COLUMN a VARCHAR(10), ALGORITHM = $algorithm; +SHOW CREATE TABLE t; +--source $MYSQL_TMP_DIR/duckdb_column_meta.inc +DROP TABLE t; + + +--echo # +--echo # 16) Cleanup +--echo # +--remove_file $MYSQL_TMP_DIR/duckdb_column_meta.inc + +--disable_query_log +DROP DATABASE db_alter_col; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_index.inc b/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_index.inc new file mode 100644 index 0000000000000..312a600b8bfa3 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/alter_duckdb_index.inc @@ -0,0 +1,128 @@ +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_alter_idx CHARACTER SET utf8mb4; +USE db_alter_idx; +--enable_query_log + +--write_file $MYSQL_TMP_DIR/duckdb_index_meta.inc + SELECT run_in_duckdb("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); + SELECT run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); + SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; + --echo +EOF + +--echo # +--echo # 1) ADD non primary key will be ignore. +--echo # +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +eval ALTER TABLE t ADD UNIQUE KEY uk_b(b) COMMENT 'unique key b', ADD UNIQUE KEY uk_cd(c, d) COMMENT 'unique key cd', ADD INDEX ke(e) COMMENT 'index e', ALGORITHM = $algorithm; +CREATE UNIQUE INDEX uk_b ON t(b) COMMENT 'unique key b'; +CREATE UNIQUE INDEX uk_cd ON t(c, d) COMMENT 'unique key cd'; +CREATE INDEX ke ON t(e) COMMENT 'index e'; +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc + + +--echo # +--echo # 2) Special indexes +--echo # +DROP TABLE t; +--error ER_GET_ERRMSG +CREATE TABLE t (id INT PRIMARY KEY, a INT, b VARCHAR(10), c INT, d JSON, UNIQUE INDEX uk_a((ABS(a))), INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY)))) ENGINE = DuckDB; + +--echo +--echo # functional index is not supported. +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +--error ER_GET_ERRMSG +eval ALTER TABLE t ADD INDEX uk_a((ABS(a))), ALGORITHM = $algorithm; +--error ER_GET_ERRMSG +CREATE UNIQUE INDEX uk_a ON t ((ABS(a))); +--error ER_GET_ERRMSG +eval ALTER TABLE t ADD UNIQUE INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))), ALGORITHM = $algorithm; +--error ER_GET_ERRMSG +CREATE UNIQUE INDEX uk_d ON t ((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))); +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc + +--echo +--echo # Spatial index is not supported for spatial types are not supported. +DROP TABLE t; +--error ER_CHECK_NOT_IMPLEMENTED # The storage engine for the table doesn't support GEOMETRY +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10), e POINT, f POINT) ENGINE = DuckDB; + +--echo +--echo # Fulltext index is ignored too. +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10)) ENGINE = DuckDB; +eval ALTER TABLE t ADD FULLTEXT INDEX idx_c(c) COMMENT 'index c', ALGORITHM = $algorithm; +CREATE FULLTEXT INDEX idx_d ON t(d) COMMENT 'index d'; +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc + +--echo +--echo # Foreign key is treated as normal index, which is ignored too. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT) ENGINE = DuckDB; +CREATE TABLE t2 LIKE t; +eval ALTER TABLE t ADD FOREIGN KEY fk(b) REFERENCES t2(b), ALGORITHM = $algorithm; +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc + + +--echo # +--echo # 3) Unsupported +--echo # + +--echo +--echo # create table without primary key is not supported. +DROP TABLE t; +--error ER_REQUIRES_PRIMARY_KEY +CREATE TABLE t (id INT, a INT, b INT) ENGINE = DuckDB; + +--echo +--echo # drop PK is not supported. +CREATE TABLE t (a INT, b VARCHAR(10), c INT, PRIMARY KEY(a)) ENGINE = DuckDB; +--error ER_REQUIRES_PRIMARY_KEY +eval ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = $algorithm; +--error ER_REQUIRES_PRIMARY_KEY +DROP INDEX `PRIMARY` ON t; + +# Fix ha_duckdb::external_lock +if ($copy_ddl == 1) { + # for COPY ddl, the table name is '#sql-*', so we skip this warning to fix. + --disable_warnings +} +eval ALTER TABLE t ENABLE KEYS, ALGORITHM = $algorithm; +eval ALTER TABLE t DISABLE KEYS, ALGORITHM = $algorithm; +if ($copy_ddl == 1) { + --enable_warnings +} + +--echo # +--echo # 4) DESC, desc will be ignored by DuckDB +--echo # +DROP TABLE t; +CREATE TABLE t(id INT, name VARCHAR(255), PRIMARY KEY(id DESC)) ENGINE = DuckDB; +eval ALTER TABLE t ADD INDEX idx(name DESC), ALGORITHM = $algorithm; +INSERT INTO t VALUES(1, "1"); +INSERT INTO t VALUES(2, "2"); +SELECT * FROM t; +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc + + +--echo # +--echo # 5) DROP PRIMARY KEY +--echo # +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +SET GLOBAL duckdb_require_primary_key = OFF; +eval ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = $algorithm; +SHOW CREATE TABLE t; +--source $MYSQL_TMP_DIR/duckdb_index_meta.inc +SET GLOBAL duckdb_require_primary_key = default; + + +--echo # +--echo # 6) Cleanup +--echo # +DROP TABLE t, t2; +--remove_file $MYSQL_TMP_DIR/duckdb_index_meta.inc + +--disable_query_log +DROP DATABASE db_alter_idx; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/alter_engine_duckdb.inc b/storage/duckdb/mysql-test/duckdb/include/alter_engine_duckdb.inc new file mode 100644 index 0000000000000..aad49bcc5706a --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/alter_engine_duckdb.inc @@ -0,0 +1,113 @@ +--echo # +--echo # 1. Test Alter Table Engine = duckdb for all data types +--echo # + + +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 int, + col2 bigint, + col3 float, + col4 double, + col5 decimal(10,4), + col6 tinyint, + col7 smallint, + col8 mediumint +) ENGINE = innodb; + +insert into t1 values (1, 1, -1, 1.01, -1.001, 1.0001, 1, 1, 1), + (2, -2, 2, -2.02, 2.002, -2.0002, 2, 2, 2), + (-3, 3, 3, 3.03, 3.003, 3.0003, 3, 3, 3), + (-2147483648, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -999999.9999, -128, -32768, -8388608), + (2147483647, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, 127, 32767, 8388607); + +--sorted_result +SELECT * FROM t1; + +ALTER TABLE t1 ENGINE = duckdb; +--sorted_result +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 int unsigned, + col2 bigint unsigned, + col3 float unsigned, + col4 double unsigned, + col5 decimal(10,4) unsigned, + col6 bit(10), + col7 tinyint unsigned, + col8 smallint unsigned, + col9 mediumint unsigned +) ENGINE = innodb; + +insert into t1 values (1, 1, 1, 1.01, 1.001, 1.0001, B'111111111', 1, 1, 1), + (2, 2, 2, 2.02, 2.002, 2.0002, B'0000000000', 1, 1, 1), + (3, 3, 3, 3.03, 3.003, 3.0003, B'0001100110', 1, 1, 1), + (-2147483648, 0, 0, 0, 0, 0, B'00000000000', 0, 0, 0), + (2147483647, 255, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, B'1111111111', 255, 65535, 8388607); + +--sorted_result +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; + +ALTER TABLE t1 ENGINE = duckdb; +--sorted_result +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 decimal(65, 30), + col2 decimal(65, 15), + col3 decimal(65, 0), + col4 decimal(38,30), + col5 decimal(38,18), + col6 decimal(38,0), + col7 decimal(9,9), + col8 decimal(9,4), + col9 decimal(9,0) +) ENGINE = innodb; + +insert into t1 values (1, + 99999999999999999999999999999999999.999999999999999999999999999999, + 99999999999999999999999999999999999999999999999999.999999999999999, + 99999999999999999999999999999999999999999999999999999999999999999, + 99999999.999999999999999999999999999999, + 99999999999999999999.999999999999999999, + 99999999999999999999999999999999999999, + 0.99999999, + 99999.9999, + 999999999); + +--sorted_result +SELECT * FROM t1; + +ALTER TABLE t1 ENGINE = duckdb; +--sorted_result +SELECT * FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 date, + col2 datetime, + col3 timestamp, + col4 datetime(6), + col5 time, + col6 time(6), + col7 year +) ENGINE = innodb; + +insert into t1 values (1, '2020-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1', '12:00:00', '12:00:00.1', 2020), + (2, '2020-12-31', '2020-12-31 00:00:00', '2020-12-31 00:00:00', '2020-12-31 00:00:00.123456789', '00:00:00', '00:00:00.123456789', 1970), + (3, '1970-01-01', '1970-01-01 23:59:59', '1970-01-01 23:59:59', '2020-12-31 23:59:59.123456', '23:59:59', '23:59:59.123456', 2050), + (4, '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '23:59:59.123456', '23:59:59.123456', 2100); + +--sorted_result +SELECT * FROM t1; + +ALTER TABLE t1 ENGINE = duckdb; +--sorted_result +SELECT * FROM t1; +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/include/check_field_correctness.inc b/storage/duckdb/mysql-test/duckdb/include/check_field_correctness.inc new file mode 100644 index 0000000000000..76887086cc4e4 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/check_field_correctness.inc @@ -0,0 +1,171 @@ +# Test the compatibility of duckdb engine fields and compare with innodb_db engine +--disable_query_log +--disable_warnings +DROP DATABASE IF EXISTS innodb_db; +DROP DATABASE IF EXISTS duckdb_db; +DROP DATABASE IF EXISTS duckdb_db_batch_insert; +DROP DATABASE IF EXISTS innotoduck; +DROP DATABASE IF EXISTS ducktoinno; +--enable_warnings +CREATE DATABASE IF NOT EXISTS innodb_db CHARACTER SET utf8mb4; +CREATE DATABASE IF NOT EXISTS duckdb_db CHARACTER SET utf8mb4; +CREATE DATABASE IF NOT EXISTS duckdb_db_batch_insert CHARACTER SET utf8mb4; +CREATE DATABASE IF NOT EXISTS innotoduck CHARACTER SET utf8mb4; +CREATE DATABASE IF NOT EXISTS ducktoinno CHARACTER SET utf8mb4; + +# create table with engine innodb and duckdb. +USE innodb_db; +--eval $create_sql engine = innodb; + +USE duckdb_db; +--eval $create_sql engine = duckdb; + +USE duckdb_db_batch_insert; +--eval $create_sql engine = duckdb; + +use innotoduck; +--eval $create_sql engine = innodb; + +use ducktoinno; +--eval $create_sql engine = duckdb; + +--echo # display innodb table structure. +--eval desc innodb_db.$table_name + +--echo # display duckdb table structure. +--eval desc duckdb_db.$table_name + +--enable_query_log + +--let $db_name=duckdb_db +--let $table_name=$table_name +--source ../include/show_duckdb_table_structure.inc + +--disable_query_log + +# insert data +USE innodb_db; +--eval $insert_sql + +USE innotoduck; +--eval $insert_sql + +USE ducktoinno; +--eval $insert_sql + +USE duckdb_db; +SET GLOBAL duckdb_dml_in_batch = OFF; +--eval $insert_sql + +USE duckdb_db_batch_insert; +SET GLOBAL duckdb_dml_in_batch = ON; +--eval $insert_sql + +--if ($config_sql) { +--eval $config_sql +} + +USE innotoduck; +--eval ALTER TABLE $table_name ENGINE = duckdb; + +USE ducktoinno; +--if($table_name != "t_decimal"){ +--eval ALTER TABLE $table_name ENGINE = innodb; +} + +--echo # check inserted data +# select data +USE innodb_db; +--let $innodb_result=`$select_sql` +--replace_regex /[a-z_]+// +--let $innodb_checksum=`checksum table $table_name` + +USE duckdb_db; +--let $duckdb_result_1=`$select_sql` +--replace_regex /[a-z_]+// +--let $duckdb_checksum=`checksum table $table_name` + +USE duckdb_db_batch_insert; +--let $duckdb_result_2=`$select_sql` +--replace_regex /[a-z_]+// +--let $duckdb_batch_insert_checksum=`checksum table $table_name` + + +USE innotoduck; +--let $innotoduck_result=`$select_sql` +--replace_regex /[a-z_]+// +--let $innotoduck_checksum=`checksum table $table_name` + +USE ducktoinno; +--let $ducktoinno_result=`$select_sql` +--eval ALTER TABLE $table_name ENGINE = duckdb; +--let $ducktoinnotoduck_result=`$select_sql` +--replace_regex /[a-z_]+// +--let $ducktoinno_checksum=`checksum table $table_name` + +--enable_query_log + +--echo innodb_result : $innodb_result +--echo duckdb_result_1: $duckdb_result_1 +--echo duckdb_result_2: $duckdb_result_2 +--echo innotoduck_result: $innotoduck_result +--echo ducktoinno_result: $ducktoinno_result +--echo ducktoinnotoduck_result: $ducktoinnotoduck_result + +--echo innodb_checksum: $innodb_checksum +--echo duckdb_checksum : $duckdb_checksum +--echo duckdb_batch_insert_checksum: $duckdb_batch_insert_checksum +--echo innotoduck_checksum: $innotoduck_checksum +--echo ducktoinno_checksum: $ducktoinno_checksum + + +--if ($innodb_checksum != $duckdb_checksum) { +--let $error=Results are not same, innodb_checksum: $innodb_checksum, duckdb_checksum: $duckdb_checksum +--echo $error +--if ($die_on_error) { +--die $error +} +} + +--if ($innodb_checksum != $duckdb_batch_insert_checksum) { +--let $error=Results are not same, innodb_checksum: $innodb_checksum, duckdb_batch_insert_checksum: $duckdb_batch_insert_checksum +--echo $error +--if ($die_on_error) { +--die $error +} +} + +--if ($innodb_checksum != $innotoduck_checksum) { +--let $error=Results are not same, innodb_checksum: $innodb_checksum, innotoduck_checksum: $innotoduck_checksum +--echo $error +--if ($die_on_error) { +--die $error +} +} + +--if ($innodb_checksum != $ducktoinno_checksum) { +--let $error=Results are not same, innodb_checksum: $innodb_checksum, ducktoinno_checksum: $ducktoinno_checksum +--echo $error +--if ($die_on_error) { +--die $error +} +} + +--disable_query_log +USE test; + +--echo # cleanup +--eval DROP TABLE duckdb_db.$TABLE_NAME +--eval DROP TABLE innodb_db.$TABLE_NAME +--eval DROP TABLE innotoduck.$TABLE_NAME +--eval DROP TABLE ducktoinno.$TABLE_NAME + + +DROP DATABASE innodb_db; +DROP DATABASE duckdb_db; +DROP DATABASE duckdb_db_batch_insert; +DROP DATABASE innotoduck; +DROP DATABASE ducktoinno; + + +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/cleanup_duckdb.inc b/storage/duckdb/mysql-test/duckdb/include/cleanup_duckdb.inc new file mode 100644 index 0000000000000..0f8af16fb4f1b --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/cleanup_duckdb.inc @@ -0,0 +1,2 @@ +--disable_query_log +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/decimal_precision_all_possibilities.inc b/storage/duckdb/mysql-test/duckdb/include/decimal_precision_all_possibilities.inc new file mode 100644 index 0000000000000..d2a5af567af47 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/decimal_precision_all_possibilities.inc @@ -0,0 +1,109 @@ +--echo # +--echo #1. prepare +--echo # +SELECT @@duckdb_dml_in_batch; + +--echo # +--echo #2. duckdb_use_double_for_decimal = on (default) +--echo # +SET GLOBAL duckdb_use_double_for_decimal = on; + + +--echo #2.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +--echo # should be decimal(38, 5) +SELECT run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); + + +INSERT INTO t1 values (1, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 1; + +INSERT INTO t1 values (101, 0); +SELECT * FROM t1 WHERE id = 101; + +INSERT INTO t1 values (102, 0.00000); +SELECT * FROM t1 WHERE id = 102; + +--echo #2.2 low precision(<=38) with high precision value(>38) + +--echo #ok, The precision after point can be lost +INSERT INTO t1 values (2, 123456789012345678901234567890123.123456); +SELECT * FROM t1 WHERE id = 2; + +--echo #error, The precision before point can not be lost +--error 1264 +INSERT INTO t1 values (3, 1234567890123456789012345678901234.123456); +SELECT * FROM t1 WHERE id = 3; +DROP TABLE t1; + +--echo #2.3 high precision column(>38) with low precision value(<38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +--echo # should be double +SELECT run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); + +INSERT INTO t1 values (4, 123456789012345678901234567890.12345); +SELECT * FROM t1 WHERE id = 4; + +--echo #2.4 high precision column(>38) with low precision value(<38) +INSERT INTO t1 values (5, 12345678901234567890123456789012345.12345678); +SELECT * FROM t1 WHERE id = 5; + +--error 1264 +INSERT INTO t1 values (6, 12345678901234567890123456789012345678.12345); +SELECT * FROM t1 WHERE id = 6; +SELECT run_in_duckdb("INSERT INTO t1 values (5, 12345678901234567890123456789012345678.12345)"); +SELECT * FROM t1 WHERE id = 6; +DROP TABLE t1; + +--echo # +--echo #3. duckdb_use_double_for_decimal = false +--echo # +SET GLOBAL duckdb_use_double_for_decimal = off; + +--echo #3.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +--echo # should be decimal(38, 5) +SELECT run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); + +INSERT INTO t1 values (7, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 7; + +INSERT INTO t1 values (701, 0); +SELECT * FROM t1 WHERE id = 701; + +INSERT INTO t1 values (702, 0.00000); +SELECT * FROM t1 WHERE id = 702; + +--echo #3.2 low precision(<=38) with high precision value(>38) + +--echo #ok, The precision after point can be lost +INSERT INTO t1 values (8, 123456789012345678901234567890123.123456); +SELECT * FROM t1 WHERE id = 8; + +--echo #error, The precision before point can not be lost +--error 1264 +INSERT INTO t1 values (9, 1234567890123456789012345678901234.123456); +SELECT * FROM t1 WHERE id = 9; +DROP TABLE t1; + +--echo #3.3 high precision column(>38) with low precision value(<38) +--echo #duckdb use decimal(38, dec) for both high and low precision(<=38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +--echo # should be decimal(38, 5) +SELECT run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); + +INSERT INTO t1 values (10, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 10; + +--echo #3.4 high precision column(>38) with high precision value(>38) +--echo #duckdb use decimal(38, dec) + +--error ER_GET_ERRMSG, ER_DUCKDB_APPENDER_ERROR +INSERT INTO t1 values (11, 1234567890123456789012345678901234.12345); +SELECT * FROM t1 WHERE id = 11; + +INSERT INTO t1 values (12, 123456789012345678901234567890123.12345678); +SELECT * FROM t1 WHERE id = 12; +DROP TABLE t1; +set global duckdb_use_double_for_decimal = default; + diff --git a/storage/duckdb/mysql-test/duckdb/include/have_duckdb.inc b/storage/duckdb/mysql-test/duckdb/include/have_duckdb.inc new file mode 100644 index 0000000000000..e2cb59c39b777 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/have_duckdb.inc @@ -0,0 +1,4 @@ +if (`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'DuckDB' AND support IN ('YES', 'DEFAULT', 'ENABLED')`) +{ + --skip Test requires engine DuckDB. +} \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/include/have_mysqld_safe.inc b/storage/duckdb/mysql-test/duckdb/include/have_mysqld_safe.inc new file mode 100644 index 0000000000000..ab699c4abd7a0 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/have_mysqld_safe.inc @@ -0,0 +1,39 @@ +################################################# +# Checks if mariadbd-safe (mysqld_safe) is present +# in the build scripts directory. +################################################# + +--disable_query_log +--let $temp_inc=$MYSQL_TMP_DIR/tmp.inc +let inc_tmp=$temp_inc; + +--perl +use strict; +use File::Basename; + +my $mysqld = $ENV{'MYSQLD'} or die "MYSQLD not set"; +my $bindir = dirname($mysqld); +# mariadbd-safe lives in scripts/ next to sql/ where mariadbd is +my $scriptsdir = "$bindir/../scripts"; +my $found = 0; +$found = 1 if -e "$scriptsdir/mariadbd-safe" || -e "$scriptsdir/mysqld_safe"; + +my $temp_inc = $ENV{'inc_tmp'} or die "temp_inc not set"; +open(FILE_INC, ">", "$temp_inc") or die("can't open file \"$temp_inc\": $!"); +print FILE_INC '--let $is_found= ' . $found . "\n"; +if ($found) { + my $safe = -e "$scriptsdir/mariadbd-safe" ? "$scriptsdir/mariadbd-safe" : "$scriptsdir/mysqld_safe"; + print FILE_INC '--let $MYSQLD_SAFE= ' . $safe . "\n"; +} +close(FILE_INC); +EOF + +--source $temp_inc + +if (!$is_found) +{ + --die Test requires mariadbd-safe (not found) +} + +--remove_file $temp_inc +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/show_duckdb_table_structure.inc b/storage/duckdb/mysql-test/duckdb/include/show_duckdb_table_structure.inc new file mode 100644 index 0000000000000..34a9699b0a312 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/show_duckdb_table_structure.inc @@ -0,0 +1,33 @@ +--disable_query_log +--let $check_constraint = 1 +--let $check_index = 1 +--let $check_sequence = 1 + +--echo ------------------------------------------------------------------------ +--echo ## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print $TABLE_NAME begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +--echo # ① Print columns of $TABLE_NAME ## +--let $sql=SELECT run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = '$db_name' AND table_name = '$table_name'") +--eval $sql + +-- if ($check_constraint) { +--echo # ② Print CONSTRAINTs of $TABLE_NAME ## +--let $sql=SELECT run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = '$db_name' AND table_name = '$table_name'") +--eval $sql +--} + +-- if ($check_index) { +--echo # ③ Print INDEXs of $TABLE_NAME ## +--let $sql=SELECT run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = '$db_name' AND table_name = '$table_name'") +--eval $sql +--} + +-- if ($check_sequence) { +--echo # ④ Print SEQUENCE of $TABLE_NAME ## +--let $sql=SELECT run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='$db_name' AND sequence_name LIKE 'seq_$table_name%'") +--eval $sql +--} + +--echo ## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table $TABLE_NAME end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +--echo ------------------------------------------------------------------------ + +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/include/transaction_basic.inc b/storage/duckdb/mysql-test/duckdb/include/transaction_basic.inc new file mode 100644 index 0000000000000..04aaf38ca083b --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/include/transaction_basic.inc @@ -0,0 +1,197 @@ +# +# Basic transaction tests for DuckDB engine +# Tests: Atomicity, Durability, Autocommit, Isolation +# Also tests mixed InnoDB+DuckDB transactions +# + +--source include/have_innodb.inc + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_transaction CHARACTER SET utf8mb4; +USE db_transaction; +--enable_query_log + +# ----------------------------------------------------------------- +# Test 1: Atomicity - ROLLBACK discards changes +# ----------------------------------------------------------------- +--echo # Test 1: Atomicity + +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; + +INSERT INTO t1 (id, name) VALUES (1, '1'); + +BEGIN; +INSERT INTO t1 (id, name) VALUES (2, '2'); +INSERT INTO t1 (id, name) VALUES (3, '3'); +ROLLBACK; + +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Test 2: Durability - data survives restart +# ----------------------------------------------------------------- +--echo # Test 2: Durability after restart + +--source include/restart_mysqld.inc +--disable_query_log +USE db_transaction; +--enable_query_log +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Test 3: Uncommitted data lost on restart +# ----------------------------------------------------------------- +--echo # Test 3: Restart rolls back uncommitted transaction + +BEGIN; +INSERT INTO t1 (id, name) VALUES (4, '4'); +INSERT INTO t1 (id, name) VALUES (5, '5'); +--source include/restart_mysqld.inc +--disable_query_log +USE db_transaction; +--enable_query_log +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Test 4: Autocommit +# ----------------------------------------------------------------- +--echo # Test 4: Autocommit persists through restart + +SET autocommit = 1; +INSERT INTO t1 (id, name) VALUES (3, '3'); +--source include/restart_mysqld.inc +--disable_query_log +USE db_transaction; +--enable_query_log +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Test 5: Isolation - uncommitted data not visible to other sessions +# ----------------------------------------------------------------- +--echo # Test 5: Isolation + +--disable_query_log +DROP TABLE t1; +CREATE TABLE t1 ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +--enable_query_log + +connect (con1,localhost,root,,db_transaction); +connect (con2,localhost,root,,db_transaction); + +connection con1; +BEGIN; +INSERT INTO t1 (id, name) VALUES (1, '1'); + +connection con2; +--echo # Should be empty +SELECT * FROM t1; + +connection con1; +COMMIT; + +connection con2; +--echo # Should show 1 row +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Test 6: Mixed InnoDB+DuckDB atomicity +# ----------------------------------------------------------------- +--echo # Test 6: Mixed InnoDB+DuckDB atomicity + +--disable_query_log +connection default; +DROP TABLE t1; +--enable_query_log + +DROP TABLE IF EXISTS t_duck; +DROP TABLE IF EXISTS t_inno; +CREATE TABLE t_duck ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +CREATE TABLE t_inno ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = InnoDB; + +INSERT INTO t_duck (id, name) VALUES (1, '1'); +INSERT INTO t_inno (id, name) VALUES (1, '1'); + +BEGIN; +INSERT INTO t_duck (id, name) VALUES (2, '2'); +INSERT INTO t_inno (id, name) VALUES (2, '2'); +ROLLBACK; + +SELECT * FROM t_duck; +SELECT * FROM t_inno; + +# ----------------------------------------------------------------- +# Test 7: Mixed InnoDB+DuckDB isolation +# ----------------------------------------------------------------- +--echo # Test 7: Mixed InnoDB+DuckDB isolation + +--disable_query_log +DROP TABLE t_duck; +DROP TABLE t_inno; +CREATE TABLE t_duck ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +CREATE TABLE t_inno ( + id INT DEFAULT 1 PRIMARY KEY, + name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = InnoDB; +--enable_query_log + +connection con1; +BEGIN; +INSERT INTO t_duck (id, name) VALUES (3, '3'); +INSERT INTO t_inno (id, name) VALUES (3, '3'); + +connection con2; +SELECT * FROM t_duck; +SELECT * FROM t_inno; + +connection con1; +COMMIT; + +connection con2; +SELECT * FROM t_duck; +SELECT * FROM t_inno; + +# ----------------------------------------------------------------- +# Test 8: Mixed InnoDB+DuckDB durability +# ----------------------------------------------------------------- +--echo # Test 8: Mixed InnoDB+DuckDB durability + +connection default; +INSERT INTO t_duck (id, name) VALUES (4, '4'); +INSERT INTO t_inno (id, name) VALUES (4, '4'); + +disconnect con1; +disconnect con2; + +--source include/restart_mysqld.inc +--disable_query_log +USE db_transaction; +--enable_query_log +SELECT * FROM t_duck; +SELECT * FROM t_inno; + +# ----------------------------------------------------------------- +# Cleanup +# ----------------------------------------------------------------- +--disable_query_log +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP DATABASE db_transaction; +USE test; +--enable_query_log + diff --git a/storage/duckdb/mysql-test/duckdb/my.cnf b/storage/duckdb/mysql-test/duckdb/my.cnf new file mode 100644 index 0000000000000..f8899a51f6f0f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/my.cnf @@ -0,0 +1,10 @@ +!include include/default_my.cnf + +[mysqld] +plugin-maturity=experimental +plugin-load-add=ha_duckdb.so +duckdb_dml_in_batch=OFF +character-set-server=utf8mb4 + +[mysqld.1] +innodb diff --git a/storage/duckdb/mysql-test/duckdb/my.cnf.broken b/storage/duckdb/mysql-test/duckdb/my.cnf.broken new file mode 100644 index 0000000000000..cc9ea94531a70 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/my.cnf.broken @@ -0,0 +1,11 @@ +# Use default setting for mysqld processes +!include include/default_mysqld.cnf +!include include/default_client.cnf + +[mysqld] +plugin-maturity=experimental +plugin-load-add=ha_duckdb.so + +[ENV] +MASTER_MYPORT= @mysqld.1.port +MASTER_MYSOCK= @mysqld.1.socket diff --git a/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column.result b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column.result new file mode 100644 index 0000000000000..2c8cf374fd821 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column.result @@ -0,0 +1,1756 @@ +#################### +# TEST FOR INSTANT # +#################### +# +# 1) ADD AND DROP +# +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN d INT NOT NULL DEFAULT 100 COMMENT "col d", +ADD COLUMN e INT NULL DEFAULT 1000 COMMENT "", +ADD COLUMN f INT, ALGORITHM = INSTANT; +INSERT INTO t VALUES(3, 3, 3, 3, 3, 3, 3); +INSERT INTO t(id, a, b, c) VALUES(4, 4, 4, 4); +INSERT INTO t VALUES(5, 5, 5, 5, 5, NULL, NULL); +INSERT INTO t VALUES(6, 6, 6, 6, NULL, NULL, NULL); +ERROR 23000: Column 'd' cannot be null +SELECT * FROM t; +id a b c d e f +1 1 1 1 100 1000 NULL +2 NULL NULL NULL 100 1000 NULL +3 3 3 3 3 3 3 +4 4 4 4 100 1000 NULL +5 5 5 5 5 NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t d '100' NO INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t d 100 NO int col d +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t DROP COLUMN a, DROP COLUMN d, ALGORITHM = INSTANT; +INSERT INTO t VALUES(6, 6, 6, 6, 6); +INSERT INTO t(id, c) VALUES(7, 7); +SELECT * FROM t; +id b c e f +1 1 1 1000 NULL +2 NULL NULL 1000 NULL +3 3 3 3 3 +4 4 4 1000 NULL +5 5 5 NULL NULL +6 6 6 6 6 +7 NULL 7 1000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +# +# 3) SET AND DROP DEFAULT VALUE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 100, +ALTER COLUMN c SET DEFAULT 100, ALGORITHM = INSTANT; +INSERT INTO t VALUES(3, 3, 3, 3); +INSERT INTO t(id) VALUES(4); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '100' YES INTEGER NULL +db_alter_col t c '100' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 100 YES int +db_alter_col t c 100 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 1000, ALGORITHM = INSTANT; +INSERT INTO t VALUES(5, 5, 5, 5); +INSERT INTO t(id) VALUES(6); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '1000' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c 1000 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 10000, +ALTER COLUMN c DROP DEFAULT, ALGORITHM = INSTANT; +INSERT INTO t VALUES(7, 7, 7, 7); +INSERT INTO t(id) VALUES(8); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +7 7 7 7 +8 NULL 10000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '10000' YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 10000 YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +# +# 3) RENAME, MODIFY AND CHANGE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t RENAME COLUMN a TO a1, +RENAME COLUMN b TO b1, +RENAME COLUMN c TO c1, ALGORITHM = INSTANT; +INSERT INTO t(id, a1, b1, c1) VALUES(3, 3, 3, 3); +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b1 NULL YES INTEGER NULL +db_alter_col t c1 NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int +db_alter_col t b1 NULL YES int +db_alter_col t c1 NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c1 IS NULL"); +ALTER TABLE t MODIFY COLUMN a1 BIGINT COMMENT "col a1", +MODIFY COLUMN b1 BIGINT DEFAULT 100 COMMENT "", +MODIFY COLUMN c1 BIGINT NOT NULL DEFAULT 100, ALGORITHM = INSTANT; +INSERT INTO t VALUES(4, 4, 4, 4); +INSERT INTO t(id) VALUES(5); +INSERT INTO t VALUES(6, 6, NULL, 6); +INSERT INTO t VALUES(7, 7, 7, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +6 6 NULL 6 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b1 '100' YES INTEGER NULL +db_alter_col t c1 '100' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES bigint col a1 +db_alter_col t b1 100 YES bigint +db_alter_col t c1 100 NO bigint +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE b1 IS NULL"); +ALTER TABLE t CHANGE a1 a INT COMMENT "col a", +CHANGE b1 b INT NOT NULL DEFAULT 1000, +CHANGE c1 c INT COMMENT "", ALGORITHM = INSTANT; +INSERT INTO t VALUES(8, 8, 8, 8); +INSERT INTO t(id) VALUES(9); +INSERT INTO t VALUES(10, 10, 10, NULL); +INSERT INTO t VALUES(11, 11, NULL, 11); +ERROR 23000: Column 'b' cannot be null +SELECT * FROM t; +id a b c +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +9 NULL 1000 NULL +10 10 10 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '1000' NO INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b 1000 NO int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c IS NULL"); +ALTER TABLE t RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 10000 COMMENT "col b", +CHANGE c c1 BIGINT NOT NULL DEFAULT 10000 COMMENT "col c1", ALGORITHM = INSTANT; +INSERT INTO t VALUES(12, 12, 12, 12); +INSERT INTO t(id) VALUES(13); +INSERT INTO t VALUES(14, 14, NULL, 14); +INSERT INTO t VALUES(15, 15, 15, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +12 12 12 12 +13 NULL 10000 10000 +14 14 NULL 14 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b '10000' YES INTEGER NULL +db_alter_col t c1 '10000' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int col a +db_alter_col t b 10000 YES bigint col b +db_alter_col t c1 10000 NO bigint col c1 +db_alter_col t id NULL NO int + +# +# 4) DEFAULT NULL +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d') ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN a SET DEFAULT NULL, +ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 10, +ALTER COLUMN d SET DEFAULT 10, ALGORITHM = INSTANT; +INSERT INTO t(id, b) VALUES(3, 3); +INSERT INTO t(id, b) VALUES(4, NULL); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '10' YES INTEGER NULL +db_alter_col t d '10' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b NULL YES int col b +db_alter_col t c 10 YES int col c +db_alter_col t d 10 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t MODIFY a BIGINT, +CHANGE b b BIGINT DEFAULT NULL, +MODIFY COLUMN c BIGINT, +CHANGE d d BIGINT DEFAULT NULL, ALGORITHM = INSTANT; +INSERT INTO t(id) VALUES(5); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +5 NULL NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t d NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES bigint +db_alter_col t b NULL YES bigint +db_alter_col t c NULL YES bigint +db_alter_col t d NULL YES bigint +db_alter_col t id NULL NO int + +# +# 5) DEFAULT TIMESTAMP +# +SET @saved_time_zone = @@session.time_zone; +SET time_zone = '+00:00'; +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00', +b TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00') ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +INSERT INTO t VALUES(2, '2020-01-01 12:00:00', '2020-01-01 12:00:00'); +ALTER TABLE t MODIFY COLUMN a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, +CHANGE COLUMN b b TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM = INSTANT; +SET TIMESTAMP = 1303197722.534231; +INSERT INTO t(id) VALUES(3); +SELECT * FROM t; +id a b +1 1970-01-01 12:00:00 1970-01-01 12:00:00 +2 2020-01-01 12:00:00 2020-01-01 12:00:00 +3 2011-04-19 07:22:02 2011-04-19 07:22:02 +DROP TABLE t; +SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 00:00:00'); +CREATE TABLE t(id INT PRIMARY KEY, a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, b TIMESTAMP DEFAULT NOW()) ENGINE = InnoDB; +INSERT INTO t(id) VALUES (1); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +SET TIMESTAMP=UNIX_TIMESTAMP('2001-03-01 00:00:00'); +ALTER TABLE t MODIFY COLUMN b TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; +INSERT INTO t(id) VALUES (3); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +3 2001-03-01 00:00:00 2001-03-01 00:00:00 +SET time_zone = @saved_time_zone; +# +# 6) DEFAULT VALUE EXPRESSION +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT DEFAULT (1+1), b VARCHAR(100) DEFAULT ('1+1'), c VARCHAR(100) DEFAULT (1+1)) ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN d INT DEFAULT (2+2), +ADD COLUMN e VARCHAR(100) DEFAULT ('2+2'), +ADD COLUMN f VARCHAR(100) DEFAULT (2+2), ALGORITHM = INSTANT; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t MODIFY COLUMN a INT DEFAULT (3+3), +MODIFY COLUMN b VARCHAR(100) DEFAULT ('3+3'), +MODIFY COLUMN c VARCHAR(100) DEFAULT (3+3), ALGORITHM = INSTANT; +INSERT INTO t(id) VALUES(5); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT * FROM t; +id a b c d e f +1 2 1+1 2 4 2+2 4 +2 2 1+1 2 4 2+2 4 +3 2 1+1 2 4 2+2 4 +4 2 1+1 2 4 2+2 4 +5 6 3+3 6 4 2+2 4 +6 6 3+3 6 4 2+2 4 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '6' YES INTEGER NULL +db_alter_col t b '3+3' YES VARCHAR NULL +db_alter_col t c '6' YES VARCHAR NULL +db_alter_col t d '4' YES INTEGER NULL +db_alter_col t e '2+2' YES VARCHAR NULL +db_alter_col t f '4' YES VARCHAR NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a (3 + 3) YES int +db_alter_col t b '3+3' YES varchar +db_alter_col t c (3 + 3) YES varchar +db_alter_col t d (2 + 2) YES int +db_alter_col t e '2+2' YES varchar +db_alter_col t f (2 + 2) YES varchar +db_alter_col t id NULL NO int + +# +# 7) DEFAULT VALUE OF BIT +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, B0 BIT(1) NOT NULL DEFAULT 0, B1 BIT(32) NOT NULL DEFAULT b'1111') ENGINE=DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN B2 BIT(64) NOT NULL DEFAULT b'11111', +ALTER COLUMN B1 SET DEFAULT 3333, ALGORITHM = INSTANT; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT id, hex(B0), hex(B1), hex(B2) FROM t; +id hex(B0) hex(B1) hex(B2) +1 00 0000000F 000000000000001F +2 00 0000000F 000000000000001F +3 00 00000D05 000000000000001F +4 00 00000D05 000000000000001F +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t B0 CAST('\x00' AS "BLOB") NO BLOB NULL +db_alter_col t B1 CAST('\x00\x00\x0D\x05' AS "BLOB") NO BLOB NULL +db_alter_col t B2 CAST('\x00\x00\x00\x00\x00\x00\x00\x1F' AS "BLOB") NO BLOB NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t B0 b'0' NO bit +db_alter_col t B1 b'110100000101' NO bit +db_alter_col t B2 b'11111' NO bit +db_alter_col t id NULL NO int + +# +# 8) MULTIPLE DDL ABOUT COLUMNS +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d', +e INT DEFAULT 1 COMMENT 'col e', +f INT DEFAULT 1 COMMENT 'col f', +g INT DEFAULT 1 COMMENT 'col g') ENGINE = DuckDB; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL +db_alter_col t e '1' YES INTEGER NULL +db_alter_col t f '1' YES INTEGER NULL +db_alter_col t g '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t e 1 YES int col e +db_alter_col t f 1 YES int col f +db_alter_col t g 1 YES int col g +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN h INT NOT NULL DEFAULT 2 COMMENT 'col h1', +DROP COLUMN g, +ALTER COLUMN f SET DEFAULT 2, +ALTER COLUMN e SET DEFAULT NULL, +ALTER COLUMN d DROP DEFAULT, +RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 2 COMMENT "col b1", +CHANGE c c1 BIGINT NOT NULL DEFAULT 2 COMMENT "col c1", ALGORITHM = INSTANT; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 '1' YES INTEGER NULL +db_alter_col t b '2' YES INTEGER NULL +db_alter_col t c1 '2' NO INTEGER NULL +db_alter_col t d NULL YES INTEGER NULL +db_alter_col t e NULL YES INTEGER NULL +db_alter_col t f '2' YES INTEGER NULL +db_alter_col t h '2' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 1 YES int col a +db_alter_col t b 2 YES bigint col b1 +db_alter_col t c1 2 NO bigint col c1 +db_alter_col t d NULL YES int col d +db_alter_col t e NULL YES int col e +db_alter_col t f 2 YES int col f +db_alter_col t h 2 NO int col h1 +db_alter_col t id NULL NO int + +# +# 9) Not supported +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +CREATE TABLE t1(id INT PRIMARY KEY, a INT INVISIBLE) ENGINE = DuckDB; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'INVISIBLE column' +ALTER TABLE t ADD COLUMN d INT INVISIBLE, ALGORITHM = INSTANT; +ERROR HY000: Got error 168 'INVISIBLE column is not supported. Try removing INVISIBLE from column 'd'' from DuckDB +ALTER TABLE t ALTER COLUMN b SET INVISIBLE, ALGORITHM = INSTANT; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'INVISIBLE, ALGORITHM = INSTANT' at line 1 +ALTER TABLE t MODIFY COLUMN c INT AUTO_INCREMENT KEY, ALGORITHM = INSTANT; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +ALTER TABLE t ADD COLUMN d INT AUTO_INCREMENT, ALGORITHM = INSTANT; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +CREATE TABLE t1(id INT PRIMARY KEY, a INT) ENGINE = DuckDB ENGINE_ATTRIBUTE='{"KEY":"VALUE"}'; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t ADD COLUMN d INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INSTANT; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t MODIFY COLUMN c INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INSTANT; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t ADD COLUMN d INT AFTER a, ALGORITHM = INSTANT; +ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t ADD COLUMN d INT FIRST, ALGORITHM = INSTANT; +ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t CHANGE b b INT AFTER c, ALGORITHM = INSTANT; +ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t MODIFY COLUMN a INT FIRST, ALGORITHM = INSTANT; +ERROR 0A000: ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY +CREATE TABLE t1(id INT PRIMARY KEY, a INT, b INT GENERATED ALWAYS AS (a - 1) STORED) ENGINE = DuckDB; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (a - 1) STORED, ALGORITHM = INSTANT; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (b + 1) VIRTUAL, ALGORITHM = INSTANT; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t MODIFY COLUMN b INT GENERATED ALWAYS AS (a+1) STORED, ALGORITHM = INSTANT; +ERROR HY000: DUCKDB storage engine does not support generated columns +# +# 10) Ignore column format, secondary engine attribute, storage, they can be found in DD. +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t ADD COLUMN d INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INSTANT; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e...' at line 1 +ALTER TABLE t MODIFY COLUMN b INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INSTANT; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c I...' at line 1 +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 11) Drop primary key column which leads to drop primary key. +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INSTANT; +ERROR 42000: This table type requires a primary key +# +# 12) BUG#117725 +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a INT, MODIFY COLUMN a INT, ALGORITHM = INSTANT; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, ALTER COLUMN a DROP DEFAULT, ALGORITHM = INSTANT; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, RENAME COLUMN a to c, ALGORITHM = INSTANT; +ERROR 42S22: Unknown column 'a' in 't' +DROP TABLE t; +# +# 13) DROP COLUMN IN PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b, c)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INSTANT; +ERROR 42000: Key column 'a' doesn't exist in table +ALTER TABLE t DROP COLUMN c, ALGORITHM = INSTANT; +ERROR 42000: Key column 'c' doesn't exist in table +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int(11) NOT NULL, + `b` int(11) NOT NULL, + `c` int(11) NOT NULL, + PRIMARY KEY (`a`,`b`,`c`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 3] +db_alter_col t a NULL NO INTEGER NULL +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t c NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO int +db_alter_col t b NULL NO int +db_alter_col t c NULL NO int + +DROP TABLE t; +# +# 14) DROP COLUMN BEFORE PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, d INT, PRIMARY KEY(d, b)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INSTANT; +ALTER TABLE t DROP COLUMN c, ALGORITHM = INSTANT; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `b` int(11) NOT NULL, + `d` int(11) NOT NULL, + PRIMARY KEY (`d`,`b`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t d NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL NO int +db_alter_col t d NULL NO int + +DROP TABLE t; +# +# 15) CHANGE COLUMN TYPE WITH INDEX +# +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a VARCHAR(10), ALGORITHM = INSTANT; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` varchar(10) NOT NULL, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t a NULL NO INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO varchar +db_alter_col t b NULL YES int + +DROP TABLE t; +# +# 16) Cleanup +# +#################### +# TEST FOR INPLACE # +#################### +# +# 1) ADD AND DROP +# +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN d INT NOT NULL DEFAULT 100 COMMENT "col d", +ADD COLUMN e INT NULL DEFAULT 1000 COMMENT "", +ADD COLUMN f INT, ALGORITHM = INPLACE; +INSERT INTO t VALUES(3, 3, 3, 3, 3, 3, 3); +INSERT INTO t(id, a, b, c) VALUES(4, 4, 4, 4); +INSERT INTO t VALUES(5, 5, 5, 5, 5, NULL, NULL); +INSERT INTO t VALUES(6, 6, 6, 6, NULL, NULL, NULL); +ERROR 23000: Column 'd' cannot be null +SELECT * FROM t; +id a b c d e f +1 1 1 1 100 1000 NULL +2 NULL NULL NULL 100 1000 NULL +3 3 3 3 3 3 3 +4 4 4 4 100 1000 NULL +5 5 5 5 5 NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t d '100' NO INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t d 100 NO int col d +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t DROP COLUMN a, DROP COLUMN d, ALGORITHM = INPLACE; +INSERT INTO t VALUES(6, 6, 6, 6, 6); +INSERT INTO t(id, c) VALUES(7, 7); +SELECT * FROM t; +id b c e f +1 1 1 1000 NULL +2 NULL NULL 1000 NULL +3 3 3 3 3 +4 4 4 1000 NULL +5 5 5 NULL NULL +6 6 6 6 6 +7 NULL 7 1000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +# +# 3) SET AND DROP DEFAULT VALUE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 100, +ALTER COLUMN c SET DEFAULT 100, ALGORITHM = INPLACE; +INSERT INTO t VALUES(3, 3, 3, 3); +INSERT INTO t(id) VALUES(4); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '100' YES INTEGER NULL +db_alter_col t c '100' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 100 YES int +db_alter_col t c 100 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 1000, ALGORITHM = INPLACE; +INSERT INTO t VALUES(5, 5, 5, 5); +INSERT INTO t(id) VALUES(6); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '1000' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c 1000 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 10000, +ALTER COLUMN c DROP DEFAULT, ALGORITHM = INPLACE; +INSERT INTO t VALUES(7, 7, 7, 7); +INSERT INTO t(id) VALUES(8); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +7 7 7 7 +8 NULL 10000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '10000' YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 10000 YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +# +# 3) RENAME, MODIFY AND CHANGE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t RENAME COLUMN a TO a1, +RENAME COLUMN b TO b1, +RENAME COLUMN c TO c1, ALGORITHM = INPLACE; +INSERT INTO t(id, a1, b1, c1) VALUES(3, 3, 3, 3); +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b1 NULL YES INTEGER NULL +db_alter_col t c1 NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int +db_alter_col t b1 NULL YES int +db_alter_col t c1 NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c1 IS NULL"); +ALTER TABLE t MODIFY COLUMN a1 BIGINT COMMENT "col a1", +MODIFY COLUMN b1 BIGINT DEFAULT 100 COMMENT "", +MODIFY COLUMN c1 BIGINT NOT NULL DEFAULT 100, ALGORITHM = INPLACE; +INSERT INTO t VALUES(4, 4, 4, 4); +INSERT INTO t(id) VALUES(5); +INSERT INTO t VALUES(6, 6, NULL, 6); +INSERT INTO t VALUES(7, 7, 7, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +6 6 NULL 6 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b1 '100' YES INTEGER NULL +db_alter_col t c1 '100' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES bigint col a1 +db_alter_col t b1 100 YES bigint +db_alter_col t c1 100 NO bigint +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE b1 IS NULL"); +ALTER TABLE t CHANGE a1 a INT COMMENT "col a", +CHANGE b1 b INT NOT NULL DEFAULT 1000, +CHANGE c1 c INT COMMENT "", ALGORITHM = INPLACE; +INSERT INTO t VALUES(8, 8, 8, 8); +INSERT INTO t(id) VALUES(9); +INSERT INTO t VALUES(10, 10, 10, NULL); +INSERT INTO t VALUES(11, 11, NULL, 11); +ERROR 23000: Column 'b' cannot be null +SELECT * FROM t; +id a b c +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +9 NULL 1000 NULL +10 10 10 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '1000' NO INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b 1000 NO int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c IS NULL"); +ALTER TABLE t RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 10000 COMMENT "col b", +CHANGE c c1 BIGINT NOT NULL DEFAULT 10000 COMMENT "col c1", ALGORITHM = INPLACE; +INSERT INTO t VALUES(12, 12, 12, 12); +INSERT INTO t(id) VALUES(13); +INSERT INTO t VALUES(14, 14, NULL, 14); +INSERT INTO t VALUES(15, 15, 15, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +12 12 12 12 +13 NULL 10000 10000 +14 14 NULL 14 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b '10000' YES INTEGER NULL +db_alter_col t c1 '10000' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int col a +db_alter_col t b 10000 YES bigint col b +db_alter_col t c1 10000 NO bigint col c1 +db_alter_col t id NULL NO int + +# +# 4) DEFAULT NULL +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d') ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN a SET DEFAULT NULL, +ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 10, +ALTER COLUMN d SET DEFAULT 10, ALGORITHM = INPLACE; +INSERT INTO t(id, b) VALUES(3, 3); +INSERT INTO t(id, b) VALUES(4, NULL); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '10' YES INTEGER NULL +db_alter_col t d '10' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b NULL YES int col b +db_alter_col t c 10 YES int col c +db_alter_col t d 10 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t MODIFY a BIGINT, +CHANGE b b BIGINT DEFAULT NULL, +MODIFY COLUMN c BIGINT, +CHANGE d d BIGINT DEFAULT NULL, ALGORITHM = INPLACE; +INSERT INTO t(id) VALUES(5); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +5 NULL NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t d NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES bigint +db_alter_col t b NULL YES bigint +db_alter_col t c NULL YES bigint +db_alter_col t d NULL YES bigint +db_alter_col t id NULL NO int + +# +# 5) DEFAULT TIMESTAMP +# +SET @saved_time_zone = @@session.time_zone; +SET time_zone = '+00:00'; +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00', +b TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00') ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +INSERT INTO t VALUES(2, '2020-01-01 12:00:00', '2020-01-01 12:00:00'); +ALTER TABLE t MODIFY COLUMN a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, +CHANGE COLUMN b b TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM = INPLACE; +SET TIMESTAMP = 1303197722.534231; +INSERT INTO t(id) VALUES(3); +SELECT * FROM t; +id a b +1 1970-01-01 12:00:00 1970-01-01 12:00:00 +2 2020-01-01 12:00:00 2020-01-01 12:00:00 +3 2011-04-19 07:22:02 2011-04-19 07:22:02 +DROP TABLE t; +SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 00:00:00'); +CREATE TABLE t(id INT PRIMARY KEY, a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, b TIMESTAMP DEFAULT NOW()) ENGINE = InnoDB; +INSERT INTO t(id) VALUES (1); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +SET TIMESTAMP=UNIX_TIMESTAMP('2001-03-01 00:00:00'); +ALTER TABLE t MODIFY COLUMN b TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; +INSERT INTO t(id) VALUES (3); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +3 2001-03-01 00:00:00 2001-03-01 00:00:00 +SET time_zone = @saved_time_zone; +# +# 6) DEFAULT VALUE EXPRESSION +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT DEFAULT (1+1), b VARCHAR(100) DEFAULT ('1+1'), c VARCHAR(100) DEFAULT (1+1)) ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN d INT DEFAULT (2+2), +ADD COLUMN e VARCHAR(100) DEFAULT ('2+2'), +ADD COLUMN f VARCHAR(100) DEFAULT (2+2), ALGORITHM = INPLACE; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t MODIFY COLUMN a INT DEFAULT (3+3), +MODIFY COLUMN b VARCHAR(100) DEFAULT ('3+3'), +MODIFY COLUMN c VARCHAR(100) DEFAULT (3+3), ALGORITHM = INPLACE; +INSERT INTO t(id) VALUES(5); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT * FROM t; +id a b c d e f +1 2 1+1 2 4 2+2 4 +2 2 1+1 2 4 2+2 4 +3 2 1+1 2 4 2+2 4 +4 2 1+1 2 4 2+2 4 +5 6 3+3 6 4 2+2 4 +6 6 3+3 6 4 2+2 4 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '6' YES INTEGER NULL +db_alter_col t b '3+3' YES VARCHAR NULL +db_alter_col t c '6' YES VARCHAR NULL +db_alter_col t d '4' YES INTEGER NULL +db_alter_col t e '2+2' YES VARCHAR NULL +db_alter_col t f '4' YES VARCHAR NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a (3 + 3) YES int +db_alter_col t b '3+3' YES varchar +db_alter_col t c (3 + 3) YES varchar +db_alter_col t d (2 + 2) YES int +db_alter_col t e '2+2' YES varchar +db_alter_col t f (2 + 2) YES varchar +db_alter_col t id NULL NO int + +# +# 7) DEFAULT VALUE OF BIT +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, B0 BIT(1) NOT NULL DEFAULT 0, B1 BIT(32) NOT NULL DEFAULT b'1111') ENGINE=DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN B2 BIT(64) NOT NULL DEFAULT b'11111', +ALTER COLUMN B1 SET DEFAULT 3333, ALGORITHM = INPLACE; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT id, hex(B0), hex(B1), hex(B2) FROM t; +id hex(B0) hex(B1) hex(B2) +1 00 0000000F 000000000000001F +2 00 0000000F 000000000000001F +3 00 00000D05 000000000000001F +4 00 00000D05 000000000000001F +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t B0 CAST('\x00' AS "BLOB") NO BLOB NULL +db_alter_col t B1 CAST('\x00\x00\x0D\x05' AS "BLOB") NO BLOB NULL +db_alter_col t B2 CAST('\x00\x00\x00\x00\x00\x00\x00\x1F' AS "BLOB") NO BLOB NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t B0 b'0' NO bit +db_alter_col t B1 b'110100000101' NO bit +db_alter_col t B2 b'11111' NO bit +db_alter_col t id NULL NO int + +# +# 8) MULTIPLE DDL ABOUT COLUMNS +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d', +e INT DEFAULT 1 COMMENT 'col e', +f INT DEFAULT 1 COMMENT 'col f', +g INT DEFAULT 1 COMMENT 'col g') ENGINE = DuckDB; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL +db_alter_col t e '1' YES INTEGER NULL +db_alter_col t f '1' YES INTEGER NULL +db_alter_col t g '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t e 1 YES int col e +db_alter_col t f 1 YES int col f +db_alter_col t g 1 YES int col g +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN h INT NOT NULL DEFAULT 2 COMMENT 'col h1', +DROP COLUMN g, +ALTER COLUMN f SET DEFAULT 2, +ALTER COLUMN e SET DEFAULT NULL, +ALTER COLUMN d DROP DEFAULT, +RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 2 COMMENT "col b1", +CHANGE c c1 BIGINT NOT NULL DEFAULT 2 COMMENT "col c1", ALGORITHM = INPLACE; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 '1' YES INTEGER NULL +db_alter_col t b '2' YES INTEGER NULL +db_alter_col t c1 '2' NO INTEGER NULL +db_alter_col t d NULL YES INTEGER NULL +db_alter_col t e NULL YES INTEGER NULL +db_alter_col t f '2' YES INTEGER NULL +db_alter_col t h '2' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 1 YES int col a +db_alter_col t b 2 YES bigint col b1 +db_alter_col t c1 2 NO bigint col c1 +db_alter_col t d NULL YES int col d +db_alter_col t e NULL YES int col e +db_alter_col t f 2 YES int col f +db_alter_col t h 2 NO int col h1 +db_alter_col t id NULL NO int + +# +# 9) Not supported +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +CREATE TABLE t1(id INT PRIMARY KEY, a INT INVISIBLE) ENGINE = DuckDB; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'INVISIBLE column' +ALTER TABLE t ADD COLUMN d INT INVISIBLE, ALGORITHM = INPLACE; +ERROR HY000: Got error 168 'INVISIBLE column is not supported. Try removing INVISIBLE from column 'd'' from DuckDB +ALTER TABLE t ALTER COLUMN b SET INVISIBLE, ALGORITHM = INPLACE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'INVISIBLE, ALGORITHM = INPLACE' at line 1 +ALTER TABLE t MODIFY COLUMN c INT AUTO_INCREMENT KEY, ALGORITHM = INPLACE; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +ALTER TABLE t ADD COLUMN d INT AUTO_INCREMENT, ALGORITHM = INPLACE; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +CREATE TABLE t1(id INT PRIMARY KEY, a INT) ENGINE = DuckDB ENGINE_ATTRIBUTE='{"KEY":"VALUE"}'; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t ADD COLUMN d INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INPLACE; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t MODIFY COLUMN c INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INPLACE; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t ADD COLUMN d INT AFTER a, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t ADD COLUMN d INT FIRST, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t CHANGE b b INT AFTER c, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +ALTER TABLE t MODIFY COLUMN a INT FIRST, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY +CREATE TABLE t1(id INT PRIMARY KEY, a INT, b INT GENERATED ALWAYS AS (a - 1) STORED) ENGINE = DuckDB; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (a - 1) STORED, ALGORITHM = INPLACE; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (b + 1) VIRTUAL, ALGORITHM = INPLACE; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t MODIFY COLUMN b INT GENERATED ALWAYS AS (a+1) STORED, ALGORITHM = INPLACE; +ERROR HY000: DUCKDB storage engine does not support generated columns +# +# 10) Ignore column format, secondary engine attribute, storage, they can be found in DD. +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t ADD COLUMN d INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INPLACE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e...' at line 1 +ALTER TABLE t MODIFY COLUMN b INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = INPLACE; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c I...' at line 1 +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 11) Drop primary key column which leads to drop primary key. +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INPLACE; +ERROR 42000: This table type requires a primary key +# +# 12) BUG#117725 +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a INT, MODIFY COLUMN a INT, ALGORITHM = INPLACE; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, ALTER COLUMN a DROP DEFAULT, ALGORITHM = INPLACE; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, RENAME COLUMN a to c, ALGORITHM = INPLACE; +ERROR 42S22: Unknown column 'a' in 't' +DROP TABLE t; +# +# 13) DROP COLUMN IN PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b, c)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INPLACE; +ERROR 42000: Key column 'a' doesn't exist in table +ALTER TABLE t DROP COLUMN c, ALGORITHM = INPLACE; +ERROR 42000: Key column 'c' doesn't exist in table +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int(11) NOT NULL, + `b` int(11) NOT NULL, + `c` int(11) NOT NULL, + PRIMARY KEY (`a`,`b`,`c`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 3] +db_alter_col t a NULL NO INTEGER NULL +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t c NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO int +db_alter_col t b NULL NO int +db_alter_col t c NULL NO int + +DROP TABLE t; +# +# 14) DROP COLUMN BEFORE PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, d INT, PRIMARY KEY(d, b)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = INPLACE; +ALTER TABLE t DROP COLUMN c, ALGORITHM = INPLACE; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `b` int(11) NOT NULL, + `d` int(11) NOT NULL, + PRIMARY KEY (`d`,`b`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t d NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL NO int +db_alter_col t d NULL NO int + +DROP TABLE t; +# +# 15) CHANGE COLUMN TYPE WITH INDEX +# +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a VARCHAR(10), ALGORITHM = INPLACE; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` varchar(10) NOT NULL, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t a NULL NO INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO varchar +db_alter_col t b NULL YES int + +DROP TABLE t; +# +# 16) Cleanup +# diff --git a/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column_copy.result b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column_copy.result new file mode 100644 index 0000000000000..a8158a2afb010 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_column_copy.result @@ -0,0 +1,870 @@ +################# +# TEST FOR COPY # +################# +# +# 1) ADD AND DROP +# +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN d INT NOT NULL DEFAULT 100 COMMENT "col d", +ADD COLUMN e INT NULL DEFAULT 1000 COMMENT "", +ADD COLUMN f INT, ALGORITHM = COPY; +INSERT INTO t VALUES(3, 3, 3, 3, 3, 3, 3); +INSERT INTO t(id, a, b, c) VALUES(4, 4, 4, 4); +INSERT INTO t VALUES(5, 5, 5, 5, 5, NULL, NULL); +INSERT INTO t VALUES(6, 6, 6, 6, NULL, NULL, NULL); +ERROR 23000: Column 'd' cannot be null +SELECT * FROM t; +id a b c d e f +1 1 1 1 100 1000 NULL +2 NULL NULL NULL 100 1000 NULL +3 3 3 3 3 3 3 +4 4 4 4 100 1000 NULL +5 5 5 5 5 NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t d '100' NO INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t d 100 NO int col d +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t DROP COLUMN a, DROP COLUMN d, ALGORITHM = COPY; +INSERT INTO t VALUES(6, 6, 6, 6, 6); +INSERT INTO t(id, c) VALUES(7, 7); +SELECT * FROM t; +id b c e f +1 1 1 1000 NULL +2 NULL NULL 1000 NULL +3 3 3 3 3 +4 4 4 1000 NULL +5 5 5 NULL NULL +6 6 6 6 6 +7 NULL 7 1000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL +db_alter_col t e '1000' YES INTEGER NULL +db_alter_col t f NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t e 1000 YES int +db_alter_col t f NULL YES int +db_alter_col t id NULL NO int + +# +# 3) SET AND DROP DEFAULT VALUE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 100, +ALTER COLUMN c SET DEFAULT 100, ALGORITHM = COPY; +INSERT INTO t VALUES(3, 3, 3, 3); +INSERT INTO t(id) VALUES(4); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '100' YES INTEGER NULL +db_alter_col t c '100' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 100 YES int +db_alter_col t c 100 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 1000, ALGORITHM = COPY; +INSERT INTO t VALUES(5, 5, 5, 5); +INSERT INTO t(id) VALUES(6); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '1000' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c 1000 YES int +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN b SET DEFAULT 10000, +ALTER COLUMN c DROP DEFAULT, ALGORITHM = COPY; +INSERT INTO t VALUES(7, 7, 7, 7); +INSERT INTO t(id) VALUES(8); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +4 NULL 100 100 +5 5 5 5 +6 NULL NULL 1000 +7 7 7 7 +8 NULL 10000 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '10000' YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b 10000 YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +# +# 3) RENAME, MODIFY AND CHANGE +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c +1 1 1 1 +2 NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int +db_alter_col t b NULL YES int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +ALTER TABLE t RENAME COLUMN a TO a1, +RENAME COLUMN b TO b1, +RENAME COLUMN c TO c1, ALGORITHM = COPY; +INSERT INTO t(id, a1, b1, c1) VALUES(3, 3, 3, 3); +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +2 NULL NULL NULL +3 3 3 3 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b1 NULL YES INTEGER NULL +db_alter_col t c1 NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int +db_alter_col t b1 NULL YES int +db_alter_col t c1 NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c1 IS NULL"); +ALTER TABLE t MODIFY COLUMN a1 BIGINT COMMENT "col a1", +MODIFY COLUMN b1 BIGINT DEFAULT 100 COMMENT "", +MODIFY COLUMN c1 BIGINT NOT NULL DEFAULT 100, ALGORITHM = COPY; +INSERT INTO t VALUES(4, 4, 4, 4); +INSERT INTO t(id) VALUES(5); +INSERT INTO t VALUES(6, 6, NULL, 6); +INSERT INTO t VALUES(7, 7, 7, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b1 c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +6 6 NULL 6 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES BIGINT NULL +db_alter_col t b1 '100' YES BIGINT NULL +db_alter_col t c1 '100' NO BIGINT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES bigint col a1 +db_alter_col t b1 100 YES bigint +db_alter_col t c1 100 NO bigint +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE b1 IS NULL"); +ALTER TABLE t CHANGE a1 a INT COMMENT "col a", +CHANGE b1 b INT NOT NULL DEFAULT 1000, +CHANGE c1 c INT COMMENT "", ALGORITHM = COPY; +INSERT INTO t VALUES(8, 8, 8, 8); +INSERT INTO t(id) VALUES(9); +INSERT INTO t VALUES(10, 10, 10, NULL); +INSERT INTO t VALUES(11, 11, NULL, 11); +ERROR 23000: Column 'b' cannot be null +SELECT * FROM t; +id a b c +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +9 NULL 1000 NULL +10 10 10 NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b '1000' NO INTEGER NULL +db_alter_col t c NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b 1000 NO int +db_alter_col t c NULL YES int +db_alter_col t id NULL NO int + +SELECT run_in_duckdb("DELETE FROM db_alter_col.t WHERE c IS NULL"); +ALTER TABLE t RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 10000 COMMENT "col b", +CHANGE c c1 BIGINT NOT NULL DEFAULT 10000 COMMENT "col c1", ALGORITHM = COPY; +INSERT INTO t VALUES(12, 12, 12, 12); +INSERT INTO t(id) VALUES(13); +INSERT INTO t VALUES(14, 14, NULL, 14); +INSERT INTO t VALUES(15, 15, 15, NULL); +ERROR 23000: Column 'c1' cannot be null +SELECT * FROM t; +id a1 b c1 +1 1 1 1 +3 3 3 3 +4 4 4 4 +5 NULL 100 100 +8 8 8 8 +12 12 12 12 +13 NULL 10000 10000 +14 14 NULL 14 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 NULL YES INTEGER NULL +db_alter_col t b '10000' YES BIGINT NULL +db_alter_col t c1 '10000' NO BIGINT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 NULL YES int col a +db_alter_col t b 10000 YES bigint col b +db_alter_col t c1 10000 NO bigint col c1 +db_alter_col t id NULL NO int + +# +# 4) DEFAULT NULL +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d') ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1, 1); +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t ALTER COLUMN a SET DEFAULT NULL, +ALTER COLUMN b DROP DEFAULT, +ALTER COLUMN c SET DEFAULT 10, +ALTER COLUMN d SET DEFAULT 10, ALGORITHM = COPY; +INSERT INTO t(id, b) VALUES(3, 3); +INSERT INTO t(id, b) VALUES(4, NULL); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES INTEGER NULL +db_alter_col t b NULL YES INTEGER NULL +db_alter_col t c '10' YES INTEGER NULL +db_alter_col t d '10' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES int col a +db_alter_col t b NULL YES int col b +db_alter_col t c 10 YES int col c +db_alter_col t d 10 YES int col d +db_alter_col t id NULL NO int + +ALTER TABLE t MODIFY a BIGINT, +CHANGE b b BIGINT DEFAULT NULL, +MODIFY COLUMN c BIGINT, +CHANGE d d BIGINT DEFAULT NULL, ALGORITHM = COPY; +INSERT INTO t(id) VALUES(5); +SELECT * FROM t; +id a b c d +1 1 1 1 1 +2 1 1 1 1 +3 NULL 3 10 10 +4 NULL NULL 10 10 +5 NULL NULL NULL NULL +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a NULL YES BIGINT NULL +db_alter_col t b NULL YES BIGINT NULL +db_alter_col t c NULL YES BIGINT NULL +db_alter_col t d NULL YES BIGINT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL YES bigint +db_alter_col t b NULL YES bigint +db_alter_col t c NULL YES bigint +db_alter_col t d NULL YES bigint +db_alter_col t id NULL NO int + +# +# 5) DEFAULT TIMESTAMP +# +SET @saved_time_zone = @@session.time_zone; +SET time_zone = '+00:00'; +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00', +b TIMESTAMP NOT NULL DEFAULT '1970-01-01 12:00:00') ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +INSERT INTO t VALUES(2, '2020-01-01 12:00:00', '2020-01-01 12:00:00'); +ALTER TABLE t MODIFY COLUMN a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, +CHANGE COLUMN b b TIMESTAMP NOT NULL DEFAULT NOW(), ALGORITHM = COPY; +SET TIMESTAMP = 1303197722.534231; +INSERT INTO t(id) VALUES(3); +SELECT * FROM t; +id a b +1 1970-01-01 12:00:00 1970-01-01 12:00:00 +2 2020-01-01 12:00:00 2020-01-01 12:00:00 +3 2011-04-19 07:22:02 2011-04-19 07:22:02 +DROP TABLE t; +SET TIMESTAMP=UNIX_TIMESTAMP('2001-01-01 00:00:00'); +CREATE TABLE t(id INT PRIMARY KEY, a TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, b TIMESTAMP DEFAULT NOW()) ENGINE = InnoDB; +INSERT INTO t(id) VALUES (1); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +SET TIMESTAMP=UNIX_TIMESTAMP('2001-03-01 00:00:00'); +ALTER TABLE t MODIFY COLUMN b TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; +INSERT INTO t(id) VALUES (3); +SELECT * FROM t; +id a b +1 2001-01-01 00:00:00 2001-01-01 00:00:00 +3 2001-03-01 00:00:00 2001-03-01 00:00:00 +SET time_zone = @saved_time_zone; +# +# 6) DEFAULT VALUE EXPRESSION +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT DEFAULT (1+1), b VARCHAR(100) DEFAULT ('1+1'), c VARCHAR(100) DEFAULT (1+1)) ENGINE = DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN d INT DEFAULT (2+2), +ADD COLUMN e VARCHAR(100) DEFAULT ('2+2'), +ADD COLUMN f VARCHAR(100) DEFAULT (2+2), ALGORITHM = COPY; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t MODIFY COLUMN a INT DEFAULT (3+3), +MODIFY COLUMN b VARCHAR(100) DEFAULT ('3+3'), +MODIFY COLUMN c VARCHAR(100) DEFAULT (3+3), ALGORITHM = COPY; +INSERT INTO t(id) VALUES(5); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(6)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT * FROM t; +id a b c d e f +1 2 1+1 2 4 2+2 4 +2 2 1+1 2 4 2+2 4 +3 2 1+1 2 4 2+2 4 +4 2 1+1 2 4 2+2 4 +5 6 3+3 6 4 2+2 4 +6 6 3+3 6 4 2+2 4 +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 7] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a (3 + 3) YES INTEGER NULL +db_alter_col t b '3+3' YES VARCHAR NULL +db_alter_col t c (3 + 3) YES VARCHAR NULL +db_alter_col t d (2 + 2) YES INTEGER NULL +db_alter_col t e '2+2' YES VARCHAR NULL +db_alter_col t f (2 + 2) YES VARCHAR NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a (3 + 3) YES int +db_alter_col t b '3+3' YES varchar +db_alter_col t c (3 + 3) YES varchar +db_alter_col t d (2 + 2) YES int +db_alter_col t e '2+2' YES varchar +db_alter_col t f (2 + 2) YES varchar +db_alter_col t id NULL NO int + +# +# 7) DEFAULT VALUE OF BIT +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, B0 BIT(1) NOT NULL DEFAULT 0, B1 BIT(32) NOT NULL DEFAULT b'1111') ENGINE=DuckDB; +INSERT INTO t(id) VALUES(1); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(2)") +Count +BIGINT +[ Rows: 1] +1 + + +ALTER TABLE t ADD COLUMN B2 BIT(64) NOT NULL DEFAULT b'11111', +ALTER COLUMN B1 SET DEFAULT 3333, ALGORITHM = COPY; +INSERT INTO t(id) VALUES(3); +SELECT run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)"); +run_in_duckdb("INSERT INTO db_alter_col.t(id) VALUES(4)") +Count +BIGINT +[ Rows: 1] +1 + + +SELECT id, hex(B0), hex(B1), hex(B2) FROM t; +id hex(B0) hex(B1) hex(B2) +1 00 0000000F 000000000000001F +2 00 0000000F 000000000000001F +3 00 00000D05 000000000000001F +4 00 00000D05 000000000000001F +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t B0 CAST('\x00' AS "BLOB") NO BLOB NULL +db_alter_col t B1 CAST('\x00\x00\x0D\x05' AS "BLOB") NO BLOB NULL +db_alter_col t B2 CAST('\x00\x00\x00\x00\x00\x00\x00\x1F' AS "BLOB") NO BLOB NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t B0 b'0' NO bit +db_alter_col t B1 b'110100000101' NO bit +db_alter_col t B2 b'11111' NO bit +db_alter_col t id NULL NO int + +# +# 8) MULTIPLE DDL ABOUT COLUMNS +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, +a INT DEFAULT 1 COMMENT 'col a', +b INT DEFAULT 1 COMMENT 'col b', +c INT DEFAULT 1 COMMENT 'col c', +d INT DEFAULT 1 COMMENT 'col d', +e INT DEFAULT 1 COMMENT 'col e', +f INT DEFAULT 1 COMMENT 'col f', +g INT DEFAULT 1 COMMENT 'col g') ENGINE = DuckDB; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a '1' YES INTEGER NULL +db_alter_col t b '1' YES INTEGER NULL +db_alter_col t c '1' YES INTEGER NULL +db_alter_col t d '1' YES INTEGER NULL +db_alter_col t e '1' YES INTEGER NULL +db_alter_col t f '1' YES INTEGER NULL +db_alter_col t g '1' YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a 1 YES int col a +db_alter_col t b 1 YES int col b +db_alter_col t c 1 YES int col c +db_alter_col t d 1 YES int col d +db_alter_col t e 1 YES int col e +db_alter_col t f 1 YES int col f +db_alter_col t g 1 YES int col g +db_alter_col t id NULL NO int + +ALTER TABLE t ADD COLUMN h INT NOT NULL DEFAULT 2 COMMENT 'col h1', +DROP COLUMN g, +ALTER COLUMN f SET DEFAULT 2, +ALTER COLUMN e SET DEFAULT NULL, +ALTER COLUMN d DROP DEFAULT, +RENAME COLUMN a TO a1, +MODIFY COLUMN b BIGINT DEFAULT 2 COMMENT "col b1", +CHANGE c c1 BIGINT NOT NULL DEFAULT 2 COMMENT "col c1", ALGORITHM = COPY; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 8] +db_alter_col t id NULL NO INTEGER NULL +db_alter_col t a1 '1' YES INTEGER NULL +db_alter_col t b '2' YES BIGINT NULL +db_alter_col t c1 '2' NO BIGINT NULL +db_alter_col t d NULL YES INTEGER NULL +db_alter_col t e NULL YES INTEGER NULL +db_alter_col t f '2' YES INTEGER NULL +db_alter_col t h '2' NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a1 1 YES int col a +db_alter_col t b 2 YES bigint col b1 +db_alter_col t c1 2 NO bigint col c1 +db_alter_col t d NULL YES int col d +db_alter_col t e NULL YES int col e +db_alter_col t f 2 YES int col f +db_alter_col t h 2 NO int col h1 +db_alter_col t id NULL NO int + +# +# 9) Not supported +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +CREATE TABLE t1(id INT PRIMARY KEY, a INT INVISIBLE) ENGINE = DuckDB; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'INVISIBLE column' +ALTER TABLE t ADD COLUMN d INT INVISIBLE, ALGORITHM = COPY; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'INVISIBLE column' +ALTER TABLE t ALTER COLUMN b SET INVISIBLE, ALGORITHM = COPY; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'INVISIBLE, ALGORITHM = COPY' at line 1 +ALTER TABLE t MODIFY COLUMN c INT AUTO_INCREMENT KEY, ALGORITHM = COPY; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +ALTER TABLE t ADD COLUMN d INT AUTO_INCREMENT, ALGORITHM = COPY; +ERROR 42000: Storage engine DUCKDB doesn't support AUTO_INCREMENT columns +CREATE TABLE t1(id INT PRIMARY KEY, a INT) ENGINE = DuckDB ENGINE_ATTRIBUTE='{"KEY":"VALUE"}'; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t ADD COLUMN d INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = COPY; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +ALTER TABLE t MODIFY COLUMN c INT ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = COPY; +ERROR HY000: Unknown option 'ENGINE_ATTRIBUTE' +CREATE TABLE t1(id INT PRIMARY KEY, a INT, b INT GENERATED ALWAYS AS (a - 1) STORED) ENGINE = DuckDB; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (a - 1) STORED, ALGORITHM = COPY; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t ADD COLUMN d INT GENERATED ALWAYS AS (b + 1) VIRTUAL, ALGORITHM = COPY; +ERROR HY000: DUCKDB storage engine does not support generated columns +ALTER TABLE t MODIFY COLUMN b INT GENERATED ALWAYS AS (a+1) STORED, ALGORITHM = COPY; +ERROR HY000: DUCKDB storage engine does not support generated columns +# +# 10) Ignore column format, secondary engine attribute, storage, they can be found in DD. +# +DROP TABLE t; +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t ADD COLUMN d INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = COPY; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +ADD COLUMN e...' at line 1 +ALTER TABLE t MODIFY COLUMN b INT COLUMN_FORMAT FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c INT COLUMN_FORMAT DYNAMIC STORAGE MEMORY SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', ALGORITHM = COPY; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FIXED STORAGE DISK SECONDARY_ENGINE_ATTRIBUTE='{"KEY":"VALUE"}', +CHANGE c c I...' at line 1 +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) NOT NULL, + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 11) Drop primary key column which leads to drop primary key. +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = COPY; +ERROR 42000: This table type requires a primary key +# +# 12) BUG#117725 +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a INT, MODIFY COLUMN a INT, ALGORITHM = COPY; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, ALTER COLUMN a DROP DEFAULT, ALGORITHM = COPY; +ERROR 42S22: Unknown column 'a' in 't' +ALTER TABLE t ALTER COLUMN a SET DEFAULT 10, RENAME COLUMN a to c, ALGORITHM = COPY; +ERROR 42S22: Unknown column 'a' in 't' +DROP TABLE t; +# +# 13) DROP COLUMN IN PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b, c)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = COPY; +ERROR 42000: Key column 'a' doesn't exist in table +ALTER TABLE t DROP COLUMN c, ALGORITHM = COPY; +ERROR 42000: Key column 'c' doesn't exist in table +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int(11) NOT NULL, + `b` int(11) NOT NULL, + `c` int(11) NOT NULL, + PRIMARY KEY (`a`,`b`,`c`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 3] +db_alter_col t a NULL NO INTEGER NULL +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t c NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO int +db_alter_col t b NULL NO int +db_alter_col t c NULL NO int + +DROP TABLE t; +# +# 14) DROP COLUMN BEFORE PRIMARY KEY +# +CREATE TABLE t(a INT, b INT, c INT, d INT, PRIMARY KEY(d, b)) ENGINE = DuckDB; +ALTER TABLE t DROP COLUMN a, ALGORITHM = COPY; +ALTER TABLE t DROP COLUMN c, ALGORITHM = COPY; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `b` int(11) NOT NULL, + `d` int(11) NOT NULL, + PRIMARY KEY (`d`,`b`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t b NULL NO INTEGER NULL +db_alter_col t d NULL NO INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t b NULL NO int +db_alter_col t d NULL NO int + +DROP TABLE t; +# +# 15) CHANGE COLUMN TYPE WITH INDEX +# +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +ALTER TABLE t MODIFY COLUMN a VARCHAR(10), ALGORITHM = COPY; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` varchar(10) NOT NULL, + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'"); +run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type, COLUMN_COMMENT FROM information_schema.columns WHERE table_name = 't'") +table_schema table_name column_name column_default is_nullable data_type COLUMN_COMMENT +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db_alter_col t a NULL NO VARCHAR NULL +db_alter_col t b NULL YES INTEGER NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, COLUMN_COMMENT FROM information_schema.columns WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME COLUMN_NAME COLUMN_DEFAULT IS_NULLABLE DATA_TYPE COLUMN_COMMENT +db_alter_col t a NULL NO varchar +db_alter_col t b NULL YES int + +DROP TABLE t; +# +# 16) Cleanup +# diff --git a/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_index.result b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_index.result new file mode 100644 index 0000000000000..dc7943cd70de5 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/alter_duckdb_index.result @@ -0,0 +1,680 @@ +#################### +# TEST FOR INSTANT # +#################### +# +# 1) ADD non primary key will be ignore. +# +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD UNIQUE KEY uk_b(b) COMMENT 'unique key b', ADD UNIQUE KEY uk_cd(c, d) COMMENT 'unique key cd', ADD INDEX ke(e) COMMENT 'index e', ALGORITHM = INSTANT; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +Warning 7505 [DuckDB] Index 'ke' is removed. +CREATE UNIQUE INDEX uk_b ON t(b) COMMENT 'unique key b'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +CREATE UNIQUE INDEX uk_cd ON t(c, d) COMMENT 'unique key cd'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +CREATE INDEX ke ON t(e) COMMENT 'index e'; +Warnings: +Warning 7505 [DuckDB] Index 'ke' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + +# +# 2) Special indexes +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b VARCHAR(10), c INT, d JSON, UNIQUE INDEX uk_a((ABS(a))), INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY)))) ENGINE = DuckDB; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. + +# functional index is not supported. +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD INDEX uk_a((ABS(a))), ALGORITHM = INSTANT; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_a ON t ((ABS(a))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +ALTER TABLE t ADD UNIQUE INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))), ALGORITHM = INSTANT; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_d ON t ((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + + +# Spatial index is not supported for spatial types are not supported. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10), e POINT, f POINT) ENGINE = DuckDB; +ERROR 42000: The storage engine for the table doesn't support GEOMETRY + +# Fulltext index is ignored too. +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10)) ENGINE = DuckDB; +ALTER TABLE t ADD FULLTEXT INDEX idx_c(c) COMMENT 'index c', ALGORITHM = INSTANT; +Warnings: +Warning 7505 [DuckDB] Index 'idx_c' is removed. +CREATE FULLTEXT INDEX idx_d ON t(d) COMMENT 'index d'; +Warnings: +Warning 7505 [DuckDB] Index 'idx_d' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + + +# Foreign key is treated as normal index, which is ignored too. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT) ENGINE = DuckDB; +CREATE TABLE t2 LIKE t; +ALTER TABLE t ADD FOREIGN KEY fk(b) REFERENCES t2(b), ALGORITHM = INSTANT; +Warnings: +Warning 7505 [DuckDB] Index '(null)' is removed. +Warning 7505 [DuckDB] Index 'fk' is removed. +Warning 7509 [DuckDB] 'ADD FOREIGN KEY' operation will not take effect. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 3) Unsupported +# + +# create table without primary key is not supported. +DROP TABLE t; +CREATE TABLE t (id INT, a INT, b INT) ENGINE = DuckDB; +ERROR 42000: This table type requires a primary key + +# drop PK is not supported. +CREATE TABLE t (a INT, b VARCHAR(10), c INT, PRIMARY KEY(a)) ENGINE = DuckDB; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = INSTANT; +ERROR 42000: This table type requires a primary key +DROP INDEX `PRIMARY` ON t; +ERROR 42000: This table type requires a primary key +ALTER TABLE t ENABLE KEYS, ALGORITHM = INSTANT; +Warnings: +Note 1031 Table storage engine for 't' doesn't have this option +ALTER TABLE t DISABLE KEYS, ALGORITHM = INSTANT; +Warnings: +Note 1031 Table storage engine for 't' doesn't have this option +# +# 4) DESC, desc will be ignored by DuckDB +# +DROP TABLE t; +CREATE TABLE t(id INT, name VARCHAR(255), PRIMARY KEY(id DESC)) ENGINE = DuckDB; +ALTER TABLE t ADD INDEX idx(name DESC), ALGORITHM = INSTANT; +Warnings: +Warning 7505 [DuckDB] Index 'idx' is removed. +INSERT INTO t VALUES(1, "1"); +INSERT INTO t VALUES(2, "2"); +SELECT * FROM t; +id name +1 1 +2 2 +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 5) DROP PRIMARY KEY +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +SET GLOBAL duckdb_require_primary_key = OFF; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = INSTANT; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int NOT NULL, + `b` int DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME + +SET GLOBAL duckdb_require_primary_key = default; +# +# 6) Cleanup +# +DROP TABLE t, t2; +#################### +# TEST FOR INPLACE # +#################### +# +# 1) ADD non primary key will be ignore. +# +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD UNIQUE KEY uk_b(b) COMMENT 'unique key b', ADD UNIQUE KEY uk_cd(c, d) COMMENT 'unique key cd', ADD INDEX ke(e) COMMENT 'index e', ALGORITHM = INPLACE; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +Warning 7505 [DuckDB] Index 'ke' is removed. +CREATE UNIQUE INDEX uk_b ON t(b) COMMENT 'unique key b'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +CREATE UNIQUE INDEX uk_cd ON t(c, d) COMMENT 'unique key cd'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +CREATE INDEX ke ON t(e) COMMENT 'index e'; +Warnings: +Warning 7505 [DuckDB] Index 'ke' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + +# +# 2) Special indexes +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b VARCHAR(10), c INT, d JSON, UNIQUE INDEX uk_a((ABS(a))), INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY)))) ENGINE = DuckDB; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. + +# functional index is not supported. +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD INDEX uk_a((ABS(a))), ALGORITHM = INPLACE; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_a ON t ((ABS(a))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +ALTER TABLE t ADD UNIQUE INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))), ALGORITHM = INPLACE; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_d ON t ((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + + +# Spatial index is not supported for spatial types are not supported. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10), e POINT, f POINT) ENGINE = DuckDB; +ERROR 42000: The storage engine for the table doesn't support GEOMETRY + +# Fulltext index is ignored too. +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10)) ENGINE = DuckDB; +ALTER TABLE t ADD FULLTEXT INDEX idx_c(c) COMMENT 'index c', ALGORITHM = INPLACE; +Warnings: +Warning 7505 [DuckDB] Index 'idx_c' is removed. +CREATE FULLTEXT INDEX idx_d ON t(d) COMMENT 'index d'; +Warnings: +Warning 7505 [DuckDB] Index 'idx_d' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + + +# Foreign key is treated as normal index, which is ignored too. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT) ENGINE = DuckDB; +CREATE TABLE t2 LIKE t; +ALTER TABLE t ADD FOREIGN KEY fk(b) REFERENCES t2(b), ALGORITHM = INPLACE; +Warnings: +Warning 7505 [DuckDB] Index '(null)' is removed. +Warning 7505 [DuckDB] Index 'fk' is removed. +Warning 7509 [DuckDB] 'ADD FOREIGN KEY' operation will not take effect. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 3) Unsupported +# + +# create table without primary key is not supported. +DROP TABLE t; +CREATE TABLE t (id INT, a INT, b INT) ENGINE = DuckDB; +ERROR 42000: This table type requires a primary key + +# drop PK is not supported. +CREATE TABLE t (a INT, b VARCHAR(10), c INT, PRIMARY KEY(a)) ENGINE = DuckDB; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = INPLACE; +ERROR 42000: This table type requires a primary key +DROP INDEX `PRIMARY` ON t; +ERROR 42000: This table type requires a primary key +ALTER TABLE t ENABLE KEYS, ALGORITHM = INPLACE; +Warnings: +Note 1031 Table storage engine for 't' doesn't have this option +ALTER TABLE t DISABLE KEYS, ALGORITHM = INPLACE; +Warnings: +Note 1031 Table storage engine for 't' doesn't have this option +# +# 4) DESC, desc will be ignored by DuckDB +# +DROP TABLE t; +CREATE TABLE t(id INT, name VARCHAR(255), PRIMARY KEY(id DESC)) ENGINE = DuckDB; +ALTER TABLE t ADD INDEX idx(name DESC), ALGORITHM = INPLACE; +Warnings: +Warning 7505 [DuckDB] Index 'idx' is removed. +INSERT INTO t VALUES(1, "1"); +INSERT INTO t VALUES(2, "2"); +SELECT * FROM t; +id name +1 1 +2 2 +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 5) DROP PRIMARY KEY +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +SET GLOBAL duckdb_require_primary_key = OFF; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = INPLACE; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int NOT NULL, + `b` int DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME + +SET GLOBAL duckdb_require_primary_key = default; +# +# 6) Cleanup +# +DROP TABLE t, t2; +################# +# TEST FOR COPY # +################# +# +# 1) ADD non primary key will be ignore. +# +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD UNIQUE KEY uk_b(b) COMMENT 'unique key b', ADD UNIQUE KEY uk_cd(c, d) COMMENT 'unique key cd', ADD INDEX ke(e) COMMENT 'index e', ALGORITHM = COPY; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +Warning 7505 [DuckDB] Index 'ke' is removed. +CREATE UNIQUE INDEX uk_b ON t(b) COMMENT 'unique key b'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_b' is removed. +CREATE UNIQUE INDEX uk_cd ON t(c, d) COMMENT 'unique key cd'; +Warnings: +Warning 7505 [DuckDB] Index 'uk_cd' is removed. +CREATE INDEX ke ON t(e) COMMENT 'index e'; +Warnings: +Warning 7505 [DuckDB] Index 'ke' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + +# +# 2) Special indexes +# +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b VARCHAR(10), c INT, d JSON, UNIQUE INDEX uk_a((ABS(a))), INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY)))) ENGINE = DuckDB; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. + +# functional index is not supported. +CREATE TABLE t(a INT, b INT, c INT, d INT, e INT, PRIMARY KEY(a) COMMENT 'primary key') ENGINE = DuckDB; +ALTER TABLE t ADD INDEX uk_a((ABS(a))), ALGORITHM = COPY; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_a ON t ((ABS(a))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +ALTER TABLE t ADD UNIQUE INDEX uk_d((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))), ALGORITHM = COPY; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CREATE UNIQUE INDEX uk_d ON t ((CAST(CAST(d AS JSON) AS UNSIGNED ARRAY))); +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: functional index is not supported. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY primary key 0 a + + +# Spatial index is not supported for spatial types are not supported. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10), e POINT, f POINT) ENGINE = DuckDB; +ERROR 42000: The storage engine for the table doesn't support GEOMETRY + +# Fulltext index is ignored too. +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT, c VARCHAR(10), d VARCHAR(10)) ENGINE = DuckDB; +ALTER TABLE t ADD FULLTEXT INDEX idx_c(c) COMMENT 'index c', ALGORITHM = COPY; +Warnings: +Warning 7505 [DuckDB] Index 'idx_c' is removed. +CREATE FULLTEXT INDEX idx_d ON t(d) COMMENT 'index d'; +Warnings: +Warning 7505 [DuckDB] Index 'idx_d' is removed. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + + +# Foreign key is treated as normal index, which is ignored too. +DROP TABLE t; +CREATE TABLE t (id INT PRIMARY KEY, a INT, b INT) ENGINE = DuckDB; +CREATE TABLE t2 LIKE t; +ALTER TABLE t ADD FOREIGN KEY fk(b) REFERENCES t2(b), ALGORITHM = COPY; +Warnings: +Warning 7505 [DuckDB] Index '(null)' is removed. +Warning 7505 [DuckDB] Index 'fk' is removed. +Warning 7509 [DuckDB] 'ADD FOREIGN KEY' operation will not take effect. +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 3) Unsupported +# + +# create table without primary key is not supported. +DROP TABLE t; +CREATE TABLE t (id INT, a INT, b INT) ENGINE = DuckDB; +ERROR 42000: This table type requires a primary key + +# drop PK is not supported. +CREATE TABLE t (a INT, b VARCHAR(10), c INT, PRIMARY KEY(a)) ENGINE = DuckDB; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = COPY; +ERROR 42000: This table type requires a primary key +DROP INDEX `PRIMARY` ON t; +ERROR 42000: This table type requires a primary key +ALTER TABLE t ENABLE KEYS, ALGORITHM = COPY; +ALTER TABLE t DISABLE KEYS, ALGORITHM = COPY; +# +# 4) DESC, desc will be ignored by DuckDB +# +DROP TABLE t; +CREATE TABLE t(id INT, name VARCHAR(255), PRIMARY KEY(id DESC)) ENGINE = DuckDB; +ALTER TABLE t ADD INDEX idx(name DESC), ALGORITHM = COPY; +Warnings: +Warning 7505 [DuckDB] Index 'idx' is removed. +INSERT INTO t VALUES(1, "1"); +INSERT INTO t VALUES(2, "2"); +SELECT * FROM t; +id name +1 1 +2 2 +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME +db_alter_idx t PRIMARY 0 id + +# +# 5) DROP PRIMARY KEY +# +DROP TABLE t; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +SET GLOBAL duckdb_require_primary_key = OFF; +ALTER TABLE t DROP PRIMARY KEY, ALGORITHM = COPY; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` int NOT NULL, + `b` int DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL dbms_duckdb.query("SELECT schema_name, table_name, index_name, comment, is_unique, is_primary, expressions FROM duckdb_indexes WHERE table_name = 't'"); +RESULT +schema_name table_name index_name comment is_unique is_primary expressions +VARCHAR VARCHAR VARCHAR VARCHAR BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, INDEX_COMMENT, NON_UNIQUE, COLUMN_NAME FROM information_schema.statistics WHERE TABLE_NAME = 't'; +TABLE_SCHEMA TABLE_NAME INDEX_NAME INDEX_COMMENT NON_UNIQUE COLUMN_NAME + +SET GLOBAL duckdb_require_primary_key = default; +# +# 6) Cleanup +# +DROP TABLE t, t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/alter_engine_duckdb.result b/storage/duckdb/mysql-test/duckdb/r/alter_engine_duckdb.result new file mode 100644 index 0000000000000..ad338262547e4 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/alter_engine_duckdb.result @@ -0,0 +1,274 @@ +SET GLOBAL duckdb_copy_ddl_in_batch=ON; +# +# 1. Test Alter Table Engine = duckdb for all data types +# +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int, +col2 bigint, +col3 float, +col4 double, +col5 decimal(10,4), +col6 tinyint, +col7 smallint, +col8 mediumint +) ENGINE = innodb; +insert into t1 values (1, 1, -1, 1.01, -1.001, 1.0001, 1, 1, 1), +(2, -2, 2, -2.02, 2.002, -2.0002, 2, 2, 2), +(-3, 3, 3, 3.03, 3.003, 3.0003, 3, 3, 3), +(-2147483648, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -999999.9999, -128, -32768, -8388608), +(2147483647, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, 127, 32767, 8388607); +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 +-2147483648 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -999999.9999 -128 -32768 -8388608 +-3 3 3 3.03 3.003 3.0003 3 3 3 +1 1 -1 1.01 -1.001 1.0001 1 1 1 +2 -2 2 -2.02 2.002 -2.0002 2 2 2 +2147483647 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 999999.9999 127 32767 8388607 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 +-2147483648 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -999999.9999 -128 -32768 -8388608 +-3 3 3 3.03 3.003 3.0003 3 3 3 +1 1 -1 1.01 -1.001 1.0001 1 1 1 +2 -2 2 -2.02 2.002 -2.0002 2 2 2 +2147483647 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 999999.9999 127 32767 8388607 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int unsigned, +col2 bigint unsigned, +col3 float unsigned, +col4 double unsigned, +col5 decimal(10,4) unsigned, +col6 bit(10), +col7 tinyint unsigned, +col8 smallint unsigned, +col9 mediumint unsigned +) ENGINE = innodb; +Warnings: +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +insert into t1 values (1, 1, 1, 1.01, 1.001, 1.0001, B'111111111', 1, 1, 1), +(2, 2, 2, 2.02, 2.002, 2.0002, B'0000000000', 1, 1, 1), +(3, 3, 3, 3.03, 3.003, 3.0003, B'0001100110', 1, 1, 1), +(-2147483648, 0, 0, 0, 0, 0, B'00000000000', 0, 0, 0), +(2147483647, 255, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, B'1111111111', 255, 65535, 8388607); +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; +id col1 col2 col3 col4 col5 hex(col6) hex(col7) hex(col8) hex(col9) +-2147483648 0 0 0 0 0.0000 0 0 0 0 +1 1 1 1.01 1.001 1.0001 1FF 1 1 1 +2 2 2 2.02 2.002 2.0002 0 1 1 1 +2147483647 255 18446744073709551615 3.40282e38 1.7976931348623157e308 999999.9999 3FF FF FFFF 7FFFFF +3 3 3 3.03 3.003 3.0003 66 1 1 1 +ALTER TABLE t1 ENGINE = duckdb; +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; +id col1 col2 col3 col4 col5 hex(col6) hex(col7) hex(col8) hex(col9) +-2147483648 0 0 0 0 0.0000 0000 0 0 0 +1 1 1 1.01 1.001 1.0001 01FF 1 1 1 +2 2 2 2.02 2.002 2.0002 0000 1 1 1 +2147483647 255 18446744073709551615 3.40282e38 1.7976931348623157e308 999999.9999 03FF FF FFFF 7FFFFF +3 3 3 3.03 3.003 3.0003 0066 1 1 1 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 decimal(65, 30), +col2 decimal(65, 15), +col3 decimal(65, 0), +col4 decimal(38,30), +col5 decimal(38,18), +col6 decimal(38,0), +col7 decimal(9,9), +col8 decimal(9,4), +col9 decimal(9,0) +) ENGINE = innodb; +insert into t1 values (1, +99999999999999999999999999999999999.999999999999999999999999999999, +99999999999999999999999999999999999999999999999999.999999999999999, +99999999999999999999999999999999999999999999999999999999999999999, +99999999.999999999999999999999999999999, +99999999999999999999.999999999999999999, +99999999999999999999999999999999999999, +0.99999999, +99999.9999, +999999999); +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 +1 99999999999999999999999999999999999.999999999999999999999999999999 99999999999999999999999999999999999999999999999999.999999999999999 99999999999999999999999999999999999999999999999999999999999999999 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 0.999999990 99999.9999 999999999 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 +1 1e35 1e50 1e65 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 .999999990 99999.9999 999999999 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 date, +col2 datetime, +col3 timestamp, +col4 datetime(6), +col5 time, +col6 time(6), +col7 year +) ENGINE = innodb; +insert into t1 values (1, '2020-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1', '12:00:00', '12:00:00.1', 2020), +(2, '2020-12-31', '2020-12-31 00:00:00', '2020-12-31 00:00:00', '2020-12-31 00:00:00.123456789', '00:00:00', '00:00:00.123456789', 1970), +(3, '1970-01-01', '1970-01-01 23:59:59', '1970-01-01 23:59:59', '2020-12-31 23:59:59.123456', '23:59:59', '23:59:59.123456', 2050), +(4, '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '23:59:59.123456', '23:59:59.123456', 2100); +Warnings: +Note 1292 Incorrect date value: '2000-12-31 23:59:59.123456' for column 'col1' at row 4 +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 +1 2020-01-01 2020-01-01 12:00:00 2020-01-01 12:00:00 2020-01-01 12:00:00.100000 12:00:00 12:00:00.100000 2020 +2 2020-12-31 2020-12-31 00:00:00 2020-12-31 00:00:00 2020-12-31 00:00:00.123457 00:00:00 00:00:00.123457 1970 +3 1970-01-01 1970-01-01 23:59:59 1970-01-01 23:59:59 2020-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2050 +4 2000-12-31 2000-12-31 23:59:59 2000-12-31 23:59:59 2000-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2100 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 +1 2020-01-01 2020-01-01 12:00:00 2020-01-01 12:00:00 2020-01-01 12:00:00.100000 12:00:00 12:00:00.100000 2020 +2 2020-12-31 2020-12-31 00:00:00 2020-12-31 00:00:00 2020-12-31 00:00:00.123457 00:00:00 00:00:00.123457 1970 +3 1970-01-01 1970-01-01 23:59:59 1970-01-01 23:59:59 2020-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2050 +4 2000-12-31 2000-12-31 23:59:59 2000-12-31 23:59:59 2000-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2100 +DROP TABLE t1; +SET GLOBAL duckdb_copy_ddl_in_batch=OFF; +# +# 1. Test Alter Table Engine = duckdb for all data types +# +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int, +col2 bigint, +col3 float, +col4 double, +col5 decimal(10,4), +col6 tinyint, +col7 smallint, +col8 mediumint +) ENGINE = innodb; +insert into t1 values (1, 1, -1, 1.01, -1.001, 1.0001, 1, 1, 1), +(2, -2, 2, -2.02, 2.002, -2.0002, 2, 2, 2), +(-3, 3, 3, 3.03, 3.003, 3.0003, 3, 3, 3), +(-2147483648, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -999999.9999, -128, -32768, -8388608), +(2147483647, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, 127, 32767, 8388607); +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 +-2147483648 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -999999.9999 -128 -32768 -8388608 +-3 3 3 3.03 3.003 3.0003 3 3 3 +1 1 -1 1.01 -1.001 1.0001 1 1 1 +2 -2 2 -2.02 2.002 -2.0002 2 2 2 +2147483647 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 999999.9999 127 32767 8388607 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 +-2147483648 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -999999.9999 -128 -32768 -8388608 +-3 3 3 3.03 3.003 3.0003 3 3 3 +1 1 -1 1.01 -1.001 1.0001 1 1 1 +2 -2 2 -2.02 2.002 -2.0002 2 2 2 +2147483647 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 999999.9999 127 32767 8388607 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int unsigned, +col2 bigint unsigned, +col3 float unsigned, +col4 double unsigned, +col5 decimal(10,4) unsigned, +col6 bit(10), +col7 tinyint unsigned, +col8 smallint unsigned, +col9 mediumint unsigned +) ENGINE = innodb; +Warnings: +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +Warning 1681 UNSIGNED for decimal and floating point data types is deprecated and support for it will be removed in a future release. +insert into t1 values (1, 1, 1, 1.01, 1.001, 1.0001, B'111111111', 1, 1, 1), +(2, 2, 2, 2.02, 2.002, 2.0002, B'0000000000', 1, 1, 1), +(3, 3, 3, 3.03, 3.003, 3.0003, B'0001100110', 1, 1, 1), +(-2147483648, 0, 0, 0, 0, 0, B'00000000000', 0, 0, 0), +(2147483647, 255, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, B'1111111111', 255, 65535, 8388607); +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; +id col1 col2 col3 col4 col5 hex(col6) hex(col7) hex(col8) hex(col9) +-2147483648 0 0 0 0 0.0000 0 0 0 0 +1 1 1 1.01 1.001 1.0001 1FF 1 1 1 +2 2 2 2.02 2.002 2.0002 0 1 1 1 +2147483647 255 18446744073709551615 3.40282e38 1.7976931348623157e308 999999.9999 3FF FF FFFF 7FFFFF +3 3 3 3.03 3.003 3.0003 66 1 1 1 +ALTER TABLE t1 ENGINE = duckdb; +SELECT id,col1,col2,col3,col4,col5,hex(col6),hex(col7),hex(col8),hex(col9) FROM t1; +id col1 col2 col3 col4 col5 hex(col6) hex(col7) hex(col8) hex(col9) +-2147483648 0 0 0 0 0.0000 0000 0 0 0 +1 1 1 1.01 1.001 1.0001 01FF 1 1 1 +2 2 2 2.02 2.002 2.0002 0000 1 1 1 +2147483647 255 18446744073709551615 3.40282e38 1.7976931348623157e308 999999.9999 03FF FF FFFF 7FFFFF +3 3 3 3.03 3.003 3.0003 0066 1 1 1 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 decimal(65, 30), +col2 decimal(65, 15), +col3 decimal(65, 0), +col4 decimal(38,30), +col5 decimal(38,18), +col6 decimal(38,0), +col7 decimal(9,9), +col8 decimal(9,4), +col9 decimal(9,0) +) ENGINE = innodb; +insert into t1 values (1, +99999999999999999999999999999999999.999999999999999999999999999999, +99999999999999999999999999999999999999999999999999.999999999999999, +99999999999999999999999999999999999999999999999999999999999999999, +99999999.999999999999999999999999999999, +99999999999999999999.999999999999999999, +99999999999999999999999999999999999999, +0.99999999, +99999.9999, +999999999); +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 +1 99999999999999999999999999999999999.999999999999999999999999999999 99999999999999999999999999999999999999999999999999.999999999999999 99999999999999999999999999999999999999999999999999999999999999999 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 0.999999990 99999.9999 999999999 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 +1 1e35 1e50 1e65 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 .999999990 99999.9999 999999999 +DROP TABLE t1; +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 date, +col2 datetime, +col3 timestamp, +col4 datetime(6), +col5 time, +col6 time(6), +col7 year +) ENGINE = innodb; +insert into t1 values (1, '2020-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1', '12:00:00', '12:00:00.1', 2020), +(2, '2020-12-31', '2020-12-31 00:00:00', '2020-12-31 00:00:00', '2020-12-31 00:00:00.123456789', '00:00:00', '00:00:00.123456789', 1970), +(3, '1970-01-01', '1970-01-01 23:59:59', '1970-01-01 23:59:59', '2020-12-31 23:59:59.123456', '23:59:59', '23:59:59.123456', 2050), +(4, '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '23:59:59.123456', '23:59:59.123456', 2100); +Warnings: +Note 1292 Incorrect date value: '2000-12-31 23:59:59.123456' for column 'col1' at row 4 +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 +1 2020-01-01 2020-01-01 12:00:00 2020-01-01 12:00:00 2020-01-01 12:00:00.100000 12:00:00 12:00:00.100000 2020 +2 2020-12-31 2020-12-31 00:00:00 2020-12-31 00:00:00 2020-12-31 00:00:00.123457 00:00:00 00:00:00.123457 1970 +3 1970-01-01 1970-01-01 23:59:59 1970-01-01 23:59:59 2020-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2050 +4 2000-12-31 2000-12-31 23:59:59 2000-12-31 23:59:59 2000-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2100 +ALTER TABLE t1 ENGINE = duckdb; +SELECT * FROM t1; +id col1 col2 col3 col4 col5 col6 col7 +1 2020-01-01 2020-01-01 12:00:00 2020-01-01 12:00:00 2020-01-01 12:00:00.100000 12:00:00 12:00:00.100000 2020 +2 2020-12-31 2020-12-31 00:00:00 2020-12-31 00:00:00 2020-12-31 00:00:00.123457 00:00:00 00:00:00.123457 1970 +3 1970-01-01 1970-01-01 23:59:59 1970-01-01 23:59:59 2020-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2050 +4 2000-12-31 2000-12-31 23:59:59 2000-12-31 23:59:59 2000-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2100 +DROP TABLE t1; +CREATE TEMPORARY TABLE t1(id INT PRIMARY KEY) ENGINE = InnoDB; +ALTER TABLE t1 ENGINE = DuckDB; +ERROR HY000: Table storage engine 'DUCKDB' does not support the create option 'TEMPORARY' +SET SESSION sql_mode = ''; +CREATE TEMPORARY TABLE t2(id INT PRIMARY KEY) ENGINE = DuckDB; +ALTER TABLE t2 ENGINE = DuckDB; +ERROR HY000: Table storage engine 'DUCKDB' does not support the create option 'TEMPORARY' +SET GLOBAL duckdb_copy_ddl_in_batch=default; diff --git a/storage/duckdb/mysql-test/duckdb/r/bugfix_crash_after_commit_error.result b/storage/duckdb/mysql-test/duckdb/r/bugfix_crash_after_commit_error.result new file mode 100644 index 0000000000000..9cdc929806086 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/bugfix_crash_after_commit_error.result @@ -0,0 +1,17 @@ +create table t1(id int primary key) engine = duckdb; +set duckdb_dml_in_batch = on; +begin; +insert into t1 values (1); +insert into t1 values (1); +commit; +ERROR HY000: [DuckDB] DuckDB commit transaction error. PRIMARY KEY or UNIQUE constraint violation: duplicate key "1" +drop table t1; +# restart: --skip-log-bin +create table t1(id int primary key) engine = duckdb; +set duckdb_dml_in_batch = on; +begin; +insert into t1 values (1); +insert into t1 values (1); +commit; +ERROR HY000: [DuckDB] DuckDB commit transaction error. PRIMARY KEY or UNIQUE constraint violation: duplicate key "1" +drop table t1; diff --git a/storage/duckdb/mysql-test/duckdb/r/bugfix_temp_and_system_database.result b/storage/duckdb/mysql-test/duckdb/r/bugfix_temp_and_system_database.result new file mode 100644 index 0000000000000..65b55fe8056d6 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/bugfix_temp_and_system_database.result @@ -0,0 +1,100 @@ +# +# TEST FOR `temp` and `system` +# +CALL dbms_duckdb.query("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); +RESULT +catalog_name schema_name schema_owner default_character_set_catalog default_character_set_schema default_character_set_name sql_path +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +system information_schema duckdb NULL NULL NULL NULL +system main duckdb NULL NULL NULL NULL +system pg_catalog duckdb NULL NULL NULL NULL +temp main duckdb NULL NULL NULL NULL + + +CREATE DATABASE `temp`; +USE `temp`; +CREATE TABLE t1(a INT KEY) ENGINE = DuckDB; +CALL dbms_duckdb.query("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); +RESULT +catalog_name schema_name schema_owner default_character_set_catalog default_character_set_schema default_character_set_name sql_path +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 5] +duckdb temp duckdb NULL NULL NULL NULL +system information_schema duckdb NULL NULL NULL NULL +system main duckdb NULL NULL NULL NULL +system pg_catalog duckdb NULL NULL NULL NULL +temp main duckdb NULL NULL NULL NULL + + +RENAME TABLE t1 TO t2; +ALTER TABLE t2 RENAME TO t1; +ALTER TABLE t1 ADD COLUMN b INT; +TRUNCATE TABLE t1; +USE `test`; +SET GLOBAL duckdb_dml_in_batch = ON; +INSERT INTO temp.t1 VALUES (1, 1), (2, 1), (3, 1); +SET GLOBAL duckdb_dml_in_batch = OFF; +INSERT INTO temp.t1 VALUES (4, 1); +ERROR HY000: [DuckDB] Execute sql failed. Binder Error: Ambiguous reference to catalog or schema "temp" - use a fully qualified path like "duckdb.temp" +SELECT * FROM temp.t1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "temp" - use a fully qualified path like "duckdb.temp". +UPDATE temp.t1 SET b = 1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "temp" - use a fully qualified path like "duckdb.temp". +DELETE FROM temp.t1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "temp" - use a fully qualified path like "duckdb.temp". +USE `temp`; +DROP TABLE t1; +CREATE DATABASE `system`; +USE `system`; +CREATE TABLE t1(a INT KEY) ENGINE = DuckDB; +CALL dbms_duckdb.query("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); +RESULT +catalog_name schema_name schema_owner default_character_set_catalog default_character_set_schema default_character_set_name sql_path +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 6] +duckdb system duckdb NULL NULL NULL NULL +duckdb temp duckdb NULL NULL NULL NULL +system information_schema duckdb NULL NULL NULL NULL +system main duckdb NULL NULL NULL NULL +system pg_catalog duckdb NULL NULL NULL NULL +temp main duckdb NULL NULL NULL NULL + + +RENAME TABLE t1 TO t2; +ALTER TABLE t2 RENAME TO t1; +ALTER TABLE t1 ADD COLUMN b INT; +TRUNCATE TABLE t1; +USE `test`; +SET GLOBAL duckdb_dml_in_batch = ON; +INSERT INTO system.t1 VALUES (1, 1), (2, 1), (3, 1); +SET GLOBAL duckdb_dml_in_batch = OFF; +INSERT INTO system.t1 VALUES (4, 1); +ERROR HY000: [DuckDB] Execute sql failed. Binder Error: Ambiguous reference to catalog or schema "system" - use a fully qualified path like "duckdb.system" +SELECT * FROM system.t1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "system" - use a fully qualified path like "duckdb.system". +UPDATE system.t1 SET b = 1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "system" - use a fully qualified path like "duckdb.system". +DELETE FROM system.t1; +ERROR HY000: [DuckDB] Binder Error: Ambiguous reference to catalog or schema "system" - use a fully qualified path like "duckdb.system". +USE `system`; +DROP TABLE t1; +CREATE DATABASE `d``b`; +ERROR HY000: [DuckDB] Incorrect database name d`b for DuckDB.. +# +# CLEANUP +# +SET GLOBAL duckdb_dml_in_batch = default; +DROP DATABASE `temp`; +DROP DATABASE `system`; +CALL dbms_duckdb.query("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); +RESULT +catalog_name schema_name schema_owner default_character_set_catalog default_character_set_schema default_character_set_name sql_path +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +system information_schema duckdb NULL NULL NULL NULL +system main duckdb NULL NULL NULL NULL +system pg_catalog duckdb NULL NULL NULL NULL +temp main duckdb NULL NULL NULL NULL + + diff --git a/storage/duckdb/mysql-test/duckdb/r/charset_and_collation.result b/storage/duckdb/mysql-test/duckdb/r/charset_and_collation.result new file mode 100644 index 0000000000000..d68d56c90820c --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/charset_and_collation.result @@ -0,0 +1,681 @@ +# +# check collation config when execute +# +create database if not exists charset_and_collation; +use charset_and_collation; +CREATE TABLE t_duckdb (a varchar(32) PRIMARY KEY, b varchar(32), c varchar(32)) engine=duckdb; +CREATE TABLE t_innodb (a varchar(32) PRIMARY KEY, b varchar(32), c varchar(32)) engine=innodb; +INSERT INTO t_duckdb VALUES ('a', 'A', 'á'); +INSERT INTO t_innodb VALUES ('a', 'A', 'á'); +# 1.1 Test for utf8mb4 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_ai_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 1 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 1 0 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_520_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_uca1400_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_uca1400_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +# 1.2 Test for utf8mb3 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_general_mysql500_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_uca1400_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 1 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 1 0 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_unicode_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_unicode_520_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_swedish_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8' COLLATE 'utf8_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +1 1 1 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +1 1 1 +SET NAMES 'utf8' COLLATE 'utf8_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 0 0 +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_uca1400_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 1 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 1 0 +# 1.3 Test for latin1 +SET NAMES 'latin1' COLLATE 'latin1_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +ai ci ai_ci +0 0 0 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; +ai ci ai_ci +0 1 0 +SET NAMES 'utf8mb4'; +DROP TABLE t_duckdb, t_innodb; +# +# Test 2. SELECT col = 'a' +# +# 2.1 define collation for table with utf8mb4 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb4_general_ci, c varchar(32) COLLATE utf8mb4_0900_as_cs, d varchar(32) COLLATE utf8mb4_0900_as_ci) engine=duckdb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb4_general_ci, c varchar(32) COLLATE utf8mb4_0900_as_cs, d varchar(32) COLLATE utf8mb4_0900_as_ci) engine=innodb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +SELECT a FROM t_innodb where a = 'A'; +a +SELECT a FROM t_duckdb where a = 'á'; +a +SELECT a FROM t_innodb where a = 'á'; +a +SELECT b FROM t_duckdb where b = 'a'; +b +a +SELECT b FROM t_innodb where b = 'a'; +b +a +SELECT b FROM t_duckdb where b = 'A'; +b +a +SELECT b FROM t_innodb where b = 'A'; +b +a +SELECT b FROM t_duckdb where b = 'á'; +b +a +SELECT b FROM t_innodb where b = 'á'; +b +a +SELECT c FROM t_duckdb where c = 'a'; +c +a +SELECT c FROM t_innodb where c = 'a'; +c +a +SELECT c FROM t_duckdb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'A'; +c +SELECT c FROM t_duckdb where c = 'á'; +c +SELECT c FROM t_innodb where c = 'á'; +c +SELECT d FROM t_duckdb where d = 'a'; +d +a +SELECT d FROM t_innodb where d = 'a'; +d +a +SELECT d FROM t_duckdb where d = 'A'; +d +a +SELECT d FROM t_innodb where d = 'A'; +d +a +SELECT d FROM t_duckdb where d = 'á'; +d +SELECT d FROM t_innodb where d = 'á'; +d +DROP TABLE t_duckdb, t_innodb; +# 2.2 define collation for table with utf8mb3 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb3_general_mysql500_ci, c varchar(32) COLLATE utf8mb3_bin, d varchar(32) COLLATE utf8mb3_unicode_ci, e varchar(32) COLLATE utf8mb3_general_ci) engine=duckdb CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb3_general_mysql500_ci, c varchar(32) COLLATE utf8mb3_bin, d varchar(32) COLLATE utf8mb3_unicode_ci, e varchar(32) COLLATE utf8mb3_general_ci) engine=innodb CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT a FROM t_duckdb where a = 'á'; +a +a +SELECT a FROM t_innodb where a = 'á'; +a +a +SELECT b FROM t_duckdb where b = 'a'; +b +a +SELECT b FROM t_innodb where b = 'a'; +b +a +SELECT b FROM t_duckdb where b = 'A'; +b +a +SELECT b FROM t_innodb where b = 'A'; +b +a +SELECT b FROM t_duckdb where b = 'á'; +b +a +SELECT b FROM t_innodb where b = 'á'; +b +a +SELECT c FROM t_duckdb where c = 'a'; +c +a +SELECT c FROM t_innodb where c = 'a'; +c +a +SELECT c FROM t_duckdb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'A'; +c +SELECT c FROM t_duckdb where c = 'á'; +c +SELECT c FROM t_innodb where c = 'á'; +c +SELECT d FROM t_duckdb where d = 'a'; +d +a +SELECT d FROM t_innodb where d = 'a'; +d +a +SELECT d FROM t_duckdb where d = 'A'; +d +a +SELECT d FROM t_innodb where d = 'A'; +d +a +SELECT d FROM t_duckdb where d = 'á'; +d +a +SELECT d FROM t_innodb where d = 'á'; +d +a +SELECT e FROM t_duckdb where e = 'a'; +e +a +SELECT e FROM t_innodb where e = 'a'; +e +a +SELECT e FROM t_duckdb where e = 'A'; +e +a +SELECT e FROM t_innodb where e = 'A'; +e +a +SELECT e FROM t_duckdb where e = 'á'; +e +a +SELECT e FROM t_innodb where e = 'á'; +e +a +DROP TABLE t_duckdb, t_innodb; +# +# Test 3. ALTER COLUMN COLLATION +# +# 3.1 define collation for table with utf8mb4 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8mb4_general_ci) engine=duckdb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8mb4_general_ci) engine=innodb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT a FROM t_duckdb where a = 'á'; +a +a +SELECT a FROM t_innodb where a = 'á'; +a +a +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb") +Count +BIGINT +[ Rows: 1] +1 + + +DELETE FROM t_innodb; +ALTER TABLE t_duckdb MODIFY COLUMN a varchar(32), +ADD COLUMN b varchar(32) COLLATE utf8mb4_general_ci, +ADD COLUMN c varchar(32) COLLATE utf8mb4_0900_as_cs, +ADD COLUMN d varchar(32) COLLATE utf8mb4_0900_as_ci; +ALTER TABLE t_innodb MODIFY COLUMN a varchar(32), +ADD COLUMN b varchar(32) COLLATE utf8mb4_general_ci, +ADD COLUMN c varchar(32) COLLATE utf8mb4_0900_as_cs, +ADD COLUMN d varchar(32) COLLATE utf8mb4_0900_as_ci; +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +SELECT a FROM t_duckdb where a = 'á'; +a +a +SELECT a FROM t_innodb where a = 'á'; +a +SELECT b FROM t_duckdb where b = 'a'; +b +a +SELECT b FROM t_innodb where b = 'a'; +b +a +SELECT b FROM t_duckdb where b = 'A'; +b +a +SELECT b FROM t_innodb where b = 'A'; +b +a +SELECT b FROM t_duckdb where b = 'á'; +b +a +SELECT b FROM t_innodb where b = 'á'; +b +a +SELECT c FROM t_duckdb where c = 'a'; +c +a +SELECT c FROM t_innodb where c = 'a'; +c +a +SELECT c FROM t_duckdb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'A'; +c +SELECT c FROM t_duckdb where c = 'á'; +c +SELECT c FROM t_innodb where c = 'á'; +c +SELECT d FROM t_duckdb where d = 'a'; +d +a +SELECT d FROM t_innodb where d = 'a'; +d +a +SELECT d FROM t_duckdb where d = 'A'; +d +a +SELECT d FROM t_innodb where d = 'A'; +d +a +SELECT d FROM t_duckdb where d = 'á'; +d +SELECT d FROM t_innodb where d = 'á'; +d +DROP TABLE t_duckdb, t_innodb; +# 3.2 define collation for table with utf8mb3 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8_general_ci) engine=duckdb CHARSET=utf8mb3 COLLATE=utf8_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8_general_ci) engine=innodb CHARSET=utf8mb3 COLLATE=utf8_general_ci; +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT a FROM t_duckdb where a = 'á'; +a +a +SELECT a FROM t_innodb where a = 'á'; +a +a +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb") +Count +BIGINT +[ Rows: 1] +1 + + +DELETE FROM t_innodb; +ALTER TABLE t_duckdb MODIFY a varchar(32), +ADD COLUMN b varchar(32) COLLATE utf8mb3_general_mysql500_ci, +ADD COLUMN c varchar(32) COLLATE utf8mb3_bin, +ADD COLUMN d varchar(32) COLLATE utf8mb3_unicode_ci, +ADD COLUMN e varchar(32) COLLATE utf8mb3_uca1400_as_ci; +ALTER TABLE t_innodb MODIFY a varchar(32), +ADD COLUMN b varchar(32) COLLATE utf8mb3_general_mysql500_ci, +ADD COLUMN c varchar(32) COLLATE utf8mb3_bin, +ADD COLUMN d varchar(32) COLLATE utf8mb3_unicode_ci, +ADD COLUMN e varchar(32) COLLATE utf8mb3_uca1400_as_ci; +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT a FROM t_duckdb where a = 'á'; +a +a +SELECT a FROM t_innodb where a = 'á'; +a +a +SELECT b FROM t_duckdb where b = 'a'; +b +a +SELECT b FROM t_innodb where b = 'a'; +b +a +SELECT b FROM t_duckdb where b = 'A'; +b +a +SELECT b FROM t_innodb where b = 'A'; +b +a +SELECT b FROM t_duckdb where b = 'á'; +b +a +SELECT b FROM t_innodb where b = 'á'; +b +a +SELECT c FROM t_duckdb where c = 'a'; +c +a +SELECT c FROM t_innodb where c = 'a'; +c +a +SELECT c FROM t_duckdb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'á'; +c +SELECT d FROM t_duckdb where d = 'a'; +d +a +SELECT d FROM t_innodb where d = 'a'; +d +a +SELECT d FROM t_duckdb where d = 'A'; +d +a +SELECT d FROM t_innodb where d = 'A'; +d +a +SELECT d FROM t_duckdb where d = 'á'; +d +a +SELECT d FROM t_innodb where d = 'á'; +d +a +SELECT e FROM t_duckdb where e = 'a'; +e +a +SELECT e FROM t_innodb where e = 'a'; +e +a +SELECT e FROM t_duckdb where e = 'A'; +e +a +SELECT e FROM t_innodb where e = 'A'; +e +a +SELECT e FROM t_duckdb where e = 'á'; +e +SELECT e FROM t_innodb where e = 'á'; +e +DROP TABLE t_duckdb, t_innodb; +# 3.3 define collation for table with ascii +set names ascii; +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE ascii_general_ci) engine=duckdb CHARSET=ascii COLLATE=ascii_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE ascii_general_ci) engine=innodb CHARSET=ascii COLLATE=ascii_general_ci; +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb") +Count +BIGINT +[ Rows: 1] +1 + + +DELETE FROM t_innodb; +ALTER TABLE t_duckdb ADD COLUMN b varchar(32) COLLATE ascii_general_ci, +ADD COLUMN c varchar(32) COLLATE ascii_bin; +ALTER TABLE t_innodb ADD COLUMN b varchar(32) COLLATE ascii_general_ci, +ADD COLUMN c varchar(32) COLLATE ascii_bin; +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a'); +SELECT a FROM t_duckdb where a = 'a'; +a +a +SELECT a FROM t_innodb where a = 'a'; +a +a +SELECT a FROM t_duckdb where a = 'A'; +a +a +SELECT a FROM t_innodb where a = 'A'; +a +a +SELECT b FROM t_duckdb where b = 'a'; +b +a +SELECT b FROM t_innodb where b = 'a'; +b +a +SELECT b FROM t_duckdb where b = 'A'; +b +a +SELECT b FROM t_innodb where b = 'A'; +b +a +SELECT c FROM t_duckdb where c = 'a'; +c +a +SELECT c FROM t_innodb where c = 'a'; +c +a +SELECT c FROM t_duckdb where c = 'A'; +c +SELECT c FROM t_innodb where c = 'A'; +c +DROP TABLE t_duckdb, t_innodb; +SET NAMES utf8mb4; +# +# Test 4. NON-UTF8 CHARSET +# +# 4.1 CREATE TABLE +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32)) engine=duckdb CHARSET=latin1; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'non-utf8 charset' +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) CHARSET latin1) engine=duckdb CHARSET=utf8mb4; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'non-utf8 charset' +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE latin1_general_ci) engine=duckdb CHARSET=utf8mb4; +ERROR HY000: Table storage engine 'DuckDB' does not support the create option 'non-utf8 charset' +# 4.2 ALTER TABLE +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) CHARSET utf8mb3, b varchar(32) CHARSET utf8mb4) engine=duckdb CHARSET=latin1; +ALTER TABLE t_duckdb ADD COLUMN c varchar(32); +ERROR HY000: Got error 168 'non-utf8 charset is not supported. Try using utf8mb4 charset for column 'c'' from DuckDB +ALTER TABLE t_duckdb ADD COLUMN c varchar(32) CHARSET utf8mb3, ADD COLUMN d varchar(32) CHARSET utf8mb4; +ALTER TABLE t_duckdb MODIFY COLUMN a varchar(32); +ERROR HY000: Got error 168 'non-utf8 charset is not supported. Try using utf8mb4 charset for column 'a'' from DuckDB +ALTER TABLE t_duckdb MODIFY COLUMN b varchar(32) CHARSET latin1; +ERROR HY000: Got error 168 'non-utf8 charset is not supported. Try using utf8mb4 charset for column 'b'' from DuckDB +# +# Test 5. UTF8MB4 with Emoji +# +CREATE TABLE t_mb4 ( +id int primary key, +a varchar(32) CHARSET utf8mb4, +b char(32) CHARSET utf8mb4, +c text(32) CHARSET utf8mb4, +d varchar(32) CHARSET utf8mb4 +) ENGINE=duckdb; +INSERT INTO t_mb4 VALUES (1, 'a', 'b', 'c', 'd'); +INSERT INTO t_mb4 VALUES (2, UNHEX('F09F9884'), UNHEX('F09F9884'), UNHEX('F09F9884'), '😭h😄h😭'); +SET GLOBAL duckdb_dml_in_batch = on; +INSERT INTO t_mb4 VALUES (3, UNHEX('F09F9884'), UNHEX('F09F9884'), UNHEX('F09F9884'), '😭h😄h😭'); +SELECT * from t_mb4; +id a b c d +1 a b c d +2 😄 😄 😄 😭h😄h😭 +3 😄 😄 😄 😭h😄h😭 +ALTER TABLE t_mb4 ADD e varchar(32) AFTER a, algorithm=copy; +SELECT * from t_mb4; +id a e b c d +1 a NULL b c d +2 😄 NULL 😄 😄 😭h😄h😭 +3 😄 NULL 😄 😄 😭h😄h😭 +ALTER TABLE t_mb4 ENGINE=innodb; +SELECT * from t_mb4; +id a e b c d +1 a NULL b c d +2 😄 NULL 😄 😄 😭h😄h😭 +3 😄 NULL 😄 😄 😭h😄h😭 +DROP DATABASE charset_and_collation; diff --git a/storage/duckdb/mysql-test/duckdb/r/create_table_column.result b/storage/duckdb/mysql-test/duckdb/r/create_table_column.result new file mode 100644 index 0000000000000..fd99e18e799a4 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/create_table_column.result @@ -0,0 +1,2608 @@ +# 1) Prepare + +# 2) Create table + +CREATE TABLE test_table ( +id INT PRIMARY KEY, +name VARCHAR(32), +index idx_id(name), +unique index uk_name(name), +unique index uk_id(id), +unique index uk_id_name(id,name) +) engine=duckdb; +DROP TABLE test_table; +# Range for each integer type from MySQL +# +# Tinyint +# +# display innodb table structure. +Field Type Null Key Default Extra +a tinyint(4) YES NULL +b tinyint(4) YES NULL +c tinyint(3) unsigned YES NULL +d tinyint(3) unsigned YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a tinyint(4) YES NULL +b tinyint(4) YES NULL +c tinyint(3) unsigned YES NULL +d tinyint(3) unsigned YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_tinyint begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_tinyint ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyint'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 TINYINT 8 +b 2 TINYINT 8 +c 3 UTINYINT NULL +d 4 UTINYINT NULL + + +# ② Print CONSTRAINTs of t_tinyint ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyint'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_tinyint ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyint'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_tinyint ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_tinyint%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_tinyint end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : -128 FFFFFFFFFFFFFF80 127 7F +duckdb_result_1: -128 FFFFFFFFFFFFFF80 127 7F +duckdb_result_2: -128 FFFFFFFFFFFFFF80 127 7F +innotoduck_result: -128 FFFFFFFFFFFFFF80 127 7F +ducktoinno_result: -128 FFFFFFFFFFFFFF80 127 7F +ducktoinnotoduck_result: -128 FFFFFFFFFFFFFF80 127 7F +innodb_checksum: . 1281388457 +duckdb_checksum : . 1281388457 +duckdb_batch_insert_checksum: . 1281388457 +innotoduck_checksum: . 1281388457 +ducktoinno_checksum: . 1281388457 +# cleanup +# +# smallint +# +# display innodb table structure. +Field Type Null Key Default Extra +a smallint(6) YES NULL +b smallint(6) YES NULL +c smallint(5) unsigned YES NULL +d smallint(5) unsigned YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a smallint(6) YES NULL +b smallint(6) YES NULL +c smallint(5) unsigned YES NULL +d smallint(5) unsigned YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_smallint begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_smallint ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_smallint'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 SMALLINT 16 +b 2 SMALLINT 16 +c 3 USMALLINT NULL +d 4 USMALLINT NULL + + +# ② Print CONSTRAINTs of t_smallint ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_smallint'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_smallint ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_smallint'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_smallint ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_smallint%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_smallint end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : -32768 FFFFFFFFFFFF8000 32767 7FFF +duckdb_result_1: -32768 FFFFFFFFFFFF8000 32767 7FFF +duckdb_result_2: -32768 FFFFFFFFFFFF8000 32767 7FFF +innotoduck_result: -32768 FFFFFFFFFFFF8000 32767 7FFF +ducktoinno_result: -32768 FFFFFFFFFFFF8000 32767 7FFF +ducktoinnotoduck_result: -32768 FFFFFFFFFFFF8000 32767 7FFF +innodb_checksum: . 1647486425 +duckdb_checksum : . 1647486425 +duckdb_batch_insert_checksum: . 1647486425 +innotoduck_checksum: . 1647486425 +ducktoinno_checksum: . 1647486425 +# cleanup +# +# mediumint +# +# display innodb table structure. +Field Type Null Key Default Extra +a mediumint(9) YES NULL +b mediumint(9) YES NULL +c mediumint(8) unsigned YES NULL +d mediumint(8) unsigned YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a mediumint(9) YES NULL +b mediumint(9) YES NULL +c mediumint(8) unsigned YES NULL +d mediumint(8) unsigned YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_mediumint begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_mediumint ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumint'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 INTEGER 32 +b 2 INTEGER 32 +c 3 UINTEGER NULL +d 4 UINTEGER NULL + + +# ② Print CONSTRAINTs of t_mediumint ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumint'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_mediumint ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumint'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_mediumint ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_mediumint%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_mediumint end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : -8388608 FFFFFFFFFF800000 8388607 7FFFFF +duckdb_result_1: -8388608 FFFFFFFFFF800000 8388607 7FFFFF +duckdb_result_2: -8388608 FFFFFFFFFF800000 8388607 7FFFFF +innotoduck_result: -8388608 FFFFFFFFFF800000 8388607 7FFFFF +ducktoinno_result: -8388608 FFFFFFFFFF800000 8388607 7FFFFF +ducktoinnotoduck_result: -8388608 FFFFFFFFFF800000 8388607 7FFFFF +innodb_checksum: . 4024755506 +duckdb_checksum : . 4024755506 +duckdb_batch_insert_checksum: . 4024755506 +innotoduck_checksum: . 4024755506 +ducktoinno_checksum: . 4024755506 +# cleanup +# +# int +# +# display innodb table structure. +Field Type Null Key Default Extra +a int(11) YES NULL +b int(11) YES NULL +c int(10) unsigned YES NULL +d int(10) unsigned YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a int(11) YES NULL +b int(11) YES NULL +c int(10) unsigned YES NULL +d int(10) unsigned YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_int begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_int ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_int'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 INTEGER 32 +b 2 INTEGER 32 +c 3 UINTEGER NULL +d 4 UINTEGER NULL + + +# ② Print CONSTRAINTs of t_int ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_int'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_int ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_int'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_int ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_int%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_int end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +duckdb_result_1: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +duckdb_result_2: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +innotoduck_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +ducktoinno_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +ducktoinnotoduck_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +innodb_checksum: . 462330419 +duckdb_checksum : . 462330419 +duckdb_batch_insert_checksum: . 462330419 +innotoduck_checksum: . 462330419 +ducktoinno_checksum: . 462330419 +# cleanup +# +# bigint +# +# display innodb table structure. +Field Type Null Key Default Extra +a bigint(20) YES NULL +b bigint(20) YES NULL +c bigint(20) unsigned YES NULL +d bigint(20) unsigned YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a bigint(20) YES NULL +b bigint(20) YES NULL +c bigint(20) unsigned YES NULL +d bigint(20) unsigned YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_bigint begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_bigint ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_bigint'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 BIGINT 64 +b 2 BIGINT 64 +c 3 UBIGINT NULL +d 4 UBIGINT NULL + + +# ② Print CONSTRAINTs of t_bigint ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_bigint'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_bigint ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_bigint'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_bigint ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_bigint%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_bigint end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +duckdb_result_1: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +duckdb_result_2: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +innotoduck_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +ducktoinno_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +ducktoinnotoduck_result: -2147483648 FFFFFFFF80000000 2147483647 7FFFFFFF +innodb_checksum: . 3958888086 +duckdb_checksum : . 3958888086 +duckdb_batch_insert_checksum: . 3958888086 +innotoduck_checksum: . 3958888086 +ducktoinno_checksum: . 3958888086 +# cleanup +# +# bool +# +# display innodb table structure. +Field Type Null Key Default Extra +a tinyint(1) YES NULL +b tinyint(1) YES NULL +c tinyint(1) YES NULL +d tinyint(1) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a tinyint(1) YES NULL +b tinyint(1) YES NULL +c tinyint(1) YES NULL +d tinyint(1) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_bool begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_bool ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_bool'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 TINYINT 8 +b 2 TINYINT 8 +c 3 TINYINT 8 +d 4 TINYINT 8 + + +# ② Print CONSTRAINTs of t_bool ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_bool'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_bool ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_bool'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_bool ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_bool%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_bool end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1 1 1 1 0 0 1 1 +duckdb_result_1: 1 1 1 1 0 0 1 1 +duckdb_result_2: 1 1 1 1 0 0 1 1 +innotoduck_result: 1 1 1 1 0 0 1 1 +ducktoinno_result: 1 1 1 1 0 0 1 1 +ducktoinnotoduck_result: 1 1 1 1 0 0 1 1 +innodb_checksum: . 502211766 +duckdb_checksum : . 502211766 +duckdb_batch_insert_checksum: . 502211766 +innotoduck_checksum: . 502211766 +ducktoinno_checksum: . 502211766 +# cleanup +# +# float +# +# display innodb table structure. +Field Type Null Key Default Extra +a float YES NULL +b float(7,4) YES NULL +c float YES NULL +d float YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a float YES NULL +b float(7,4) YES NULL +c float YES NULL +d float YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_float begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_float ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 FLOAT 24 +b 2 FLOAT 24 +c 3 FLOAT 24 +d 4 FLOAT 24 + + +# ② Print CONSTRAINTs of t_float ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_float ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_float ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_float%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_float end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1.01 1.0100 99999 3.40282e20 +duckdb_result_1: 1.01 1.0100 99999 3.40282e20 +duckdb_result_2: 1.01 1.0100 99999 3.40282e20 +innotoduck_result: 1.01 1.0100 99999 3.40282e20 +ducktoinno_result: 1.01 1.0100 99999 3.40282e20 +ducktoinnotoduck_result: 1.01 1.0100 99999 3.40282e20 +innodb_checksum: . 1318115434 +duckdb_checksum : . 1318115434 +duckdb_batch_insert_checksum: . 1318115434 +innotoduck_checksum: . 1318115434 +ducktoinno_checksum: . 1318115434 +# cleanup +# +# float with high precision +# +# display innodb table structure. +Field Type Null Key Default Extra +b float YES NULL +c float YES NULL +d float YES NULL +e float YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +b float YES NULL +c float YES NULL +d float YES NULL +e float YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_float begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_float ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +b 1 FLOAT 24 +c 2 FLOAT 24 +d 3 FLOAT 24 +e 4 FLOAT 24 + + +# ② Print CONSTRAINTs of t_float ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_float ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_float'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_float ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_float%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_float end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +duckdb_result_1: 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +duckdb_result_2: 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +innotoduck_result: 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +ducktoinno_result: 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +ducktoinnotoduck_result: 3.40282e38 -3.40282e38 1.17549e-38 -1.17549e-38 +innodb_checksum: . 500819813 +duckdb_checksum : . 500819813 +duckdb_batch_insert_checksum: . 500819813 +innotoduck_checksum: . 500819813 +ducktoinno_checksum: . 500819813 +# cleanup +# +# double +# +# display innodb table structure. +Field Type Null Key Default Extra +a double YES NULL +b double(7,4) YES NULL +c double YES NULL +d double YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a double YES NULL +b double(7,4) YES NULL +c double YES NULL +d double YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_double begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_double ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 DOUBLE 53 +b 2 DOUBLE 53 +c 3 DOUBLE 53 +d 4 DOUBLE 53 + + +# ② Print CONSTRAINTs of t_double ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_double ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_double ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_double%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_double end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1.01 1.0100 99999 3.40282e20 +duckdb_result_1: 1.01 1.0100 99999 3.40282e20 +duckdb_result_2: 1.01 1.0100 99999 3.40282e20 +innotoduck_result: 1.01 1.0100 99999 3.40282e20 +ducktoinno_result: 1.01 1.0100 99999 3.40282e20 +ducktoinnotoduck_result: 1.01 1.0100 99999 3.40282e20 +innodb_checksum: . 1878390312 +duckdb_checksum : . 1878390312 +duckdb_batch_insert_checksum: . 1878390312 +innotoduck_checksum: . 1878390312 +ducktoinno_checksum: . 1878390312 +# cleanup +# +# double with high precision +# +# display innodb table structure. +Field Type Null Key Default Extra +b double YES NULL +c double YES NULL +d double YES NULL +e double YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +b double YES NULL +c double YES NULL +d double YES NULL +e double YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_double begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_double ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +b 1 DOUBLE 53 +c 2 DOUBLE 53 +d 3 DOUBLE 53 +e 4 DOUBLE 53 + + +# ② Print CONSTRAINTs of t_double ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_double ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_double'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_double ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_double%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_double end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +duckdb_result_1: 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +duckdb_result_2: 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +innotoduck_result: 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +ducktoinno_result: 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +ducktoinnotoduck_result: 3.402823466e38 -3.402823466e38 1.175494351e-38 -1.175494351e-38 +innodb_checksum: . 648909613 +duckdb_checksum : . 648909613 +duckdb_batch_insert_checksum: . 648909613 +innotoduck_checksum: . 648909613 +ducktoinno_checksum: . 648909613 +# cleanup +# +# decimal +# +# display innodb table structure. +Field Type Null Key Default Extra +g decimal(9,9) YES NULL +h decimal(9,4) YES NULL +i decimal(9,0) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +g decimal(9,9) YES NULL +h decimal(9,4) YES NULL +i decimal(9,0) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_decimal begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_decimal ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_decimal'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +g 1 DECIMAL(9,9) 9 +h 2 DECIMAL(9,4) 9 +i 3 DECIMAL(9,0) 9 + + +# ② Print CONSTRAINTs of t_decimal ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_decimal'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_decimal ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_decimal'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_decimal ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_decimal%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_decimal end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 0.999999990 99999.9999 999999999 +duckdb_result_1: 0.999999990 99999.9999 999999999 +duckdb_result_2: 0.999999990 99999.9999 999999999 +innotoduck_result: 0.999999990 99999.9999 999999999 +ducktoinno_result: 0.999999990 99999.9999 999999999 +ducktoinnotoduck_result: 0.999999990 99999.9999 999999999 +innodb_checksum: . 1289911351 +duckdb_checksum : . 1289911351 +duckdb_batch_insert_checksum: . 1289911351 +innotoduck_checksum: . 1289911351 +ducktoinno_checksum: . 1289911351 +# cleanup +# +# check all possibilities of decimal with kinds of precisions +# +# +# decimal(1): low precision(<=38) with low precision value and duckdb_use_double_for_decimal=on +# +set global duckdb_use_double_for_decimal=on; +# display innodb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(38,5) YES NULL +c2 decimal(38,5) YES NULL +c3 decimal(38,5) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(38,5) YES NULL +c2 decimal(38,5) YES NULL +c3 decimal(38,5) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t1 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t1 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +id 1 INTEGER 32 +c1 2 DECIMAL(38,5) 38 +c2 3 DECIMAL(38,5) 38 +c3 4 DECIMAL(38,5) 38 + + +# ② Print CONSTRAINTs of t1 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t1 NOT NULL NOT NULL + + +# ③ Print INDEXs of t1 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t1 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t1%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t1 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1 123456789012345678901234567890123.12345 0.00000 0.00000 +duckdb_result_1: 1 123456789012345678901234567890123.12345 0.00000 0.00000 +duckdb_result_2: 1 123456789012345678901234567890123.12345 0.00000 0.00000 +innotoduck_result: 1 123456789012345678901234567890123.12345 0.00000 0.00000 +ducktoinno_result: 1 123456789012345678901234567890123.12345 0.00000 0.00000 +ducktoinnotoduck_result: 1 123456789012345678901234567890123.12345 0.00000 0.00000 +innodb_checksum: .1 1705554001 +duckdb_checksum : .1 1705554001 +duckdb_batch_insert_checksum: .1 1705554001 +innotoduck_checksum: .1 1705554001 +ducktoinno_checksum: .1 1705554001 +# cleanup +set global duckdb_use_double_for_decimal=default; +# +# decimal(4): low precision(<=38) with low precision value and duckdb_use_double_for_decimal=off +# +set global duckdb_use_double_for_decimal=off; +# use decimal +# display innodb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(38,5) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(38,5) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t1 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t1 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +id 1 INTEGER 32 +c1 2 DECIMAL(38,5) 38 + + +# ② Print CONSTRAINTs of t1 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t1 NOT NULL NOT NULL + + +# ③ Print INDEXs of t1 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t1 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t1%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t1 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1 123456789012345678901234567890123.12345 +duckdb_result_1: 1 123456789012345678901234567890123.12345 +duckdb_result_2: 1 123456789012345678901234567890123.12345 +innotoduck_result: 1 123456789012345678901234567890123.12345 +ducktoinno_result: 1 123456789012345678901234567890123.12345 +ducktoinnotoduck_result: 1 123456789012345678901234567890123.12345 +innodb_checksum: .1 463031321 +duckdb_checksum : .1 463031321 +duckdb_batch_insert_checksum: .1 463031321 +innotoduck_checksum: .1 463031321 +ducktoinno_checksum: .1 463031321 +# cleanup +set global duckdb_use_double_for_decimal=default; +# +# decimal(5): high precision(>38) with low precision value(<=38) and duckdb_use_double_for_decimal=off +# +set global duckdb_use_double_for_decimal=off; +# use decimal(38, dec) +# display innodb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t1 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t1 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +id 1 INTEGER 32 +c1 2 DECIMAL(38,5) 38 + + +# ② Print CONSTRAINTs of t1 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t1 NOT NULL NOT NULL + + +# ③ Print INDEXs of t1 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't1'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t1 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t1%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t1 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 4 123456789012345678901234567890.12345 +duckdb_result_1: 4 123456789012345678901234567890.12345 +duckdb_result_2: 4 123456789012345678901234567890.12345 +innotoduck_result: 4 123456789012345678901234567890.12345 +ducktoinno_result: 4 123456789012345678901234567890.12345 +ducktoinnotoduck_result: 4 123456789012345678901234567890.12345 +innodb_checksum: .1 622477275 +duckdb_checksum : .1 622477275 +duckdb_batch_insert_checksum: .1 622477275 +innotoduck_checksum: .1 622477275 +ducktoinno_checksum: .1 622477275 +# cleanup +set global duckdb_use_double_for_decimal=default; +# +# bit +# +# display innodb table structure. +Field Type Null Key Default Extra +a bit(8) YES NULL +b bit(8) YES NULL +c bit(64) YES NULL +d bit(64) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a bit(8) YES NULL +b bit(8) YES NULL +c bit(64) YES NULL +d bit(64) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_bit begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_bit ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_bit'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 BLOB NULL +b 2 BLOB NULL +c 3 BLOB NULL +d 4 BLOB NULL + + +# ② Print CONSTRAINTs of t_bit ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_bit'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_bit ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_bit'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_bit ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_bit%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_bit end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +duckdb_result_1: AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +duckdb_result_2: AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +innotoduck_result: AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +ducktoinno_result: AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +ducktoinnotoduck_result: AA 17 FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFB +innodb_checksum: . 4269358085 +duckdb_checksum : . 4269358085 +duckdb_batch_insert_checksum: . 4269358085 +innotoduck_checksum: . 4269358085 +ducktoinno_checksum: . 4269358085 +# cleanup +# +# char +# +# display innodb table structure. +Field Type Null Key Default Extra +a char(1) YES NULL +b char(1) YES NULL +c char(10) YES NULL +d char(64) YES NULL +e char(64) YES NULL +f char(1) YES NULL +g char(32) YES NULL +h char(64) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a char(1) YES NULL +b char(1) YES NULL +c char(10) YES NULL +d char(64) YES NULL +e char(64) YES NULL +f char(1) YES NULL +g char(32) YES NULL +h char(64) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_char begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_char ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_char'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 8] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL +e 5 VARCHAR NULL +f 6 VARCHAR NULL +g 7 VARCHAR NULL +h 8 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_char ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_char'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_char ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_char'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_char ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_char%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_char end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_1: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_2: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innotoduck_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinno_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinnotoduck_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innodb_checksum: . 2458706466 +duckdb_checksum : . 2458706466 +duckdb_batch_insert_checksum: . 2458706466 +innotoduck_checksum: . 2458706466 +ducktoinno_checksum: . 2458706466 +# cleanup +# +# varchar +# +# display innodb table structure. +Field Type Null Key Default Extra +a varchar(1) YES NULL +b varchar(1) YES NULL +c varchar(10) YES NULL +d varchar(64) YES NULL +e varchar(64) YES NULL +f varchar(64) YES NULL +g varchar(32) YES NULL +h varchar(32) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a varchar(1) YES NULL +b varchar(1) YES NULL +c varchar(10) YES NULL +d varchar(64) YES NULL +e varchar(64) YES NULL +f varchar(64) YES NULL +g varchar(32) YES NULL +h varchar(32) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_varchar begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_varchar ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 8] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL +e 5 VARCHAR NULL +f 6 VARCHAR NULL +g 7 VARCHAR NULL +h 8 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_varchar ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_varchar ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_varchar ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_varchar%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_varchar end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_1: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_2: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innotoduck_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinno_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinnotoduck_result: @ 40 A 41  17 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innodb_checksum: . 2848211136 +duckdb_checksum : . 2848211136 +duckdb_batch_insert_checksum: . 2848211136 +innotoduck_checksum: . 2848211136 +ducktoinno_checksum: . 2848211136 +# cleanup +# +# tinytext +# +# display innodb table structure. +Field Type Null Key Default Extra +a tinytext YES NULL +b tinytext YES NULL +d tinytext YES NULL +e tinytext YES NULL +f tinytext YES NULL +g tinytext YES NULL +h tinytext YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a tinytext YES NULL +b tinytext YES NULL +d tinytext YES NULL +e tinytext YES NULL +f tinytext YES NULL +g tinytext YES NULL +h tinytext YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_varchar begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_varchar ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 7] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +d 3 VARCHAR NULL +e 4 VARCHAR NULL +f 5 VARCHAR NULL +g 6 VARCHAR NULL +h 7 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_varchar ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_varchar ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_varchar'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_varchar ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_varchar%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_varchar end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_1: @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +duckdb_result_2: @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innotoduck_result: @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinno_result: @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +ducktoinnotoduck_result: @ 40 A 41 abcdefg0 6162636465666730 1111111111111111111111111111111111111111111111111111111111111011 31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131303131 I'm duckdb Helloworld +innodb_checksum: . 2311353533 +duckdb_checksum : . 2311353533 +duckdb_batch_insert_checksum: . 2311353533 +innotoduck_checksum: . 2311353533 +ducktoinno_checksum: . 2311353533 +# cleanup +# +# text +# +# display innodb table structure. +Field Type Null Key Default Extra +a text YES NULL +b text YES NULL +c text YES NULL +d text YES NULL +e text YES NULL +f text YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a text YES NULL +b text YES NULL +c text YES NULL +d text YES NULL +e text YES NULL +f text YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_text begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_text ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_text'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 6] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL +e 5 VARCHAR NULL +f 6 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_text ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_text'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_text ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_text'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_text ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_text%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_text end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 I'm duckdb Helloworld +innodb_checksum: . 3440606212 +duckdb_checksum : . 3440606212 +duckdb_batch_insert_checksum: . 3440606212 +innotoduck_checksum: . 3440606212 +ducktoinno_checksum: . 3440606212 +# cleanup +# +# mediumtext +# +# display innodb table structure. +Field Type Null Key Default Extra +a mediumtext YES NULL +b mediumtext YES NULL +c mediumtext YES NULL +d mediumtext YES NULL +e mediumtext YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a mediumtext YES NULL +b mediumtext YES NULL +c mediumtext YES NULL +d mediumtext YES NULL +e mediumtext YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_mediumtext begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_mediumtext ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumtext'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL +e 5 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_mediumtext ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumtext'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_mediumtext ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumtext'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_mediumtext ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_mediumtext%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_mediumtext end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 167 I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 167 I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 167 I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 167 I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 167 I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 167 I'm duckdb Helloworld +innodb_checksum: . 2377660202 +duckdb_checksum : . 2377660202 +duckdb_batch_insert_checksum: . 2377660202 +innotoduck_checksum: . 2377660202 +ducktoinno_checksum: . 2377660202 +# cleanup +# +# longtext +# +# display innodb table structure. +Field Type Null Key Default Extra +a longtext YES NULL +b longtext YES NULL +c longtext YES NULL +d longtext YES NULL +e longtext YES NULL +f longtext YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a longtext YES NULL +b longtext YES NULL +c longtext YES NULL +d longtext YES NULL +e longtext YES NULL +f longtext YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_longtext begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_longtext ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_longtext'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 6] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL +e 5 VARCHAR NULL +f 6 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_longtext ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_longtext'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_longtext ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_longtext'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_longtext ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_longtext%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_longtext end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 167 1 I'm duckdb Helloworld +innodb_checksum: . 1541516253 +duckdb_checksum : . 1541516253 +duckdb_batch_insert_checksum: . 1541516253 +innotoduck_checksum: . 1541516253 +ducktoinno_checksum: . 1541516253 +# cleanup +# +# tinyblob +# +# display innodb table structure. +Field Type Null Key Default Extra +a tinyblob YES NULL +b tinyblob YES NULL +c tinyblob YES NULL +d tinyblob YES NULL +e tinyblob YES NULL +f tinyblob YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a tinyblob YES NULL +b tinyblob YES NULL +c tinyblob YES NULL +d tinyblob YES NULL +e tinyblob YES NULL +f tinyblob YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_tinyblob begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_tinyblob ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyblob'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 6] +a 1 BLOB NULL +b 2 BLOB NULL +c 3 BLOB NULL +d 4 BLOB NULL +e 5 BLOB NULL +f 6 BLOB NULL + + +# ② Print CONSTRAINTs of t_tinyblob ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyblob'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_tinyblob ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_tinyblob'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_tinyblob ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_tinyblob%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_tinyblob end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 30 Hello 48656C6C6F I'm duckdb Helloworld +innodb_checksum: . 2145087190 +duckdb_checksum : . 2145087190 +duckdb_batch_insert_checksum: . 2145087190 +innotoduck_checksum: . 2145087190 +ducktoinno_checksum: . 2145087190 +# cleanup +# +# blob +# +# display innodb table structure. +Field Type Null Key Default Extra +a blob YES NULL +b blob YES NULL +c blob YES NULL +d blob YES NULL +e blob YES NULL +f blob YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a blob YES NULL +b blob YES NULL +c blob YES NULL +d blob YES NULL +e blob YES NULL +f blob YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_blob begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_blob ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_blob'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 6] +a 1 BLOB NULL +b 2 BLOB NULL +c 3 BLOB NULL +d 4 BLOB NULL +e 5 BLOB NULL +f 6 BLOB NULL + + +# ② Print CONSTRAINTs of t_blob ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_blob'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_blob ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_blob'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_blob ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_blob%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_blob end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 bb27ef28f3e1c5b38f5e46acfc9a2ce4 30 Hello 48656C6C6F I'm duckdb Helloworld +innodb_checksum: . 837462579 +duckdb_checksum : . 837462579 +duckdb_batch_insert_checksum: . 837462579 +innotoduck_checksum: . 837462579 +ducktoinno_checksum: . 837462579 +# cleanup +# +# mediumblob +# +# display innodb table structure. +Field Type Null Key Default Extra +a mediumblob YES NULL +b mediumblob YES NULL +c mediumblob YES NULL +d mediumblob YES NULL +e mediumblob YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a mediumblob YES NULL +b mediumblob YES NULL +c mediumblob YES NULL +d mediumblob YES NULL +e mediumblob YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_mediumblob begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_mediumblob ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumblob'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 BLOB NULL +b 2 BLOB NULL +c 3 BLOB NULL +d 4 BLOB NULL +e 5 BLOB NULL + + +# ② Print CONSTRAINTs of t_mediumblob ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumblob'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_mediumblob ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_mediumblob'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_mediumblob ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_mediumblob%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_mediumblob end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 167 Hello 48656C6C6F I'm duckdb Helloworld +innodb_checksum: . 1370500407 +duckdb_checksum : . 1370500407 +duckdb_batch_insert_checksum: . 1370500407 +innotoduck_checksum: . 1370500407 +ducktoinno_checksum: . 1370500407 +# cleanup +# +# longblob +# +# display innodb table structure. +Field Type Null Key Default Extra +a longblob YES NULL +b longblob YES NULL +c longblob YES NULL +d longblob YES NULL +e longblob YES NULL +f longblob YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a longblob YES NULL +b longblob YES NULL +c longblob YES NULL +d longblob YES NULL +e longblob YES NULL +f longblob YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_longblob begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_longblob ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_longblob'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 6] +a 1 BLOB NULL +b 2 BLOB NULL +c 3 BLOB NULL +d 4 BLOB NULL +e 5 BLOB NULL +f 6 BLOB NULL + + +# ② Print CONSTRAINTs of t_longblob ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_longblob'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_longblob ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_longblob'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_longblob ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_longblob%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_longblob end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_1: 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +duckdb_result_2: 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +innotoduck_result: 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinno_result: 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +ducktoinnotoduck_result: 30313233343536373839414243444546 167 1 Hello 48656C6C6F I'm duckdb Helloworld +innodb_checksum: . 779642098 +duckdb_checksum : . 779642098 +duckdb_batch_insert_checksum: . 779642098 +innotoduck_checksum: . 779642098 +ducktoinno_checksum: . 779642098 +# cleanup +# +# binary +# +# display innodb table structure. +Field Type Null Key Default Extra +a binary(16) YES NULL +b binary(32) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a binary(16) YES NULL +b binary(32) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_binary begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_binary ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_binary'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 BLOB NULL +b 2 BLOB NULL + + +# ② Print CONSTRAINTs of t_binary ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_binary'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_binary ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_binary'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_binary ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_binary%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_binary end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : E80B5017098950FC58AAD83C8C14978E 16 +duckdb_result_1: E80B5017098950FC58AAD83C8C14978E 16 +duckdb_result_2: E80B5017098950FC58AAD83C8C14978E 16 +innotoduck_result: E80B5017098950FC58AAD83C8C14978E 16 +ducktoinno_result: E80B5017098950FC58AAD83C8C14978E 16 +ducktoinnotoduck_result: E80B5017098950FC58AAD83C8C14978E 16 +innodb_checksum: . 3309099147 +duckdb_checksum : . 3309099147 +duckdb_batch_insert_checksum: . 3309099147 +innotoduck_checksum: . 3309099147 +ducktoinno_checksum: . 3309099147 +# cleanup +# +# varbinary +# +# display innodb table structure. +Field Type Null Key Default Extra +a varbinary(16) YES NULL +b binary(32) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a varbinary(16) YES NULL +b binary(32) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_varbinary begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_varbinary ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_varbinary'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 BLOB NULL +b 2 BLOB NULL + + +# ② Print CONSTRAINTs of t_varbinary ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_varbinary'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_varbinary ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_varbinary'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_varbinary ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_varbinary%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_varbinary end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +duckdb_result_1: E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +duckdb_result_2: E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +innotoduck_result: E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ducktoinno_result: E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ducktoinnotoduck_result: E80B5017098950FC58AAD83C8C14978E 16 6161616161616161616161616161616161616161616161616161616161616161 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +innodb_checksum: . 446120215 +duckdb_checksum : . 446120215 +duckdb_batch_insert_checksum: . 446120215 +innotoduck_checksum: . 446120215 +ducktoinno_checksum: . 446120215 +# cleanup +# +# year +# +# display innodb table structure. +Field Type Null Key Default Extra +a year(4) YES NULL +b year(4) YES NULL +c year(4) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a year(4) YES NULL +b year(4) YES NULL +c year(4) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_year begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_year ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_year'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 INTEGER 32 +b 2 INTEGER 32 +c 3 INTEGER 32 + + +# ② Print CONSTRAINTs of t_year ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_year'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_year ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_year'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_year ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_year%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_year end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 2020 1970 1969 +duckdb_result_1: 2020 1970 1969 +duckdb_result_2: 2020 1970 1969 +innotoduck_result: 2020 1970 1969 +ducktoinno_result: 2020 1970 1969 +ducktoinnotoduck_result: 2020 1970 1969 +innodb_checksum: . 3857304234 +duckdb_checksum : . 3857304234 +duckdb_batch_insert_checksum: . 3857304234 +innotoduck_checksum: . 3857304234 +ducktoinno_checksum: . 3857304234 +# cleanup +# +# date +# +# display innodb table structure. +Field Type Null Key Default Extra +a date YES NULL +b date YES NULL +c date YES NULL +d date YES NULL +e date YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a date YES NULL +b date YES NULL +c date YES NULL +d date YES NULL +e date YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_date begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_date ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_date'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 DATE NULL +b 2 DATE NULL +c 3 DATE NULL +d 4 DATE NULL +e 5 DATE NULL + + +# ② Print CONSTRAINTs of t_date ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_date'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_date ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_date'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_date ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_date%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_date end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +Warnings: +Note 1265 Data truncated for column 'd' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'd' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'd' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'd' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'd' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +# check inserted data +innodb_result : 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +duckdb_result_1: 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +duckdb_result_2: 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +innotoduck_result: 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +ducktoinno_result: 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +ducktoinnotoduck_result: 2020-01-01 1970-01-01 1969-01-01 2020-01-01 2020-01-01 +innodb_checksum: . 1408669212 +duckdb_checksum : . 1408669212 +duckdb_batch_insert_checksum: . 1408669212 +innotoduck_checksum: . 1408669212 +ducktoinno_checksum: . 1408669212 +# cleanup +# +# time +# +# display innodb table structure. +Field Type Null Key Default Extra +a time YES NULL +b time YES NULL +c time YES NULL +d time YES NULL +e time YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a time YES NULL +b time YES NULL +c time YES NULL +d time YES NULL +e time YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_time'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIME NULL +b 2 TIME NULL +c 3 TIME NULL +d 4 TIME NULL +e 5 TIME NULL + + +# ② Print CONSTRAINTs of t_time ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_time'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_time'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_time%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +# check inserted data +innodb_result : 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +duckdb_result_1: 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +duckdb_result_2: 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +innotoduck_result: 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +ducktoinno_result: 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +ducktoinnotoduck_result: 12:00:00 00:00:00 23:59:59 23:59:59 12:00:00 +innodb_checksum: . 2599517108 +duckdb_checksum : . 2599517108 +duckdb_batch_insert_checksum: . 2599517108 +innotoduck_checksum: . 2599517108 +ducktoinno_checksum: . 2599517108 +# cleanup +# +# time(6) +# +# display innodb table structure. +Field Type Null Key Default Extra +a time(6) YES NULL +b time(6) YES NULL +c time(6) YES NULL +d time(6) YES NULL +e time(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a time(6) YES NULL +b time(6) YES NULL +c time(6) YES NULL +d time(6) YES NULL +e time(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_6 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_6 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_time_6'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIME NULL +b 2 TIME NULL +c 3 TIME NULL +d 4 TIME NULL +e 5 TIME NULL + + +# ② Print CONSTRAINTs of t_time_6 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_time_6'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_6 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_time_6'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_6 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_time_6%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_6 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +Warnings: +Note 1265 Data truncated for column 'a' at row 1 +Note 1265 Data truncated for column 'e' at row 1 +# check inserted data +innodb_result : 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +duckdb_result_1: 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +duckdb_result_2: 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +innotoduck_result: 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +ducktoinno_result: 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +ducktoinnotoduck_result: 12:00:00.100000 00:00:00.000000 23:59:59.000000 23:59:59.123456 12:00:00.100000 +innodb_checksum: .6 1377980872 +duckdb_checksum : .6 1377980872 +duckdb_batch_insert_checksum: .6 1377980872 +innotoduck_checksum: .6 1377980872 +ducktoinno_checksum: .6 1377980872 +# cleanup +# +# datetime +# +# display innodb table structure. +Field Type Null Key Default Extra +a datetime YES NULL +b datetime YES NULL +c datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a datetime YES NULL +b datetime YES NULL +c datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_datetime begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_datetime ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 TIMESTAMP NULL +b 2 TIMESTAMP NULL +c 3 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_datetime ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_datetime ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_datetime ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_datetime%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_datetime end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +duckdb_result_1: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +duckdb_result_2: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +innotoduck_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +ducktoinno_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +innodb_checksum: . 2724791001 +duckdb_checksum : . 2724791001 +duckdb_batch_insert_checksum: . 2724791001 +innotoduck_checksum: . 2724791001 +ducktoinno_checksum: . 2724791001 +# cleanup +# +# datetime(6) +# +# display innodb table structure. +Field Type Null Key Default Extra +a datetime(6) YES NULL +b datetime(6) YES NULL +c datetime(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a datetime(6) YES NULL +b datetime(6) YES NULL +c datetime(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_datetime_6 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_datetime_6 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime_6'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 TIMESTAMP NULL +b 2 TIMESTAMP NULL +c 3 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_datetime_6 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime_6'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_datetime_6 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_datetime_6'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_datetime_6 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_datetime_6%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_datetime_6 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +duckdb_result_1: 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +duckdb_result_2: 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +innotoduck_result: 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +ducktoinno_result: 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +ducktoinnotoduck_result: 2020-01-01 12:00:00.000000 1969-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 +innodb_checksum: .6 3268924007 +duckdb_checksum : .6 3268924007 +duckdb_batch_insert_checksum: .6 3268924007 +innotoduck_checksum: .6 3268924007 +ducktoinno_checksum: .6 3268924007 +# cleanup +# +# timestamp +# +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp YES NULL +c timestamp YES NULL +d timestamp YES NULL +e timestamp YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp YES NULL +c timestamp YES NULL +d timestamp YES NULL +e timestamp YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_timestamp begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_timestamp ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP WITH TIME ZONE NULL +d 4 TIMESTAMP WITH TIME ZONE NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_timestamp ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_timestamp ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_timestamp ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_timestamp%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_timestamp end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 2020-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +innodb_checksum: . 474721387 +duckdb_checksum : . 474721387 +duckdb_batch_insert_checksum: . 474721387 +innotoduck_checksum: . 474721387 +ducktoinno_checksum: . 474721387 +# cleanup +# +# timestamp(6) +# +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp(6) YES NULL +b timestamp(6) YES NULL +c timestamp(6) YES NULL +d timestamp(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp(6) YES NULL +b timestamp(6) YES NULL +c timestamp(6) YES NULL +d timestamp(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_timestamp_6 begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_timestamp_6 ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp_6'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP WITH TIME ZONE NULL +d 4 TIMESTAMP WITH TIME ZONE NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_timestamp_6 ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp_6'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_timestamp_6 ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_timestamp_6'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_timestamp_6 ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_timestamp_6%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_timestamp_6 end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_1: 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_2: 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innotoduck_result: 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinno_result: 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 12:00:00.000000 1970-01-01 12:00:00.000000 2020-01-01 12:00:00.001000 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innodb_checksum: .6 333230012 +duckdb_checksum : .6 333230012 +duckdb_batch_insert_checksum: .6 333230012 +innotoduck_checksum: .6 333230012 +ducktoinno_checksum: .6 333230012 +# cleanup +# +# json, enum, set +# +# display innodb table structure. +Field Type Null Key Default Extra +a longtext YES NULL +b enum('v1','v2','v3') YES NULL +c set('v1','v2','v3') YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a longtext YES NULL +b enum('v1','v2','v3') YES NULL +c set('v1','v2','v3') YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_json begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_json ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_json'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_json ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_json'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_json ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_json'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_json ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_json%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_json end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +duckdb_result_1: {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +duckdb_result_2: {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +innotoduck_result: {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +ducktoinno_result: {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +ducktoinnotoduck_result: {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +innodb_checksum: . 3250090082 +duckdb_checksum : . 3250090082 +duckdb_batch_insert_checksum: . 3250090082 +innotoduck_checksum: . 3250090082 +ducktoinno_checksum: . 3250090082 +# cleanup +# +# NULL +# +# display innodb table structure. +Field Type Null Key Default Extra +a int(11) YES NULL +b int(11) YES NULL +c varchar(32) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a int(11) YES NULL +b int(11) YES NULL +c varchar(32) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_null begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_null ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_null'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 INTEGER 32 +b 2 INTEGER 32 +c 3 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_null ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_null'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_null ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_null'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_null ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_null%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_null end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : 1 +duckdb_result_1: 1 +duckdb_result_2: 1 +innotoduck_result: 1 +ducktoinno_result: 1 +ducktoinnotoduck_result: 1 +innodb_checksum: . 2308603460 +duckdb_checksum : . 2308603460 +duckdb_batch_insert_checksum: . 2308603460 +innotoduck_checksum: . 2308603460 +ducktoinno_checksum: . 2308603460 +# cleanup +# +# column with charset ascii +# +# display innodb table structure. +Field Type Null Key Default Extra +a varchar(32) YES NULL +b char(32) YES NULL +c text YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a varchar(32) YES NULL +b char(32) YES NULL +c text YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_ascii begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_ascii ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_ascii'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_ascii ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_ascii'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_ascii ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_ascii'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_ascii ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_ascii%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_ascii end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : ascii ASCII ascII +duckdb_result_1: ascii ASCII ascII +duckdb_result_2: ascii ASCII ascII +innotoduck_result: ascii ASCII ascII +ducktoinno_result: ascii ASCII ascII +ducktoinnotoduck_result: ascii ASCII ascII +innodb_checksum: . 1028691471 +duckdb_checksum : . 1028691471 +duckdb_batch_insert_checksum: . 1028691471 +innotoduck_checksum: . 1028691471 +ducktoinno_checksum: . 1028691471 +# cleanup +# +# column with utf8mb4 and emoji +# +# display innodb table structure. +Field Type Null Key Default Extra +a varchar(32) YES NULL +b char(32) YES NULL +c tinytext YES NULL +d varchar(32) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a varchar(32) YES NULL +b char(32) YES NULL +c tinytext YES NULL +d varchar(32) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_mb4_emoji begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_mb4_emoji ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'duckdb_db' AND table_name = 't_mb4_emoji'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 4] +a 1 VARCHAR NULL +b 2 VARCHAR NULL +c 3 VARCHAR NULL +d 4 VARCHAR NULL + + +# ② Print CONSTRAINTs of t_mb4_emoji ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'duckdb_db' AND table_name = 't_mb4_emoji'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_mb4_emoji ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'duckdb_db' AND table_name = 't_mb4_emoji'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_mb4_emoji ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='duckdb_db' AND sequence_name LIKE 'seq_t_mb4_emoji%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_mb4_emoji end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +# check inserted data +innodb_result : ? ? ? 😭h😄h😭 +duckdb_result_1: ? ? ? 😭h😄h😭 +duckdb_result_2: ? ? ? 😭h😄h😭 +innotoduck_result: ? ? ? 😭h😄h😭 +ducktoinno_result: ? ? ? 😭h😄h😭 +ducktoinnotoduck_result: ? ? ? 😭h😄h😭 +innodb_checksum: .4 1043399204 +duckdb_checksum : .4 1043399204 +duckdb_batch_insert_checksum: .4 1043399204 +innotoduck_checksum: .4 1043399204 +ducktoinno_checksum: .4 1043399204 +# cleanup diff --git a/storage/duckdb/mysql-test/duckdb/r/create_table_column_timestamp.result b/storage/duckdb/mysql-test/duckdb/r/create_table_column_timestamp.result new file mode 100644 index 0000000000000..d21e01204ae8b --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/create_table_column_timestamp.result @@ -0,0 +1,1304 @@ +# +# datetime type, test insert and SELECT unchanged +# +# display innodb table structure. +Field Type Null Key Default Extra +a datetime YES NULL +b datetime YES NULL +c datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a datetime YES NULL +b datetime YES NULL +c datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_datetime begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_datetime ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 3] +a 1 TIMESTAMP NULL +b 2 TIMESTAMP NULL +c 3 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_datetime ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_datetime ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_datetime ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_datetime end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_datetime +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_datetime +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_datetime' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_DATETIME2 1 0 0 0 +b MYSQL_TYPE_DATETIME2 1 0 0 0 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +duckdb_result_1: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +duckdb_result_2: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +innotoduck_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +ducktoinno_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1969-01-01 12:00:00 2020-01-01 12:00:00 +innodb_checksum: . 2724791001 +duckdb_checksum : . 2724791001 +duckdb_batch_insert_checksum: . 2724791001 +innotoduck_checksum: . 2724791001 +ducktoinno_checksum: . 2724791001 +# cleanup +# +# timestamp,test insert and SELECT unchanged +# +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp YES NULL +c timestamp YES NULL +d timestamp YES NULL +e timestamp YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp YES NULL +c timestamp YES NULL +d timestamp YES NULL +e timestamp YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_timestamp begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_timestamp ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP WITH TIME ZONE NULL +d 4 TIMESTAMP WITH TIME ZONE NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_timestamp ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_timestamp ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_timestamp ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_timestamp end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_timestamp +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_timestamp +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_timestamp' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +c MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +d MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 1970-01-01 12:00:00 2025-02-21 05:51:01 2025-02-21 05:51:01 +innodb_checksum: . 1669652304 +duckdb_checksum : . 1669652304 +duckdb_batch_insert_checksum: . 1669652304 +innotoduck_checksum: . 1669652304 +ducktoinno_checksum: . 1669652304 +# cleanup +# +# Under system timezone, test linux `date` command and mysql `SELECT now()` get the same result +# +# restart +SET TIME_ZONE = system; +SELECT sleep(1); +sleep(1) +0 +CREATE TABLE t(a timestamp) ENGINE=duckdb; +INSERT INTO t VALUES (now()); +DROP TABLE t; +# +# For unsupported timezone +# +CREATE TABLE t_time_zone_unsupported (a timestamp) ENGINE=duckdb; +INSERT INTO t_time_zone_unsupported VALUES (from_unixtime(1740117061)); +SET time_zone = '+02:00'; +SELECT a FROM t_time_zone_unsupported; +a +2025-02-21 07:51:01 +SET time_zone = '+02:30'; +SELECT a FROM t_time_zone_unsupported; +a +2025-02-21 08:21:01 +Warnings: +Warning 7508 [DuckDB] Exception when setting duckdb session variables. Can't find corresponding duckdb time_zone, using Etc/GMT. +SET time_zone = '+03:00'; +SELECT a FROM t_time_zone_unsupported; +a +2025-02-21 08:51:01 +DROP TABLE t_time_zone_unsupported; +# +# Insert and Select under the same timezone, check duckdb and innodb are the same +# +SET time_zone = 'UTC'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innodb_checksum: . 3346791475 +duckdb_checksum : . 3346791475 +duckdb_batch_insert_checksum: . 3346791475 +innotoduck_checksum: . 3346791475 +ducktoinno_checksum: . 3346791475 +# cleanup +SET time_zone = 'Europe/Moscow'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 08:51:01.000000 2025-02-21 08:51:01.123400 +innodb_checksum: . 501294496 +duckdb_checksum : . 501294496 +duckdb_batch_insert_checksum: . 501294496 +innotoduck_checksum: . 501294496 +ducktoinno_checksum: . 501294496 +# cleanup +SET time_zone = system; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 05:51:01.123400 +innodb_checksum: . 3346791475 +duckdb_checksum : . 3346791475 +duckdb_batch_insert_checksum: . 3346791475 +innotoduck_checksum: . 3346791475 +ducktoinno_checksum: . 3346791475 +# cleanup +SET time_zone = '+10:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 15:51:01.123400 +innodb_checksum: . 3514541412 +duckdb_checksum : . 3514541412 +duckdb_batch_insert_checksum: . 3514541412 +innotoduck_checksum: . 3514541412 +ducktoinno_checksum: . 3514541412 +# cleanup +# +# Timezone changed between Insert and Select, check duckdb and innodb are the same +# +# Time zone changed from '+10:00' to '+09:00' +SET time_zone = '+10:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +duckdb_result_1: 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +duckdb_result_2: 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +innotoduck_result: 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +ducktoinno_result: 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 11:00:00 1970-01-01 11:00:00.000000 1969-01-01 12:00:00 2025-02-21 15:51:01.000000 2025-02-21 14:51:01.123400 +innodb_checksum: . 3514541412 +duckdb_checksum : . 3514541412 +duckdb_batch_insert_checksum: . 3514541412 +innotoduck_checksum: . 3514541412 +ducktoinno_checksum: . 3514541412 +# cleanup +# Time zone changed from SYSTEM to 'Europe/Moscow' +SET time_zone = SYSTEM; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +duckdb_result_1: 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +duckdb_result_2: 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +innotoduck_result: 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +ducktoinno_result: 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 15:00:00 1970-01-01 15:00:00.000000 1969-01-01 12:00:00 2025-02-21 05:51:01.000000 2025-02-21 08:51:01.123400 +innodb_checksum: . 3346791475 +duckdb_checksum : . 3346791475 +duckdb_batch_insert_checksum: . 3346791475 +innotoduck_checksum: . 3346791475 +ducktoinno_checksum: . 3346791475 +# cleanup +# Time zone changed from '+02:00' to Japan +SET time_zone = '+02:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b timestamp(6) YES NULL +c datetime YES NULL +d datetime(6) YES NULL +e timestamp(6) YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 5] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP WITH TIME ZONE NULL +c 3 TIMESTAMP NULL +d 4 TIMESTAMP NULL +e 5 TIMESTAMP WITH TIME ZONE NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +c MYSQL_TYPE_DATETIME2 1 0 0 0 +d MYSQL_TYPE_DATETIME2 1 0 0 6 +e MYSQL_TYPE_TIMESTAMP2 1 0 0 6 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +duckdb_result_1: 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +duckdb_result_2: 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +innotoduck_result: 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +ducktoinno_result: 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +ducktoinnotoduck_result: 2020-01-01 19:00:00 1970-01-01 19:00:00.000000 1969-01-01 12:00:00 2025-02-21 07:51:01.000000 2025-02-21 14:51:01.123400 +innodb_checksum: . 223280672 +duckdb_checksum : . 223280672 +duckdb_batch_insert_checksum: . 223280672 +innotoduck_checksum: . 223280672 +ducktoinno_checksum: . 223280672 +# cleanup +# +# Use time colum to do compare with string, check duckdb and innodb are the same +# It test the timezone setting of duckdb connection is the consistent with mysql thd +# +# test1 +SET time_zone = '+08:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_DATETIME2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 12:00:00 1970-01-01 12:00:00 +duckdb_result_1: 2020-01-01 12:00:00 1970-01-01 12:00:00 +duckdb_result_2: 2020-01-01 12:00:00 1970-01-01 12:00:00 +innotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 +ducktoinno_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 12:00:00 1970-01-01 12:00:00 +innodb_checksum: . 809274229 +duckdb_checksum : . 809274229 +duckdb_batch_insert_checksum: . 809274229 +innotoduck_checksum: . 809274229 +ducktoinno_checksum: . 809274229 +# cleanup +# test2 +SET time_zone = '+08:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_DATETIME2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 04:00:00 1970-01-01 12:00:00 +duckdb_result_1: 2020-01-01 04:00:00 1970-01-01 12:00:00 +duckdb_result_2: 2020-01-01 04:00:00 1970-01-01 12:00:00 +innotoduck_result: 2020-01-01 04:00:00 1970-01-01 12:00:00 +ducktoinno_result: 2020-01-01 04:00:00 1970-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 04:00:00 1970-01-01 12:00:00 +innodb_checksum: . 809274229 +duckdb_checksum : . 809274229 +duckdb_batch_insert_checksum: . 809274229 +innotoduck_checksum: . 809274229 +ducktoinno_checksum: . 809274229 +# cleanup +# test3 +SET time_zone = '+07:00'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_DATETIME2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 06:00:00 1970-01-01 12:00:00 +duckdb_result_1: 2020-01-01 06:00:00 1970-01-01 12:00:00 +duckdb_result_2: 2020-01-01 06:00:00 1970-01-01 12:00:00 +innotoduck_result: 2020-01-01 06:00:00 1970-01-01 12:00:00 +ducktoinno_result: 2020-01-01 06:00:00 1970-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 06:00:00 1970-01-01 12:00:00 +innodb_checksum: . 3417724897 +duckdb_checksum : . 3417724897 +duckdb_batch_insert_checksum: . 3417724897 +innotoduck_checksum: . 3417724897 +ducktoinno_checksum: . 3417724897 +# cleanup +# test4 +SET time_zone = 'MET'; +# display innodb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +# display duckdb table structure. +Field Type Null Key Default Extra +a timestamp YES NULL +b datetime YES NULL +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print t_time_zone begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of t_time_zone ## +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +a 1 TIMESTAMP WITH TIME ZONE NULL +b 2 TIMESTAMP NULL + + +# ② Print CONSTRAINTs of t_time_zone ## +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 0] + + +# ③ Print INDEXs of t_time_zone ## +RESULT +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of t_time_zone ## +RESULT +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table t_time_zone end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ +SET DEBUG='+d,skip_dd_table_access_check'; +# +# ======================================================================= +# Show mysql dd structure of duckdb_db.t_time_zone +# ======================================================================= +# +include/assert.inc [table is not duckdb, which is DUCKDB] +include/assert.inc [duckdb not support partition_expression, which is ] +# Show colunmn info of t_time_zone +SELECT c.name, c.type, c.is_nullable, c.is_zerofill, c.is_unsigned, c.datetime_precision, c.comment FROM mysql.columns c, mysql.tables t, mysql.schemata s WHERE s.name='duckdb_db' and s.id = t.schema_id and c.table_id = t.id and t.name = 't_time_zone' ORDER BY t.schema_id, t.name, c.name; +name type is_nullable is_zerofill is_unsigned datetime_precision comment +a MYSQL_TYPE_TIMESTAMP2 1 0 0 0 +b MYSQL_TYPE_DATETIME2 1 0 0 0 +include/assert.inc [duckdb do not support auto increment] +include/assert.inc [duckdb do not support virtual column, but is_virtual is 0] +include/assert.inc [duckdb do not support virtual column, but generation_expression is ] +include/assert.inc [duckdb do not support se_private_data, but it is ] +SET DEBUG='-d,skip_dd_table_access_check'; +# check inserted data +innodb_result : 2020-01-01 18:00:00 1970-01-01 12:00:00 +duckdb_result_1: 2020-01-01 18:00:00 1970-01-01 12:00:00 +duckdb_result_2: 2020-01-01 18:00:00 1970-01-01 12:00:00 +innotoduck_result: 2020-01-01 18:00:00 1970-01-01 12:00:00 +ducktoinno_result: 2020-01-01 18:00:00 1970-01-01 12:00:00 +ducktoinnotoduck_result: 2020-01-01 18:00:00 1970-01-01 12:00:00 +innodb_checksum: . 965201824 +duckdb_checksum : . 965201824 +duckdb_batch_insert_checksum: . 965201824 +innotoduck_checksum: . 965201824 +ducktoinno_checksum: . 965201824 +# cleanup +SET time_zone='+00:00'; +CREATE TABLE t_duckdb (a timestamp, b datetime) ENGINE=duckdb; +CREATE TABLE t_innodb (a timestamp, b datetime) ENGINE=innodb; +INSERT INTO t_duckdb VALUES ('2020-01-01 00:00:00', '1970-01-01 00:00:00'); +INSERT INTO t_innodb VALUES ('2020-01-01 00:00:00', '1970-01-01 00:00:00'); +SET time_zone='-11:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 13:00:01' and a > '2019-12-31 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 13:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 13:00:01' and a > '2019-12-31 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 13:00:00 1970-01-01 00:00:00 +SET time_zone='-10:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 14:00:01' and a > '2019-12-31 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 14:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 14:00:01' and a > '2019-12-31 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 14:00:00 1970-01-01 00:00:00 +SET time_zone='-09:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 15:00:01' and a > '2019-12-31 14:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 15:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 15:00:01' and a > '2019-12-31 14:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 15:00:00 1970-01-01 00:00:00 +SET time_zone='-08:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 16:00:01' and a > '2019-12-31 15:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 16:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 16:00:01' and a > '2019-12-31 15:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 16:00:00 1970-01-01 00:00:00 +SET time_zone='-07:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 17:00:01' and a > '2019-12-31 16:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 17:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 17:00:01' and a > '2019-12-31 16:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 17:00:00 1970-01-01 00:00:00 +SET time_zone='-06:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 18:00:01' and a > '2019-12-31 17:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 18:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 18:00:01' and a > '2019-12-31 17:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 18:00:00 1970-01-01 00:00:00 +SET time_zone='-05:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 19:00:01' and a > '2019-12-31 18:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 19:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 19:00:01' and a > '2019-12-31 18:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 19:00:00 1970-01-01 00:00:00 +SET time_zone='-04:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 20:00:01' and a > '2019-12-31 19:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 20:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 20:00:01' and a > '2019-12-31 19:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 20:00:00 1970-01-01 00:00:00 +SET time_zone='-03:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 21:00:01' and a > '2019-12-31 20:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 21:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 21:00:01' and a > '2019-12-31 20:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 21:00:00 1970-01-01 00:00:00 +SET time_zone='-02:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 22:00:01' and a > '2019-12-31 21:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 22:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 22:00:01' and a > '2019-12-31 21:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 22:00:00 1970-01-01 00:00:00 +SET time_zone='-01:00'; +SELECT * FROM t_innodb WHERE a < '2019-12-31 23:00:01' and a > '2019-12-31 22:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 23:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2019-12-31 23:00:01' and a > '2019-12-31 22:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2019-12-31 23:00:00 1970-01-01 00:00:00 +SET time_zone='+00:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 23:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 00:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 23:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 00:00:00 1970-01-01 00:00:00 +SET time_zone='+01:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 01:00:01' and a > '2020-01-01 00:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 01:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 01:00:01' and a > '2020-01-01 00:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 01:00:00 1970-01-01 00:00:00 +SET time_zone='+02:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 02:00:01' and a > '2020-01-01 01:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 02:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 02:00:01' and a > '2020-01-01 01:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 02:00:00 1970-01-01 00:00:00 +SET time_zone='+03:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 03:00:01' and a > '2020-01-01 02:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 03:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 03:00:01' and a > '2020-01-01 02:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 03:00:00 1970-01-01 00:00:00 +SET time_zone='+04:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 04:00:01' and a > '2020-01-01 03:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 04:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 04:00:01' and a > '2020-01-01 03:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 04:00:00 1970-01-01 00:00:00 +SET time_zone='+05:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 05:00:01' and a > '2020-01-01 04:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 05:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 05:00:01' and a > '2020-01-01 04:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 05:00:00 1970-01-01 00:00:00 +SET time_zone='+06:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 06:00:01' and a > '2020-01-01 05:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 06:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 06:00:01' and a > '2020-01-01 05:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 06:00:00 1970-01-01 00:00:00 +SET time_zone='+07:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 07:00:01' and a > '2020-01-01 06:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 07:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 07:00:01' and a > '2020-01-01 06:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 07:00:00 1970-01-01 00:00:00 +SET time_zone='+08:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 08:00:01' and a > '2020-01-01 07:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 08:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 08:00:01' and a > '2020-01-01 07:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 08:00:00 1970-01-01 00:00:00 +SET time_zone='+09:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 09:00:01' and a > '2020-01-01 08:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 09:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 09:00:01' and a > '2020-01-01 08:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 09:00:00 1970-01-01 00:00:00 +SET time_zone='+10:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 10:00:01' and a > '2020-01-01 09:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 10:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 10:00:01' and a > '2020-01-01 09:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 10:00:00 1970-01-01 00:00:00 +SET time_zone='+11:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 11:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 11:00:00 1970-01-01 00:00:00 +SET time_zone='+12:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 12:00:01' and a > '2020-01-01 11:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 12:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 12:00:01' and a > '2020-01-01 11:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 12:00:00 1970-01-01 00:00:00 +SET time_zone='+13:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 13:00:01' and a > '2020-01-01 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 13:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 13:00:01' and a > '2020-01-01 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 13:00:00 1970-01-01 00:00:00 +SET time_zone='+14:00'; +SELECT * FROM t_innodb WHERE a < '2020-01-01 14:00:01' and a > '2020-01-01 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 14:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 14:00:01' and a > '2020-01-01 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +a b +2020-01-01 14:00:00 1970-01-01 00:00:00 +DROP TABLE t_innodb; +DROP TABLE t_duckdb; +SET time_zone=default; diff --git a/storage/duckdb/mysql-test/duckdb/r/create_table_constraint.result b/storage/duckdb/mysql-test/duckdb/r/create_table_constraint.result new file mode 100644 index 0000000000000..e1aaf5bc6e2ec --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/create_table_constraint.result @@ -0,0 +1,47 @@ +# 1) Prepare + +# 2) Create table + +CREATE TABLE test_table ( +id INT PRIMARY KEY, +name VARCHAR(32), +index idx_id(name), +unique index uk_name(name), +unique index uk_id(id), +unique index uk_id_name(id,name) +) engine=duckdb; +------------------------------------------------------------------------ +## ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Print test_table begin ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ ## +# ① Print columns of test_table ## +run_in_duckdb("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'create_table_constraint' AND table_name = 'test_table'") +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 2] +id 1 INTEGER 32 +name 2 VARCHAR NULL + + +# ② Print CONSTRAINTs of test_table ## +run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints() WHERE schema_name = 'create_table_constraint' AND table_name = 'test_table'") +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +test_table NOT NULL NOT NULL + + +# ③ Print INDEXs of test_table ## +run_in_duckdb("SELECT index_name, comment, tags, is_unique, is_primary, expressions FROM duckdb_indexes() WHERE schema_name = 'create_table_constraint' AND table_name = 'test_table'") +index_name comment tags is_unique is_primary expressions +VARCHAR VARCHAR MAP(VARCHAR, VARCHAR) BOOLEAN BOOLEAN VARCHAR +[ Rows: 0] + + +# ④ Print SEQUENCE of test_table ## +run_in_duckdb("SELECT sequence_name, min_value, increment_by, last_value FROM duckdb_sequences() WHERE schema_name='create_table_constraint' AND sequence_name LIKE 'seq_test_table%'") +sequence_name min_value increment_by last_value +VARCHAR BIGINT BIGINT BIGINT +[ Rows: 0] + + +## ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ Print table test_table end ↑↑↑↑↑↑↑↑↑↑↑↑↑↑ ## +------------------------------------------------------------------------ diff --git a/storage/duckdb/mysql-test/duckdb/r/cross_engine_join.result b/storage/duckdb/mysql-test/duckdb/r/cross_engine_join.result new file mode 100644 index 0000000000000..c7664f6a8d394 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/cross_engine_join.result @@ -0,0 +1,128 @@ +# +# Cross-engine join: DuckDB + InnoDB via _mdb_scan replacement scan +# + +# Setup: create tables in both engines + +CREATE DATABASE IF NOT EXISTS cross_engine_join CHARACTER SET utf8mb4; +USE cross_engine_join; +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, name VARCHAR(50)) ENGINE=InnoDB; +INSERT INTO t_duck VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'); +INSERT INTO t_inno VALUES (1, 'Alice'), (2, 'Bob'), (4, 'Dave'); + +# (1) Basic INNER JOIN + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +ORDER BY d.id; +id val name +1 alpha Alice +2 beta Bob + +# (2) LEFT JOIN — DuckDB left, InnoDB right + +SELECT d.id, d.val, i.name +FROM t_duck d LEFT JOIN t_inno i ON d.id = i.id +ORDER BY d.id; +id val name +1 alpha Alice +2 beta Bob +3 gamma NULL + +# (3) RIGHT JOIN — InnoDB left, DuckDB right + +SELECT d.id, d.val, i.name +FROM t_duck d RIGHT JOIN t_inno i ON d.id = i.id +ORDER BY i.id; +id val name +1 alpha Alice +2 beta Bob +NULL NULL Dave + +# (4) Cross join (cartesian product) + +SELECT d.id AS did, i.id AS iid +FROM t_duck d, t_inno i +ORDER BY did, iid; +did iid +1 1 +1 2 +1 4 +2 1 +2 2 +2 4 +3 1 +3 2 +3 4 + +# (5) Aggregation over cross-engine join + +SELECT COUNT(*), SUM(d.id) +FROM t_duck d JOIN t_inno i ON d.id = i.id; +COUNT(*) SUM(d.id) +2 3 + +# (6) Subquery with InnoDB table in WHERE + +SELECT d.id, d.val +FROM t_duck d +WHERE d.id IN (SELECT id FROM t_inno) +ORDER BY d.id; +id val +1 alpha +2 beta + +# (7) Multiple data types + +CREATE TABLE t_duck_types ( +id INT PRIMARY KEY, +ti TINYINT, +bi BIGINT, +f FLOAT, +d DOUBLE, +dc DECIMAL(10,2), +dt DATE, +ts TIMESTAMP, +txt VARCHAR(100) +) ENGINE=DuckDB; +CREATE TABLE t_inno_types ( +id INT PRIMARY KEY, +ti TINYINT, +bi BIGINT, +f FLOAT, +d DOUBLE, +dc DECIMAL(10,2), +dt DATE, +ts TIMESTAMP, +txt VARCHAR(100) +) ENGINE=InnoDB; +INSERT INTO t_duck_types VALUES (1, 10, 1000000, 1.5, 2.5, 99.99, '2025-01-15', '2025-01-15 10:30:00', 'duck'); +INSERT INTO t_inno_types VALUES (1, 20, 2000000, 3.5, 4.5, 88.88, '2025-06-20', '2025-06-20 14:00:00', 'inno'); +SELECT d.id, d.ti, d.bi, d.f, d.dc, d.txt, i.ti, i.bi, i.f, i.dc, i.txt +FROM t_duck_types d JOIN t_inno_types i ON d.id = i.id; +id ti bi f dc txt ti bi f dc txt +1 10 1000000 1.5 99.99 duck 20 2000000 3.5 88.88 inno + +# (8) NULL handling across engines + +CREATE TABLE t_duck_null (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno_null (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=InnoDB; +INSERT INTO t_duck_null VALUES (1, 'a'), (2, NULL); +INSERT INTO t_inno_null VALUES (1, NULL), (2, 'b'); +SELECT d.id, d.val AS dval, i.val AS ival +FROM t_duck_null d JOIN t_inno_null i ON d.id = i.id +ORDER BY d.id; +id dval ival +1 a NULL +2 NULL b + +# Cleanup + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_duck_types; +DROP TABLE t_inno_types; +DROP TABLE t_duck_null; +DROP TABLE t_inno_null; +DROP DATABASE cross_engine_join; diff --git a/storage/duckdb/mysql-test/duckdb/r/cross_engine_union.result b/storage/duckdb/mysql-test/duckdb/r/cross_engine_union.result new file mode 100644 index 0000000000000..cf257e46a109f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/cross_engine_union.result @@ -0,0 +1,117 @@ +# +# Cross-engine UNION: DuckDB + InnoDB via select_handler unit pushdown +# + +# Setup + +CREATE DATABASE IF NOT EXISTS cross_engine_union CHARACTER SET utf8mb4; +USE cross_engine_union; +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=InnoDB; +INSERT INTO t_duck VALUES (1, 'duck_a'), (2, 'duck_b'), (3, 'duck_c'); +INSERT INTO t_inno VALUES (2, 'inno_b'), (3, 'inno_c'), (4, 'inno_d'); + +# (1) UNION ALL — keeps duplicates + +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_inno +ORDER BY id, val; +id val +1 duck_a +2 duck_b +2 inno_b +3 duck_c +3 inno_c +4 inno_d + +# (2) UNION — removes duplicates + +SELECT id FROM t_duck +UNION +SELECT id FROM t_inno +ORDER BY id; +id +1 +2 +3 +4 + +# (3) UNION ALL with WHERE filters + +SELECT id, val FROM t_duck WHERE id >= 2 +UNION ALL +SELECT id, val FROM t_inno WHERE id <= 3 +ORDER BY id, val; +id val +2 duck_b +2 inno_b +3 duck_c +3 inno_c + +# (4) Three-way UNION across engines + +CREATE TABLE t_duck2 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t_duck2 VALUES (10, 'extra'); +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_inno +UNION ALL +SELECT id, val FROM t_duck2 +ORDER BY id; +id val +1 duck_a +10 extra +2 duck_b +2 inno_b +3 duck_c +3 inno_c +4 inno_d + +# (5) UNION with aggregation + +SELECT COUNT(*) AS cnt FROM ( +SELECT id FROM t_duck +UNION ALL +SELECT id FROM t_inno +) AS combined; +cnt +6 + +# (6) UNION between two DuckDB tables (pure DuckDB, no cross-engine) + +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_duck2 +ORDER BY id; +id val +1 duck_a +10 extra +2 duck_b +3 duck_c + +# (7) EXCEPT + +SELECT id FROM t_duck +EXCEPT +SELECT id FROM t_inno +ORDER BY id; +id +1 + +# (8) INTERSECT + +SELECT id FROM t_duck +INTERSECT +SELECT id FROM t_inno +ORDER BY id; +id +2 +3 + +# Cleanup + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_duck2; +DROP DATABASE cross_engine_union; diff --git a/storage/duckdb/mysql-test/duckdb/r/cross_engine_where.result b/storage/duckdb/mysql-test/duckdb/r/cross_engine_where.result new file mode 100644 index 0000000000000..924993519dde1 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/cross_engine_where.result @@ -0,0 +1,147 @@ +# +# Cross-engine: WHERE pushdown, LIMIT, EXPLAIN, error cases +# + +# Setup + +DROP DATABASE IF EXISTS cross_engine_where; +Warnings: +Note 1008 Can't drop database 'cross_engine_where'; database doesn't exist +CREATE DATABASE cross_engine_where CHARACTER SET utf8mb4; +USE cross_engine_where; +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, name VARCHAR(50), score INT) ENGINE=InnoDB; +INSERT INTO t_duck VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'), (4, 'delta'), (5, 'epsilon'); +INSERT INTO t_inno VALUES (1, 'Alice', 90), (2, 'Bob', 80), (3, 'Carol', 70), (4, 'Dave', 60), (5, 'Eve', 50); + +# (1) WHERE on InnoDB table — equality + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.name = 'Bob'; +id val name +2 beta Bob + +# (2) WHERE on InnoDB table — range + +SELECT d.id, d.val, i.name, i.score +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.score >= 70 +ORDER BY d.id; +id val name score +1 alpha Alice 90 +2 beta Bob 80 +3 gamma Carol 70 + +# (3) WHERE on InnoDB table — compound AND + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.score > 50 AND i.name < 'Dave' + ORDER BY d.id; +id val name +1 alpha Alice +2 beta Bob +3 gamma Carol + +# (4) WHERE on InnoDB table — OR + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.name = 'Alice' OR i.name = 'Eve' + ORDER BY d.id; +id val name +1 alpha Alice +5 epsilon Eve + +# (5) WHERE on InnoDB table — IN list + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.id IN (1, 3, 5) +ORDER BY d.id; +id val name +1 alpha Alice +3 gamma Carol +5 epsilon Eve + +# (6) WHERE on InnoDB table — BETWEEN + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.score BETWEEN 60 AND 80 +ORDER BY d.id; +id val name +2 beta Bob +3 gamma Carol +4 delta Dave + +# (7) LIMIT on cross-engine join + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +ORDER BY d.id +LIMIT 2; +id val name +1 alpha Alice +2 beta Bob + +# (8) LIMIT with OFFSET + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +ORDER BY d.id +LIMIT 2 OFFSET 1; +id val name +2 beta Bob +3 gamma Carol + +# (9) WHERE + LIMIT combined + +SELECT d.id, d.val, i.name +FROM t_duck d JOIN t_inno i ON d.id = i.id +WHERE i.score >= 60 +ORDER BY d.id +LIMIT 2; +id val name +1 alpha Alice +2 beta Bob + +# (10) Large-ish scan to check multi-chunk fiber yield + +CREATE TABLE t_inno_big (id INT PRIMARY KEY, val INT) ENGINE=InnoDB; +INSERT INTO t_inno_big SELECT seq, seq * 10 FROM test.seq_1_to_3000; +CREATE TABLE t_duck_big (id INT PRIMARY KEY, label VARCHAR(20)) ENGINE=DuckDB; +INSERT INTO t_duck_big VALUES (1, 'start'), (1500, 'mid'), (3000, 'end'); +SELECT d.id, d.label, i.val +FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id +ORDER BY d.id; +id label val +1 start 10 +1500 mid 15000 +3000 end 30000 + +# (11) COUNT over large InnoDB scan + +SELECT COUNT(*) FROM ( +SELECT d.id FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id +) AS sub; +COUNT(*) +3 + +# (12) WHERE on large table — should push down + +SELECT d.id, d.label, i.val +FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id +WHERE i.val > 20000 +ORDER BY d.id; +id label val +3000 end 30000 + +# Cleanup + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_inno_big; +DROP TABLE t_duck_big; +DROP DATABASE cross_engine_where; diff --git a/storage/duckdb/mysql-test/duckdb/r/decimal_high_precision.result b/storage/duckdb/mysql-test/duckdb/r/decimal_high_precision.result new file mode 100644 index 0000000000000..96a766bdea449 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/decimal_high_precision.result @@ -0,0 +1,91 @@ +# +# decimal with high precision +# decimal precision more than 38 is not supported by DuckDB, use double by default +# +# Create tables +USE innodb_db; +create table t_decimal (id int primary key, a decimal(65, 30),b decimal(65, 15),c decimal(65, 0),d decimal(38,30),e decimal(38,18),f decimal(38,0),g decimal(9,9),h decimal(9,4),i decimal(9,0)) engine = innodb; +USE duckdb_db; +create table t_decimal (id int primary key, a decimal(65, 30),b decimal(65, 15),c decimal(65, 0),d decimal(38,30),e decimal(38,18),f decimal(38,0),g decimal(9,9),h decimal(9,4),i decimal(9,0)) engine = duckdb; +# display innodb table structure +desc innodb_db.t_decimal; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +a decimal(65,30) YES NULL +b decimal(65,15) YES NULL +c decimal(65,0) YES NULL +d decimal(38,30) YES NULL +e decimal(38,18) YES NULL +f decimal(38,0) YES NULL +g decimal(9,9) YES NULL +h decimal(9,4) YES NULL +i decimal(9,0) YES NULL +# display duckdb table structure +desc duckdb_db.t_decimal; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +a decimal(65,30) YES NULL +b decimal(65,15) YES NULL +c decimal(65,0) YES NULL +d decimal(38,30) YES NULL +e decimal(38,18) YES NULL +f decimal(38,0) YES NULL +g decimal(9,9) YES NULL +h decimal(9,4) YES NULL +i decimal(9,0) YES NULL +# Insert data into innodb +USE innodb_db; +insert into t_decimal values (1, 99999999999999999999999999999999999.999999999999999999999999999999,99999999999999999999999999999999999999999999999999.999999999999999,99999999999999999999999999999999999999999999999999999999999999999,99999999.999999999999999999999999999999,99999999999999999999.999999999999999999,99999999999999999999999999999999999999,0.99999999,99999.9999,999999999); +# Insert data into duckdb +USE duckdb_db; +SET GLOBAL duckdb_dml_in_batch = OFF; +insert into t_decimal values (1, 99999999999999999999999999999999999.999999999999999999999999999999,99999999999999999999999999999999999999999999999999.999999999999999,99999999999999999999999999999999999999999999999999999999999999999,99999999.999999999999999999999999999999,99999999999999999999.999999999999999999,99999999999999999999999999999999999999,0.99999999,99999.9999,999999999); +# Compare results +innodb_result : 1 99999999999999999999999999999999999.999999999999999999999999999999 99999999999999999999999999999999999999999999999999.999999999999999 99999999999999999999999999999999999999999999999999999999999999999 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 0.999999990 99999.9999 999999999 +duckdb_result : 1 99999999999999999999999999999999999.999999999999999999999999999999 99999999999999999999999999999999999999999999999999.999999999999999 99999999999999999999999999999999999999999999999999999999999999999 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 0.999999990 99999.9999 999999999 +# Note: precision differences are expected for columns with precision > 38 +# cleanup +# +# decimal(2): high precision(>38) with low precision value(<=38) and duckdb_use_double_for_decimal=on +# +set global duckdb_use_double_for_decimal=on; +# display innodb table structure +desc innodb_db.t1; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +c2 decimal(40,5) YES NULL +c3 decimal(40,5) YES NULL +# display duckdb table structure +desc duckdb_db.t1; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +c2 decimal(40,5) YES NULL +c3 decimal(40,5) YES NULL +# use double +innodb_result : 4 123456789012345678901234567890.12345 +duckdb_result : 4 123456789012345680000000000000.00000 +set global duckdb_use_double_for_decimal=default; +# +# decimal(3): high precision(>38) with high precision value(>38) and duckdb_use_double_for_decimal=on +# +set global duckdb_use_double_for_decimal=on; +# display innodb table structure +desc innodb_db.t1; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +c2 decimal(40,5) YES NULL +c3 decimal(40,5) YES NULL +# display duckdb table structure +desc duckdb_db.t1; +Field Type Null Key Default Extra +id int(11) NO PRI NULL +c1 decimal(40,5) YES NULL +c2 decimal(40,5) YES NULL +c3 decimal(40,5) YES NULL +# use double +innodb_result : 4 12345678901234567890123456789012345.12345 +duckdb_result : 4 12345678901234570000000000000000000.00000 +set global duckdb_use_double_for_decimal=default; diff --git a/storage/duckdb/mysql-test/duckdb/r/decimal_precision_all_possibilities.result b/storage/duckdb/mysql-test/duckdb/r/decimal_precision_all_possibilities.result new file mode 100644 index 0000000000000..39f438d3547c1 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/decimal_precision_all_possibilities.result @@ -0,0 +1,313 @@ +set global duckdb_dml_in_batch = OFF; +# +#1. prepare +# +SELECT @@duckdb_dml_in_batch; +@@duckdb_dml_in_batch +0 +# +#2. duckdb_use_double_for_decimal = on (default) +# +SET GLOBAL duckdb_use_double_for_decimal = on; +#2.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (1, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 1; +id c1 +1 123456789012345678901234567890123.12345 +INSERT INTO t1 values (101, 0); +SELECT * FROM t1 WHERE id = 101; +id c1 +101 0.00000 +INSERT INTO t1 values (102, 0.00000); +SELECT * FROM t1 WHERE id = 102; +id c1 +102 0.00000 +#2.2 low precision(<=38) with high precision value(>38) +#ok, The precision after point can be lost +INSERT INTO t1 values (2, 123456789012345678901234567890123.123456); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 2; +id c1 +2 123456789012345678901234567890123.12346 +#error, The precision before point can not be lost +INSERT INTO t1 values (3, 1234567890123456789012345678901234.123456); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 3; +id c1 +DROP TABLE t1; +#2.3 high precision column(>38) with low precision value(<38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +# should be double +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DOUBLE 53 + + +INSERT INTO t1 values (4, 123456789012345678901234567890.12345); +SELECT * FROM t1 WHERE id = 4; +id c1 +4 1.2345678901234568e29 +#2.4 high precision column(>38) with low precision value(<38) +INSERT INTO t1 values (5, 12345678901234567890123456789012345.12345678); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 5; +id c1 +5 1.234567890123457e34 +INSERT INTO t1 values (6, 12345678901234567890123456789012345678.12345); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 6; +id c1 +CALL dbms_duckdb.query("INSERT INTO t1 values (5, 12345678901234567890123456789012345678.12345)"); +RESULT +Count +BIGINT +[ Rows: 1] +1 + + +SELECT * FROM t1 WHERE id = 6; +id c1 +DROP TABLE t1; +# +#3. duckdb_use_double_for_decimal = false +# +SET GLOBAL duckdb_use_double_for_decimal = off; +#3.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (7, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 7; +id c1 +7 123456789012345678901234567890123.12345 +INSERT INTO t1 values (701, 0); +SELECT * FROM t1 WHERE id = 701; +id c1 +701 0.00000 +INSERT INTO t1 values (702, 0.00000); +SELECT * FROM t1 WHERE id = 702; +id c1 +702 0.00000 +#3.2 low precision(<=38) with high precision value(>38) +#ok, The precision after point can be lost +INSERT INTO t1 values (8, 123456789012345678901234567890123.123456); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 8; +id c1 +8 123456789012345678901234567890123.12346 +#error, The precision before point can not be lost +INSERT INTO t1 values (9, 1234567890123456789012345678901234.123456); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 9; +id c1 +DROP TABLE t1; +#3.3 high precision column(>38) with low precision value(<38) +#duckdb use decimal(38, dec) for both high and low precision(<=38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (10, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 10; +id c1 +10 123456789012345678901234567890123.12345 +#3.4 high precision column(>38) with high precision value(>38) +#duckdb use decimal(38, dec) +INSERT INTO t1 values (11, 1234567890123456789012345678901234.12345); +Got one of the listed errors +SELECT * FROM t1 WHERE id = 11; +id c1 +INSERT INTO t1 values (12, 123456789012345678901234567890123.12345678); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 12; +id c1 +12 123456789012345678901234567890123.12346 +DROP TABLE t1; +set global duckdb_use_double_for_decimal = default; +set global duckdb_dml_in_batch = ON; +# +#1. prepare +# +SELECT @@duckdb_dml_in_batch; +@@duckdb_dml_in_batch +1 +# +#2. duckdb_use_double_for_decimal = on (default) +# +SET GLOBAL duckdb_use_double_for_decimal = on; +#2.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (1, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 1; +id c1 +1 123456789012345678901234567890123.12345 +INSERT INTO t1 values (101, 0); +SELECT * FROM t1 WHERE id = 101; +id c1 +101 0.00000 +INSERT INTO t1 values (102, 0.00000); +SELECT * FROM t1 WHERE id = 102; +id c1 +102 0.00000 +#2.2 low precision(<=38) with high precision value(>38) +#ok, The precision after point can be lost +INSERT INTO t1 values (2, 123456789012345678901234567890123.123456); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 2; +id c1 +2 123456789012345678901234567890123.12346 +#error, The precision before point can not be lost +INSERT INTO t1 values (3, 1234567890123456789012345678901234.123456); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 3; +id c1 +DROP TABLE t1; +#2.3 high precision column(>38) with low precision value(<38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +# should be double +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DOUBLE 53 + + +INSERT INTO t1 values (4, 123456789012345678901234567890.12345); +SELECT * FROM t1 WHERE id = 4; +id c1 +4 1.2345678901234568e29 +#2.4 high precision column(>38) with low precision value(<38) +INSERT INTO t1 values (5, 12345678901234567890123456789012345.12345678); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 5; +id c1 +5 1.234567890123457e34 +INSERT INTO t1 values (6, 12345678901234567890123456789012345678.12345); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 6; +id c1 +CALL dbms_duckdb.query("INSERT INTO t1 values (5, 12345678901234567890123456789012345678.12345)"); +RESULT +Count +BIGINT +[ Rows: 1] +1 + + +SELECT * FROM t1 WHERE id = 6; +id c1 +DROP TABLE t1; +# +#3. duckdb_use_double_for_decimal = false +# +SET GLOBAL duckdb_use_double_for_decimal = off; +#3.1 low precision(<=38) with low precision value +CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (7, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 7; +id c1 +7 123456789012345678901234567890123.12345 +INSERT INTO t1 values (701, 0); +SELECT * FROM t1 WHERE id = 701; +id c1 +701 0.00000 +INSERT INTO t1 values (702, 0.00000); +SELECT * FROM t1 WHERE id = 702; +id c1 +702 0.00000 +#3.2 low precision(<=38) with high precision value(>38) +#ok, The precision after point can be lost +INSERT INTO t1 values (8, 123456789012345678901234567890123.123456); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 8; +id c1 +8 123456789012345678901234567890123.12346 +#error, The precision before point can not be lost +INSERT INTO t1 values (9, 1234567890123456789012345678901234.123456); +ERROR 22003: Out of range value for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 9; +id c1 +DROP TABLE t1; +#3.3 high precision column(>38) with low precision value(<38) +#duckdb use decimal(38, dec) for both high and low precision(<=38) +CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) engine=duckdb; +# should be decimal(38, 5) +CALL dbms_duckdb.query("SELECT column_name, column_index, data_type, numeric_precision FROM duckdb_columns() WHERE schema_name = 'test' AND table_name = 't1' and column_name = 'c1'"); +RESULT +column_name column_index data_type numeric_precision +VARCHAR INTEGER VARCHAR INTEGER +[ Rows: 1] +c1 2 DECIMAL(38,5) 38 + + +INSERT INTO t1 values (10, 123456789012345678901234567890123.12345); +SELECT * FROM t1 WHERE id = 10; +id c1 +10 123456789012345678901234567890123.12345 +#3.4 high precision column(>38) with high precision value(>38) +#duckdb use decimal(38, dec) +INSERT INTO t1 values (11, 1234567890123456789012345678901234.12345); +Got one of the listed errors +SELECT * FROM t1 WHERE id = 11; +id c1 +INSERT INTO t1 values (12, 123456789012345678901234567890123.12345678); +Warnings: +Note 1265 Data truncated for column 'c1' at row 1 +SELECT * FROM t1 WHERE id = 12; +id c1 +12 123456789012345678901234567890123.12346 +DROP TABLE t1; +set global duckdb_use_double_for_decimal = default; +set global duckdb_dml_in_batch = default; diff --git a/storage/duckdb/mysql-test/duckdb/r/dml_delete.result b/storage/duckdb/mysql-test/duckdb/r/dml_delete.result new file mode 100644 index 0000000000000..7a7529dd24f4e --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/dml_delete.result @@ -0,0 +1,89 @@ +# +# DuckDB DELETE operations test +# +# Setup +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +CREATE TABLE t1 ( +id INT PRIMARY KEY, +val VARCHAR(50) +) ENGINE = DuckDB; +CREATE TABLE t2 ( +id INT PRIMARY KEY, +val VARCHAR(50) +) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); +INSERT INTO t2 VALUES (1, 'alpha'); +INSERT INTO t2 VALUES (3, 'beta'); +INSERT INTO t2 VALUES (5, 'gamma'); +# Test 1: DELETE single row by PK +DELETE FROM t1 WHERE id = 1; +SELECT * FROM t1 ORDER BY id; +id val +2 two +3 three +4 four +5 five +# Test 2: DELETE by non-PK condition +DELETE FROM t1 WHERE val = 'three'; +SELECT * FROM t1 ORDER BY id; +id val +2 two +4 four +5 five +# Test 3: DELETE with range condition +DELETE FROM t1 WHERE id >= 4; +SELECT * FROM t1 ORDER BY id; +id val +2 two +# Test 4: DELETE all rows +DELETE FROM t1; +SELECT * FROM t1 ORDER BY id; +id val +SELECT COUNT(*) FROM t1; +COUNT(*) +0 +# Test 5: DELETE with subquery +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); +DELETE FROM t1 WHERE id IN (SELECT id FROM t2); +ERROR HY000: Storage engine DUCKDB of the table `db_dml_delete`.`t2` doesn't have this option +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 two +3 three +4 four +5 five +# Test 6: DELETE in transaction — COMMIT +BEGIN; +DELETE FROM t1 WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +id val +1 one +3 three +COMMIT; +SELECT * FROM t1 ORDER BY id; +id val +1 one +3 three +# Test 7: DELETE in transaction — ROLLBACK +BEGIN; +DELETE FROM t1 WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +id val +1 one +3 three +ROLLBACK; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 two +3 three diff --git a/storage/duckdb/mysql-test/duckdb/r/dml_update.result b/storage/duckdb/mysql-test/duckdb/r/dml_update.result new file mode 100644 index 0000000000000..d156c32299ac0 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/dml_update.result @@ -0,0 +1,103 @@ +# +# DuckDB UPDATE operations test +# +# Setup +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( +id INT PRIMARY KEY, +val VARCHAR(50) +) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); +# Test 1: UPDATE single row by PK +UPDATE t1 SET val = 'ONE' WHERE id = 1; +SELECT * FROM t1 ORDER BY id; +id val +1 ONE +2 two +3 three +4 four +5 five +# Test 2: UPDATE by non-PK condition +UPDATE t1 SET val = 'THREE' WHERE val = 'three'; +SELECT * FROM t1 ORDER BY id; +id val +1 ONE +2 two +3 THREE +4 four +5 five +# Test 3: UPDATE with range condition +UPDATE t1 SET val = 'big' WHERE id >= 4; +SELECT * FROM t1 ORDER BY id; +id val +1 ONE +2 two +3 THREE +4 big +5 big +# Test 4: UPDATE multiple columns +UPDATE t1 SET id = 10, val = 'ten' WHERE id = 5; +SELECT * FROM t1 ORDER BY id; +id val +1 ONE +2 two +3 THREE +4 big +10 ten +# Test 5: UPDATE all rows +UPDATE t1 SET val = 'reset'; +SELECT * FROM t1 ORDER BY id; +id val +1 reset +2 reset +3 reset +4 reset +10 reset +# Test 6: UPDATE with expression +UPDATE t1 SET val = CONCAT('id_', id); +SELECT * FROM t1 ORDER BY id; +id val +1 id_1 +2 id_2 +3 id_3 +4 id_4 +10 id_10 +# Test 7: UPDATE in transaction — COMMIT +BEGIN; +UPDATE t1 SET val = 'UPDATED' WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 UPDATED +3 three +COMMIT; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 UPDATED +3 three +# Test 8: UPDATE in transaction — ROLLBACK +BEGIN; +UPDATE t1 SET val = 'UPDATED' WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 UPDATED +3 three +ROLLBACK; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 two +3 three +# Test 9: UPDATE — no matching rows +UPDATE t1 SET val = 'ghost' WHERE id = 999; +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 two +3 three diff --git a/storage/duckdb/mysql-test/duckdb/r/drop_database.result b/storage/duckdb/mysql-test/duckdb/r/drop_database.result new file mode 100644 index 0000000000000..23b321b82eda7 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/drop_database.result @@ -0,0 +1,84 @@ +# +# DuckDB DROP DATABASE operations test +# +# Test 1: Basic CREATE/DROP DATABASE with DuckDB table +CREATE DATABASE IF NOT EXISTS db_duck1 CHARACTER SET utf8mb4; +USE db_duck1; +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three'); +SELECT * FROM t1 ORDER BY id; +id val +1 one +2 two +3 three +USE test; +DROP DATABASE db_duck1; +USE db_duck1; +ERROR 42000: Unknown database 'db_duck1' +# Test 2: Re-create same database after DROP +CREATE DATABASE db_duck1 CHARACTER SET utf8mb4; +USE db_duck1; +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (10, 'ten'); +SELECT * FROM t1 ORDER BY id; +id val +10 ten +USE test; +DROP DATABASE db_duck1; +# Test 3: DROP DATABASE with multiple DuckDB tables +CREATE DATABASE db_duck2 CHARACTER SET utf8mb4; +USE db_duck2; +CREATE TABLE t1 (id INT PRIMARY KEY, a INT) ENGINE = DuckDB; +CREATE TABLE t2 (id INT PRIMARY KEY, b VARCHAR(20)) ENGINE = DuckDB; +CREATE TABLE t3 (id INT PRIMARY KEY, c DECIMAL(10,2)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 100), (2, 200); +INSERT INTO t2 VALUES (1, 'alpha'), (2, 'beta'); +INSERT INTO t3 VALUES (1, 1.11), (2, 2.22); +SELECT * FROM t1 ORDER BY id; +id a +1 100 +2 200 +SELECT * FROM t2 ORDER BY id; +id b +1 alpha +2 beta +SELECT * FROM t3 ORDER BY id; +id c +1 1.11 +2 2.22 +USE test; +DROP DATABASE db_duck2; +CREATE DATABASE db_duck2 CHARACTER SET utf8mb4; +USE db_duck2; +CREATE TABLE t1 (id INT PRIMARY KEY, x INT) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 999); +SELECT * FROM t1; +id x +1 999 +USE test; +DROP DATABASE db_duck2; +# Test 4: DROP DATABASE with special name +CREATE DATABASE `my-duck-db` CHARACTER SET utf8mb4; +USE `my-duck-db`; +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'special'); +SELECT * FROM t1; +id val +1 special +USE test; +DROP DATABASE `my-duck-db`; +# Test 5: DROP DATABASE IF EXISTS on non-existent DB +DROP DATABASE IF EXISTS db_nonexistent_duck; +Warnings: +Note 1008 Can't drop database 'db_nonexistent_duck'; database doesn't exist +# Test 6: CREATE/DROP DATABASE interleaved with DML on test +CREATE DATABASE db_duck3 CHARACTER SET utf8mb4; +USE db_duck3; +CREATE TABLE t1 (id INT PRIMARY KEY, val INT) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 10); +SELECT * FROM t1; +id val +1 10 +USE test; +DROP DATABASE db_duck3; +# All tests passed diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_add_backticks.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_add_backticks.result new file mode 100644 index 0000000000000..50df896721ab2 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_add_backticks.result @@ -0,0 +1,406 @@ +SET GLOBAL duckdb_require_primary_key=OFF; +# +# Test whether the convertor accurately adds backticks to schema names, +# table names, and column names in DDL statements +# and correctly passes them to the DuckDB engine for execution. +# +# +# Test: drop and create databases with a name starting with a digit or containing only digits. +# +DROP DATABASE IF EXISTS `09898141`; +CREATE DATABASE `09898141`; +DROP DATABASE IF EXISTS `011fq123`; +CREATE DATABASE `011fq123`; +USE `09898141`; +SELECT schema_name FROM information_schema.schemata WHERE schema_name = '09898141'; +SCHEMA_NAME +09898141 +CALL DBMS_DUCKDB.QUERY("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '09898141'"); +RESULT +schema_name +VARCHAR +[ Rows: 1] +09898141 + + +SELECT schema_name FROM information_schema.schemata WHERE schema_name = '011fq123'; +SCHEMA_NAME +011fq123 +CALL DBMS_DUCKDB.QUERY("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '011fq123'"); +RESULT +schema_name +VARCHAR +[ Rows: 1] +011fq123 + + +# +# Test: drop and create tables with a name starting with a digit or containing only digits. +# +DROP TABLE IF EXISTS `001`; +CREATE TABLE `001` ( +`00000000000` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY, +`0a01131` VARCHAR(50) +) ENGINE=DuckDB; +DROP TABLE IF EXISTS `111a`; +CREATE TABLE `111a` ( +`#0x1141` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY +) ENGINE=DuckDB; +CREATE TABLE `321` ( +`009` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY +) ENGINE=DuckDB; +SELECT table_name FROM information_schema.tables WHERE table_schema='09898141'; +TABLE_NAME +001 +111a +321 +CALL DBMS_DUCKDB.QUERY("SELECT table_name FROM information_schema.tables WHERE table_schema='09898141'"); +RESULT +table_name +VARCHAR +[ Rows: 3] +001 +111a +321 + + +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='001'; +COLUMN_NAME +00000000000 +0a01131 +CALL DBMS_DUCKDB.QUERY("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='001'"); +RESULT +column_name +VARCHAR +[ Rows: 2] +00000000000 +0a01131 + + +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='111a'; +COLUMN_NAME +#0x1141 +CALL DBMS_DUCKDB.QUERY("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='111a'"); +RESULT +column_name +VARCHAR +[ Rows: 1] +#0x1141 + + +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='321'; +COLUMN_NAME +009 +CALL DBMS_DUCKDB.QUERY("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='321'"); +RESULT +column_name +VARCHAR +[ Rows: 1] +009 + + +# +# Test: insert(delete and update are not currently supported) +# +USE `09898141`; +INSERT INTO `001` VALUES (1, 'a'),(2, 'b'),(3, 'c'),(4, 'd'),(5, 'e'); +INSERT INTO `111a` VALUES (1), (2), (3), (4), (5); +SELECT * from `09898141`.`001`; +00000000000 0a01131 +1 a +2 b +3 c +4 d +5 e +CALL DBMS_DUCKDB.QUERY("SELECT * FROM `09898141`.`001`"); +RESULT +00000000000 0a01131 +BIGINT VARCHAR +[ Rows: 5] +1 a +2 b +3 c +4 d +5 e + + +SELECT * from `09898141`.`111a`; +#0x1141 +1 +2 +3 +4 +5 +CALL DBMS_DUCKDB.QUERY("SELECT * from `09898141`.`111a`"); +RESULT +#0x1141 +BIGINT +[ Rows: 5] +1 +2 +3 +4 +5 + + +# +# Test: select +# +SELECT * FROM `001` ORDER BY `00000000000` DESC; +00000000000 0a01131 +5 e +4 d +3 c +2 b +1 a +CALL DBMS_DUCKDB.QUERY("SELECT * FROM `09898141`.`001` ORDER BY `00000000000` DESC"); +RESULT +00000000000 0a01131 +BIGINT VARCHAR +[ Rows: 5] +5 e +4 d +3 c +2 b +1 a + + +SELECT COUNT(*) FROM `001` WHERE `00000000000` > 2 GROUP BY `00000000000`; +COUNT(*) +1 +1 +1 +CALL DBMS_DUCKDB.QUERY("SELECT COUNT(*) FROM `09898141`.`001` WHERE `00000000000` > 2 GROUP BY `00000000000`"); +RESULT +count_star() +BIGINT +[ Rows: 3] +1 +1 +1 + + +USE `011fq123`; +SELECT `#0x1141` FROM `09898141`.`111a` ORDER BY `#0x1141` DESC; +#0x1141 +5 +4 +3 +2 +1 +CALL DBMS_DUCKDB.QUERY("SELECT `#0x1141` FROM `09898141`.`111a` ORDER BY `#0x1141` DESC"); +RESULT +#0x1141 +BIGINT +[ Rows: 5] +5 +4 +3 +2 +1 + + +SELECT `0a01131` FROM `09898141`.`001` ORDER BY `0a01131` DESC; +0a01131 +e +d +c +b +a +CALL DBMS_DUCKDB.QUERY("SELECT `0a01131` FROM `09898141`.`001` ORDER BY `0a01131` DESC"); +RESULT +0a01131 +VARCHAR +[ Rows: 5] +e +d +c +b +a + + +# +# Test: alter table +# +USE `09898141`; +ALTER TABLE `001` ADD COLUMN `0A` BIGINT DEFAULT 0; +SHOW CREATE TABLE `001`; +Table Create Table +001 CREATE TABLE `001` ( + `00000000000` bigint NOT NULL DEFAULT '0', + `0a01131` varchar(50) DEFAULT NULL, + `0A` bigint DEFAULT '0', + PRIMARY KEY (`00000000000`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL DBMS_DUCKDB.QUERY("PRAGMA table_info(`09898141`.`001`)"); +RESULT +cid name type notnull dflt_value pk +INTEGER VARCHAR VARCHAR BOOLEAN VARCHAR BOOLEAN +[ Rows: 3] +0 00000000000 BIGINT true '0' false +1 0a01131 VARCHAR false NULL false +2 0A BIGINT false '0' false + + +SELECT * FROM `001`; +00000000000 0a01131 0A +1 a 0 +2 b 0 +3 c 0 +4 d 0 +5 e 0 +CALL DBMS_DUCKDB.QUERY("SELECT * FROM `09898141`.`001`"); +RESULT +00000000000 0a01131 0A +BIGINT VARCHAR BIGINT +[ Rows: 5] +1 a 0 +2 b 0 +3 c 0 +4 d 0 +5 e 0 + + +ALTER TABLE `001` MODIFY `0A` INT; +ALTER TABLE `001` RENAME COLUMN `0A` TO `0B`; +ALTER TABLE `001` MODIFY `0B` INT DEFAULT '0'; +ALTER TABLE `001` MODIFY `0B` INT NOT NULL; +ALTER TABLE `09898141`.`001` MODIFY `0B` INT NULL; +ALTER TABLE `09898141`.`001` MODIFY `0B` INT; +ALTER TABLE `09898141`.`001` RENAME TO `101`; +SHOW CREATE TABLE `101`; +Table Create Table +101 CREATE TABLE `101` ( + `00000000000` bigint NOT NULL DEFAULT '0', + `0a01131` varchar(50) DEFAULT NULL, + `0B` int DEFAULT NULL, + PRIMARY KEY (`00000000000`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL DBMS_DUCKDB.QUERY("PRAGMA table_info(`09898141`.`101`)"); +RESULT +cid name type notnull dflt_value pk +INTEGER VARCHAR VARCHAR BOOLEAN VARCHAR BOOLEAN +[ Rows: 3] +0 00000000000 BIGINT true '0' false +1 0a01131 VARCHAR false NULL false +2 0B INTEGER false NULL false + + +SELECT * FROM `101`; +00000000000 0a01131 0B +1 a 0 +2 b 0 +3 c 0 +4 d 0 +5 e 0 +CALL DBMS_DUCKDB.QUERY("SELECT * FROM `09898141`.`101`"); +RESULT +00000000000 0a01131 0B +BIGINT VARCHAR INTEGER +[ Rows: 5] +1 a 0 +2 b 0 +3 c 0 +4 d 0 +5 e 0 + + +ALTER TABLE `101` DROP COLUMN `0B`; +SELECT * FROM `101`; +00000000000 0a01131 +1 a +2 b +3 c +4 d +5 e +CALL DBMS_DUCKDB.QUERY("SELECT * FROM `09898141`.`101`"); +RESULT +00000000000 0a01131 +BIGINT VARCHAR +[ Rows: 5] +1 a +2 b +3 c +4 d +5 e + + +# +# Test: truncate tables +# +TRUNCATE TABLE `09898141`.`111a`; +# +# Test: create tables with special characters +# +DROP DATABASE IF EXISTS mydb1; +CREATE DATABASE mydb1; +CREATE TABLE mydb1.t (i INT, d DECIMAL, f FLOAT); +INSERT INTO mydb1.t VALUES(1,1,1); +CREATE TABLE mydb1.y ENGINE=DuckDB AS SELECT AVG(i), AVG(d), AVG(f) FROM mydb1.t; +SHOW CREATE TABLE mydb1.y; +Table Create Table +y CREATE TABLE `y` ( + `AVG(i)` decimal(14,4) DEFAULT NULL, + `AVG(d)` decimal(14,4) DEFAULT NULL, + `AVG(f)` double DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL DBMS_DUCKDB.QUERY("PRAGMA table_info(mydb1.y)"); +RESULT +cid name type notnull dflt_value pk +INTEGER VARCHAR VARCHAR BOOLEAN VARCHAR BOOLEAN +[ Rows: 3] +0 AVG(i) DECIMAL(14,4) false NULL false +1 AVG(d) DECIMAL(14,4) false NULL false +2 AVG(f) DOUBLE false NULL false + + +# +# Test: drop databases(cascade) +# +DROP DATABASE IF EXISTS `09898141`; +DROP DATABASE IF EXISTS `011fq123`; +DROP DATABASE IF EXISTS mydb1; +# +# Test: database, table and column names with space. +# +CREATE DATABASE `my db`; +CREATE TABLE `my db`.`my table` ( +`my column` INT +) ENGINE=DuckDB; +INSERT INTO `my db`.`my table` VALUES(1); +SELECT `my column` FROM `my db`.`my table`; +my column +1 +CALL DBMS_DUCKDB.QUERY("SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'"); +RESULT +schema_name +VARCHAR +[ Rows: 1] +my db + + +SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'; +SCHEMA_NAME +my db +CALL DBMS_DUCKDB.QUERY("PRAGMA table_info(`my db`.`my table`)"); +RESULT +cid name type notnull dflt_value pk +INTEGER VARCHAR VARCHAR BOOLEAN VARCHAR BOOLEAN +[ Rows: 1] +0 my column INTEGER false NULL false + + +SHOW CREATE TABLE `my db`.`my table`; +Table Create Table +my table CREATE TABLE `my table` ( + `my column` int DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +DROP DATABASE `my db`; +CALL DBMS_DUCKDB.QUERY("SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'"); +RESULT +schema_name +VARCHAR +[ Rows: 0] + + +SET GLOBAL duckdb_require_primary_key=ON; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_agg_func.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_agg_func.result new file mode 100644 index 0000000000000..2c6ab397d9d53 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_agg_func.result @@ -0,0 +1,456 @@ +CREATE TABLE t_innodb ( +id INT PRIMARY KEY, +col1 TINYINT, +col2 INT, +col3 BIGINT, +col4 DOUBLE, +col5 DECIMAL(38, 10), +col6 TIME, +col7 DATE, +col8 DATETIME, +col9 TIMESTAMP, +col10 VARCHAR(100), +col11 BLOB +); +CREATE TABLE t_duckdb ( +id INT PRIMARY KEY, +col1 TINYINT, +col2 INT, +col3 BIGINT, +col4 DOUBLE, +col5 DECIMAL(38, 10), +col6 TIME, +col7 DATE, +col8 DATETIME, +col9 TIMESTAMP, +col10 VARCHAR(100), +col11 BLOB +) ENGINE=DuckDB; +INSERT INTO t_innodb VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +(2, 10, 123456789, 123456789011121314, 123456789011121314.12345678910, 1234567890111213141516171819.123456789, '23:59:59', '2025-12-31', '9999-12-31 23:59:59', '2025-12-31 23:59:59', '1234567890111213141516171819.123456789', '1234567890111213141516171819.123456789'), +(3, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', '-1234567890111213141516171819.123456789', '-1234567890111213141516171819.123456789'), +(4, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', REPEAT('a', 100), REPEAT('a', 65535)); +INSERT INTO t_duckdb VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), +(2, 10, 123456789, 123456789011121314, 123456789011121314.12345678910, 1234567890111213141516171819.123456789, '23:59:59', '2025-12-31', '9999-12-31 23:59:59', '2025-12-31 23:59:59', '1234567890111213141516171819.123456789', '1234567890111213141516171819.123456789'), +(3, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', '-1234567890111213141516171819.123456789', '-1234567890111213141516171819.123456789'), +(4, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', REPEAT('a', 100), REPEAT('a', 65535)); +-------------------------- +1. AVG() +-------------------------- +SELECT AVG(col1), AVG(col2), AVG(col3), AVG(col4), +AVG(col5), AVG(col6), AVG(col7), AVG(col8), +AVG(col9), AVG(col10) +FROM t_innodb; +AVG(col1) AVG(col2) AVG(col3) AVG(col4) AVG(col5) AVG(col6) AVG(col7) AVG(col8) AVG(col9) AVG(col10) +-3.3333 -41152263.0000 -41152263003707104.6667 -4.11522630037071e16 -411522630037071047172057273.04115226300000 78653.0000 19883811.0000 33337144411986.3333 19883811745319.6667 0 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT AVG(col1), AVG(col2), AVG(col3), AVG(col4), +AVG(col5), AVG(col6), AVG(col7), AVG(col8), +AVG(col9), AVG(col10) +FROM t_duckdb; +AVG(col1) AVG(col2) AVG(col3) AVG(col4) AVG(col5) AVG(col6) AVG(col7) AVG(col8) AVG(col9) AVG(col10) +-3.3333333333333335 -41152263 -4.11522630037071e16 -4.11522630037071e16 -4.11522630037071e26 78653 19883811 33337144411986.332 19883811745319.668 0 +SELECT AVG(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'avg(BLOB)'. You might need to add explicit type casts. + Candidate functions: + avg(DECIMAL) -> DECIMAL + avg(SMALLINT) -> DOUBLE + avg(INTEGER) -> DOUBLE + avg(BIGINT) -> DOUBLE + avg(HUGEINT) -> DOUBLE + avg(INTERVAL) -> INTERVAL + avg(DOUBLE) -> DOUBLE + + +LINE 1: SELECT AVG(col11) + ^. +-------------------------- +2. BIT_AND() +-------------------------- +SELECT BIT_AND(col1), BIT_AND(col2), BIT_AND(col3) +FROM t_innodb; +BIT_AND(col1) BIT_AND(col2) BIT_AND(col3) +2 1 2 +SELECT BIT_AND(col1), BIT_AND(col2), BIT_AND(col3) +FROM t_duckdb; +BIT_AND(col1) BIT_AND(col2) BIT_AND(col3) +2 1 2 +SELECT BIT_AND(col4), BIT_AND(col5), BIT_AND(col6), +BIT_AND(col7), BIT_AND(col8), BIT_AND(col9), +BIT_AND(col10) +FROM t_duckdb; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'bit_and(TIME)'. You might need to add explicit type casts. + Candidate functions: + bit_and(TINYINT) -> UBIGINT + bit_and(SMALLINT) -> UBIGINT + bit_and(INTEGER) -> UBIGINT + bit_and(BIGINT) -> UBIGINT + bit_and(HUGEINT) -> HUGEINT + bit_and(UTINYINT) -> UBIGINT + bit_and(USMALLINT) -> UBIGINT + bit_and(UINTEGER) -> UBIGINT + bit_and(UBIGINT) -> UBIGINT + bit_and(UHUGEINT) -> UHUGEINT + bit_and(BIT) -> BIT + + +LINE 1: SELECT BIT_AND(col4), BIT +-------------------------- +3. BIT_OR() +-------------------------- +SELECT BIT_OR(col1), BIT_OR(col2), BIT_OR(col3) +FROM t_innodb; +BIT_OR(col1) BIT_OR(col2) BIT_OR(col3) +18446744073709551614 18446744073709551615 18446744073709551614 +SELECT BIT_OR(col1), BIT_OR(col2), BIT_OR(col3) +FROM t_duckdb; +BIT_OR(col1) BIT_OR(col2) BIT_OR(col3) +18446744073709551614 18446744073709551615 18446744073709551614 +SELECT BIT_OR(col4), BIT_OR(col5), BIT_OR(col6), +BIT_OR(col7), BIT_OR(col8), BIT_OR(col9), +BIT_OR(col10) +FROM t_duckdb; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'bit_or(TIME)'. You might need to add explicit type casts. + Candidate functions: + bit_or(TINYINT) -> UBIGINT + bit_or(SMALLINT) -> UBIGINT + bit_or(INTEGER) -> UBIGINT + bit_or(BIGINT) -> UBIGINT + bit_or(HUGEINT) -> HUGEINT + bit_or(UTINYINT) -> UBIGINT + bit_or(USMALLINT) -> UBIGINT + bit_or(UINTEGER) -> UBIGINT + bit_or(UBIGINT) -> UBIGINT + bit_or(UHUGEINT) -> UHUGEINT + bit_or(BIT) -> BIT + + +LINE 1: SELECT BIT_OR(col4), BIT_OR(col5), BI +-------------------------- +4. BIT_XOR() +-------------------------- +SELECT BIT_XOR(col1), BIT_XOR(col2), BIT_XOR(col3) +FROM t_innodb; +BIT_XOR(col1) BIT_XOR(col2) BIT_XOR(col3) +10 123456789 123456789011121314 +SELECT BIT_XOR(col1), BIT_XOR(col2), BIT_XOR(col3) +FROM t_duckdb; +BIT_XOR(col1) BIT_XOR(col2) BIT_XOR(col3) +10 123456789 123456789011121314 +SELECT BIT_XOR(col4), BIT_XOR(col5), BIT_XOR(col6), +BIT_XOR(col7), BIT_XOR(col8), BIT_XOR(col9), +BIT_XOR(col10) +FROM t_duckdb; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'bit_xor(TIME)'. You might need to add explicit type casts. + Candidate functions: + bit_xor(TINYINT) -> UBIGINT + bit_xor(SMALLINT) -> UBIGINT + bit_xor(INTEGER) -> UBIGINT + bit_xor(BIGINT) -> UBIGINT + bit_xor(HUGEINT) -> HUGEINT + bit_xor(UTINYINT) -> UBIGINT + bit_xor(USMALLINT) -> UBIGINT + bit_xor(UINTEGER) -> UBIGINT + bit_xor(UBIGINT) -> UBIGINT + bit_xor(UHUGEINT) -> UHUGEINT + bit_xor(BIT) -> BIT + + +LINE 1: SELECT BIT_XOR(col4), BIT +-------------------------- +5. COUNT() +-------------------------- +SELECT COUNT(col1), COUNT(col2), COUNT(col3), COUNT(col4), +COUNT(col5), COUNT(col6), COUNT(col7), COUNT(col8), +COUNT(col9), COUNT(col10), COUNT(col11) +FROM t_innodb; +COUNT(col1) COUNT(col2) COUNT(col3) COUNT(col4) COUNT(col5) COUNT(col6) COUNT(col7) COUNT(col8) COUNT(col9) COUNT(col10) COUNT(col11) +3 3 3 3 3 3 3 3 3 3 3 +SELECT COUNT(col1), COUNT(col2), COUNT(col3), COUNT(col4), +COUNT(col5), COUNT(col6), COUNT(col7), COUNT(col8), +COUNT(col9), COUNT(col10), COUNT(col11) +FROM t_duckdb; +COUNT(col1) COUNT(col2) COUNT(col3) COUNT(col4) COUNT(col5) COUNT(col6) COUNT(col7) COUNT(col8) COUNT(col9) COUNT(col10) COUNT(col11) +3 3 3 3 3 3 3 3 3 3 3 +SELECT COUNT(DISTINCT col1), COUNT(DISTINCT col2), COUNT(DISTINCT col3), COUNT(DISTINCT col4), +COUNT(DISTINCT col5), COUNT(DISTINCT col6), COUNT(DISTINCT col7), COUNT(DISTINCT col8), +COUNT(DISTINCT col9), COUNT(DISTINCT col10), COUNT(DISTINCT col11) +FROM t_innodb; +COUNT(DISTINCT col1) COUNT(DISTINCT col2) COUNT(DISTINCT col3) COUNT(DISTINCT col4) COUNT(DISTINCT col5) COUNT(DISTINCT col6) COUNT(DISTINCT col7) COUNT(DISTINCT col8) COUNT(DISTINCT col9) COUNT(DISTINCT col10) COUNT(DISTINCT col11) +2 2 2 2 2 2 2 2 2 3 3 +SELECT COUNT(DISTINCT col1), COUNT(DISTINCT col2), COUNT(DISTINCT col3), COUNT(DISTINCT col4), +COUNT(DISTINCT col5), COUNT(DISTINCT col6), COUNT(DISTINCT col7), COUNT(DISTINCT col8), +COUNT(DISTINCT col9), COUNT(DISTINCT col10), COUNT(DISTINCT col11) +FROM t_duckdb; +COUNT(DISTINCT col1) COUNT(DISTINCT col2) COUNT(DISTINCT col3) COUNT(DISTINCT col4) COUNT(DISTINCT col5) COUNT(DISTINCT col6) COUNT(DISTINCT col7) COUNT(DISTINCT col8) COUNT(DISTINCT col9) COUNT(DISTINCT col10) COUNT(DISTINCT col11) +2 2 2 2 2 2 2 2 2 3 3 +-------------------------- +6. GROUP_CONCAT() +-------------------------- +SET group_concat_max_len = 100000; +SELECT GROUP_CONCAT(col1), GROUP_CONCAT(col2), GROUP_CONCAT(col3), GROUP_CONCAT(col4), +GROUP_CONCAT(col5), GROUP_CONCAT(col6), GROUP_CONCAT(col7), GROUP_CONCAT(col8), +GROUP_CONCAT(col9), LENGTH(GROUP_CONCAT(col10)), LENGTH(GROUP_CONCAT(col11)) +FROM t_innodb; +GROUP_CONCAT(col1) GROUP_CONCAT(col2) GROUP_CONCAT(col3) GROUP_CONCAT(col4) GROUP_CONCAT(col5) GROUP_CONCAT(col6) GROUP_CONCAT(col7) GROUP_CONCAT(col8) GROUP_CONCAT(col9) LENGTH(GROUP_CONCAT(col10)) LENGTH(GROUP_CONCAT(col11)) +10,-10,-10 123456789,-123456789,-123456789 123456789011121314,-123456789011121314,-123456789011121314 1.2345678901112131e17,-1.2345678901112131e17,-1.2345678901112131e17 1234567890111213141516171819.1234567890,-1234567890111213141516171819.1234567890,-1234567890111213141516171819.1234567890 23:59:59,00:00:00,00:00:00 2025-12-31,1970-01-01,1970-01-01 9999-12-31 23:59:59,0001-01-01 00:00:00,0001-01-01 00:00:00 2025-12-31 23:59:59,1970-01-02 00:00:00,1970-01-02 00:00:00 179 65614 +SELECT GROUP_CONCAT(col1), GROUP_CONCAT(col2), GROUP_CONCAT(col3), GROUP_CONCAT(col4), +GROUP_CONCAT(col5), GROUP_CONCAT(col6), GROUP_CONCAT(col7), GROUP_CONCAT(col8), +GROUP_CONCAT(col9), LENGTH(GROUP_CONCAT(col10)), LENGTH(GROUP_CONCAT(col11)) +FROM t_duckdb; +GROUP_CONCAT(col1) GROUP_CONCAT(col2) GROUP_CONCAT(col3) GROUP_CONCAT(col4) GROUP_CONCAT(col5) GROUP_CONCAT(col6) GROUP_CONCAT(col7) GROUP_CONCAT(col8) GROUP_CONCAT(col9) LENGTH(GROUP_CONCAT(col10)) LENGTH(GROUP_CONCAT(col11)) +10,-10,-10 123456789,-123456789,-123456789 123456789011121314,-123456789011121314,-123456789011121314 1.2345678901112131e17,-1.2345678901112131e17,-1.2345678901112131e17 1234567890111213141516171819.1234567890,-1234567890111213141516171819.1234567890,-1234567890111213141516171819.1234567890 23:59:59,00:00:00,00:00:00 2025-12-31,1970-01-01,1970-01-01 9999-12-31 23:59:59,0001-01-01 00:00:00,0001-01-01 00:00:00 2025-12-31 23:59:59,1970-01-02 00:00:00,1970-01-02 00:00:00 179 65614 +-------------------------- +7. JSON_ARRAYAGG() +-------------------------- +-------------------------- +8. JSON_OBJECTAGG() +-------------------------- +-------------------------- +9. MIN() +-------------------------- +SELECT MIN(col1), MIN(col2), MIN(col3), MIN(col4), +MIN(col5), MIN(col6), MIN(col7), MIN(col8), +MIN(col9), MIN(col10), MIN(col11) +FROM t_innodb; +MIN(col1) MIN(col2) MIN(col3) MIN(col4) MIN(col5) MIN(col6) MIN(col7) MIN(col8) MIN(col9) MIN(col10) MIN(col11) +-10 -123456789 -123456789011121314 -1.2345678901112131e17 -1234567890111213141516171819.1234567890 00:00:00 1970-01-01 0001-01-01 00:00:00 1970-01-02 00:00:00 -1234567890111213141516171819.123456789 -1234567890111213141516171819.123456789 +SELECT MIN(col1), MIN(col2), MIN(col3), MIN(col4), +MIN(col5), MIN(col6), MIN(col7), MIN(col8), +MIN(col9), MIN(col10), MIN(col11) +FROM t_duckdb; +MIN(col1) MIN(col2) MIN(col3) MIN(col4) MIN(col5) MIN(col6) MIN(col7) MIN(col8) MIN(col9) MIN(col10) MIN(col11) +-10 -123456789 -123456789011121314 -1.2345678901112131e17 -1234567890111213141516171819.1234567890 00:00:00 1970-01-01 0001-01-01 00:00:00 1970-01-02 00:00:00 -1234567890111213141516171819.123456789 -1234567890111213141516171819.123456789 +-------------------------- +10. MAX() +-------------------------- +SELECT MAX(col1), MAX(col2), MAX(col3), MAX(col4), +MAX(col5), MAX(col6), MAX(col7), MAX(col8), +MAX(col9), LENGTH(MAX(col10)), LENGTH(MAX(col11)) +FROM t_innodb; +MAX(col1) MAX(col2) MAX(col3) MAX(col4) MAX(col5) MAX(col6) MAX(col7) MAX(col8) MAX(col9) LENGTH(MAX(col10)) LENGTH(MAX(col11)) +10 123456789 123456789011121314 1.2345678901112131e17 1234567890111213141516171819.1234567890 23:59:59 2025-12-31 9999-12-31 23:59:59 2025-12-31 23:59:59 100 65535 +SELECT MAX(col1), MAX(col2), MAX(col3), MAX(col4), +MAX(col5), MAX(col6), MAX(col7), MAX(col8), +MAX(col9), LENGTH(MAX(col10)), LENGTH(MAX(col11)) +FROM t_duckdb; +MAX(col1) MAX(col2) MAX(col3) MAX(col4) MAX(col5) MAX(col6) MAX(col7) MAX(col8) MAX(col9) LENGTH(MAX(col10)) LENGTH(MAX(col11)) +10 123456789 123456789011121314 1.2345678901112131e17 1234567890111213141516171819.1234567890 23:59:59 2025-12-31 9999-12-31 23:59:59 2025-12-31 23:59:59 100 65535 +-------------------------- +11. STD() +-------------------------- +SELECT STD(col1), STD(col2), STD(col3), STD(col4), +STD(col5), STD(col6), STD(col7), STD(col8), +STD(col9), STD(col10) +FROM t_innodb; +STD(col1) STD(col2) STD(col3) STD(col4) STD(col5) STD(col6) STD(col7) STD(col8) STD(col9) STD(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT STD(col1), STD(col2), STD(col3), STD(col4), +STD(col5), STD(col6), STD(col7), STD(col8), +STD(col9), STD(col10) +FROM t_duckdb; +STD(col1) STD(col2) STD(col3) STD(col4) STD(col5) STD(col6) STD(col7) STD(col8) STD(col9) STD(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +SELECT STD(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'std(BLOB)'. You might need to add explicit type casts. + Candidate functions: + std(DOUBLE) -> DOUBLE + + +LINE 1: SELECT STD(col11) + ^. +-------------------------- +12. STDDEV() +-------------------------- +SELECT STDDEV(col1), STDDEV(col2), STDDEV(col3), STDDEV(col4), +STDDEV(col5), STDDEV(col6), STDDEV(col7), STDDEV(col8), +STDDEV(col9), STDDEV(col10) +FROM t_innodb; +STDDEV(col1) STDDEV(col2) STDDEV(col3) STDDEV(col4) STDDEV(col5) STDDEV(col6) STDDEV(col7) STDDEV(col8) STDDEV(col9) STDDEV(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT STDDEV(col1), STDDEV(col2), STDDEV(col3), STDDEV(col4), +STDDEV(col5), STDDEV(col6), STDDEV(col7), STDDEV(col8), +STDDEV(col9), STDDEV(col10) +FROM t_duckdb; +STDDEV(col1) STDDEV(col2) STDDEV(col3) STDDEV(col4) STDDEV(col5) STDDEV(col6) STDDEV(col7) STDDEV(col8) STDDEV(col9) STDDEV(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +SELECT STDDEV(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'stddev(BLOB)'. You might need to add explicit type casts. + Candidate functions: + stddev(DOUBLE) -> DOUBLE + + +LINE 1: SELECT STDDEV(col11) + ^. +-------------------------- +13. STDDEV_POP() +-------------------------- +SELECT STDDEV_POP(col1), STDDEV_POP(col2), STDDEV_POP(col3), STDDEV_POP(col4), +STDDEV_POP(col5), STDDEV_POP(col6), STDDEV_POP(col7), STDDEV_POP(col8), +STDDEV_POP(col9), STDDEV_POP(col10) +FROM t_innodb; +STDDEV_POP(col1) STDDEV_POP(col2) STDDEV_POP(col3) STDDEV_POP(col4) STDDEV_POP(col5) STDDEV_POP(col6) STDDEV_POP(col7) STDDEV_POP(col8) STDDEV_POP(col9) STDDEV_POP(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT STDDEV_POP(col1), STDDEV_POP(col2), STDDEV_POP(col3), STDDEV_POP(col4), +STDDEV_POP(col5), STDDEV_POP(col6), STDDEV_POP(col7), STDDEV_POP(col8), +STDDEV_POP(col9), STDDEV_POP(col10) +FROM t_duckdb; +STDDEV_POP(col1) STDDEV_POP(col2) STDDEV_POP(col3) STDDEV_POP(col4) STDDEV_POP(col5) STDDEV_POP(col6) STDDEV_POP(col7) STDDEV_POP(col8) STDDEV_POP(col9) STDDEV_POP(col10) +9.428090415820632 116396176.91388902 1.163961769243743e17 1.163961769243743e17 1.1639617692437429e27 111232.13932133105 259805.17354356128 47131556787027.98 259804813371.18005 1.0080204611989621e27 +SELECT STDDEV_POP(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'stddev_pop(BLOB)'. You might need to add explicit type casts. + Candidate functions: + stddev_pop(DOUBLE) -> DOUBLE + + +LINE 1: SELECT STDDEV_POP(col11) + ^. +-------------------------- +14. STDDEV_POP() +-------------------------- +SELECT STDDEV_SAMP(col1), STDDEV_SAMP(col2), STDDEV_SAMP(col3), STDDEV_SAMP(col4), +STDDEV_SAMP(col5), STDDEV_SAMP(col6), STDDEV_SAMP(col7), STDDEV_SAMP(col8), +STDDEV_SAMP(col9), STDDEV_SAMP(col10) +FROM t_innodb; +STDDEV_SAMP(col1) STDDEV_SAMP(col2) STDDEV_SAMP(col3) STDDEV_SAMP(col4) STDDEV_SAMP(col5) STDDEV_SAMP(col6) STDDEV_SAMP(col7) STDDEV_SAMP(col8) STDDEV_SAMP(col9) STDDEV_SAMP(col10) +11.547005383792515 142555620.72487366 1.4255562073771544e17 1.4255562073771544e17 1.4255562073771545e27 136230.9921677149 318195.05385847844 57724132455613.96 318194612739.2017 1.2345678901112131e27 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT STDDEV_SAMP(col1), STDDEV_SAMP(col2), STDDEV_SAMP(col3), STDDEV_SAMP(col4), +STDDEV_SAMP(col5), STDDEV_SAMP(col6), STDDEV_SAMP(col7), STDDEV_SAMP(col8), +STDDEV_SAMP(col9), STDDEV_SAMP(col10) +FROM t_duckdb; +STDDEV_SAMP(col1) STDDEV_SAMP(col2) STDDEV_SAMP(col3) STDDEV_SAMP(col4) STDDEV_SAMP(col5) STDDEV_SAMP(col6) STDDEV_SAMP(col7) STDDEV_SAMP(col8) STDDEV_SAMP(col9) STDDEV_SAMP(col10) +11.547005383792515 142555620.72487366 1.4255562073771544e17 1.4255562073771544e17 1.4255562073771545e27 136230.9921677149 318195.05385847844 57724132455613.96 318194612739.2017 1.2345678901112131e27 +SELECT STDDEV_SAMP(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'stddev_samp(BLOB)'. You might need to add explicit type casts. + Candidate functions: + stddev_samp(DOUBLE) -> DOUBLE + + +LINE 1: SELECT STDDEV_SAMP(col11) + ^. +-------------------------- +15. SUM() +-------------------------- +SELECT SUM(col1), SUM(col2), SUM(col3), SUM(col4), +SUM(col5), SUM(col6), SUM(col7), SUM(col8), +SUM(col9), SUM(col10) +FROM t_innodb; +SUM(col1) SUM(col2) SUM(col3) SUM(col4) SUM(col5) SUM(col6) SUM(col7) SUM(col8) SUM(col9) SUM(col10) +-10 -123456789 -123456789011121314 -1.2345678901112131e17 -1234567890111213141516171819.1234567890 235959 59651433 100011433235959 59651435235959 0 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT SUM(col1), SUM(col2), SUM(col3), SUM(col4), +SUM(col5), SUM(col6), SUM(col7), SUM(col8), +SUM(col9), SUM(col10) +FROM t_duckdb; +SUM(col1) SUM(col2) SUM(col3) SUM(col4) SUM(col5) SUM(col6) SUM(col7) SUM(col8) SUM(col9) SUM(col10) +-10 -123456789 -123456789011121314 -1.2345678901112131e17 -1234567890111213141516171819.1234567890 235959 59651433 100011433235959 59651435235959 0 +SELECT SUM(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'sum(BLOB)'. You might need to add explicit type casts. + Candidate functions: + sum(DECIMAL) -> DECIMAL + sum(BOOLEAN) -> HUGEINT + sum(SMALLINT) -> HUGEINT + sum(INTEGER) -> HUGEINT + sum(BIGINT) -> HUGEINT + sum(HUGEINT) -> HUGEINT + sum(DOUBLE) -> DOUBLE + + +LINE 1: SELECT SUM(col11) + ^. +-------------------------- +16. VAR_POP() +-------------------------- +SELECT VAR_POP(col1), VAR_POP(col2), VAR_POP(col3), VAR_POP(col4), +VAR_POP(col5), VAR_POP(col6), VAR_POP(col7), VAR_POP(col8), +VAR_POP(col9), VAR_POP(col10) +FROM t_innodb; +VAR_POP(col1) VAR_POP(col2) VAR_POP(col3) VAR_POP(col4) VAR_POP(col5) VAR_POP(col6) VAR_POP(col7) VAR_POP(col8) VAR_POP(col9) VAR_POP(col10) +88.88888888888887 1.3548070000169352e16 1.3548070002610244e34 1.3548070002610244e34 1.3548070002610242e54 12372588818 67498728200 2.2213836451688432e27 6.749854105083369e22 1.0161052501957682e54 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT VAR_POP(col1), VAR_POP(col2), VAR_POP(col3), VAR_POP(col4), +VAR_POP(col5), VAR_POP(col6), VAR_POP(col7), VAR_POP(col8), +VAR_POP(col9), VAR_POP(col10) +FROM t_duckdb; +VAR_POP(col1) VAR_POP(col2) VAR_POP(col3) VAR_POP(col4) VAR_POP(col5) VAR_POP(col6) VAR_POP(col7) VAR_POP(col8) VAR_POP(col9) VAR_POP(col10) +88.88888888888887 1.3548070000169352e16 1.3548070002610244e34 1.3548070002610244e34 1.3548070002610242e54 12372588818 67498728200 2.2213836451688432e27 6.749854105083369e22 1.0161052501957682e54 +SELECT VAR_POP(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'var_pop(BLOB)'. You might need to add explicit type casts. + Candidate functions: + var_pop(DOUBLE) -> DOUBLE + + +LINE 1: SELECT VAR_POP(col11) + ^. +-------------------------- +17. VAR_SAMP() +-------------------------- +SELECT VAR_SAMP(col1), VAR_SAMP(col2), VAR_SAMP(col3), VAR_SAMP(col4), +VAR_SAMP(col5), VAR_SAMP(col6), VAR_SAMP(col7), VAR_SAMP(col8), +VAR_SAMP(col9), VAR_SAMP(col10) +FROM t_innodb; +VAR_SAMP(col1) VAR_SAMP(col2) VAR_SAMP(col3) VAR_SAMP(col4) VAR_SAMP(col5) VAR_SAMP(col6) VAR_SAMP(col7) VAR_SAMP(col8) VAR_SAMP(col9) VAR_SAMP(col10) +133.33333333333331 2.0322105000254028e16 2.0322105003915366e34 2.0322105003915366e34 2.0322105003915365e54 18558883227 101248092300 3.332075467753265e27 1.0124781157625054e23 1.5241578752936524e54 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT VAR_SAMP(col1), VAR_SAMP(col2), VAR_SAMP(col3), VAR_SAMP(col4), +VAR_SAMP(col5), VAR_SAMP(col6), VAR_SAMP(col7), VAR_SAMP(col8), +VAR_SAMP(col9), VAR_SAMP(col10) +FROM t_duckdb; +VAR_SAMP(col1) VAR_SAMP(col2) VAR_SAMP(col3) VAR_SAMP(col4) VAR_SAMP(col5) VAR_SAMP(col6) VAR_SAMP(col7) VAR_SAMP(col8) VAR_SAMP(col9) VAR_SAMP(col10) +133.33333333333331 2.0322105000254028e16 2.0322105003915366e34 2.0322105003915366e34 2.0322105003915365e54 18558883227 101248092300 3.332075467753265e27 1.0124781157625054e23 1.5241578752936524e54 +SELECT VAR_SAMP(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'var_samp(BLOB)'. You might need to add explicit type casts. + Candidate functions: + var_samp(DOUBLE) -> DOUBLE + + +LINE 1: SELECT VAR_SAMP(col11) + ^. +-------------------------- +18. VARIANCE() +-------------------------- +SELECT VARIANCE(col1), VARIANCE(col2), VARIANCE(col3), VARIANCE(col4), +VARIANCE(col5), VARIANCE(col6), VARIANCE(col7), VARIANCE(col8), +VARIANCE(col9), VARIANCE(col10) +FROM t_innodb; +VARIANCE(col1) VARIANCE(col2) VARIANCE(col3) VARIANCE(col4) VARIANCE(col5) VARIANCE(col6) VARIANCE(col7) VARIANCE(col8) VARIANCE(col9) VARIANCE(col10) +88.88888888888887 1.3548070000169352e16 1.3548070002610244e34 1.3548070002610244e34 1.3548070002610242e54 12372588818 67498728200 2.2213836451688432e27 6.749854105083369e22 1.0161052501957682e54 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' +SELECT VARIANCE(col1), VARIANCE(col2), VARIANCE(col3), VARIANCE(col4), +VARIANCE(col5), VARIANCE(col6), VARIANCE(col7), VARIANCE(col8), +VARIANCE(col9), VARIANCE(col10) +FROM t_duckdb; +VARIANCE(col1) VARIANCE(col2) VARIANCE(col3) VARIANCE(col4) VARIANCE(col5) VARIANCE(col6) VARIANCE(col7) VARIANCE(col8) VARIANCE(col9) VARIANCE(col10) +88.88888888888887 1.3548070000169352e16 1.3548070002610244e34 1.3548070002610244e34 1.3548070002610242e54 12372588818 67498728200 2.2213836451688432e27 6.749854105083369e22 1.0161052501957682e54 +SELECT VARIANCE(col11) +FROM t_duckdb WHERE id <= 3; +ERROR HY000: [DuckDB] Binder Error: No function matches the given name and argument types 'variance(BLOB)'. You might need to add explicit type casts. + Candidate functions: + variance(DOUBLE) -> DOUBLE + + +LINE 1: SELECT VARIANCE(col11) + ^. +DROP TABLE t_innodb; +DROP TABLE t_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_allow_encryption.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_allow_encryption.result new file mode 100644 index 0000000000000..ea674d5d4e832 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_allow_encryption.result @@ -0,0 +1,105 @@ +DROP DATABASE IF EXISTS encryption_test; +CREATE DATABASE encryption_test; +USE encryption_test; +# +# Test: create duckdb table with encryption option +# +CREATE TABLE string_table ( +id INT PRIMARY KEY, +bool_field BOOLEAN, +decimal_field DECIMAL(10,2), +enum_field ENUM('small', 'medium', 'large'), +set_field SET('red', 'green', 'blue'), +varchar_field VARCHAR(255), +json_field JSON , +text_field TEXT, +new_decimal_field DECIMAL(20,6) +) ENGINE=DuckDB ENCRYPTION = 'Y'; +INSERT INTO string_table VALUES (1, TRUE, 123.45, 'small', 'red,green', 'varchar_sample','{"name": "Alice", "age": 30}', 'This is a sample text.', 123456.789012); +INSERT INTO string_table VALUES (2, NULL, NULL, NULL, NULL, NULL, NULL,NULL,NULL); +INSERT INTO string_table VALUES (3, FALSE, 100.00, 'medium', 'red,blue', 'varchar_final', '{"animal": "dog", "legs": 4}', 'End of this test text.', 778899.001122); +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ENCRYPTION='Y' +CHECKSUM TABLE string_table; +Table Checksum +encryption_test.string_table 1870613497 +# +# alter table to innodb and check whether innodb is encrypted +# +ALTER TABLE string_table ENGINE = InnoDB; +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ENCRYPTION='Y' +CHECKSUM TABLE string_table; +Table Checksum +encryption_test.string_table 1870613497 +# +# Test: create innodb table with encryption option +# +CREATE TABLE blob_table ( +id INT PRIMARY KEY, +tiny_blob_field TINYBLOB, +medium_blob_field MEDIUMBLOB, +long_blob_field LONGBLOB, +blob_field BLOB, +bit_field BIT(64) +) ENGINE=InnoDB ENCRYPTION = 'Y'; +INSERT INTO blob_table VALUES (1, 'tinyblob_data', 'mediumblob_data', 'longblob_data', 'blob_data', b'00000001'); +INSERT INTO blob_table VALUES (2, NULL, NULL, 'longblob_data3', NULL, b'00000010'); +INSERT INTO blob_table VALUES (3, 'tinyblob_data4', 'mediumblob_data4', NULL, 'blob_data2', NULL); +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ENCRYPTION='Y' +CHECKSUM TABLE blob_table; +Table Checksum +encryption_test.blob_table 814255090 +# +# Test: alter table to duckdb and check whether duckdb is encrypted +# +ALTER TABLE blob_table ENGINE = DuckDB; +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ENCRYPTION='Y' +CHECKSUM TABLE blob_table; +Table Checksum +encryption_test.blob_table 814255090 +DROP DATABASE encryption_test; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_alter_table_engine.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_alter_table_engine.result new file mode 100644 index 0000000000000..279c82fb37ba4 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_alter_table_engine.result @@ -0,0 +1,658 @@ +# +# Test DDL: alter table engine = innodb from duckdb tables +# check whether changing storage engine from duckdb to innodb is supported +# +DROP DATABASE IF EXISTS alter_engine_test; +CREATE DATABASE alter_engine_test; +USE alter_engine_test; +# +# 1. test blob types +# +CREATE TABLE blob_table ( +id INT PRIMARY KEY, +tiny_blob_field TINYBLOB, +medium_blob_field MEDIUMBLOB, +long_blob_field LONGBLOB, +blob_field BLOB, +bit_field BIT(64) +) ENGINE=DUCKDB; +INSERT INTO blob_table VALUES (1, 'tinyblob_data', 'mediumblob_data', 'longblob_data', 'blob_data', b'00000001'); +INSERT INTO blob_table VALUES (2, 'tinyblob_data2', 'mediumblob_data2', 'longblob_data2', 'blob_data2', b'00000010'); +INSERT INTO blob_table VALUES (3, NULL, NULL, 'longblob_data3', NULL, b'00000010'); +INSERT INTO blob_table VALUES (4, 'tinyblob_data4', 'mediumblob_data4', NULL, 'blob_data2', NULL); +INSERT INTO blob_table VALUES (5, 'tinyblob_data5', 'mediumblob_data5', 'longblob_data5', 'blob_data5', b'00000011'); +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT id, tiny_blob_field, medium_blob_field, long_blob_field, blob_field, hex(bit_field) FROM blob_table; +id tiny_blob_field medium_blob_field long_blob_field blob_field hex(bit_field) +1 tinyblob_data mediumblob_data longblob_data blob_data 0000000000000001 +2 tinyblob_data2 mediumblob_data2 longblob_data2 blob_data2 0000000000000002 +3 NULL NULL longblob_data3 NULL 0000000000000002 +4 tinyblob_data4 mediumblob_data4 NULL blob_data2 NULL +5 tinyblob_data5 mediumblob_data5 longblob_data5 blob_data5 0000000000000003 +CHECKSUM TABLE blob_table; +Table Checksum +alter_engine_test.blob_table 2649844247 +ALTER TABLE blob_table ENGINE = InnoDB; +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE blob_table; +Table Checksum +alter_engine_test.blob_table 2649844247 +ALTER TABLE blob_table ENGINE = DUCKDB; +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE blob_table; +Table Checksum +alter_engine_test.blob_table 2649844247 +ALTER TABLE blob_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE blob_table; +Table Create Table +blob_table CREATE TABLE `blob_table` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + `blob_field` blob, + `bit_field` bit(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE blob_table; +Table Checksum +alter_engine_test.blob_table 2649844247 +# +# 2. test string types +# +CREATE TABLE string_table ( +id INT PRIMARY KEY, +null_field VARCHAR(255), +bool_field BOOLEAN, +decimal_field DECIMAL(10,2), +enum_field ENUM('small', 'medium', 'large'), +set_field SET('red', 'green', 'blue'), +string_field VARCHAR(255), +var_string_field VARCHAR(255), +varchar_field VARCHAR(255), +json_field JSON , +text_field TEXT, +new_decimal_field DECIMAL(20,6) +) ENGINE=DUCKDB; +INSERT INTO string_table VALUES (1, NULL, TRUE, 123.45, 'small', 'red,green', 'sample_string', 'var_string_sample', 'varchar_sample','{"name": "Alice", "age": 30}', 'This is a sample text.', 123456.789012); +INSERT INTO string_table VALUES (2, 'another_value', FALSE, 678.90, 'medium', 'blue', 'another_string', 'another_var_string', NULL, NULL, NULL, 345678.901234); +INSERT INTO string_table VALUES (3, 'test_value', TRUE, 50.25, 'large', NULL, 'test_string', 'var_string_test', 'varchar_test', '{"city": "Beijing", "population": 2154}', 'Some additional text content.', 987654.321098); +INSERT INTO string_table VALUES (4, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL,NULL); +INSERT INTO string_table VALUES (5, NULL, FALSE, 999.99, 'small', 'red', 'another test', 'var_string_another', 'varchar_another', '{"country": "USA", "states": 50}', 'More sample text data.', NULL); +INSERT INTO string_table VALUES (6, 'final_value', TRUE, 100.00, 'medium', 'red,blue', 'final_string', 'var_string_final', 'varchar_final', '{"animal": "dog", "legs": 4}', 'End of this test text.', 778899.001122); +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `null_field` varchar(255) DEFAULT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `string_field` varchar(255) DEFAULT NULL, + `var_string_field` varchar(255) DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT * FROM string_table; +id null_field bool_field decimal_field enum_field set_field string_field var_string_field varchar_field json_field text_field new_decimal_field +1 NULL 1 123.45 small red,green sample_string var_string_sample varchar_sample {"age": 30, "name": "Alice"} This is a sample text. 123456.789012 +2 another_value 0 678.90 medium blue another_string another_var_string NULL NULL NULL 345678.901234 +3 test_value 1 50.25 large NULL test_string var_string_test varchar_test {"city": "Beijing", "population": 2154} Some additional text content. 987654.321098 +4 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL +5 NULL 0 999.99 small red another test var_string_another varchar_another {"states": 50, "country": "USA"} More sample text data. NULL +6 final_value 1 100.00 medium red,blue final_string var_string_final varchar_final {"legs": 4, "animal": "dog"} End of this test text. 778899.001122 +CHECKSUM TABLE string_table; +Table Checksum +alter_engine_test.string_table 2177912761 +ALTER TABLE string_table ENGINE = InnoDB; +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `null_field` varchar(255) DEFAULT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `string_field` varchar(255) DEFAULT NULL, + `var_string_field` varchar(255) DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE string_table; +Table Checksum +alter_engine_test.string_table 2177912761 +ALTER TABLE string_table ENGINE = DUCKDB; +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `null_field` varchar(255) DEFAULT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `string_field` varchar(255) DEFAULT NULL, + `var_string_field` varchar(255) DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE string_table; +Table Checksum +alter_engine_test.string_table 2177912761 +ALTER TABLE string_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE string_table; +Table Create Table +string_table CREATE TABLE `string_table` ( + `id` int NOT NULL, + `null_field` varchar(255) DEFAULT NULL, + `bool_field` tinyint(1) DEFAULT NULL, + `decimal_field` decimal(10,2) DEFAULT NULL, + `enum_field` enum('small','medium','large') DEFAULT NULL, + `set_field` set('red','green','blue') DEFAULT NULL, + `string_field` varchar(255) DEFAULT NULL, + `var_string_field` varchar(255) DEFAULT NULL, + `varchar_field` varchar(255) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + `new_decimal_field` decimal(20,6) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE string_table; +Table Checksum +alter_engine_test.string_table 2177912761 +# +# 3. test numeric types +# +CREATE TABLE numeric_table ( +id INT PRIMARY KEY, +tinyint_field TINYINT, +smallint_field SMALLINT, +mediumint_field MEDIUMINT, +int_field INT, +bigint_field BIGINT, +float_field FLOAT, +double_field DOUBLE, +decimal_field DECIMAL(20,10) +)ENGINE=DUCKDB; +INSERT INTO numeric_table VALUES +(1, 127, 32767, 8388607, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 123456789.1234567890), +(2, -128, -32768, -8388608, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -1234501234.1234567890), +(3, NULL, 0, 0, 0, 0, 0.0, 0.0,NULL), +(4, 1, 2, NULL, 4, 1844674407370955161, NULL, 2.2, 1234567890.1234567890), +(5, -1, -2, -3, -4, -1844674407370955161, NULL, -2.2, -1234567890.1234567890); +SHOW CREATE TABLE numeric_table; +Table Create Table +numeric_table CREATE TABLE `numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint DEFAULT NULL, + `smallint_field` smallint DEFAULT NULL, + `mediumint_field` mediumint DEFAULT NULL, + `int_field` int DEFAULT NULL, + `bigint_field` bigint DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT * FROM numeric_table; +id tinyint_field smallint_field mediumint_field int_field bigint_field float_field double_field decimal_field +1 127 32767 8388607 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 123456789.1234567890 +2 -128 -32768 -8388608 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -1234501234.1234567890 +3 NULL 0 0 0 0 0 0 NULL +4 1 2 NULL 4 1844674407370955161 NULL 2.2 1234567890.1234567890 +5 -1 -2 -3 -4 -1844674407370955161 NULL -2.2 -1234567890.1234567890 +CHECKSUM TABLE numeric_table; +Table Checksum +alter_engine_test.numeric_table 2088420200 +ALTER TABLE numeric_table ENGINE = InnoDB; +SHOW CREATE TABLE numeric_table; +Table Create Table +numeric_table CREATE TABLE `numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint DEFAULT NULL, + `smallint_field` smallint DEFAULT NULL, + `mediumint_field` mediumint DEFAULT NULL, + `int_field` int DEFAULT NULL, + `bigint_field` bigint DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE numeric_table; +Table Checksum +alter_engine_test.numeric_table 2088420200 +ALTER TABLE numeric_table ENGINE = DUCKDB; +SHOW CREATE TABLE numeric_table; +Table Create Table +numeric_table CREATE TABLE `numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint DEFAULT NULL, + `smallint_field` smallint DEFAULT NULL, + `mediumint_field` mediumint DEFAULT NULL, + `int_field` int DEFAULT NULL, + `bigint_field` bigint DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE numeric_table; +Table Checksum +alter_engine_test.numeric_table 2088420200 +ALTER TABLE numeric_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE numeric_table; +Table Create Table +numeric_table CREATE TABLE `numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint DEFAULT NULL, + `smallint_field` smallint DEFAULT NULL, + `mediumint_field` mediumint DEFAULT NULL, + `int_field` int DEFAULT NULL, + `bigint_field` bigint DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE numeric_table; +Table Checksum +alter_engine_test.numeric_table 2088420200 +# +# 4. test unsigned numeric types +# +CREATE TABLE unsigned_numeric_table ( +id INT PRIMARY KEY, +tinyint_field TINYINT UNSIGNED, +smallint_field SMALLINT UNSIGNED, +mediumint_field MEDIUMINT UNSIGNED, +int_field INT UNSIGNED, +bigint_field BIGINT UNSIGNED, +float_field FLOAT, +double_field DOUBLE, +decimal_field DECIMAL(20,10) +)ENGINE=DUCKDB; +# Inserting test data into unsigned_numeric_table +INSERT INTO unsigned_numeric_table VALUES (1, 255, 65535, 16777215, 4294967295, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, '9999999999.9999999999'); +INSERT INTO unsigned_numeric_table VALUES (2, 254, 65534, 16777214, 4294967294, 18446744073709551614, 3.402823465E+38, NULL, '9999999998.9999999998'); +INSERT INTO unsigned_numeric_table VALUES (3, 128, 32768, 8388607, 2147483647, 9223372036854775807, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); +INSERT INTO unsigned_numeric_table VALUES (4, NULL, 32767, NULL, 2147483647, NULL, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); +INSERT INTO unsigned_numeric_table VALUES (5, 127, 32767, NULL, NULL, NULL, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); +SHOW CREATE TABLE unsigned_numeric_table; +Table Create Table +unsigned_numeric_table CREATE TABLE `unsigned_numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint unsigned DEFAULT NULL, + `smallint_field` smallint unsigned DEFAULT NULL, + `mediumint_field` mediumint unsigned DEFAULT NULL, + `int_field` int unsigned DEFAULT NULL, + `bigint_field` bigint unsigned DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT * FROM unsigned_numeric_table; +id tinyint_field smallint_field mediumint_field int_field bigint_field float_field double_field decimal_field +1 255 65535 16777215 4294967295 18446744073709551615 3.40282e38 1.7976931348623157e308 9999999999.9999999999 +2 254 65534 16777214 4294967294 18446744073709551614 3.40282e38 NULL 9999999998.9999999998 +3 128 32768 8388607 2147483647 9223372036854775807 123457000 9876543210.012346 1234567890.0123456789 +4 NULL 32767 NULL 2147483647 NULL 123457000 9876543210.012346 1234567890.0123456789 +5 127 32767 NULL NULL NULL 123457000 9876543210.012346 1234567890.0123456789 +CHECKSUM TABLE unsigned_numeric_table; +Table Checksum +alter_engine_test.unsigned_numeric_table 953083886 +ALTER TABLE unsigned_numeric_table ENGINE = InnoDB; +SHOW CREATE TABLE unsigned_numeric_table; +Table Create Table +unsigned_numeric_table CREATE TABLE `unsigned_numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint unsigned DEFAULT NULL, + `smallint_field` smallint unsigned DEFAULT NULL, + `mediumint_field` mediumint unsigned DEFAULT NULL, + `int_field` int unsigned DEFAULT NULL, + `bigint_field` bigint unsigned DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE unsigned_numeric_table; +Table Checksum +alter_engine_test.unsigned_numeric_table 953083886 +ALTER TABLE unsigned_numeric_table ENGINE = DUCKDB; +SHOW CREATE TABLE unsigned_numeric_table; +Table Create Table +unsigned_numeric_table CREATE TABLE `unsigned_numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint unsigned DEFAULT NULL, + `smallint_field` smallint unsigned DEFAULT NULL, + `mediumint_field` mediumint unsigned DEFAULT NULL, + `int_field` int unsigned DEFAULT NULL, + `bigint_field` bigint unsigned DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE unsigned_numeric_table; +Table Checksum +alter_engine_test.unsigned_numeric_table 953083886 +ALTER TABLE unsigned_numeric_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE unsigned_numeric_table; +Table Create Table +unsigned_numeric_table CREATE TABLE `unsigned_numeric_table` ( + `id` int NOT NULL, + `tinyint_field` tinyint unsigned DEFAULT NULL, + `smallint_field` smallint unsigned DEFAULT NULL, + `mediumint_field` mediumint unsigned DEFAULT NULL, + `int_field` int unsigned DEFAULT NULL, + `bigint_field` bigint unsigned DEFAULT NULL, + `float_field` float DEFAULT NULL, + `double_field` double DEFAULT NULL, + `decimal_field` decimal(20,10) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE unsigned_numeric_table; +Table Checksum +alter_engine_test.unsigned_numeric_table 953083886 +# +# 5. test date types +# +CREATE TABLE date_table ( +id INT PRIMARY KEY, +date_field DATE, +datetime_field DATETIME, +timestamp_field TIMESTAMP, +time_field TIME +)ENGINE=DUCKDB; +INSERT INTO date_table VALUES +(1, '2023-01-01', '2023-01-01 12:00:00', '2023-01-01 12:00:00', '12:00:00'), +(2, '1999-12-31', '1999-12-31 23:59:59', '1999-12-31 23:59:59', '23:59:59'), +(3, NULL, NULL, NULL, '00:00:00'), +(4, '2024-02-29', '2024-02-29 00:00:00', '2024-02-29 00:00:00', '00:00:00'), +(5, '2023-04-05', NULL, '2023-04-05 14:30:45', NULL), +(6, '2023-04-05', '2023-04-05 14:30:45', '2023-04-05 14:30:45', '14:30:45'); +SHOW CREATE TABLE date_table; +Table Create Table +date_table CREATE TABLE `date_table` ( + `id` int NOT NULL, + `date_field` date DEFAULT NULL, + `datetime_field` datetime DEFAULT NULL, + `timestamp_field` timestamp NULL DEFAULT NULL, + `time_field` time DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SELECT * FROM date_table; +id date_field datetime_field timestamp_field time_field +1 2023-01-01 2023-01-01 12:00:00 2023-01-01 12:00:00 12:00:00 +2 1999-12-31 1999-12-31 23:59:59 1999-12-31 23:59:59 23:59:59 +3 NULL NULL NULL 00:00:00 +4 2024-02-29 2024-02-29 00:00:00 2024-02-29 00:00:00 00:00:00 +5 2023-04-05 NULL 2023-04-05 14:30:45 NULL +6 2023-04-05 2023-04-05 14:30:45 2023-04-05 14:30:45 14:30:45 +CHECKSUM TABLE date_table; +Table Checksum +alter_engine_test.date_table 1791289722 +ALTER TABLE date_table ENGINE = InnoDB; +SHOW CREATE TABLE date_table; +Table Create Table +date_table CREATE TABLE `date_table` ( + `id` int NOT NULL, + `date_field` date DEFAULT NULL, + `datetime_field` datetime DEFAULT NULL, + `timestamp_field` timestamp NULL DEFAULT NULL, + `time_field` time DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE date_table; +Table Checksum +alter_engine_test.date_table 1791289722 +ALTER TABLE date_table ENGINE = DUCKDB; +SHOW CREATE TABLE date_table; +Table Create Table +date_table CREATE TABLE `date_table` ( + `id` int NOT NULL, + `date_field` date DEFAULT NULL, + `datetime_field` datetime DEFAULT NULL, + `timestamp_field` timestamp NULL DEFAULT NULL, + `time_field` time DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE date_table; +Table Checksum +alter_engine_test.date_table 1791289722 +ALTER TABLE date_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE date_table; +Table Create Table +date_table CREATE TABLE `date_table` ( + `id` int NOT NULL, + `date_field` date DEFAULT NULL, + `datetime_field` datetime DEFAULT NULL, + `timestamp_field` timestamp NULL DEFAULT NULL, + `time_field` time DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE date_table; +Table Checksum +alter_engine_test.date_table 1791289722 +# +# 6. test large field +# +CREATE TABLE large_field_table1 ( +id INT PRIMARY KEY, +tiny_blob_field TINYBLOB, +blob_field BLOB, +medium_blob_field MEDIUMBLOB, +long_blob_field LONGBLOB +) ENGINE=DUCKDB; +INSERT INTO large_field_table1 values(1, 'a', 'b', 'ccc', repeat('d', 1024 * 1024)); +INSERT INTO large_field_table1 values(2, repeat('a', 255), repeat('b', 65535), repeat('c',16777215), repeat('d', 67108864)); +INSERT INTO large_field_table1 values(3, 'aa', 'bb', 'ccc', repeat('d', 1024 * 1024)); +INSERT INTO large_field_table1 values(4, repeat('a', 254), repeat('b', 65533), repeat('c',8000000), repeat('d', 67108864)); +INSERT INTO large_field_table1 values(5, 'aa', 'bb', 'ccc', repeat('d', 1024 * 1024)); +SHOW CREATE TABLE large_field_table1; +Table Create Table +large_field_table1 CREATE TABLE `large_field_table1` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `blob_field` blob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table1; +Table Checksum +alter_engine_test.large_field_table1 3877415242 +ALTER TABLE large_field_table1 ENGINE=INNODB; +SHOW CREATE TABLE large_field_table1; +Table Create Table +large_field_table1 CREATE TABLE `large_field_table1` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `blob_field` blob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table1; +Table Checksum +alter_engine_test.large_field_table1 3877415242 +ALTER TABLE large_field_table1 ENGINE=DUCKDB; +SHOW CREATE TABLE large_field_table1; +Table Create Table +large_field_table1 CREATE TABLE `large_field_table1` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `blob_field` blob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table1; +Table Checksum +alter_engine_test.large_field_table1 3877415242 +ALTER TABLE large_field_table1 ENGINE=DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE large_field_table1; +Table Create Table +large_field_table1 CREATE TABLE `large_field_table1` ( + `id` int NOT NULL, + `tiny_blob_field` tinyblob, + `blob_field` blob, + `medium_blob_field` mediumblob, + `long_blob_field` longblob, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table1; +Table Checksum +alter_engine_test.large_field_table1 3877415242 +CREATE TABLE large_field_table2 ( +id INT PRIMARY KEY, +bit_field BIT(64), +char_field CHAR(255), +string_field VARCHAR(8192), +json_field JSON, +text_field TEXT +) ENGINE=DUCKDB; +SET @long_str = repeat('a',67107864); +SET @json_val = JSON_OBJECT('key1', 'static_value', 'key2', @long_str); +INSERT INTO large_field_table2 values(1, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); +INSERT INTO large_field_table2 values(2, X'FFFFFFFFFFFFFFFF', repeat('C', 255), repeat('D',8192), @json_val, repeat('E', 65535)); +INSERT INTO large_field_table2 values(3, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); +INSERT INTO large_field_table2 values(4, X'FFFFFFFFFFFFFFFF', repeat('w', 255), repeat('v',4096), @json_val, repeat('o', 65535)); +INSERT INTO large_field_table2 values(5, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); +SHOW CREATE TABLE large_field_table2; +Table Create Table +large_field_table2 CREATE TABLE `large_field_table2` ( + `id` int NOT NULL, + `bit_field` bit(64) DEFAULT NULL, + `char_field` char(255) DEFAULT NULL, + `string_field` varchar(8192) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table2; +Table Checksum +alter_engine_test.large_field_table2 2298046794 +ALTER TABLE large_field_table2 ENGINE=INNODB; +SHOW CREATE TABLE large_field_table2; +Table Create Table +large_field_table2 CREATE TABLE `large_field_table2` ( + `id` int NOT NULL, + `bit_field` bit(64) DEFAULT NULL, + `char_field` char(255) DEFAULT NULL, + `string_field` varchar(8192) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table2; +Table Checksum +alter_engine_test.large_field_table2 2298046794 +ALTER TABLE large_field_table2 ENGINE=DUCKDB; +SHOW CREATE TABLE large_field_table2; +Table Create Table +large_field_table2 CREATE TABLE `large_field_table2` ( + `id` int NOT NULL, + `bit_field` bit(64) DEFAULT NULL, + `char_field` char(255) DEFAULT NULL, + `string_field` varchar(8192) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table2; +Table Checksum +alter_engine_test.large_field_table2 2298046794 +ALTER TABLE large_field_table2 ENGINE=DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE large_field_table2; +Table Create Table +large_field_table2 CREATE TABLE `large_field_table2` ( + `id` int NOT NULL, + `bit_field` bit(64) DEFAULT NULL, + `char_field` char(255) DEFAULT NULL, + `string_field` varchar(8192) DEFAULT NULL, + `json_field` json DEFAULT NULL, + `text_field` text, + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CHECKSUM TABLE large_field_table2; +Table Checksum +alter_engine_test.large_field_table2 2298046794 +# +# 7. test CHECKSUM +# special case: when NULL in fields with default value +# +CREATE TABLE `t1` ( +`id` bigint NOT NULL, +`c0` varchar(20) NOT NULL, +`c1` char(10) DEFAULT 'duckdb', +`c2` decimal(5,2) DEFAULT NULL, +`c3` datetime DEFAULT NULL, +`c4` timestamp NULL DEFAULT NULL, +`c5` blob, +PRIMARY KEY (`id`) +) ENGINE=InnoDB; +CREATE TABLE `t2` ( +`id` bigint NOT NULL, +`c0` varchar(20) NOT NULL, +`c1` char(10) DEFAULT 'duckdb', +`c2` decimal(5,2) DEFAULT NULL, +`c3` datetime DEFAULT NULL, +`c4` timestamp NULL DEFAULT NULL, +`c5` blob, +PRIMARY KEY (`id`) +) ENGINE=DuckDB; +INSERT INTO t1 (id, c0, c1, c2, c3, c4, c5) VALUES (1, 'abc', NULL, 1.23, '2020-11-20', NULL, X'e18080ff8a'); +INSERT INTO t2 (id, c0, c1, c2, c3, c4, c5) VALUES (1, 'abc', NULL, 1.23, '2020-11-20', NULL, X'e18080ff8a'); +CHECKSUM TABLE t1; +Table Checksum +alter_engine_test.t1 1521661477 +CHECKSUM TABLE t2; +Table Checksum +alter_engine_test.t2 1521661477 +SET @long_str = NULL; +SET @json_val = NULL; +DROP DATABASE IF EXISTS alter_engine_test; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_appender_allocator_flush_threshold.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_appender_allocator_flush_threshold.result new file mode 100644 index 0000000000000..210ecce410e99 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_appender_allocator_flush_threshold.result @@ -0,0 +1,36 @@ +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +Variable_name Value +duckdb_appender_allocator_flush_threshold 67108864 +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); +run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'") +name value description input_type scope aliases +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR[] +[ Rows: 1] +allocator_flush_threshold 128.0 MiB Peak allocation threshold at which to flush the allocator after completing a task. VARCHAR GLOBAL [] + + +SET GLOBAL duckdb_appender_allocator_flush_threshold = 1048576; +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +Variable_name Value +duckdb_appender_allocator_flush_threshold 1048576 +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); +run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'") +name value description input_type scope aliases +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR[] +[ Rows: 1] +allocator_flush_threshold 1.0 MiB Peak allocation threshold at which to flush the allocator after completing a task. VARCHAR GLOBAL [] + + +SET GLOBAL duckdb_appender_allocator_flush_threshold = 1048576 * 1024; +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +Variable_name Value +duckdb_appender_allocator_flush_threshold 1073741824 +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); +run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'") +name value description input_type scope aliases +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR[] +[ Rows: 1] +allocator_flush_threshold 1.0 GiB Peak allocation threshold at which to flush the allocator after completing a task. VARCHAR GLOBAL [] + + +SET GLOBAL duckdb_appender_allocator_flush_threshold = default; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_bit_string.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_bit_string.result new file mode 100644 index 0000000000000..991bdec624b38 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_bit_string.result @@ -0,0 +1,63 @@ +CREATE TABLE t1 (id INT PRIMARY KEY, col1 VARCHAR(100)) ENGINE=DuckDB; +CREATE TABLE t2 (id INT PRIMARY KEY, col1 BLOB) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 'A'), (2, x'41'), (3, b'01000001'); +INSERT INTO t2 VALUES (1, 'A'), (2, x'41'), (3, b'01000001'), (4, 0xFF), (5, x'FF'), (6, b'11111111'); +SELECT * FROM t1 WHERE col1 = 'A'; +id col1 +1 A +2 A +3 A +SELECT * FROM t1 WHERE col1 = x'41'; +id col1 +1 A +2 A +3 A +SELECT * FROM t1 WHERE col1 = b'01000001'; +id col1 +1 A +2 A +3 A +SELECT * FROM t2 WHERE col1 = 'A'; +id col1 +1 A +2 A +3 A +SELECT * FROM t2 WHERE col1 = x'41'; +id col1 +1 A +2 A +3 A +SELECT * FROM t2 WHERE col1 = b'01000001'; +id col1 +1 A +2 A +3 A +SELECT id, hex(col1) FROM t2 WHERE col1 = 0xFF; +ERROR HY000: [DuckDB] Parser Error: syntax error at or near "xFF" + +LINE 1: SELECT id, hex(col1) FROM t2 WHERE col1 = 0xFF + ^. +SELECT id, hex(col1) FROM t2 WHERE col1 = x'FF'; +id hex(col1) +4 FF +5 FF +6 FF +SELECT id, hex(col1) FROM t2 WHERE col1 = b'11111111'; +id hex(col1) +4 FF +5 FF +6 FF +DELETE FROM t1 WHERE col1 = x'41'; +SELECT * FROM t1; +id col1 +DELETE FROM t2 WHERE col1 = x'41'; +SELECT id, hex(col1) FROM t2; +id hex(col1) +4 FF +5 FF +6 FF +DELETE FROM t2 WHERE col1 = x'FF'; +SELECT id, hex(col1) FROM t2; +id hex(col1) +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_collate.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_collate.result new file mode 100644 index 0000000000000..5de319f121064 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_collate.result @@ -0,0 +1,59 @@ +CREATE TABLE t1 (col1 VARCHAR(20) COLLATE utf8mb4_0900_ai_ci PRIMARY KEY) ENGINE=DuckDB; +INSERT INTO t1 VALUES ('B'), ('a'), ('b'); +# +# 1) modify the scope of default_collation to local +# +connect con1, localhost, root,,; +[ connection default ] +connection default; +SET collation_connection = 'utf8mb4_0900_as_ci'; +[ connection con1 ] +connection con1; +SELECT 'a' = 'A' FROM t1 LIMIT 1; +'a' = 'A' +0 +[ connection default ] +connection default; +SET collation_connection = 'utf8mb4_0900_as_cs'; +[ connection con1 ] +connection con1; +SELECT 'a' = 'A' FROM t1 LIMIT 1; +'a' = 'A' +0 +SET collation_connection = 'utf8mb4_0900_as_ci'; +SELECT 'a' = 'A' FROM t1 LIMIT 1; +'a' = 'A' +1 +SET collation_connection = 'utf8mb4_0900_as_cs'; +SELECT 'a' = 'A' FROM t1 LIMIT 1; +'a' = 'A' +0 +# +# 2) force_no_collation +# +connection default; +SET duckdb_force_no_collation = false; +SELECT * FROM t1 WHERE col1 = 'A'; +col1 +a +SELECT min(col1) FROM t1; +min(col1) +a +SELECT * FROM t1 ORDER BY col1; +col1 +a +B +b +SET duckdb_force_no_collation = true; +SELECT * FROM t1 WHERE col1 = 'A'; +col1 +a +SELECT min(col1) FROM t1; +min(col1) +a +SELECT * FROM t1 ORDER BY col1; +col1 +a +B +b +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_cte.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_cte.result new file mode 100644 index 0000000000000..41e9f0cbab191 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_cte.result @@ -0,0 +1,52 @@ +CREATE DATABASE IF NOT EXISTS duckdb_cte; +USE duckdb_cte; +CREATE TABLE duckdb_table (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +CREATE TABLE innodb_table (id INT PRIMARY KEY, col1 INT) ENGINE=InnoDB; +INSERT INTO duckdb_table VALUES (1, 1); +INSERT INTO innodb_table VALUES (1, 1); + +(1) CTE + +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp; +MAX(col1) +1 +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp, duckdb_table; +MAX(col1) id col1 +1 1 1 +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp, innodb_table; +MAX(col1) id col1 +1 1 1 + +(2) Recursive CTE + +WITH RECURSIVE tmp AS ( +SELECT 1 AS n +UNION ALL +SELECT n + 1 FROM tmp WHERE n < 3 +) +SELECT * FROM duckdb_table, tmp; +id col1 n +1 1 1 +1 1 2 +1 1 3 +WITH RECURSIVE tmp AS ( +SELECT 1 AS n +UNION ALL +SELECT n + 1 FROM tmp, duckdb_table WHERE n = duckdb_table.col1 +) +SELECT * FROM duckdb_table, tmp; +id col1 n +1 1 1 +1 1 2 +WITH RECURSIVE tmp AS ( +SELECT 1 AS n +UNION ALL +SELECT n + 1 FROM tmp, innodb_table WHERE n = innodb_table.col1 +) +SELECT * FROM duckdb_table, tmp; +id col1 n +1 1 1 +1 1 2 +DROP TABLE duckdb_table; +DROP TABLE innodb_table; +DROP DATABASE duckdb_cte; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_db_table_strconvert.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_db_table_strconvert.result new file mode 100644 index 0000000000000..bedd54653ff4f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_db_table_strconvert.result @@ -0,0 +1,70 @@ +# +# 1) PREPARE +# +CREATE DATABASE `my-db`; +USE `my-db`; +CREATE TABLE `my-table` ( +`id` int(11) NOT NULL, +`name` varchar(255) NOT NULL, +PRIMARY KEY (`id`) +) ENGINE = InnoDB; +INSERT INTO `my-table` (`id`, `name`) VALUES (1, 'John'), (2, 'Jane'), (3, 'Jim'); +# +# 2) ALTER TO DUCKDB +# +ALTER TABLE `my-table` ENGINE = DuckDB; +include/assert.inc [CHECKSUM is the same] +ALTER TABLE `my-table` RENAME TO `my-table-renamed`; +RENAME TABLE `my-table-renamed` TO `my-table`; +ALTER TABLE `my-table` ADD COLUMN `col1` INT; +ALTER TABLE `my-table` ADD COLUMN `col2` INT, ALGORITHM = COPY; +SELECT COUNT(*) FROM `my-table`; +COUNT(*) +3 +INSERT INTO `my-table` (`id`, `name`) VALUES (4, 'Joe'); +UPDATE `my-table` SET `name` = 'Jack' WHERE `id` = 4; +DELETE FROM `my-table` WHERE `id` = 4; +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'my-db'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'my-db'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 1] +my-db my-table + + +SELECT run_in_duckdb("DESC `my-db`.`my-table`"); +run_in_duckdb("DESC `my-db`.`my-table`") +column_name column_type null key default extra +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 4] +id INTEGER NO NULL NULL NULL +name VARCHAR NO NULL NULL NULL +col1 INTEGER YES NULL NULL NULL +col2 INTEGER YES NULL NULL NULL + + +# +# 3) ALTER TO INNODB +# +ALTER TABLE `my-table` ENGINE = InnoDB; +SELECT run_in_duckdb("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'my-db'"); +run_in_duckdb("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'my-db'") +count_star() +BIGINT +[ Rows: 1] +0 + + +include/assert.inc [CHECKSUM is the same] +# +# 4) CLEANUP +# +DROP DATABASE `my-db`; +SELECT run_in_duckdb("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = 'my-db'"); +run_in_duckdb("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = 'my-db'") +count_star() +BIGINT +[ Rows: 1] +0 + + diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_ddl_during_transaction.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_ddl_during_transaction.result new file mode 100644 index 0000000000000..0de28cb285d0a --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_ddl_during_transaction.result @@ -0,0 +1,65 @@ +########################################################################### +# Prepare +########################################################################### +CREATE TABLE t1 ( +id BIGINT NOT NULL DEFAULT 1, +c0 SMALLINT NOT NULL DEFAULT 2, +c1 VARCHAR(20) NOT NULL DEFAULT 'abc', +PRIMARY KEY (id) +) ENGINE=DuckDB; +CREATE TABLE t2 ( +id BIGINT NOT NULL DEFAULT 1, +c0 SMALLINT NOT NULL DEFAULT 2, +c1 VARCHAR(20) NOT NULL DEFAULT 'abc', +PRIMARY KEY (id) +) ENGINE=DuckDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` bigint NOT NULL DEFAULT '1', + `c0` smallint NOT NULL DEFAULT '2', + `c1` varchar(20) NOT NULL DEFAULT 'abc', + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` bigint NOT NULL DEFAULT '1', + `c0` smallint NOT NULL DEFAULT '2', + `c1` varchar(20) NOT NULL DEFAULT 'abc', + PRIMARY KEY (`id`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +INSERT INTO t1 VALUES (3, 4, 'zzz'), (5, 6, 'xxx'); +SELECT * FROM t1; +id c0 c1 +3 4 zzz +5 6 xxx +########################################################################### +# Test executing DDL during a DuckDB transaction +########################################################################### +# 1. Test for DuckDB Appender +BEGIN; +INSERT INTO t2 (id) VALUES (7); +ALTER TABLE t1 DROP COLUMN c0; +CALL mtr.add_suppression("Call to EndRow before all columns have been appended to"); +INSERT INTO t1 (c1) VALUES ('xyz'); +ERROR HY000: [DuckDB] DuckDB appender error. Call to EndRow before all columns have been appended to! +COMMIT; +ERROR HY000: [DuckDB] DuckDB prepare transaction error. Failed to Flush appender: incomplete append to row! +SELECT * FROM t1; +id c1 +3 zzz +5 xxx +include/assert_grep.inc [Check DuckDB Warning] +# 2. Test for SELECT +BEGIN; +SELECT * FROM t2; +id c0 c1 +ALTER TABLE t1 DROP COLUMN c1, ADD COLUMN c2 DATE; +SELECT * FROM t1; +ERROR HY000: [DuckDB] DuckDB send result error. invalid date field format: "zzz", expected format is (YYYY-MM-DD) +COMMIT; +########################################################################### +# Cleanup +########################################################################### +DROP TABLE t1, t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_fix_sql.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_fix_sql.result new file mode 100644 index 0000000000000..0c74c92d49fcd --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_fix_sql.result @@ -0,0 +1,569 @@ +CREATE TABLE fake_innodb_table (id INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO fake_innodb_table VALUES (0); +CREATE TABLE fake_duckdb_table (id INT PRIMARY KEY) ENGINE=DuckDB; +INSERT INTO fake_duckdb_table VALUES (0); +CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; +SELECT BIT_AND(id), BIT_OR(id), BIT_XOR(id) FROM t1; +BIT_AND(id) BIT_OR(id) BIT_XOR(id) +18446744073709551615 0 0 +ALTER TABLE t1 ENGINE = DuckDB; +SELECT BIT_AND(id), BIT_OR(id), BIT_XOR(id) FROM t1; +BIT_AND(id) BIT_OR(id) BIT_XOR(id) +18446744073709551615 0 0 +DROP TABLE t1; +SELECT microsecond(TIME '12:12:12.123456') FROM fake_innodb_table; +microsecond(TIME '12:12:12.123456') +123456 +SELECT microsecond(TIME '12:12:12.123456') FROM fake_duckdb_table; +microsecond(TIME '12:12:12.123456') +123456 +SELECT LEAST('a', NULL) FROM fake_innodb_table; +LEAST('a', NULL) +NULL +SELECT LEAST('a', NULL) FROM fake_duckdb_table; +LEAST('a', NULL) +NULL +SELECT oct(123.123) FROM fake_innodb_table; +oct(123.123) +173 +SELECT oct(123.123) FROM fake_duckdb_table; +oct(123.123) +173 +SELECT oct('123.123a') FROM fake_innodb_table; +oct('123.123a') +173 +SELECT oct('123.123a') FROM fake_duckdb_table; +oct('123.123a') +173 +SELECT bin(123.123) FROM fake_innodb_table; +bin(123.123) +1111011 +SELECT bin(123.123) FROM fake_duckdb_table; +bin(123.123) +1111011 +SELECT bin('123.123a') FROM fake_innodb_table; +bin('123.123a') +1111011 +SELECT bin('123.123a') FROM fake_duckdb_table; +bin('123.123a') +1111011 +SELECT hex(123.123) FROM fake_innodb_table; +hex(123.123) +7B +SELECT hex(123.123) FROM fake_duckdb_table; +hex(123.123) +7B +SELECT hex('123.123a') FROM fake_innodb_table; +hex('123.123a') +3132332E31323361 +SELECT hex('123.123a') FROM fake_duckdb_table; +hex('123.123a') +3132332E31323361 +SELECT left('abc', -1) FROM fake_innodb_table; +left('abc', -1) + +SELECT left('abc', -1) FROM fake_duckdb_table; +left('abc', -1) + +SELECT right('abc', -1) FROM fake_innodb_table; +right('abc', -1) + +SELECT right('abc', -1) FROM fake_duckdb_table; +right('abc', -1) + +SELECT trim('ab' from 'aaab') FROM fake_innodb_table; +trim('ab' from 'aaab') +aa +SELECT trim('ab' from 'aaab') FROM fake_duckdb_table; +trim('ab' from 'aaab') +aa +CREATE TABLE t1 (id TIMESTAMP PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES('2020-01-01 12:12:12.123'); +SELECT CONVERT(id, TIME) FROM t1; +CONVERT(id, TIME) +12:12:12 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CONVERT(id, TIME) FROM t1; +CONVERT(id, TIME) +12:12:12 +DROP TABLE t1; +SELECT time_to_sec('2020-01-01 12:12:12') FROM fake_innodb_table; +time_to_sec('2020-01-01 12:12:12') +43932 +SELECT time_to_sec('2020-01-01 12:12:12') FROM fake_duckdb_table; +time_to_sec('2020-01-01 12:12:12') +43932 +SELECT strcmp('a', NULL) FROM fake_innodb_table; +strcmp('a', NULL) +NULL +SELECT strcmp('a', NULL) FROM fake_duckdb_table; +strcmp('a', NULL) +NULL +SELECT substring_index(NULL, 'ab', 2) FROM fake_innodb_table; +substring_index(NULL, 'ab', 2) +NULL +SELECT substring_index(NULL, 'ab', 2) FROM fake_duckdb_table; +substring_index(NULL, 'ab', 2) +NULL +SELECT substring_index('ddabddabcc', NULL, 2) FROM fake_innodb_table; +substring_index('ddabddabcc', NULL, 2) +NULL +SELECT substring_index('ddabddabcc', NULL, 2) FROM fake_duckdb_table; +substring_index('ddabddabcc', NULL, 2) +NULL +SELECT substring_index('ddabddabcc', 'ab', NULL) FROM fake_innodb_table; +substring_index('ddabddabcc', 'ab', NULL) +NULL +SELECT substring_index('ddabddabcc', 'ab', NULL) FROM fake_duckdb_table; +substring_index('ddabddabcc', 'ab', NULL) +NULL +SELECT substring_index('ddabddabcc', 'ab', 2) FROM fake_innodb_table; +substring_index('ddabddabcc', 'ab', 2) +ddabdd +SELECT substring_index('ddabddabcc', 'ab', 2) FROM fake_duckdb_table; +substring_index('ddabddabcc', 'ab', 2) +ddabdd +CREATE TABLE t1 (id TIMESTAMP PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('2020-01-01 12:12:12'); +SELECT weekday(id) FROM t1; +weekday(id) +2 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT weekday(id) FROM t1; +weekday(id) +2 +DROP TABLE t1; +SELECT CONVERT('2020-01-01', TIME) FROM fake_innodb_table; +CONVERT('2020-01-01', TIME) +00:20:20 +Warnings: +Warning 1292 Truncated incorrect time value: '2020-01-01' +SELECT CONVERT('2020-01-01', TIME) FROM fake_duckdb_table; +CONVERT('2020-01-01', TIME) +00:20:20 +SELECT CONVERT('20-01-01', DATE) FROM fake_innodb_table; +CONVERT('20-01-01', DATE) +2020-01-01 +SELECT CONVERT('20-01-01', DATE) FROM fake_duckdb_table; +CONVERT('20-01-01', DATE) +2020-01-01 +SELECT CONVERT('90-01-01', DATE) FROM fake_innodb_table; +CONVERT('90-01-01', DATE) +1990-01-01 +SELECT CONVERT('90-01-01', DATE) FROM fake_duckdb_table; +CONVERT('90-01-01', DATE) +1990-01-01 +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT) ENGINE=InnoDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +id col1 col2 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +id col1 col2 +DROP TABLE t1; +SELECT 5.6 DIV 2.2 FROM fake_innodb_table; +5.6 DIV 2.2 +2 +SELECT 5.6 DIV 2.2 FROM fake_duckdb_table; +5.6 DIV 2.2 +2 +SELECT concat('a', NULL) FROM fake_innodb_table; +concat('a', NULL) +NULL +SELECT concat('a', NULL) FROM fake_duckdb_table; +concat('a', NULL) +NULL +CREATE TABLE t1 (id VARCHAR(20) PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('abc\\b'); +SELECT id LIKE 'abc\\b', id LIKE 'abc\\\\b' FROM t1; +id LIKE 'abc\\b' id LIKE 'abc\\\\b' +0 1 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id LIKE 'abc\\b', id LIKE 'abc\\\\b' FROM t1; +id LIKE 'abc\\b' id LIKE 'abc\\\\b' +0 1 +DROP TABLE t1; +SELECT substring('abcde', 0, 2) FROM fake_innodb_table; +substring('abcde', 0, 2) + +SELECT substring('abcde', 0, 2) FROM fake_duckdb_table; +substring('abcde', 0, 2) + +CREATE TABLE t1 (id VARCHAR(20) collate utf8mb3_general_ci PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +INSERT INTO t1 VALUES ('abc'); +SELECT position('C' IN id) FROM t1; +position('C' IN id) +3 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT position('C' IN id) FROM t1; +position('C' IN id) +3 +DROP TABLE t1; +CREATE TABLE t1 (id VARCHAR(20) collate utf8mb3_bin PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_bin' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +INSERT INTO t1 VALUES ('abc'); +SELECT position('C' IN id) FROM t1; +position('C' IN id) +0 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT position('C' IN id) FROM t1; +position('C' IN id) +0 +DROP TABLE t1; +SELECT CONVERT('', DOUBLE), CONVERT('123.123a', DOUBLE), CONVERT('a123', DOUBLE), CONVERT('1_23', DOUBLE) FROM fake_innodb_table; +CONVERT('', DOUBLE) CONVERT('123.123a', DOUBLE) CONVERT('a123', DOUBLE) CONVERT('1_23', DOUBLE) +0 123.123 0 1 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '123.123a' +Warning 1292 Truncated incorrect DOUBLE value: 'a123' +Warning 1292 Truncated incorrect DOUBLE value: '1_23' +SELECT CONVERT('', DOUBLE), CONVERT('123.123a', DOUBLE), CONVERT('a123', DOUBLE), CONVERT('1_23', DOUBLE) FROM fake_duckdb_table; +CONVERT('', DOUBLE) CONVERT('123.123a', DOUBLE) CONVERT('a123', DOUBLE) CONVERT('1_23', DOUBLE) +0 123.123 0 1 +CREATE TABLE t1 (col1 INT PRIMARY KEY, col2 INT) ENGINE=InnoDB; +CREATE TABLE t2 (col3 INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (1); +SELECT col1, col2, col3 FROM t1 JOIN t2 ON t1.col1 = t2.col3 GROUP BY col1, col2, col3 WITH ROLLUP ORDER BY 1, 2, 3; +col1 col2 col3 +NULL NULL NULL +1 NULL NULL +1 1 NULL +1 1 1 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT col1, col2, col3 FROM t1 JOIN t2 ON t1.col1 = t2.col3 GROUP BY col1, col2, col3 WITH ROLLUP ORDER BY 1, 2, 3; +col1 col2 col3 +NULL NULL NULL +1 NULL NULL +1 1 NULL +1 1 1 +DROP TABLE t1; +DROP TABLE t2; +SELECT CONVERT('abcde', CHAR(3)) FROM fake_innodb_table; +CONVERT('abcde', CHAR(3)) +abc +Warnings: +Warning 1292 Truncated incorrect CHAR(3) value: 'abcde' +SELECT CONVERT('abcde', CHAR(3)) FROM fake_duckdb_table; +CONVERT('abcde', CHAR(3)) +abc +SELECT CONVERT(12345.12345, DECIMAL) FROM fake_innodb_table; +CONVERT(12345.12345, DECIMAL) +12345 +SELECT CONVERT(12345.12345, DECIMAL) FROM fake_duckdb_table; +CONVERT(12345.12345, DECIMAL) +12345 +CREATE TABLE t1(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +CREATE TABLE t2(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +INSERT INTO t1 VALUES ('A'); +INSERT INTO t2 VALUES ('a'); +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +COUNT(*) +1 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +COUNT(*) +1 +DROP TABLE t1; +DROP TABLE t2; +CREATE TABLE t1(id VARCHAR(20) COLLATE utf8mb3_bin PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_bin' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +CREATE TABLE t2(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +Warnings: +Warning 3778 'utf8mb3_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +INSERT INTO t1 VALUES ('A'); +INSERT INTO t2 VALUES ('a'); +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +COUNT(*) +2 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +COUNT(*) +2 +DROP TABLE t1; +DROP TABLE t2; +CREATE TABLE t1(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_ai_ci) ENGINE=InnoDB; +CREATE TABLE t2(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_as_ci) ENGINE=InnoDB; +CREATE TABLE t3(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_as_cs) ENGINE=InnoDB; +CREATE TABLE t4(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_bin) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t2 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t3 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t4 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +SELECT * FROM t1 d1, t1 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +1 a 3 ä +2 A 1 a +2 A 2 A +2 A 3 ä +3 ä 1 a +3 ä 2 A +3 ä 3 ä +SELECT * FROM t2 d1, t2 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +2 A 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t3 d1, t3 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t4 d1, t4 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1 d1, (SELECT * FROM t1 UNION SELECT * FROM t1) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +1 a 3 ä +2 A 1 a +2 A 2 A +2 A 3 ä +3 ä 1 a +3 ä 2 A +3 ä 3 ä +SELECT * FROM t2 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +2 A 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t3 d1, (SELECT * FROM t3 UNION SELECT * FROM t3) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t4 d1, (SELECT * FROM t4 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1, t2 WHERE t1.col1 = t2.col1; +ERROR HY000: Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_0900_as_ci,IMPLICIT) for operation '=' +SELECT * FROM t1 UNION SELECT * FROM t2; +ERROR HY000: Illegal mix of collations for operation 'UNION' +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1; +ERROR HY000: Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_0900_as_ci,IMPLICIT) for operation '=' +SELECT * FROM t1, t4 WHERE t1.col1 = t4.col1 ORDER BY t1.id, t4.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1 UNION SELECT * FROM t4 ORDER BY id, col1; +id col1 +1 a +2 A +3 ä +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +ALTER TABLE t3 ENGINE=DuckDB; +ALTER TABLE t4 ENGINE=DuckDB; +SELECT * FROM t1 d1, t1 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +1 a 3 ä +2 A 1 a +2 A 2 A +2 A 3 ä +3 ä 1 a +3 ä 2 A +3 ä 3 ä +SELECT * FROM t2 d1, t2 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +2 A 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t3 d1, t3 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t4 d1, t4 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1 d1, (SELECT * FROM t1 UNION SELECT * FROM t1) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +1 a 3 ä +2 A 1 a +2 A 2 A +2 A 3 ä +3 ä 1 a +3 ä 2 A +3 ä 3 ä +SELECT * FROM t2 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +1 a 2 A +2 A 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t3 d1, (SELECT * FROM t3 UNION SELECT * FROM t3) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t4 d1, (SELECT * FROM t4 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1, t2 WHERE t1.col1 = t2.col1; +ERROR HY000: Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_0900_as_ci,IMPLICIT) for operation '=' +SELECT * FROM t1 UNION SELECT * FROM t2; +ERROR HY000: Illegal mix of collations for operation 'UNION' +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1; +ERROR HY000: Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_0900_as_ci,IMPLICIT) for operation '=' +SELECT * FROM t1, t4 WHERE t1.col1 = t4.col1 ORDER BY t1.id, t4.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +SELECT * FROM t1 UNION SELECT * FROM t4 ORDER BY id, col1; +id col1 +1 a +2 A +3 ä +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +id col1 id col1 +1 a 1 a +2 A 2 A +3 ä 3 ä +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +DROP TABLE t4; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 2), (2, 1); +SELECT GROUP_CONCAT(col1 ORDER BY 1) FROM t1; +GROUP_CONCAT(col1 ORDER BY 1) +1,2 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT GROUP_CONCAT(col1 ORDER BY 1) FROM t1; +GROUP_CONCAT(col1 ORDER BY 1) +1,2 +DROP TABLE t1; +SELECT CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END FROM fake_innodb_table; +CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END +1a +SELECT CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END FROM fake_duckdb_table; +CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END +1a +CREATE TABLE t1 (id INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +SELECT CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END FROM t1; +CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END +1a +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END FROM t1; +CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END +1a +DROP TABLE t1; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1), (2, NULL); +SELECT IFNULL(col1, '1a') FROM t1; +IFNULL(col1, '1a') +1 +1a +ALTER TABLE t1 ENGINE=DuckDB; +SELECT IFNULL(col1, '1a') FROM t1; +IFNULL(col1, '1a') +1 +1a +DROP TABLE t1; +CREATE TABLE t1 (id1 INT PRIMARY KEY, col1 INT); +CREATE TABLE t2 (id2 INT PRIMARY KEY, col1 INT); +SELECT * FROM t1 JOIN t2 USING(col1); +col1 id1 id2 +SELECT * FROM t1 LEFT JOIN t2 USING(col1); +col1 id1 id2 +SELECT * FROM t1 RIGHT JOIN t2 USING(col1); +col1 id2 id1 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 JOIN t2 USING(col1); +col1 id1 id2 +SELECT * FROM t1 LEFT JOIN t2 USING(col1); +col1 id1 id2 +SELECT * FROM t1 RIGHT JOIN t2 USING(col1); +col1 id2 id1 +DROP TABLE t1; +DROP TABLE t2; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 DOUBLE); +INSERT INTO t1 VALUES (1, 1e30); +INSERT INTO t1 VALUES (2, 0); +SELECT CONVERT(id, CHAR) FROM t1; +CONVERT(id, CHAR) +1 +2 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CONVERT(id, CHAR) FROM t1; +CONVERT(id, CHAR) +1 +2 +DROP TABLE t1; +DROP TABLE fake_innodb_table; +DROP TABLE fake_duckdb_table; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 BLOB); +INSERT INTO t1 VALUES (1, 0xFF), (2, 0x61), (3, 0xE695B0E68DAEE5BA93); +SELECT CAST(col1 AS CHAR) FROM t1; +CAST(col1 AS CHAR) +NULL +a +数据库 +Warnings: +Warning 1300 Invalid utf8mb4 character string: 'FF' +SELECT CAST(col1 AS CHAR) like '%据%' FROM t1; +CAST(col1 AS CHAR) like '%据%' +NULL +0 +1 +Warnings: +Warning 1300 Invalid utf8mb4 character string: 'FF' +SELECT FROM_BASE64(TO_BASE64('数据库')), CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' FROM t1 LIMIT 1; +FROM_BASE64(TO_BASE64('数据库')) CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' +数据库 1 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CAST(col1 AS CHAR) FROM t1; +CAST(col1 AS CHAR) +NULL +a +数据库 +SELECT CAST(col1 AS CHAR) like '%据%' FROM t1; +CAST(col1 AS CHAR) like '%据%' +NULL +0 +1 +SELECT FROM_BASE64(TO_BASE64('数据库')), CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' FROM t1 LIMIT 1; +FROM_BASE64(TO_BASE64('数据库')) CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' +数据库 1 +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_json.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_json.result new file mode 100644 index 0000000000000..7b77a1c4e9574 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_json.result @@ -0,0 +1,538 @@ +CREATE TABLE t1_innodb (id INT PRIMARY KEY, col1 JSON) ENGINE=InnoDB; +CREATE TABLE t1_duckdb (id INT PRIMARY KEY, col1 JSON) ENGINE=DuckDB; +CREATE TABLE t2_innodb (id INT PRIMARY KEY, col1 VARCHAR(200)) ENGINE=InnoDB; +CREATE TABLE t2_duckdb (id INT PRIMARY KEY, col1 VARCHAR(200)) ENGINE=DuckDB; + +1. JSON_ARRAY + +INSERT INTO t1_innodb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t1_duckdb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t2_innodb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t2_duckdb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +SELECT * FROM t1_innodb; +id col1 +1 {"test": [1, 1.2, "json", true]} +SELECT * FROM t1_duckdb; +id col1 +1 {"test": [1, 1.2, "json", true]} +SELECT t1_innodb.col1 = t2_innodb.col1 FROM t1_innodb, t2_innodb; +t1_innodb.col1 = t2_innodb.col1 +0 +SELECT t1_duckdb.col1 = t2_duckdb.col1 FROM t1_duckdb, t2_duckdb; +ERROR HY000: [DuckDB] Conversion Error: Malformed JSON at byte 3 of input: unexpected content after document. Input: "\x22{\x22test\x22: [1, 1.2, \x22json\x22, true]...". + +2. JSON_ARRAY_APPEND() + + +3. JSON_ARRAY_INSERT() + + +4. JSON_CONTAINS() + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') +0 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') FROM t1_innodb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') FROM t1_duckdb; +JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') +1 +SELECT JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_innodb; +JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') +1 +SELECT JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_duckdb; +JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') +1 + +5. JSON_CONTAINS_PATH() + +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') FROM t1_innodb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') +1 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') FROM t1_duckdb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') +1 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') FROM t1_innodb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') +0 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') FROM t1_duckdb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') +0 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') FROM t1_innodb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') +1 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') FROM t1_duckdb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') +1 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') FROM t1_innodb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') +0 +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') FROM t1_duckdb; +JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') +0 + +6. JSON_DEPTH() + +SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true') FROM t1_innodb; +JSON_DEPTH('{}') JSON_DEPTH('[]') JSON_DEPTH('true') +1 1 1 +SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true') FROM t1_duckdb; +JSON_DEPTH('{}') JSON_DEPTH('[]') JSON_DEPTH('true') +1 1 1 +SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]') FROM t1_innodb; +JSON_DEPTH('[10, 20]') JSON_DEPTH('[[], {}]') +2 2 +SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]') FROM t1_duckdb; +JSON_DEPTH('[10, 20]') JSON_DEPTH('[[], {}]') +2 2 +SELECT JSON_DEPTH('[10, {"a": 20}]') FROM t1_innodb; +JSON_DEPTH('[10, {"a": 20}]') +3 +SELECT JSON_DEPTH('[10, {"a": 20}]') FROM t1_duckdb; +JSON_DEPTH('[10, {"a": 20}]') +3 + +7. JSON_EXTRACT() + +SELECT JSON_EXTRACT(col1, '$.test[0]') = 1 FROM t1_innodb; +JSON_EXTRACT(col1, '$.test[0]') = 1 +1 +SELECT JSON_EXTRACT(col1, '$.test[1]') = 1.2 FROM t1_innodb; +JSON_EXTRACT(col1, '$.test[1]') = 1.2 +1 +SELECT JSON_EXTRACT(col1, '$.test[2]') = 'json' FROM t1_innodb; +JSON_EXTRACT(col1, '$.test[2]') = 'json' +1 +SELECT JSON_EXTRACT(col1, '$.test[0]') = 1 FROM t1_duckdb; +JSON_EXTRACT(col1, '$.test[0]') = 1 +1 +SELECT JSON_EXTRACT(col1, '$.test[1]') = 1.2 FROM t1_duckdb; +JSON_EXTRACT(col1, '$.test[1]') = 1.2 +1 +SELECT JSON_EXTRACT(col1, '$.test[2]') = 'json' FROM t1_duckdb; +JSON_EXTRACT(col1, '$.test[2]') = 'json' +1 + +8. JSON_INSERT() + + +9. JSON_KEYS() + +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}') FROM t1_innodb; +JSON_KEYS('{"a": 1, "b": {"c": 30}}') +["a", "b"] +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}') FROM t1_duckdb; +JSON_KEYS('{"a": 1, "b": {"c": 30}}') +["a", "b"] +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') FROM t1_innodb; +JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') +["c"] +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') FROM t1_duckdb; +JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') +["c"] + +10. JSON_LENGTH() + +SELECT JSON_LENGTH(col1, '$'), JSON_LENGTH(col1, '$.test'), JSON_LENGTH(col1, '$.test[0]') FROM t1_innodb; +JSON_LENGTH(col1, '$') JSON_LENGTH(col1, '$.test') JSON_LENGTH(col1, '$.test[0]') +1 4 1 +SELECT JSON_LENGTH(col1, '$'), JSON_LENGTH(col1, '$.test'), JSON_LENGTH(col1, '$.test[0]') FROM t1_duckdb; +JSON_LENGTH(col1, '$') JSON_LENGTH(col1, '$.test') JSON_LENGTH(col1, '$.test[0]') +1 4 1 + +11. JSON_MERGE() + + +12. JSON_MERGE_PATCH() + +SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]') FROM t1_innodb; +JSON_MERGE_PATCH('[1, 2]', '[true, false]') +[true, false] +SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]') FROM t1_duckdb; +JSON_MERGE_PATCH('[1, 2]', '[true, false]') +[true,false] +SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') FROM t1_innodb; +JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') +{"id": 47, "name": "x"} +SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') FROM t1_duckdb; +JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') +{"name":"x","id":47} +SELECT JSON_MERGE_PATCH('1', 'true') FROM t1_innodb; +JSON_MERGE_PATCH('1', 'true') +true +SELECT JSON_MERGE_PATCH('1', 'true') FROM t1_duckdb; +JSON_MERGE_PATCH('1', 'true') +true +SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') FROM t1_innodb; +JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') +{"id": 47} +SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') FROM t1_duckdb; +JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') +{"id":47} +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') FROM t1_innodb; +JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') +{"a": 3, "b": 2, "c": 4} +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') FROM t1_duckdb; +JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') +{"b":2,"a":3,"c":4} +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') FROM t1_innodb; +JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') +{"a": 3, "b": 2, "c": 4} +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') FROM t1_duckdb; +JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') +{"b":2,"a":3,"c":4} + +13. JSON_MERGE_PRESERVE() + + +14. JSON_OBJECT() + +SELECT JSON_OBJECT('id', 87, 'name', 'carrot') FROM t1_innodb; +JSON_OBJECT('id', 87, 'name', 'carrot') +{"id": 87, "name": "carrot"} +SELECT JSON_OBJECT('id', 87, 'name', 'carrot') FROM t1_duckdb; +JSON_OBJECT('id', 87, 'name', 'carrot') +{"id":87,"name":"carrot"} + +15. JSON_OVERLAPS() + +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") FROM t1_innodb; +JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") +1 +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") FROM t1_duckdb; +JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") +1 +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") FROM t1_innodb; +JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") +1 +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") FROM t1_duckdb; +JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") +1 +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") FROM t1_innodb; +JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") +0 +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") FROM t1_duckdb; +JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") +0 +SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') FROM t1_innodb; +JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') +0 +SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') FROM t1_duckdb; +JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') +0 +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') FROM t1_innodb; +JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') +1 +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') FROM t1_duckdb; +JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') +1 +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') FROM t1_innodb; +JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') +0 +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') FROM t1_duckdb; +JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') +0 +SELECT JSON_OVERLAPS('5', '5') FROM t1_innodb; +JSON_OVERLAPS('5', '5') +1 +SELECT JSON_OVERLAPS('5', '5') FROM t1_duckdb; +JSON_OVERLAPS('5', '5') +1 +SELECT JSON_OVERLAPS('5', '6') FROM t1_innodb; +JSON_OVERLAPS('5', '6') +0 +SELECT JSON_OVERLAPS('5', '6') FROM t1_duckdb; +JSON_OVERLAPS('5', '6') +0 +SELECT JSON_OVERLAPS('[4,5,"6",7]', '6') FROM t1_innodb; +JSON_OVERLAPS('[4,5,"6",7]', '6') +0 +SELECT JSON_OVERLAPS('[4,5,"6",7]', '6') FROM t1_duckdb; +JSON_OVERLAPS('[4,5,"6",7]', '6') +0 +SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"') FROM t1_innodb; +JSON_OVERLAPS('[4,5,6,7]', '"6"') +0 +SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"') FROM t1_duckdb; +JSON_OVERLAPS('[4,5,6,7]', '"6"') +0 + +16. JSON_PRETTY() + +SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}') FROM t1_innodb; +JSON_PRETTY('{"a":"10","b":"15","x":"25"}') +{ + "a": "10", + "b": "15", + "x": "25" +} +SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}') FROM t1_duckdb; +JSON_PRETTY('{"a":"10","b":"15","x":"25"}') +{ + "a": "10", + "b": "15", + "x": "25" +} + +17. JSON_QUOTE() + +SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"') FROM t1_innodb; +JSON_QUOTE('null') JSON_QUOTE('"null"') +"null" "\"null\"" +SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"') FROM t1_duckdb; +JSON_QUOTE('null') JSON_QUOTE('"null"') +"null" "\"null\"" +SELECT JSON_QUOTE('[1, 2, 3]') FROM t1_innodb; +JSON_QUOTE('[1, 2, 3]') +"[1, 2, 3]" +SELECT JSON_QUOTE('[1, 2, 3]') FROM t1_duckdb; +JSON_QUOTE('[1, 2, 3]') +"[1, 2, 3]" +SELECT JSON_QUOTE('test') FROM t1_innodb; +JSON_QUOTE('test') +"test" +SELECT JSON_QUOTE('test') FROM t1_duckdb; +JSON_QUOTE('test') +"test" + +18. JSON_REMOVE() + + +19. JSON_REPLACE() + + +20. JSON_SCHEMA_VALID() + + +21. JSON_SCHEMA_VALIDATION_REPORT() + + +22. JSON_SEARCH() + + +23. JSON_SET() + + +24. JSON_STORAGE_FREE() + + +25. JSON_STORAGE_SIZE() + + +26. JSON_TABLE() + + +27. JSON_TYPE() + +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$')) +OBJECT +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$')) +OBJECT +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test')) +ARRAY +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test')) +ARRAY +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) +INTEGER +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) +UBIGINT +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) +DECIMAL +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) +DOUBLE +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) +STRING +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) +VARCHAR +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_innodb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) +BOOLEAN +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_duckdb; +JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) +BOOLEAN + +28. JSON_UNQUOTE() + +SELECT JSON_UNQUOTE('"test"') FROM t1_innodb; +JSON_UNQUOTE('"test"') +test +SELECT JSON_UNQUOTE('"test"') FROM t1_duckdb; +JSON_UNQUOTE('"test"') +test +SELECT JSON_UNQUOTE(NULL) FROM t1_innodb; +JSON_UNQUOTE(NULL) +NULL +SELECT JSON_UNQUOTE(NULL) FROM t1_duckdb; +JSON_UNQUOTE(NULL) +NULL +SELECT JSON_UNQUOTE('[1, 2, 3]') FROM t1_innodb; +JSON_UNQUOTE('[1, 2, 3]') +[1, 2, 3] +SELECT JSON_UNQUOTE('[1, 2, 3]') FROM t1_duckdb; +JSON_UNQUOTE('[1, 2, 3]') +[1, 2, 3] +SELECT JSON_UNQUOTE('{"a": 1}') FROM t1_innodb; +JSON_UNQUOTE('{"a": 1}') +{"a": 1} +SELECT JSON_UNQUOTE('{"a": 1}') FROM t1_duckdb; +JSON_UNQUOTE('{"a": 1}') +{"a": 1} + +29. JSON_VALID() + +SELECT JSON_VALID('test') FROM t1_innodb; +JSON_VALID('test') +0 +SELECT JSON_VALID('test') FROM t1_duckdb; +JSON_VALID('test') +0 +SELECT JSON_VALID('"test"') FROM t1_innodb; +JSON_VALID('"test"') +1 +SELECT JSON_VALID('"test"') FROM t1_duckdb; +JSON_VALID('"test"') +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$.test')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$.test')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_innodb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) +1 +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_duckdb; +JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) +1 + +30. JSON_VALUE() + +SELECT JSON_VALUE(col1, '$') FROM t1_innodb; +JSON_VALUE(col1, '$') +{"test": [1, 1.2, "json", true]} +SELECT JSON_VALUE(col1, '$') FROM t1_duckdb; +JSON_VALUE(col1, '$') +NULL +SELECT JSON_VALUE(col1, '$.test') FROM t1_innodb; +JSON_VALUE(col1, '$.test') +[1, 1.2, "json", true] +SELECT JSON_VALUE(col1, '$.test') FROM t1_duckdb; +JSON_VALUE(col1, '$.test') +NULL +SELECT JSON_VALUE(col1, '$.test[0]') FROM t1_innodb; +JSON_VALUE(col1, '$.test[0]') +1 +SELECT JSON_VALUE(col1, '$.test[0]') FROM t1_duckdb; +JSON_VALUE(col1, '$.test[0]') +1 +SELECT JSON_VALUE(col1, '$.test[1]') FROM t1_innodb; +JSON_VALUE(col1, '$.test[1]') +1.2 +SELECT JSON_VALUE(col1, '$.test[1]') FROM t1_duckdb; +JSON_VALUE(col1, '$.test[1]') +1.2 +SELECT JSON_VALUE(col1, '$.test[2]') FROM t1_innodb; +JSON_VALUE(col1, '$.test[2]') +json +SELECT JSON_VALUE(col1, '$.test[2]') FROM t1_duckdb; +JSON_VALUE(col1, '$.test[2]') +"json" +SELECT JSON_VALUE(col1, '$.test[3]') FROM t1_innodb; +JSON_VALUE(col1, '$.test[3]') +true +SELECT JSON_VALUE(col1, '$.test[3]') FROM t1_duckdb; +JSON_VALUE(col1, '$.test[3]') +true + +31. MEMBER OF() + +DROP TABLE t1_innodb; +DROP TABLE t1_duckdb; +DROP TABLE t2_innodb; +DROP TABLE t2_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_monitor.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_monitor.result new file mode 100644 index 0000000000000..49cafc0c20898 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_monitor.result @@ -0,0 +1,82 @@ +# restart +CREATE TABLE t1(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=duckdb; +CREATE TABLE t2(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=duckdb; +CREATE TABLE t3(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=innodb; +# +# 1) INSERT: rows_insert increments per row (dml_in_batch=OFF) +# +SHOW STATUS LIKE 'Duckdb_rows_insert'; +Variable_name Value +Duckdb_rows_insert 0 +INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'); +INSERT INTO t2 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'); +SHOW STATUS LIKE 'Duckdb_rows_insert'; +Variable_name Value +Duckdb_rows_insert 10 +# +# 2) DELETE: rows_delete increments via direct_delete_rows +# +SHOW STATUS LIKE 'Duckdb_rows_delete'; +Variable_name Value +Duckdb_rows_delete 0 +DELETE FROM t1 WHERE id IN (1,2); +SHOW STATUS LIKE 'Duckdb_rows_delete'; +Variable_name Value +Duckdb_rows_delete 2 +# +# 3) UPDATE: rows_update increments via direct_update_rows +# +SHOW STATUS LIKE 'Duckdb_rows_update'; +Variable_name Value +Duckdb_rows_update 0 +UPDATE t1 SET v = 'x' WHERE id IN (3,4); +SHOW STATUS LIKE 'Duckdb_rows_update'; +Variable_name Value +Duckdb_rows_update 2 +# +# 4) INSERT SELECT: rows_insert increments +# +SHOW STATUS LIKE 'Duckdb_rows_insert'; +Variable_name Value +Duckdb_rows_insert 10 +INSERT INTO t1 SELECT * FROM t2; +SHOW STATUS LIKE 'Duckdb_rows_insert'; +Variable_name Value +Duckdb_rows_insert 11 +# +# 5) Duckdb_commit increments on explicit COMMIT +# +@commits_before := 2 +2 +BEGIN; +INSERT INTO t1 VALUES(6,'f'); +COMMIT; +commit_delta +1 +# +# 6) Duckdb_rollback increments on explicit ROLLBACK +# +SHOW STATUS LIKE 'Duckdb_rollback'; +Variable_name Value +Duckdb_rollback 0 +BEGIN; +INSERT INTO t1 VALUES(7,'g'); +ROLLBACK; +SHOW STATUS LIKE 'Duckdb_rollback'; +Variable_name Value +Duckdb_rollback 1 +# +# 7) Final state: batch counters should be 0 (dml_in_batch=OFF) +# +SHOW STATUS LIKE 'Duckdb_rows_insert_in_batch'; +Variable_name Value +Duckdb_rows_insert_in_batch 0 +SHOW STATUS LIKE 'Duckdb_rows_update_in_batch'; +Variable_name Value +Duckdb_rows_update_in_batch 0 +SHOW STATUS LIKE 'Duckdb_rows_delete_in_batch'; +Variable_name Value +Duckdb_rows_delete_in_batch 0 +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_numeric_func.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_numeric_func.result new file mode 100644 index 0000000000000..6bbcddcc8cf87 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_numeric_func.result @@ -0,0 +1,310 @@ +CREATE DATABASE test_duckdb; +USE test_duckdb; +CREATE TABLE t_innodb (id int PRIMARY KEY, col1 TINYINT, col2 INT, col3 BIGINT, col4 DECIMAL(38, 10), col5 DOUBLE ) ENGINE=Innodb; +CREATE TABLE t_duckdb (id int PRIMARY KEY, col1 TINYINT, col2 INT, col3 BIGINT, col4 DECIMAL(38, 10), col5 DOUBLE ) ENGINE=DuckDB; +INSERT INTO t_innodb VALUES (1, 10, 12345678, 23372036854775808, 123456789101112131415161718.1234567891, 123456789101112131415161718.1234567891); +INSERT INTO t_innodb VALUES (2, -10, -12345678, -12345678910111213, -123456789101112131415161718.1234567891, -123456789101112131415161718.1234567891); +INSERT INTO t_duckdb VALUES (1, 10, 12345678, 23372036854775808, 123456789101112131415161718.1234567891, 123456789101112131415161718.1234567891); +INSERT INTO t_duckdb VALUES (2, -10, -12345678, -12345678910111213, -123456789101112131415161718.1234567891, -123456789101112131415161718.1234567891); +--------------------------- +1. ABS() +--------------------------- +SELECT ABS(col1), ABS(col2), ABS(col3), ABS(col4), ABS(col5) FROM t_innodb; +ABS(col1) ABS(col2) ABS(col3) ABS(col4) ABS(col5) +10 12345678 23372036854775808 123456789101112131415161718.1234567891 1.2345678910111214e26 +10 12345678 12345678910111213 123456789101112131415161718.1234567891 1.2345678910111214e26 +SELECT ABS(col1), ABS(col2), ABS(col3), ABS(col4), ABS(col5) FROM t_duckdb; +ABS(col1) ABS(col2) ABS(col3) ABS(col4) ABS(col5) +10 12345678 23372036854775808 123456789101112131415161718.1234567891 1.2345678910111214e26 +10 12345678 12345678910111213 123456789101112131415161718.1234567891 1.2345678910111214e26 +--------------------------- +2. ACOS() +--------------------------- +SELECT ACOS(COS(col1)), ACOS(COS(col2)), ACOS(COS(col3)), ACOS(COS(col4)), ACOS(COS(col5)), ACOS(col1) FROM t_innodb; +ACOS(COS(col1)) ACOS(COS(col2)) ACOS(COS(col3)) ACOS(COS(col4)) ACOS(COS(col5)) ACOS(col1) +2.566370614359173 2.013629797158435 1.0957328091276692 1.9687451843101136 1.9687451843101136 NULL +2.566370614359173 2.013629797158435 2.3766802678405354 1.9687451843101136 1.9687451843101136 NULL +SELECT ACOS(COS(col1)), ACOS(COS(col2)), ACOS(COS(col3)), ACOS(COS(col4)), ACOS(COS(col5)), ACOS(col1) FROM t_duckdb; +ACOS(COS(col1)) ACOS(COS(col2)) ACOS(COS(col3)) ACOS(COS(col4)) ACOS(COS(col5)) ACOS(col1) +2.566370614359173 2.013629797158435 1.0957328091276692 1.9687451843101136 1.9687451843101136 NULL +2.566370614359173 2.013629797158435 2.3766802678405354 1.9687451843101136 1.9687451843101136 NULL +--------------------------- +3. ASIN() +--------------------------- +SELECT ASIN(SIN(col1)), ASIN(SIN(col2)), ASIN(SIN(col3)), ASIN(SIN(col4)), ASIN(SIN(col5)), ASIN(col1) FROM t_innodb; +ASIN(SIN(col1)) ASIN(SIN(col2)) ASIN(SIN(col3)) ASIN(SIN(col4)) ASIN(SIN(col5)) ASIN(col1) +-0.5752220392306202 -1.1279628564313582 1.0957328091276692 -1.17284746927968 -1.17284746927968 NULL +0.5752220392306202 1.1279628564313582 -0.7649123857492577 1.17284746927968 1.17284746927968 NULL +SELECT ASIN(SIN(col1)), ASIN(SIN(col2)), ASIN(SIN(col3)), ASIN(SIN(col4)), ASIN(SIN(col5)), ASIN(col1) FROM t_duckdb; +ASIN(SIN(col1)) ASIN(SIN(col2)) ASIN(SIN(col3)) ASIN(SIN(col4)) ASIN(SIN(col5)) ASIN(col1) +-0.5752220392306202 -1.1279628564313582 1.0957328091276692 -1.17284746927968 -1.17284746927968 NULL +0.5752220392306202 1.1279628564313582 -0.7649123857492577 1.17284746927968 1.17284746927968 NULL +--------------------------- +3. ATAN() +--------------------------- +SELECT ATAN(TAN(col1)), ATAN(TAN(col2)), ATAN(TAN(col3)), ATAN(TAN(col4)), ATAN(TAN(col5)) FROM t_innodb; +ATAN(TAN(col1)) ATAN(TAN(col2)) ATAN(TAN(col3)) ATAN(TAN(col4)) ATAN(TAN(col5)) +0.5752220392306202 1.1279628564313582 1.0957328091276692 1.1728474692796798 1.1728474692796798 +-0.5752220392306202 -1.1279628564313582 0.7649123857492577 -1.1728474692796798 -1.1728474692796798 +SELECT ATAN(TAN(col1)), ATAN(TAN(col2)), ATAN(TAN(col3)), ATAN(TAN(col4)), ATAN(TAN(col5)) FROM t_duckdb; +ATAN(TAN(col1)) ATAN(TAN(col2)) ATAN(TAN(col3)) ATAN(TAN(col4)) ATAN(TAN(col5)) +0.5752220392306202 1.1279628564313582 1.0957328091276692 1.1728474692796798 1.1728474692796798 +-0.5752220392306202 -1.1279628564313582 0.7649123857492577 -1.1728474692796798 -1.1728474692796798 +--------------------------- +4. ATAN2() +--------------------------- +SELECT ATAN2(TAN(col1), 2), ATAN2(TAN(col2), 2), ATAN2(TAN(col3), 2), ATAN2(TAN(col4), 2), ATAN2(TAN(col5), 2) FROM t_innodb; +ATAN2(TAN(col1), 2) ATAN2(TAN(col2), 2) ATAN2(TAN(col3), 2) ATAN2(TAN(col4), 2) ATAN2(TAN(col5), 2) +0.31349043283554906 0.8118261525978633 0.7712497528429478 0.8716939864894593 0.8716939864894593 +-0.31349043283554906 -0.8118261525978633 0.44745715834771166 -0.8716939864894593 -0.8716939864894593 +SELECT ATAN2(TAN(col1), 2), ATAN2(TAN(col2), 2), ATAN2(TAN(col3), 2), ATAN2(TAN(col4), 2), ATAN2(TAN(col5), 2) FROM t_duckdb; +ATAN2(TAN(col1), 2) ATAN2(TAN(col2), 2) ATAN2(TAN(col3), 2) ATAN2(TAN(col4), 2) ATAN2(TAN(col5), 2) +0.31349043283554906 0.8118261525978633 0.7712497528429478 0.8716939864894593 0.8716939864894593 +-0.31349043283554906 -0.8118261525978633 0.44745715834771166 -0.8716939864894593 -0.8716939864894593 +--------------------------- +5. CEIL(), CEILING() +--------------------------- +SELECT CEIL(col1), CEIL(col2), CEIL(col3), CEIL(col4), CEIL(col5) FROM t_innodb; +CEIL(col1) CEIL(col2) CEIL(col3) CEIL(col4) CEIL(col5) +10 12345678 23372036854775808 123456789101112131415161719 1.2345678910111214e26 +-10 -12345678 -12345678910111213 -123456789101112131415161718 -1.2345678910111214e26 +SELECT CEIL(col1), CEIL(col2), CEIL(col3), CEIL(col4), CEIL(col5) FROM t_duckdb; +CEIL(col1) CEIL(col2) CEIL(col3) CEIL(col4) CEIL(col5) +10 12345678 23372036854775808 123456789101112131415161719 1.2345678910111214e26 +-10 -12345678 -12345678910111212 -123456789101112131415161718 -1.2345678910111214e26 +SELECT CEILING(col1), CEILING(col2), CEILING(col3), CEILING(col4), CEILING(col5) FROM t_innodb; +CEILING(col1) CEILING(col2) CEILING(col3) CEILING(col4) CEILING(col5) +10 12345678 23372036854775808 123456789101112131415161719 1.2345678910111214e26 +-10 -12345678 -12345678910111213 -123456789101112131415161718 -1.2345678910111214e26 +SELECT CEILING(col1), CEILING(col2), CEILING(col3), CEILING(col4), CEILING(col5) FROM t_duckdb; +CEILING(col1) CEILING(col2) CEILING(col3) CEILING(col4) CEILING(col5) +10 12345678 23372036854775808 123456789101112131415161719 1.2345678910111214e26 +-10 -12345678 -12345678910111212 -123456789101112131415161718 -1.2345678910111214e26 +--------------------------- +6. COS() +--------------------------- +SELECT COS(col1), COS(col2), COS(col3), COS(col4), COS(col5) FROM t_innodb; +COS(col1) COS(col2) COS(col3) COS(col4) COS(col5) +-0.8390715290764524 -0.4285013388218624 0.4573949320555127 -0.38752829712018294 -0.38752829712018294 +-0.8390715290764524 -0.4285013388218624 -0.721443030778011 -0.38752829712018294 -0.38752829712018294 +SELECT COS(col1), COS(col2), COS(col3), COS(col4), COS(col5) FROM t_duckdb; +COS(col1) COS(col2) COS(col3) COS(col4) COS(col5) +-0.8390715290764524 -0.4285013388218624 0.4573949320555127 -0.38752829712018294 -0.38752829712018294 +-0.8390715290764524 -0.4285013388218624 -0.721443030778011 -0.38752829712018294 -0.38752829712018294 +--------------------------- +7. COT() +--------------------------- +SELECT COT(col1), COT(col2), COT(col3), COT(col4), COT(col5) FROM t_innodb; +COT(col1) COT(col2) COT(col3) COT(col4) COT(col5) +1.5423510453569202 0.4742466247267493 0.5143524323507457 0.42037751764290476 0.42037751764290476 +-1.5423510453569202 -0.4742466247267493 1.041834418226068 -0.42037751764290476 -0.42037751764290476 +SELECT COT(col1), COT(col2), COT(col3), COT(col4), COT(col5) FROM t_duckdb; +COT(col1) COT(col2) COT(col3) COT(col4) COT(col5) +1.5423510453569202 0.4742466247267493 0.5143524323507457 0.42037751764290476 0.42037751764290476 +-1.5423510453569202 -0.4742466247267493 1.041834418226068 -0.42037751764290476 -0.42037751764290476 +--------------------------- +8. DEGREES() +--------------------------- +SELECT DEGREES(col1), DEGREES(col2), DEGREES(col3), DEGREES(col4), DEGREES(col5) FROM t_innodb; +DEGREES(col1) DEGREES(col2) DEGREES(col3) DEGREES(col4) DEGREES(col5) +572.9577951308232 707355244.6275111 1.3391190704028687e18 7.073552967730426e27 7.073552967730426e27 +-572.9577951308232 -707355244.6275111 -7.073552967730424e17 -7.073552967730426e27 -7.073552967730426e27 +SELECT DEGREES(col1), DEGREES(col2), DEGREES(col3), DEGREES(col4), DEGREES(col5) FROM t_duckdb; +DEGREES(col1) DEGREES(col2) DEGREES(col3) DEGREES(col4) DEGREES(col5) +572.9577951308232 707355244.6275111 1.3391190704028687e18 7.073552967730426e27 7.073552967730426e27 +-572.9577951308232 -707355244.6275111 -7.073552967730424e17 -7.073552967730426e27 -7.073552967730426e27 +--------------------------- +9. EXP() +--------------------------- +SELECT EXP(LN(ABS(col1))), EXP(LN(ABS(col2))), EXP(LN(ABS(col3))), EXP(LN(ABS(col4))), EXP(LN(ABS(col5))) FROM t_innodb; +EXP(LN(ABS(col1))) EXP(LN(ABS(col2))) EXP(LN(ABS(col3))) EXP(LN(ABS(col4))) EXP(LN(ABS(col5))) +10.000000000000002 12345678.00000002 2.337203685477588e16 1.2345678910111183e26 1.2345678910111183e26 +10.000000000000002 12345678.00000002 1.2345678910111222e16 1.2345678910111183e26 1.2345678910111183e26 +SELECT EXP(LN(ABS(col1))), EXP(LN(ABS(col2))), EXP(LN(ABS(col3))), EXP(LN(ABS(col4))), EXP(LN(ABS(col5))) FROM t_duckdb; +EXP(LN(ABS(col1))) EXP(LN(ABS(col2))) EXP(LN(ABS(col3))) EXP(LN(ABS(col4))) EXP(LN(ABS(col5))) +10.000000000000002 12345678.00000002 2.337203685477588e16 1.2345678910111183e26 1.2345678910111183e26 +10.000000000000002 12345678.00000002 1.2345678910111222e16 1.2345678910111183e26 1.2345678910111183e26 +--------------------------- +10. FLOOR() +--------------------------- +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_innodb; +FLOOR(col1) FLOOR(col2) FLOOR(col3) FLOOR(col4) FLOOR(col5) +10 12345678 23372036854775808 123456789101112131415161718 1.2345678910111214e26 +-10 -12345678 -12345678910111213 -123456789101112131415161719 -1.2345678910111214e26 +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_duckdb; +FLOOR(col1) FLOOR(col2) FLOOR(col3) FLOOR(col4) FLOOR(col5) +10 12345678 23372036854775808 123456789101112131415161718 1.2345678910111214e26 +-10 -12345678 -12345678910111212 -123456789101112131415161719 -1.2345678910111214e26 +--------------------------- +11. FLOOR() +--------------------------- +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_innodb; +FLOOR(col1) FLOOR(col2) FLOOR(col3) FLOOR(col4) FLOOR(col5) +10 12345678 23372036854775808 123456789101112131415161718 1.2345678910111214e26 +-10 -12345678 -12345678910111213 -123456789101112131415161719 -1.2345678910111214e26 +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_duckdb; +FLOOR(col1) FLOOR(col2) FLOOR(col3) FLOOR(col4) FLOOR(col5) +10 12345678 23372036854775808 123456789101112131415161718 1.2345678910111214e26 +-10 -12345678 -12345678910111212 -123456789101112131415161719 -1.2345678910111214e26 +--------------------------- +12. LN() +--------------------------- +SELECT LN(ABS(col1)), LN(ABS(col2)), LN(ABS(col3)), LN(ABS(col4)), LN(ABS(col5)), LN(col1) FROM t_innodb; +LN(ABS(col1)) LN(ABS(col2)) LN(ABS(col3)) LN(ABS(col4)) LN(ABS(col5)) LN(col1) +2.302585092994046 16.32881660027397 37.69031669647892 60.077933440879846 60.077933440879846 2.302585092994046 +2.302585092994046 16.32881660027397 37.05208251093939 60.077933440879846 60.077933440879846 NULL +Warnings: +Warning 3020 Invalid argument for logarithm +SELECT LN(ABS(col1)), LN(ABS(col2)), LN(ABS(col3)), LN(ABS(col4)), LN(ABS(col5)), LN(col1) FROM t_duckdb; +LN(ABS(col1)) LN(ABS(col2)) LN(ABS(col3)) LN(ABS(col4)) LN(ABS(col5)) LN(col1) +2.302585092994046 16.32881660027397 37.69031669647892 60.077933440879846 60.077933440879846 2.302585092994046 +2.302585092994046 16.32881660027397 37.05208251093939 60.077933440879846 60.077933440879846 NULL +--------------------------- +13. LOG() +--------------------------- +SELECT LOG(ABS(col1)), LOG(ABS(col2)), LOG(ABS(col3)), LOG(ABS(col4)), LOG(ABS(col5)), LOG(col1) FROM t_innodb; +LOG(ABS(col1)) LOG(ABS(col2)) LOG(ABS(col3)) LOG(ABS(col4)) LOG(ABS(col5)) LOG(col1) +2.302585092994046 16.32881660027397 37.69031669647892 60.077933440879846 60.077933440879846 2.302585092994046 +2.302585092994046 16.32881660027397 37.05208251093939 60.077933440879846 60.077933440879846 NULL +Warnings: +Warning 3020 Invalid argument for logarithm +SELECT LOG(ABS(col1)), LOG(ABS(col2)), LOG(ABS(col3)), LOG(ABS(col4)), LOG(ABS(col5)), LOG(col1) FROM t_duckdb; +LOG(ABS(col1)) LOG(ABS(col2)) LOG(ABS(col3)) LOG(ABS(col4)) LOG(ABS(col5)) LOG(col1) +2.302585092994046 16.32881660027397 37.69031669647892 60.077933440879846 60.077933440879846 2.302585092994046 +2.302585092994046 16.32881660027397 37.05208251093939 60.077933440879846 60.077933440879846 NULL +--------------------------- +14. LOG10() +--------------------------- +SELECT LOG10(ABS(col1)), LOG10(ABS(col2)), LOG10(ABS(col3)), LOG10(ABS(col4)), LOG10(ABS(col5)), LOG10(col1) FROM t_innodb; +LOG10(ABS(col1)) LOG10(ABS(col2)) LOG10(ABS(col3)) LOG10(ABS(col4)) LOG10(ABS(col5)) LOG10(col1) +1 7.091514945509202 16.36869656246679 26.091514977524962 26.091514977524962 1 +1 7.091514945509202 16.091514977524962 26.091514977524962 26.091514977524962 NULL +Warnings: +Warning 3020 Invalid argument for logarithm +SELECT LOG10(ABS(col1)), LOG10(ABS(col2)), LOG10(ABS(col3)), LOG10(ABS(col4)), LOG10(ABS(col5)), LOG10(col1) FROM t_duckdb; +LOG10(ABS(col1)) LOG10(ABS(col2)) LOG10(ABS(col3)) LOG10(ABS(col4)) LOG10(ABS(col5)) LOG10(col1) +1 7.091514945509202 16.36869656246679 26.091514977524962 26.091514977524962 1 +1 7.091514945509202 16.091514977524962 26.091514977524962 26.091514977524962 NULL +--------------------------- +15. LOG2() +--------------------------- +SELECT LOG2(ABS(col1)), LOG2(ABS(col2)), LOG2(ABS(col3)), LOG2(ABS(col4)), LOG2(ABS(col5)), LOG2(col1) FROM t_innodb; +LOG2(ABS(col1)) LOG2(ABS(col2)) LOG2(ABS(col3)) LOG2(ABS(col4)) LOG2(ABS(col5)) LOG2(col1) +3.321928094887362 23.55750273280064 54.37563298754463 86.67413664201457 86.67413664201457 3.321928094887362 +3.321928094887362 23.55750273280064 53.45485569314095 86.67413664201457 86.67413664201457 NULL +Warnings: +Warning 3020 Invalid argument for logarithm +SELECT LOG2(ABS(col1)), LOG2(ABS(col2)), LOG2(ABS(col3)), LOG2(ABS(col4)), LOG2(ABS(col5)), LOG2(col1) FROM t_duckdb; +LOG2(ABS(col1)) LOG2(ABS(col2)) LOG2(ABS(col3)) LOG2(ABS(col4)) LOG2(ABS(col5)) LOG2(col1) +3.321928094887362 23.55750273280064 54.37563298754463 86.67413664201457 86.67413664201457 3.321928094887362 +3.321928094887362 23.55750273280064 53.45485569314095 86.67413664201457 86.67413664201457 NULL +--------------------------- +16. PI() +--------------------------- +SELECT PI() FROM t_innodb; +PI() +3.141593 +3.141593 +SELECT PI() FROM t_duckdb; +PI() +3.141593 +3.141593 +--------------------------- +17. POW(), POWER() +--------------------------- +SELECT POW(ABS(col1), 0.5), POW(ABS(col2), 0.5), POW(ABS(col3), 0.5), POW(ABS(col4), 0.5), POW(ABS(col5), 0.5) FROM t_innodb; +POW(ABS(col1), 0.5) POW(ABS(col2), 0.5) POW(ABS(col3), 0.5) POW(ABS(col4), 0.5) POW(ABS(col5), 0.5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +3.1622776601683795 3513.641700572214 111111110.651056 11111111065105.602 11111111065105.602 +SELECT POW(ABS(col1), 0.5), POW(ABS(col2), 0.5), POW(ABS(col3), 0.5), POW(ABS(col4), 0.5), POW(ABS(col5), 0.5) FROM t_duckdb; +POW(ABS(col1), 0.5) POW(ABS(col2), 0.5) POW(ABS(col3), 0.5) POW(ABS(col4), 0.5) POW(ABS(col5), 0.5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +3.1622776601683795 3513.641700572214 111111110.651056 11111111065105.602 11111111065105.602 +SELECT POWER(ABS(col1), 0.5), POWER(ABS(col2), 0.5), POWER(ABS(col3), 0.5), POWER(ABS(col4), 0.5), POWER(ABS(col5), 0.5) FROM t_innodb; +POWER(ABS(col1), 0.5) POWER(ABS(col2), 0.5) POWER(ABS(col3), 0.5) POWER(ABS(col4), 0.5) POWER(ABS(col5), 0.5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +3.1622776601683795 3513.641700572214 111111110.651056 11111111065105.602 11111111065105.602 +SELECT POWER(ABS(col1), 0.5), POWER(ABS(col2), 0.5), POWER(ABS(col3), 0.5), POWER(ABS(col4), 0.5), POWER(ABS(col5), 0.5) FROM t_duckdb; +POWER(ABS(col1), 0.5) POWER(ABS(col2), 0.5) POWER(ABS(col3), 0.5) POWER(ABS(col4), 0.5) POWER(ABS(col5), 0.5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +3.1622776601683795 3513.641700572214 111111110.651056 11111111065105.602 11111111065105.602 +--------------------------- +18. RADIANS() +--------------------------- +SELECT RADIANS(col1), RADIANS(col2), RADIANS(col3), RADIANS(col4), RADIANS(col5) FROM t_innodb; +RADIANS(col1) RADIANS(col2) RADIANS(col3) RADIANS(col4) RADIANS(col5) +0.17453292519943295 215472.7294910285 407918996013297.6 2.1547274537546575e24 2.1547274537546575e24 +-0.17453292519943295 -215472.7294910285 -215472745375465.72 -2.1547274537546575e24 -2.1547274537546575e24 +SELECT RADIANS(col1), RADIANS(col2), RADIANS(col3), RADIANS(col4), RADIANS(col5) FROM t_duckdb; +RADIANS(col1) RADIANS(col2) RADIANS(col3) RADIANS(col4) RADIANS(col5) +0.17453292519943295 215472.7294910285 407918996013297.6 2.1547274537546575e24 2.1547274537546575e24 +-0.17453292519943295 -215472.7294910285 -215472745375465.72 -2.1547274537546575e24 -2.1547274537546575e24 +--------------------------- +19. RAND() +--------------------------- +--------------------------- +20. ROUND() +--------------------------- +SELECT ROUND(col1), ROUND(col1, 0), ROUND(col2), ROUND(col2, 0), ROUND(col3), ROUND(col3, 0), ROUND(col4), ROUND(col4, 0), ROUND(col5), ROUND(col5, 0) FROM t_innodb; +ROUND(col1) ROUND(col1, 0) ROUND(col2) ROUND(col2, 0) ROUND(col3) ROUND(col3, 0) ROUND(col4) ROUND(col4, 0) ROUND(col5) ROUND(col5, 0) +10 10 12345678 12345678 23372036854775808 23372036854775808 123456789101112131415161718 123456789101112131415161718 1.2345678910111214e26 1.2345678910111214e26 +-10 -10 -12345678 -12345678 -12345678910111213 -12345678910111213 -123456789101112131415161718 -123456789101112131415161718 -1.2345678910111214e26 -1.2345678910111214e26 +SELECT ROUND(col1), ROUND(col1, 0), ROUND(col2), ROUND(col2, 0), ROUND(col3), ROUND(col3, 0), ROUND(col4), ROUND(col4, 0), ROUND(col5), ROUND(col5, 0) FROM t_duckdb; +ROUND(col1) ROUND(col1, 0) ROUND(col2) ROUND(col2, 0) ROUND(col3) ROUND(col3, 0) ROUND(col4) ROUND(col4, 0) ROUND(col5) ROUND(col5, 0) +10 10 12345678 12345678 23372036854775808 23372036854775808 123456789101112131415161718 123456789101112131415161718 1.2345678910111214e26 1.2345678910111214e26 +-10 -10 -12345678 -12345678 -12345678910111213 -12345678910111213 -123456789101112131415161718 -123456789101112131415161718 -1.2345678910111214e26 -1.2345678910111214e26 +SELECT ROUND(col1, 3), ROUND(col1, -3), ROUND(col2, 6), ROUND(col2, -6), ROUND(col3, 6), ROUND(col3, -6), ROUND(col4, 6), ROUND(col4, 6), ROUND(col5, 6), ROUND(col5, 6) FROM t_innodb; +ROUND(col1, 3) ROUND(col1, -3) ROUND(col2, 6) ROUND(col2, -6) ROUND(col3, 6) ROUND(col3, -6) ROUND(col4, 6) ROUND(col4, 6) ROUND(col5, 6) ROUND(col5, 6) +10 0 12345678 12000000 23372036854775808 23372036855000000 123456789101112131415161718.123457 123456789101112131415161718.123457 1.2345678910111214e26 1.2345678910111214e26 +-10 0 -12345678 -12000000 -12345678910111213 -12345678910000000 -123456789101112131415161718.123457 -123456789101112131415161718.123457 -1.2345678910111214e26 -1.2345678910111214e26 +SELECT ROUND(col1, 3), ROUND(col1, -3), ROUND(col2, 6), ROUND(col2, -6), ROUND(col3, 6), ROUND(col3, -6), ROUND(col4, 6), ROUND(col4, 6), ROUND(col5, 6), ROUND(col5, 6) FROM t_duckdb; +ROUND(col1, 3) ROUND(col1, -3) ROUND(col2, 6) ROUND(col2, -6) ROUND(col3, 6) ROUND(col3, -6) ROUND(col4, 6) ROUND(col4, 6) ROUND(col5, 6) ROUND(col5, 6) +10 0 12345678 12000000 23372036854775808 23372036855000000 123456789101112131415161718.123457 123456789101112131415161718.123457 1.2345678910111214e26 1.2345678910111214e26 +-10 0 -12345678 -12000000 -12345678910111213 -12345678910000000 -123456789101112131415161718.123457 -123456789101112131415161718.123457 -1.2345678910111214e26 -1.2345678910111214e26 +--------------------------- +21. SIGN() +--------------------------- +SELECT SIGN(col1), SIGN(col2), SIGN(col3), SIGN(col4), SIGN(col5) FROM t_innodb; +SIGN(col1) SIGN(col2) SIGN(col3) SIGN(col4) SIGN(col5) +1 1 1 1 1 +-1 -1 -1 -1 -1 +SELECT SIGN(col1), SIGN(col2), SIGN(col3), SIGN(col4), SIGN(col5) FROM t_duckdb; +SIGN(col1) SIGN(col2) SIGN(col3) SIGN(col4) SIGN(col5) +1 1 1 1 1 +-1 -1 -1 -1 -1 +--------------------------- +22. SIN() +--------------------------- +SELECT SIN(col1), SIN(col2), SIN(col3), SIN(col4), SIN(col5) FROM t_innodb; +SIN(col1) SIN(col2) SIN(col3) SIN(col4) SIN(col5) +-0.5440211108893698 -0.9035411460624643 0.8892636707579664 -0.9218578084125183 -0.9218578084125183 +0.5440211108893698 0.9035411460624643 -0.6924737925306906 0.9218578084125183 0.9218578084125183 +SELECT SIN(col1), SIN(col2), SIN(col3), SIN(col4), SIN(col5) FROM t_duckdb; +SIN(col1) SIN(col2) SIN(col3) SIN(col4) SIN(col5) +-0.5440211108893698 -0.9035411460624643 0.8892636707579664 -0.9218578084125183 -0.9218578084125183 +0.5440211108893698 0.9035411460624643 -0.6924737925306906 0.9218578084125183 0.9218578084125183 +--------------------------- +23. SQRT() +--------------------------- +SELECT SQRT(col1), SQRT(col2), SQRT(col3), SQRT(col4), SQRT(col5) FROM t_innodb; +SQRT(col1) SQRT(col2) SQRT(col3) SQRT(col4) SQRT(col5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +NULL NULL NULL NULL NULL +SELECT SQRT(col1), SQRT(col2), SQRT(col3), SQRT(col4), SQRT(col5) FROM t_duckdb; +SQRT(col1) SQRT(col2) SQRT(col3) SQRT(col4) SQRT(col5) +3.1622776601683795 3513.641700572214 152879157.6859835 11111111065105.602 11111111065105.602 +NULL NULL NULL NULL NULL +--------------------------- +24. TAN() +--------------------------- +SELECT TAN(col1), TAN(col2), TAN(col3), TAN(col4), TAN(col5) FROM t_innodb; +TAN(col1) TAN(col2) TAN(col3) TAN(col4) TAN(col5) +0.6483608274590866 2.108607521616371 1.9441922252213304 2.3788141801852096 2.3788141801852096 +-0.6483608274590866 -2.108607521616371 0.9598454250558361 -2.3788141801852096 -2.3788141801852096 +SELECT TAN(col1), TAN(col2), TAN(col3), TAN(col4), TAN(col5) FROM t_duckdb; +TAN(col1) TAN(col2) TAN(col3) TAN(col4) TAN(col5) +0.6483608274590866 2.108607521616371 1.9441922252213304 2.3788141801852096 2.3788141801852096 +-0.6483608274590866 -2.108607521616371 0.9598454250558361 -2.3788141801852096 -2.3788141801852096 +DROP TABLE t_innodb; +DROP TABLE t_duckdb; +DROP DATABASE test_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_refuse_xa.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_refuse_xa.result new file mode 100644 index 0000000000000..41dfc7a6cf9bc --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_refuse_xa.result @@ -0,0 +1,35 @@ +CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; +XA START 'xa_1'; +CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +INSERT INTO t1 (c2) VALUES ('a'); +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +ALTER TABLE t1 ADD COLUMN c3 INT; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +ALTER TABLE t1 RENAME TO t2; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +DROP TABLE t1; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +TRUNCATE TABLE t1; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ACTIVE state +XA END 'xa_1'; +INSERT INTO t1 (c1, c2) VALUES (1, 'a'); +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +ALTER TABLE t1 ADD COLUMN c3 INT; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +ALTER TABLE t1 RENAME TO t2; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +TRUNCATE TABLE t1; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +DROP TABLE t1; +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state +XA PREPARE 'xa_1'; +INSERT INTO t1 (c1, c2) VALUES (1, 'a'); +XA COMMIT 'xa_1'; +CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; +INSERT INTO t1 (c1, c2) VALUES (2, 'a'); +TRUNCATE TABLE t1; +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t1 RENAME TO t3; +DROP TABLE t2; +DROP TABLE t3; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_require_primary_key.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_require_primary_key.result new file mode 100644 index 0000000000000..32482a800885d --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_require_primary_key.result @@ -0,0 +1,51 @@ +ALTER DATABASE require_primary_key CHARACTER SET utf8mb4; +SET NAMES utf8mb4; +# +# 1) duckdb_require_primary_key is ON, CREATE without PK is not allowed +# +SET GLOBAL duckdb_require_primary_key = ON; +CREATE TABLE t(id INT, a VARCHAR(10), b VARCHAR(10)) ENGINE = DuckDB; +ERROR 42000: This table type requires a primary key +# +# 2) duckdb_require_primary_key is OFF, CREATE without PK is allowed +# +SET GLOBAL duckdb_require_primary_key = OFF; +CREATE TABLE t(id INT, a VARCHAR(10), b VARCHAR(10)) ENGINE = DuckDB; +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) DEFAULT NULL, + `a` varchar(10) DEFAULT NULL, + `b` varchar(10) DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 3) duckdb_require_primary_key is ON, ALTER without PK is not allowed +# +SET GLOBAL duckdb_require_primary_key = ON; +ALTER TABLE t ADD COLUMN c VARCHAR(10); +ERROR 42000: This table type requires a primary key +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) DEFAULT NULL, + `a` varchar(10) DEFAULT NULL, + `b` varchar(10) DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 4) duckdb_require_primary_key is OFF, CREATE without PK is allowed +# +SET GLOBAL duckdb_require_primary_key = OFF; +ALTER TABLE t ADD COLUMN c VARCHAR(10); +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `id` int(11) DEFAULT NULL, + `a` varchar(10) DEFAULT NULL, + `b` varchar(10) DEFAULT NULL, + `c` varchar(10) DEFAULT NULL +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +# +# 5) Cleanup +# +SET GLOBAL duckdb_require_primary_key = default; +DROP TABLE t; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_set_operation.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_set_operation.result new file mode 100644 index 0000000000000..0d85694c55709 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_set_operation.result @@ -0,0 +1,35 @@ +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 1), (2, 1), (3, 2), (4, 2); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t2 VALUES (1, 2), (2, 2), (3, 3), (4, 3); +SELECT col1 FROM t1 UNION SELECT col1 FROM t2; +col1 +1 +2 +3 +SELECT col1 FROM t1 INTERSECT SELECT col1 FROM t2; +col1 +2 +SELECT col1 FROM t1 EXCEPT SELECT col1 FROM t2; +col1 +1 +SELECT col1 FROM t1 UNION ALL SELECT col1 FROM t2; +col1 +1 +1 +2 +2 +2 +2 +3 +3 +SELECT col1 FROM t1 INTERSECT ALL SELECT col1 FROM t2; +col1 +2 +2 +SELECT col1 FROM t1 EXCEPT ALL SELECT col1 FROM t2; +col1 +1 +1 +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_mode.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_mode.result new file mode 100644 index 0000000000000..aa906555a5062 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_mode.result @@ -0,0 +1,54 @@ + +1. ONLY_FULL_GROUP_BY + +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 2); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 2); +SET sql_mode= ''; +SELECT id, sum(id) FROM t1; +id sum(id) +1 6 +SELECT id, sum(id) FROM t2; +id sum(id) +1 6 +SELECT id, sum(id) FROM t1 GROUP BY col1; +id sum(id) +1 1 +2 5 +SELECT id, sum(id) FROM t2 GROUP BY col1; +id sum(id) +1 1 +2 5 +SELECT col1 FROM t1 GROUP BY id; +col1 +1 +2 +2 +SELECT col1 FROM t2 GROUP BY id; +col1 +1 +2 +2 +SET sql_mode='ONLY_FULL_GROUP_BY'; +SELECT id, sum(id) FROM t1; +ERROR 42000: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'test.t1.id'; this is incompatible with sql_mode=only_full_group_by +SELECT id, sum(id) FROM t2; +ERROR 42000: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'test.t2.id'; this is incompatible with sql_mode=only_full_group_by +SELECT id, sum(id) FROM t1 GROUP BY col1; +ERROR 42000: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.t1.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by +SELECT id, sum(id) FROM t2 GROUP BY col1; +ERROR 42000: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.t2.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by +SELECT col1 FROM t1 GROUP BY id; +ERROR HY000: [DuckDB] Binder Error: column "col1" must appear in the GROUP BY clause or must be part of an aggregate function. +Either add it to the GROUP BY list, or use "ANY_VALUE(col1)" if the exact value of "col1" is not important. + +LINE 1: SELECT col1 FROM t1 GROUP BY id + ^. +SELECT col1 FROM t2 GROUP BY id; +col1 +1 +2 +2 +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_syntax.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_syntax.result new file mode 100644 index 0000000000000..a46a1766f747f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_sql_syntax.result @@ -0,0 +1,129 @@ +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT); +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +id col1 col2 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +id col1 col2 +NULL NULL NULL +DROP TABLE t1; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT, col2 INT); +INSERT INTO t1 VALUES (1, 1, 1); +INSERT INTO t2 VALUES (2, 2, 2); +SELECT * FROM t1 JOIN t2; +id col1 col2 id col1 col2 +1 1 1 2 2 2 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 JOIN t2; +id col1 col2 id col1 col2 +1 1 1 2 2 2 +DROP TABLE t1; +DROP TABLE t2; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1), (2, 1); +SELECT DISTINCT col1 FROM t1; +col1 +1 +SELECT ALL col1 FROM t1; +col1 +1 +1 +SELECT HIGH_PRIORITY * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_SMALL_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_BIG_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_BUFFER_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_CALC_FOUND_ROWS * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_NO_CACHE * FROM t1; +id col1 +1 1 +2 1 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT DISTINCT col1 FROM t1; +col1 +1 +SELECT ALL col1 FROM t1; +col1 +1 +1 +SELECT HIGH_PRIORITY * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_SMALL_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_BIG_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_BUFFER_RESULT * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_CALC_FOUND_ROWS * FROM t1; +id col1 +1 1 +2 1 +SELECT SQL_NO_CACHE * FROM t1; +id col1 +1 1 +2 1 +DROP TABLE t1; +CREATE TABLE t1 (id INT PRIMARY KEY); +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +SELECT * FROM t1 LIMIT 2,2; +id +3 +4 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT * FROM t1 LIMIT 2,2; +id +3 +4 +DROP TABLE t1; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +SELECT * FROM t1 FORCE INDEX(PRI); +id col1 +SELECT * FROM t1 IGNORE INDEX(PRI); +id col1 +SELECT * FROM t1 USE INDEX(PRI); +id col1 +ALTER TABLE t1 ENGINE=DuckDB; +SELECT * FROM t1 FORCE INDEX(PRI); +id col1 +SELECT * FROM t1 IGNORE INDEX(PRI); +id col1 +SELECT * FROM t1 USE INDEX(PRI); +id col1 +DROP TABLE t1; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (2, 2); +SELECT * FROM t1 STRAIGHT_JOIN t2; +id col1 id col1 +1 1 2 2 +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 STRAIGHT_JOIN t2; +id col1 id col1 +1 1 2 2 +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/r/duckdb_string_func.result b/storage/duckdb/mysql-test/duckdb/r/duckdb_string_func.result new file mode 100644 index 0000000000000..27f6c9676530b --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/duckdb_string_func.result @@ -0,0 +1,724 @@ +CREATE DATABASE test_duckdb; +USE test_duckdb; +SET NAMES utf8mb4; +CREATE TABLE t_innodb(col1 VARCHAR(20) PRIMARY KEY, col2 BLOB); +CREATE TABLE t_duckdb(col1 VARCHAR(20) PRIMARY KEY, col2 BLOB) ENGINE=DuckDB; +insert into t_innodb values ('MySQL', 0x4D7953514CF09FA686); +insert into t_duckdb values ('MySQL', 0x4D7953514CF09FA686); +insert into t_innodb values ('数据库', 0xE695B0E68DAEE5BA93); +insert into t_duckdb values ('数据库', 0xE695B0E68DAEE5BA93); +-------------------------- +1. ASCII() +-------------------------- +SELECT ASCII('MySQL'), ASCII('数据库'), ASCII(col1) FROM t_innodb; +ASCII('MySQL') ASCII('数据库') ASCII(col1) +77 230 77 +77 230 230 +SELECT ASCII('MySQL'), ASCII('数据库'), ASCII(col1) FROM t_duckdb; +ASCII('MySQL') ASCII('数据库') ASCII(col1) +77 230 77 +77 230 230 +-------------------------- +2. BIN() +-------------------------- +SELECT BIN('MySQL'), BIN('数据库'), BIN(123), BIN(col1) FROM t_innodb; +BIN('MySQL') BIN('数据库') BIN(123) BIN(col1) +0 0 1111011 0 +0 0 1111011 0 +SELECT BIN('MySQL'), BIN('数据库'), BIN(123), BIN(col1) FROM t_duckdb; +BIN('MySQL') BIN('数据库') BIN(123) BIN(col1) +0 0 1111011 0 +0 0 1111011 0 +-------------------------- +3. BIT_LENGTH() +-------------------------- +SELECT BIT_LENGTH('MySQL'), BIT_LENGTH('数据库'), BIT_LENGTH(col1) FROM t_innodb; +BIT_LENGTH('MySQL') BIT_LENGTH('数据库') BIT_LENGTH(col1) +40 72 40 +40 72 72 +SELECT BIT_LENGTH('MySQL'), BIT_LENGTH('数据库'), BIT_LENGTH(col1) FROM t_duckdb; +BIT_LENGTH('MySQL') BIT_LENGTH('数据库') BIT_LENGTH(col1) +40 72 40 +40 72 72 +-------------------------- +4. CHAR() +-------------------------- +-------------------------- +5. CHAR_LENGTH() +-------------------------- +SELECT CHAR_LENGTH('MySQL'), CHAR_LENGTH('数据库'), CHAR_LENGTH(col1) FROM t_innodb; +CHAR_LENGTH('MySQL') CHAR_LENGTH('数据库') CHAR_LENGTH(col1) +5 3 5 +5 3 3 +SELECT CHAR_LENGTH('MySQL'), CHAR_LENGTH('数据库'), CHAR_LENGTH(col1) FROM t_duckdb; +CHAR_LENGTH('MySQL') CHAR_LENGTH('数据库') CHAR_LENGTH(col1) +5 3 5 +5 3 3 +-------------------------- +6. CHARACTER_LENGTH() +-------------------------- +SELECT CHARACTER_LENGTH('MySQL'), CHARACTER_LENGTH('数据库'), CHARACTER_LENGTH(col1) FROM t_innodb; +CHARACTER_LENGTH('MySQL') CHARACTER_LENGTH('数据库') CHARACTER_LENGTH(col1) +5 3 5 +5 3 3 +SELECT CHARACTER_LENGTH('MySQL'), CHARACTER_LENGTH('数据库'), CHARACTER_LENGTH(col1) FROM t_duckdb; +CHARACTER_LENGTH('MySQL') CHARACTER_LENGTH('数据库') CHARACTER_LENGTH(col1) +5 3 5 +5 3 3 +-------------------------- +7. CONCAT() +-------------------------- +SELECT CONCAT('MySQL', '数据库'), CONCAT(col1, 'RDS') FROM t_innodb; +CONCAT('MySQL', '数据库') CONCAT(col1, 'RDS') +MySQL数据库 MySQLRDS +MySQL数据库 数据库RDS +SELECT CONCAT('MySQL', '数据库'), CONCAT(col1, 'RDS') FROM t_duckdb; +CONCAT('MySQL', '数据库') CONCAT(col1, 'RDS') +MySQL数据库 MySQLRDS +MySQL数据库 数据库RDS +-------------------------- +8. CONCAT_WS() +-------------------------- +SELECT CONCAT_WS(',', 'MySQL', '数据库'), CONCAT_WS(',', col1, 'RDS') FROM t_innodb; +CONCAT_WS(',', 'MySQL', '数据库') CONCAT_WS(',', col1, 'RDS') +MySQL,数据库 MySQL,RDS +MySQL,数据库 数据库,RDS +SELECT CONCAT_WS(',', 'MySQL', '数据库'), CONCAT_WS(',', col1, 'RDS') FROM t_duckdb; +CONCAT_WS(',', 'MySQL', '数据库') CONCAT_WS(',', col1, 'RDS') +MySQL,数据库 MySQL,RDS +MySQL,数据库 数据库,RDS +-------------------------- +9. ELT() +-------------------------- +-------------------------- +10. EXPORT_SET() +-------------------------- +-------------------------- +11. FIELD() +-------------------------- +-------------------------- +12. FIND_IN_SET() +-------------------------- +SELECT FIND_IN_SET('MySQL', 'MySQL,RDS'), FIND_IN_SET('数据库', 'MySQL,RDS'), FIND_IN_SET(col1, 'MySQL,RDS') FROM t_innodb; +FIND_IN_SET('MySQL', 'MySQL,RDS') FIND_IN_SET('数据库', 'MySQL,RDS') FIND_IN_SET(col1, 'MySQL,RDS') +1 0 1 +1 0 0 +SELECT FIND_IN_SET('MySQL', 'MySQL,RDS'), FIND_IN_SET('数据库', 'MySQL,RDS'), FIND_IN_SET(col1, 'MySQL,RDS') FROM t_innodb; +FIND_IN_SET('MySQL', 'MySQL,RDS') FIND_IN_SET('数据库', 'MySQL,RDS') FIND_IN_SET(col1, 'MySQL,RDS') +1 0 1 +1 0 0 +-------------------------- +13. FORMAT() +-------------------------- +-------------------------- +14. HEX() +-------------------------- +SELECT HEX('MySQL'), HEX(70), HEX(col1) FROM t_innodb; +HEX('MySQL') HEX(70) HEX(col1) +4D7953514C 46 4D7953514C +4D7953514C 46 E695B0E68DAEE5BA93 +SELECT HEX('MySQL'), HEX(70), HEX(col1) FROM t_duckdb; +HEX('MySQL') HEX(70) HEX(col1) +4D7953514C 46 4D7953514C +4D7953514C 46 E695B0E68DAEE5BA93 +-------------------------- +15. INSERT() +-------------------------- +SELECT INSERT('Quadratic', 3, 4, 'What'), INSERT('Quadratic', -1, 4, 'What'), INSERT('Quadratic', 3, 100, 'What') FROM t_innodb; +INSERT('Quadratic', 3, 4, 'What') INSERT('Quadratic', -1, 4, 'What') INSERT('Quadratic', 3, 100, 'What') +QuWhattic Quadratic QuWhat +QuWhattic Quadratic QuWhat +SELECT INSERT('Quadratic', 3, 4, 'What'), INSERT('Quadratic', -1, 4, 'What'), INSERT('Quadratic', 3, 100, 'What') FROM t_duckdb; +INSERT('Quadratic', 3, 4, 'What') INSERT('Quadratic', -1, 4, 'What') INSERT('Quadratic', 3, 100, 'What') +QuWhattic Quadratic QuWhat +QuWhattic Quadratic QuWhat +-------------------------- +16. INSTR() +-------------------------- +SELECT INSTR('MySQL数据库', '数据库'), INSTR('MySQL数据库', col1) FROM t_innodb; +INSTR('MySQL数据库', '数据库') INSTR('MySQL数据库', col1) +6 1 +6 6 +SELECT INSTR('MySQL数据库', '数据库'), INSTR('MySQL数据库', col1) FROM t_duckdb; +INSTR('MySQL数据库', '数据库') INSTR('MySQL数据库', col1) +6 1 +6 6 +-------------------------- +17. LCASE() +-------------------------- +SELECT LCASE('MySQL'), LCASE('数据库'), LCASE(col1) FROM t_innodb; +LCASE('MySQL') LCASE('数据库') LCASE(col1) +mysql 数据库 mysql +mysql 数据库 数据库 +SELECT LCASE('MySQL'), LCASE('数据库'), LCASE(col1) FROM t_duckdb; +LCASE('MySQL') LCASE('数据库') LCASE(col1) +mysql 数据库 mysql +mysql 数据库 数据库 +-------------------------- +18. LEFT() +-------------------------- +SELECT LEFT('MySQL', 2), LEFT('数据库', 2), LEFT(col1, 2) FROM t_innodb; +LEFT('MySQL', 2) LEFT('数据库', 2) LEFT(col1, 2) +My 数据 My +My 数据 数据 +SELECT LEFT('MySQL', 2), LEFT('数据库', 2), LEFT(col1, 2) FROM t_duckdb; +LEFT('MySQL', 2) LEFT('数据库', 2) LEFT(col1, 2) +My 数据 My +My 数据 数据 +-------------------------- +19. LENGTH() +-------------------------- +SELECT LENGTH('MySQL'), LENGTH('数据库'), LENGTH(col1), LENGTH(col2) FROM t_innodb; +LENGTH('MySQL') LENGTH('数据库') LENGTH(col1) LENGTH(col2) +5 9 5 9 +5 9 9 9 +SELECT LENGTH('MySQL'), LENGTH('数据库'), LENGTH(col1), LENGTH(col2) FROM t_duckdb; +LENGTH('MySQL') LENGTH('数据库') LENGTH(col1) LENGTH(col2) +5 9 5 9 +5 9 9 9 +-------------------------- +20. LIKE +-------------------------- +SELECT 'MySQL' LIKE '%SQL', col1 LIKE '%SQL' FROM t_innodb; +'MySQL' LIKE '%SQL' col1 LIKE '%SQL' +1 1 +1 0 +SELECT 'MySQL' LIKE '%SQL', col1 LIKE '%SQL' FROM t_duckdb; +'MySQL' LIKE '%SQL' col1 LIKE '%SQL' +1 1 +1 0 +-------------------------- +21. LOAD_FILE() +-------------------------- +-------------------------- +22. LOCATE() +-------------------------- +SELECT LOCATE('SQL', 'MySQLSQL'), LOCATE('SQL', 'MySQLSQL', 3), LOCATE('SQL', 'MySQLSQL', 7) FROM t_innodb; +LOCATE('SQL', 'MySQLSQL') LOCATE('SQL', 'MySQLSQL', 3) LOCATE('SQL', 'MySQLSQL', 7) +3 3 0 +3 3 0 +SELECT LOCATE('SQL', 'MySQLSQL'), LOCATE('SQL', 'MySQLSQL', 3), LOCATE('SQL', 'MySQLSQL', 7) FROM t_duckdb; +LOCATE('SQL', 'MySQLSQL') LOCATE('SQL', 'MySQLSQL', 3) LOCATE('SQL', 'MySQLSQL', 7) +3 3 0 +3 3 0 +-------------------------- +23. LOWER() +-------------------------- +SELECT LOWER('MySQL'), LOWER('数据库'), LOWER(col1) FROM t_innodb; +LOWER('MySQL') LOWER('数据库') LOWER(col1) +mysql 数据库 mysql +mysql 数据库 数据库 +SELECT LOWER('MySQL'), LOWER('数据库'), LOWER(col1) FROM t_duckdb; +LOWER('MySQL') LOWER('数据库') LOWER(col1) +mysql 数据库 mysql +mysql 数据库 数据库 +-------------------------- +24. LPAD() +-------------------------- +SELECT LPAD('MySQL', '3', 'My'), LPAD('MySQL', '10', 'My'), LPAD(col1, '10', 'My') FROM t_innodb; +LPAD('MySQL', '3', 'My') LPAD('MySQL', '10', 'My') LPAD(col1, '10', 'My') +MyS MyMyMMySQL MyMyMMySQL +MyS MyMyMMySQL MyMyMyM数据库 +SELECT LPAD('MySQL', '3', 'My'), LPAD('MySQL', '10', 'My'), LPAD(col1, '10', 'My') FROM t_duckdb; +LPAD('MySQL', '3', 'My') LPAD('MySQL', '10', 'My') LPAD(col1, '10', 'My') +MyS MyMyMMySQL MyMyMMySQL +MyS MyMyMMySQL MyMyMyM数据库 +-------------------------- +25. LTRIM() +-------------------------- +SELECT LTRIM(' MySQL'), LTRIM(' 数据库') FROM t_innodb; +LTRIM(' MySQL') LTRIM(' 数据库') +MySQL 数据库 +MySQL 数据库 +SELECT LTRIM(' MySQL'), LTRIM(' 数据库') FROM t_duckdb; +LTRIM(' MySQL') LTRIM(' 数据库') +MySQL 数据库 +MySQL 数据库 +-------------------------- +26. MAKE_SET() +-------------------------- +-------------------------- +27. MATCH() +-------------------------- +-------------------------- +28. MID() +-------------------------- +SELECT MID('MySQL数据库', 3), MID('MySQL数据库', 3, 10), MID('MySQL数据库', 3, 5) FROM t_innodb; +MID('MySQL数据库', 3) MID('MySQL数据库', 3, 10) MID('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +SELECT MID('MySQL数据库', 3), MID('MySQL数据库', 3, 10), MID('MySQL数据库', 3, 5) FROM t_duckdb; +MID('MySQL数据库', 3) MID('MySQL数据库', 3, 10) MID('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +-------------------------- +29. NOT LIKE +-------------------------- +SELECT 'MySQL' NOT LIKE '%SQL', col1 LIKE '%SQL' FROM t_innodb; +'MySQL' NOT LIKE '%SQL' col1 LIKE '%SQL' +0 1 +0 0 +SELECT 'MySQL' NOT LIKE '%SQL', col1 LIKE '%SQL' FROM t_duckdb; +'MySQL' NOT LIKE '%SQL' col1 LIKE '%SQL' +0 1 +0 0 +-------------------------- +30. NOT REGEXP +-------------------------- +SELECT 'Michael!' NOT REGEXP '.*' FROM t_innodb; +'Michael!' NOT REGEXP '.*' +0 +0 +SELECT 'Michael!' NOT REGEXP '.*' FROM t_duckdb; +'Michael!' NOT REGEXP '.*' +0 +0 +SELECT 'new*\n*line' NOT REGEXP 'new\\*.\\*line' FROM t_innodb; +'new*\n*line' NOT REGEXP 'new\\*.\\*line' +1 +1 +SELECT 'new*\n*line' NOT REGEXP 'new\\*.\\*line' FROM t_duckdb; +'new*\n*line' NOT REGEXP 'new\\*.\\*line' +1 +1 +SELECT 'a' NOT REGEXP '^[a-d]' FROM t_innodb; +'a' NOT REGEXP '^[a-d]' +0 +0 +SELECT 'a' NOT REGEXP '^[a-d]' FROM t_duckdb; +'a' NOT REGEXP '^[a-d]' +0 +0 +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'Michael!', '.*'), (2, 'new*\n*line', 'new\\*.\\*line'), +(3, 'a', '^[a-d]'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT col1 NOT REGEXP col2 FROM t_regexp; +col1 NOT REGEXP col2 +0 +1 +0 +NULL +NULL +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT col1 NOT REGEXP col2 FROM t_regexp; +col1 NOT REGEXP col2 +0 +1 +0 +NULL +NULL +DROP TABLE t_regexp; +-------------------------- +31. OCT() +-------------------------- +SELECT OCT('MySQL'), OCT(20250328), OCT(col1) FROM t_innodb; +OCT('MySQL') OCT(20250328) OCT(col1) +0 115177330 0 +0 115177330 0 +SELECT OCT('MySQL'), OCT(20250328), OCT(col1) FROM t_duckdb; +OCT('MySQL') OCT(20250328) OCT(col1) +0 115177330 0 +0 115177330 0 +-------------------------- +32. OCTET_LENGTH() +-------------------------- +SELECT OCTET_LENGTH('MySQL'), OCTET_LENGTH('数据库'), OCTET_LENGTH(col1), OCTET_LENGTH(col2) FROM t_innodb; +OCTET_LENGTH('MySQL') OCTET_LENGTH('数据库') OCTET_LENGTH(col1) OCTET_LENGTH(col2) +5 9 5 9 +5 9 9 9 +SELECT OCTET_LENGTH('MySQL'), OCTET_LENGTH('数据库'), OCTET_LENGTH(col1), OCTET_LENGTH(col2) FROM t_duckdb; +OCTET_LENGTH('MySQL') OCTET_LENGTH('数据库') OCTET_LENGTH(col1) OCTET_LENGTH(col2) +5 9 5 9 +5 9 9 9 +-------------------------- +33. ORD() +-------------------------- +SELECT ORD('MySQL'), ORD('数据库'), ORD(col1) FROM t_innodb; +ORD('MySQL') ORD('数据库') ORD(col1) +77 15111600 77 +77 15111600 15111600 +SELECT ORD('MySQL'), ORD('数据库'), ORD(col1) FROM t_duckdb; +ORD('MySQL') ORD('数据库') ORD(col1) +77 15111600 77 +77 15111600 15111600 +-------------------------- +34. POSITION() +-------------------------- +SELECT POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL') FROM t_innodb; +POSITION('SQL' IN 'MySQLSQL') POSITION('SQL' IN 'MySQLSQL') POSITION('SQL' IN 'MySQLSQL') +3 3 3 +3 3 3 +SELECT POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL') FROM t_duckdb; +POSITION('SQL' IN 'MySQLSQL') POSITION('SQL' IN 'MySQLSQL') POSITION('SQL' IN 'MySQLSQL') +3 3 3 +3 3 3 +-------------------------- +35. QUOTE() +-------------------------- +-------------------------- +36. REGEXP +-------------------------- +SELECT 'Michael!' REGEXP '.*' FROM t_innodb; +'Michael!' REGEXP '.*' +1 +1 +SELECT 'Michael!' REGEXP '.*' FROM t_duckdb; +'Michael!' REGEXP '.*' +1 +1 +SELECT 'new*\n*line' REGEXP 'new\\*.\\*line' FROM t_innodb; +'new*\n*line' REGEXP 'new\\*.\\*line' +0 +0 +SELECT 'new*\n*line' REGEXP 'new\\*.\\*line' FROM t_duckdb; +'new*\n*line' REGEXP 'new\\*.\\*line' +0 +0 +SELECT 'a' REGEXP '^[a-d]' FROM t_innodb; +'a' REGEXP '^[a-d]' +1 +1 +SELECT 'a' REGEXP '^[a-d]' FROM t_duckdb; +'a' REGEXP '^[a-d]' +1 +1 +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'Michael!', '.*'), (2, 'new*\n*line', 'new\\*.\\*line'), +(3, 'a', '^[a-d]'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT col1 REGEXP col2 FROM t_regexp; +col1 REGEXP col2 +1 +0 +1 +NULL +NULL +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT col1 REGEXP col2 FROM t_regexp; +col1 REGEXP col2 +1 +0 +1 +NULL +NULL +DROP TABLE t_regexp; +-------------------------- +37. REGEXP_INSTR() +-------------------------- +SELECT REGEXP_INSTR('dog cat dog', 'dog') FROM t_innodb; +REGEXP_INSTR('dog cat dog', 'dog') +1 +1 +SELECT REGEXP_INSTR('dog cat dog', 'dog') FROM t_duckdb; +REGEXP_INSTR('dog cat dog', 'dog') +1 +1 +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{2}') FROM t_innodb; +REGEXP_INSTR('aa aaa aaaa', 'a{2}') +1 +1 +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{2}') FROM t_duckdb; +REGEXP_INSTR('aa aaa aaaa', 'a{2}') +1 +1 +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{4}') FROM t_innodb; +REGEXP_INSTR('aa aaa aaaa', 'a{4}') +8 +8 +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{4}') FROM t_duckdb; +REGEXP_INSTR('aa aaa aaaa', 'a{4}') +8 +8 +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'dog cat dog', 'dog'), (2, 'aa aaa aaaa', 'a{2}'), +(3, 'aa aaa aaaa', 'a{4}'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT REGEXP_INSTR(col1, col2) FROM t_regexp; +REGEXP_INSTR(col1, col2) +1 +1 +8 +NULL +NULL +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_INSTR(col1, col2) FROM t_regexp; +REGEXP_INSTR(col1, col2) +1 +1 +8 +NULL +NULL +DROP TABLE t_regexp; +-------------------------- +38. REGEXP_LIKE() — skipped, not available in MariaDB 11.4 +-------------------------- +-------------------------- +39. REGEXP_REPLACE() +-------------------------- +SELECT REGEXP_REPLACE('a b c', 'b', 'X') FROM t_innodb; +REGEXP_REPLACE('a b c', 'b', 'X') +a X c +a X c +SELECT REGEXP_REPLACE('a b c', 'b', 'X') FROM t_duckdb; +REGEXP_REPLACE('a b c', 'b', 'X') +a X c +a X c +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100), col3 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'a b c', 'b', 'X'), (2, 'abc def ghi', '[a-z]+', 'X'), +(3, NULL, 'a{4}', 'X'), (4, 'abc def ghi', NULL, 'X'), (5, 'abc def ghi', '[a-z]+', NULL); +SELECT REGEXP_REPLACE(col1, col2, col3) FROM t_regexp; +REGEXP_REPLACE(col1, col2, col3) +a X c +X X X +NULL +NULL +NULL +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_REPLACE(col1, col2, col3) FROM t_regexp; +REGEXP_REPLACE(col1, col2, col3) +a X c +X X X +NULL +NULL +NULL +DROP TABLE t_regexp; +-------------------------- +40. REGEXP_SUBSTR() +-------------------------- +SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+') FROM t_innodb; +REGEXP_SUBSTR('abc def ghi', '[a-z]+') +abc +abc +SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+') FROM t_duckdb; +REGEXP_SUBSTR('abc def ghi', '[a-z]+') +abc +abc +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'a b c', 'b'), (2, 'abc def ghi', '[a-z]+'); +SELECT REGEXP_SUBSTR(col1, col2) FROM t_regexp; +REGEXP_SUBSTR(col1, col2) +b +abc +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_SUBSTR(col1, col2) FROM t_regexp; +REGEXP_SUBSTR(col1, col2) +b +abc +DROP TABLE t_regexp; +-------------------------- +41. REPEAT() +-------------------------- +SELECT REPEAT('MySQL', 2), REPEAT('数据库', 2), REPEAT(col1, 2) FROM t_innodb; +REPEAT('MySQL', 2) REPEAT('数据库', 2) REPEAT(col1, 2) +MySQLMySQL 数据库数据库 MySQLMySQL +MySQLMySQL 数据库数据库 数据库数据库 +SELECT REPEAT('MySQL', 2), REPEAT('数据库', 2), REPEAT(col1, 2) FROM t_duckdb; +REPEAT('MySQL', 2) REPEAT('数据库', 2) REPEAT(col1, 2) +MySQLMySQL 数据库数据库 MySQLMySQL +MySQLMySQL 数据库数据库 数据库数据库 +-------------------------- +42. REPLACE() +-------------------------- +SELECT REPLACE('MySQL', 'SQL', 'Database'), REPLACE('数据库', '库', '管理'), REPLACE(col1, 'SQL', 'Database') FROM t_innodb; +REPLACE('MySQL', 'SQL', 'Database') REPLACE('数据库', '库', '管理') REPLACE(col1, 'SQL', 'Database') +MyDatabase 数据管理 MyDatabase +MyDatabase 数据管理 数据库 +SELECT REPLACE('MySQL', 'SQL', 'Database'), REPLACE('数据库', '库', '管理'), REPLACE(col1, 'SQL', 'Database') FROM t_duckdb; +REPLACE('MySQL', 'SQL', 'Database') REPLACE('数据库', '库', '管理') REPLACE(col1, 'SQL', 'Database') +MyDatabase 数据管理 MyDatabase +MyDatabase 数据管理 数据库 +-------------------------- +43. REVERSE() +-------------------------- +SELECT REVERSE('MySQL'), REVERSE('数据库'), REVERSE(col1) FROM t_innodb; +REVERSE('MySQL') REVERSE('数据库') REVERSE(col1) +LQSyM 库据数 LQSyM +LQSyM 库据数 库据数 +SELECT REVERSE('MySQL'), REVERSE('数据库'), REVERSE(col1) FROM t_duckdb; +REVERSE('MySQL') REVERSE('数据库') REVERSE(col1) +LQSyM 库据数 LQSyM +LQSyM 库据数 库据数 +-------------------------- +44. RIGHT() +-------------------------- +SELECT RIGHT('MySQL', 2), RIGHT('数据库', 2), RIGHT(col1, 2) FROM t_innodb; +RIGHT('MySQL', 2) RIGHT('数据库', 2) RIGHT(col1, 2) +QL 据库 QL +QL 据库 据库 +SELECT RIGHT('MySQL', 2), RIGHT('数据库', 2), RIGHT(col1, 2) FROM t_duckdb; +RIGHT('MySQL', 2) RIGHT('数据库', 2) RIGHT(col1, 2) +QL 据库 QL +QL 据库 据库 +-------------------------- +45. RLIKE +-------------------------- +SELECT 'Michael!' RLIKE '.*' FROM t_innodb; +'Michael!' RLIKE '.*' +1 +1 +SELECT 'Michael!' RLIKE '.*' FROM t_duckdb; +'Michael!' RLIKE '.*' +1 +1 +SELECT 'new*\n*line' RLIKE 'new\\*.\\*line' FROM t_innodb; +'new*\n*line' RLIKE 'new\\*.\\*line' +0 +0 +SELECT 'new*\n*line' RLIKE 'new\\*.\\*line' FROM t_duckdb; +'new*\n*line' RLIKE 'new\\*.\\*line' +0 +0 +SELECT 'a' RLIKE '^[a-d]' FROM t_innodb; +'a' RLIKE '^[a-d]' +1 +1 +SELECT 'a' RLIKE '^[a-d]' FROM t_duckdb; +'a' RLIKE '^[a-d]' +1 +1 +-------------------------- +46. RPAD() +-------------------------- +SELECT RPAD('MySQL', '3', 'My'), RPAD('MySQL', '10', 'My'), RPAD(col1, '10', 'My') FROM t_innodb; +RPAD('MySQL', '3', 'My') RPAD('MySQL', '10', 'My') RPAD(col1, '10', 'My') +MyS MySQLMyMyM MySQLMyMyM +MyS MySQLMyMyM 数据库MyMyMyM +SELECT RPAD('MySQL', '3', 'My'), RPAD('MySQL', '10', 'My'), RPAD(col1, '10', 'My') FROM t_duckdb; +RPAD('MySQL', '3', 'My') RPAD('MySQL', '10', 'My') RPAD(col1, '10', 'My') +MyS MySQLMyMyM MySQLMyMyM +MyS MySQLMyMyM 数据库MyMyMyM +-------------------------- +47. RTRIM() +-------------------------- +SELECT RTRIM('MySQL '), RTRIM('数据库 ') FROM t_innodb; +RTRIM('MySQL ') RTRIM('数据库 ') +MySQL 数据库 +MySQL 数据库 +SELECT RTRIM('MySQL '), RTRIM('数据库 ') FROM t_duckdb; +RTRIM('MySQL ') RTRIM('数据库 ') +MySQL 数据库 +MySQL 数据库 +-------------------------- +48. SOUNDEX() +-------------------------- +-------------------------- +49. SOUNDS LIKE +-------------------------- +-------------------------- +50. SPACE() +-------------------------- +-------------------------- +51. STRCMP() +-------------------------- +SELECT STRCMP('MySQL', 'mysql'), STRCMP('MySQL', 'DuckDB') FROM t_innodb; +STRCMP('MySQL', 'mysql') STRCMP('MySQL', 'DuckDB') +0 1 +0 1 +SELECT STRCMP('MySQL', 'mysql'), STRCMP('MySQL', 'DuckDB') FROM t_duckdb; +STRCMP('MySQL', 'mysql') STRCMP('MySQL', 'DuckDB') +0 1 +0 1 +-------------------------- +52. SUBSTR() +-------------------------- +SELECT SUBSTR('MySQL数据库', 3), SUBSTR('MySQL数据库', 3, 10), SUBSTR('MySQL数据库', 3, 5) FROM t_innodb; +SUBSTR('MySQL数据库', 3) SUBSTR('MySQL数据库', 3, 10) SUBSTR('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +SELECT SUBSTR('MySQL数据库', 3), SUBSTR('MySQL数据库', 3, 10), SUBSTR('MySQL数据库', 3, 5) FROM t_duckdb; +SUBSTR('MySQL数据库', 3) SUBSTR('MySQL数据库', 3, 10) SUBSTR('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +-------------------------- +53. SUBSTRING() +-------------------------- +SELECT SUBSTRING('MySQL数据库', 3), SUBSTRING('MySQL数据库', 3, 10), SUBSTRING('MySQL数据库', 3, 5) FROM t_innodb; +SUBSTRING('MySQL数据库', 3) SUBSTRING('MySQL数据库', 3, 10) SUBSTRING('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +SELECT SUBSTRING('MySQL数据库', 3), SUBSTRING('MySQL数据库', 3, 10), SUBSTRING('MySQL数据库', 3, 5) FROM t_duckdb; +SUBSTRING('MySQL数据库', 3) SUBSTRING('MySQL数据库', 3, 10) SUBSTRING('MySQL数据库', 3, 5) +SQL数据库 SQL数据库 SQL数据 +SQL数据库 SQL数据库 SQL数据 +-------------------------- +54. SUBSTRING_INDEX() +-------------------------- +SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) FROM t_innodb; +SUBSTRING_INDEX('www.mysql.com', '.', 2) +www.mysql +www.mysql +SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) FROM t_duckdb; +SUBSTRING_INDEX('www.mysql.com', '.', 2) +www.mysql +www.mysql +-------------------------- +55. TRIM() +-------------------------- +SELECT TRIM(' bar '), TRIM(LEADING 'x' FROM 'xxxbarxxx'), TRIM(BOTH 'x' FROM 'xxxbarxxx'), TRIM(TRAILING 'xyz' FROM 'barxxyz') FROM t_innodb; +TRIM(' bar ') TRIM(LEADING 'x' FROM 'xxxbarxxx') TRIM(BOTH 'x' FROM 'xxxbarxxx') TRIM(TRAILING 'xyz' FROM 'barxxyz') +bar barxxx bar barx +bar barxxx bar barx +SELECT TRIM(' bar '), TRIM(LEADING 'x' FROM 'xxxbarxxx'), TRIM(BOTH 'x' FROM 'xxxbarxxx'), TRIM(TRAILING 'xyz' FROM 'barxxyz') FROM t_duckdb; +TRIM(' bar ') TRIM(LEADING 'x' FROM 'xxxbarxxx') TRIM(BOTH 'x' FROM 'xxxbarxxx') TRIM(TRAILING 'xyz' FROM 'barxxyz') +bar barxxx bar barx +bar barxxx bar barx +-------------------------- +56. UCASE() +-------------------------- +SELECT UCASE('MySQL'), UCASE('数据库'), UCASE(col1) FROM t_innodb; +UCASE('MySQL') UCASE('数据库') UCASE(col1) +MYSQL 数据库 MYSQL +MYSQL 数据库 数据库 +SELECT UCASE('MySQL'), UCASE('数据库'), UCASE(col1) FROM t_duckdb; +UCASE('MySQL') UCASE('数据库') UCASE(col1) +MYSQL 数据库 MYSQL +MYSQL 数据库 数据库 +-------------------------- +57. UNHEX() +-------------------------- +SELECT UNHEX('4D7953514C'), UNHEX('E695B0E68DAEE5BA93'), UNHEX(HEX(col1)) FROM t_innodb; +UNHEX('4D7953514C') UNHEX('E695B0E68DAEE5BA93') UNHEX(HEX(col1)) +MySQL 数据库 MySQL +MySQL 数据库 数据库 +SELECT UNHEX('4D7953514C'), UNHEX('E695B0E68DAEE5BA93'), UNHEX(HEX(col1)) FROM t_duckdb; +UNHEX('4D7953514C') UNHEX('E695B0E68DAEE5BA93') UNHEX(HEX(col1)) +MySQL 数据库 MySQL +MySQL 数据库 数据库 +-------------------------- +58. UPPER() +-------------------------- +SELECT UPPER('MySQL'), UPPER('数据库'), UPPER(col1) FROM t_innodb; +UPPER('MySQL') UPPER('数据库') UPPER(col1) +MYSQL 数据库 MYSQL +MYSQL 数据库 数据库 +SELECT UPPER('MySQL'), UPPER('数据库'), UPPER(col1) FROM t_duckdb; +UPPER('MySQL') UPPER('数据库') UPPER(col1) +MYSQL 数据库 MYSQL +MYSQL 数据库 数据库 +-------------------------- +59. WEIGHT_STRING() +-------------------------- +-------------------------- +59. TO_BASE64() +-------------------------- +SELECT TO_BASE64('MySQL'), TO_BASE64('数据库'), TO_BASE64(col1) FROM t_innodb; +TO_BASE64('MySQL') TO_BASE64('数据库') TO_BASE64(col1) +TXlTUUw= 5pWw5o2u5bqT TXlTUUw= +TXlTUUw= 5pWw5o2u5bqT 5pWw5o2u5bqT +SELECT TO_BASE64('MySQL'), TO_BASE64('数据库'), TO_BASE64(col1) FROM t_duckdb; +TO_BASE64('MySQL') TO_BASE64('数据库') TO_BASE64(col1) +TXlTUUw= 5pWw5o2u5bqT TXlTUUw= +TXlTUUw= 5pWw5o2u5bqT 5pWw5o2u5bqT +-------------------------- +59. FROM_BASE64() +-------------------------- +SELECT FROM_BASE64(TO_BASE64('MySQL')), FROM_BASE64(TO_BASE64('数据库')), FROM_BASE64(TO_BASE64(col1)) FROM t_innodb; +FROM_BASE64(TO_BASE64('MySQL')) FROM_BASE64(TO_BASE64('数据库')) FROM_BASE64(TO_BASE64(col1)) +MySQL 数据库 MySQL +MySQL 数据库 数据库 +SELECT FROM_BASE64(TO_BASE64('MySQL')), FROM_BASE64(TO_BASE64('数据库')), FROM_BASE64(TO_BASE64(col1)) FROM t_duckdb; +FROM_BASE64(TO_BASE64('MySQL')) FROM_BASE64(TO_BASE64('数据库')) FROM_BASE64(TO_BASE64(col1)) +MySQL 数据库 MySQL +MySQL 数据库 数据库 +DROP TABLE t_innodb; +DROP TABLE t_duckdb; +DROP DATABASE test_duckdb; +SET GLOBAL duckdb_dml_in_batch = @saved_duckdb_dml_in_batch; diff --git a/storage/duckdb/mysql-test/duckdb/r/feature_duckdb_data_type.result b/storage/duckdb/mysql-test/duckdb/r/feature_duckdb_data_type.result new file mode 100644 index 0000000000000..3878b7d28e563 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/feature_duckdb_data_type.result @@ -0,0 +1,160 @@ +create database mytest; +use mytest; + +Numeric data type test +signed Numeric data type +check data consistency +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int, +col2 bigint, +col3 float, +col4 double, +col5 decimal(10,4), +col6 tinyint, +col7 smallint, +col8 mediumint +) ENGINE = DuckDB; +insert into t1 values (1, 1, -1, 1.01, -1.001, 1.0001, 1, 1, 1), +(2, -2, 2, -2.02, 2.002, -2.0002, 2, 2, 2), +(-3, 3, 3, 3.03, 3.003, 3.0003, 3, 3, 3), +(-2147483648, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -999999.9999, -128, -32768, -8388608), +(2147483647, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, 127, 32767, 8388607); +select * from mytest.t1; +id col1 col2 col3 col4 col5 col6 col7 col8 +1 1 -1 1.01 -1.001 1.0001 1 1 1 +2 -2 2 -2.02 2.002 -2.0002 2 2 2 +-3 3 3 3.03 3.003 3.0003 3 3 3 +-2147483648 -2147483648 -9223372036854775808 -3.40282e38 -1.7976931348623157e308 -999999.9999 -128 -32768 -8388608 +2147483647 2147483647 9223372036854775807 3.40282e38 1.7976931348623157e308 999999.9999 127 32767 8388607 +drop table t1; + +unsigned Numeric data type +check data consistency +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 int unsigned, +col2 bigint unsigned, +col3 float unsigned, +col4 double unsigned, +col5 decimal(10,4) unsigned, +col6 bit(10), +col7 tinyint unsigned, +col8 smallint unsigned, +col9 mediumint unsigned +) ENGINE = DuckDB; +insert into t1 values (1, 1, 1, 1.01, 1.001, 1.0001, B'111111111', 1, 1, 1), +(2, 2, 2, 2.02, 2.002, 2.0002, B'0000000000', 1, 1, 1), +(3, 3, 3, 3.03, 3.003, 3.0003, B'0001100110', 1, 1, 1), +(-2147483648, 0, 0, 0, 0, 0, B'00000000000', 0, 0, 0), +(2147483647, 255, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, B'1111111111', 255, 65535, 8388607); +select id, col1, col2, col3, col4, col5, hex(col6), col7, col8, col9 from mytest.t1; +id col1 col2 col3 col4 col5 hex(col6) col7 col8 col9 +1 1 1 1.01 1.001 1.0001 01FF 1 1 1 +2 2 2 2.02 2.002 2.0002 0000 1 1 1 +3 3 3 3.03 3.003 3.0003 0066 1 1 1 +-2147483648 0 0 0 0 0.0000 0000 0 0 0 +2147483647 255 18446744073709551615 3.40282e38 1.7976931348623157e308 999999.9999 03FF 255 65535 8388607 +drop table t1; + +decimal precision test, decimal precision more than 38 is not supported. +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 decimal(65, 30), +col2 decimal(65, 15), +col3 decimal(65, 0), +col4 decimal(38,30), +col5 decimal(38,18), +col6 decimal(38,0), +col7 decimal(9,9), +col8 decimal(9,4), +col9 decimal(9,0) +) ENGINE = DuckDB; +insert into t1 values (1, +99999999999999999999999999999999999.999999999999999999999999999999, +99999999999999999999999999999999999999999999999999.999999999999999, +99999999999999999999999999999999999999999999999999999999999999999, +99999999.999999999999999999999999999999, +99999999999999999999.999999999999999999, +99999999999999999999999999999999999999, +0.99999999, +99999.9999, +999999999); +select * from mytest.t1; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 +1 99999999999999999999999999999999999.999999999999999999999999999999 99999999999999999999999999999999999999999999999999.999999999999999 99999999999999999999999999999999999999999999999999999999999999999 99999999.999999999999999999999999999999 99999999999999999999.999999999999999999 99999999999999999999999999999999999999 0.999999990 99999.9999 999999999 +drop table t1; + +Date data type test +check data consistency +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 date, +col2 datetime, +col3 timestamp, +col4 datetime(6), +col5 time, +col6 time(6), +col7 year +) ENGINE = DuckDB; +insert into t1 values (1, '2020-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1', '12:00:00', '12:00:00.1', 2020), +(2, '2020-12-31', '2020-12-31 00:00:00', '2020-12-31 00:00:00', '2020-12-31 00:00:00.123456789', '00:00:00', '00:00:00.123456789', 1970), +(3, '1970-01-01', '1970-01-01 23:59:59', '1970-01-01 23:59:59', '2020-12-31 23:59:59.123456', '23:59:59', '23:59:59.123456', 2050), +(4, '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '23:59:59.123456', '23:59:59.123456', 2100); +Warnings: +Note 1265 Data truncated for column 'col4' at row 2 +Note 1265 Data truncated for column 'col6' at row 2 +Note 1265 Data truncated for column 'col1' at row 4 +select * from mytest.t1; +id col1 col2 col3 col4 col5 col6 col7 +1 2020-01-01 2020-01-01 12:00:00 2020-01-01 12:00:00 2020-01-01 12:00:00.100000 12:00:00 12:00:00.100000 2020 +2 2020-12-31 2020-12-31 00:00:00 2020-12-31 00:00:00 2020-12-31 00:00:00.123456 00:00:00 00:00:00.123456 1970 +3 1970-01-01 1970-01-01 23:59:59 1970-01-01 23:59:59 2020-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2050 +4 2000-12-31 2000-12-31 23:59:59 2000-12-31 23:59:59 2000-12-31 23:59:59.123456 23:59:59 23:59:59.123456 2100 +drop table t1; + +String data type test +check data consistency +CREATE TABLE t1 ( +id int PRIMARY KEY, +col1 char(100), +col2 varchar(100), +col3 binary(4), +col4 varbinary(100), +col5 tinyblob, +col6 blob, +col7 mediumblob, +col8 longblob, +col9 tinytext, +col10 text, +col11 mediumtext, +col12 longtext, +col13 json, +col14 enum('v1', 'v2', 'v3'), +col15 set('v1', 'v2', 'v3') +) ENGINE = DuckDB; +insert into t1 values (1, 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', '{"id": 1, "value":"aaaa"}', 1, 1); +insert into t1 values (2, 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', '{"id": 1, "value":"aaaa"}', 'v2', 'v2'); +insert into t1 values (3, 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', '{"id": 1, "value":"aaaa"}', 3, 'v1,v2,v3'); +insert into t1 values (4, repeat('d', 100), repeat('d', 100), repeat('d', 4), repeat('d', 100), +repeat('d', 255), repeat('d', 65535), repeat('d', 16777215), repeat('d', 60000000), +repeat('d', 255), repeat('d', 65535), repeat('d', 16777215), repeat('d', 60000000), +'{}', 3, 'v3'); +select * from mytest.t1 where id <= 3; +id col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 col11 col12 col13 col14 col15 +1 aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa {"id": 1, "value":"aaaa"} v1 v1 +2 bbbb bbbb bbbb bbbb bbbb bbbb bbbb bbbb bbbb bbbb bbbb bbbb {"id": 1, "value":"aaaa"} v2 v2 +3 cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc {"id": 1, "value":"aaaa"} v3 v1,v2,v3 +select length(col1), length(col2), octet_length(col3), +octet_length(col4), octet_length(col5), octet_length(col6), +octet_length(col7), octet_length(col8), length(col9), +length(col10),length(col11),length(col12), octet_length(col13), +length(col14), length(col15) +from mytest.t1; +length(col1) length(col2) octet_length(col3) octet_length(col4) octet_length(col5) octet_length(col6) octet_length(col7) octet_length(col8) length(col9) length(col10) length(col11) length(col12) octet_length(col13) length(col14) length(col15) +4 4 4 4 4 4 4 4 4 4 4 4 25 2 2 +4 4 4 4 4 4 4 4 4 4 4 4 25 2 2 +4 4 4 4 4 4 4 4 4 4 4 4 25 2 8 +100 100 4 100 255 65535 16777215 60000000 255 65535 16777215 60000000 2 2 2 +drop table t1; +drop database mytest; diff --git a/storage/duckdb/mysql-test/duckdb/r/ha_duckdb.result b/storage/duckdb/mysql-test/duckdb/r/ha_duckdb.result new file mode 100644 index 0000000000000..d03f3bf327c4f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/ha_duckdb.result @@ -0,0 +1,20 @@ +# +# Basic DuckDB handler test: DDL and DML operations +# +# Test 1: CREATE TABLE with various index types +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( +id BIGINT NOT NULL, +v VARCHAR(4) NOT NULL, +INDEX ind_v(v), +UNIQUE INDEX uk(v), +PRIMARY KEY (id) +) ENGINE = DuckDB; +# Test 2: INSERT rows +INSERT INTO t1 VALUES (1, '1'); +INSERT INTO t1 VALUES (1, '1'); +# Test 3: Verify inserted data +SELECT * FROM t1; +id v +1 1 +1 1 diff --git a/storage/duckdb/mysql-test/duckdb/r/pushdown_rewrite.result b/storage/duckdb/mysql-test/duckdb/r/pushdown_rewrite.result new file mode 100644 index 0000000000000..c9213bb77c889 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/pushdown_rewrite.result @@ -0,0 +1,88 @@ +DROP DATABASE IF EXISTS pushdown_rewrite; +CREATE DATABASE pushdown_rewrite CHARACTER SET utf8mb4; +USE pushdown_rewrite; +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'); +CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t2 VALUES (1, 1, 'x'), (2, 2, 'y'), (3, 3, 'z'); +# +# Bug fix: STRAIGHT_JOIN as join keyword (with ON clause) +# Was: STRAIGHT_JOIN → CROSS JOIN → DuckDB rejects "CROSS JOIN ... ON" +# Fix: STRAIGHT_JOIN → JOIN (preserves ON clause) +# +SELECT * FROM t1 STRAIGHT_JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; +id val id t1_id val +1 a 1 1 x +2 b 2 2 y +3 c 3 3 z +# +# Bug fix: STRAIGHT_JOIN as SELECT hint +# Was: SELECT STRAIGHT_JOIN → SELECT CROSS JOIN → syntax error +# Fix: hint is removed, query passes through +# +SELECT STRAIGHT_JOIN t1.id, t2.val FROM t1 JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; +id val +1 x +2 y +3 z +# +# Bug fix: NATURAL JOIN must not be converted to CROSS JOIN +# Was: scanner found no ON/USING after NATURAL JOIN → replaced with CROSS JOIN +# Fix: NATURAL (and LEFT/RIGHT/INNER/FULL/OUTER) recognized before JOIN +# +CREATE TABLE nat1 (shared_col INT PRIMARY KEY, val1 VARCHAR(10)) ENGINE=DuckDB; +CREATE TABLE nat2 (shared_col INT PRIMARY KEY, val2 VARCHAR(10)) ENGINE=DuckDB; +INSERT INTO nat1 VALUES (1, 'a'), (2, 'b'); +INSERT INTO nat2 VALUES (1, 'x'), (3, 'y'); +SELECT * FROM nat1 NATURAL JOIN nat2 ORDER BY shared_col; +shared_col val1 val2 +1 a x +# +# Bug fix: CONVERT word boundary +# Was: find("CONVERT(") matched MYCONVERT( inside identifiers +# Fix: check preceding character is not alphanumeric/_ +# +SELECT CONVERT(123, CHAR) AS c FROM t1 LIMIT 1; +c +123 +# +# Bug fix: GROUP BY ... WITH ROLLUP — handle multiple occurrences +# Was: only first WITH ROLLUP was rewritten (no loop) +# Fix: loop rewrites all WITH ROLLUP in query (including subqueries) +# +SELECT val, SUM(id) s FROM t1 GROUP BY val WITH ROLLUP; +val s +NULL 6 +a 1 +b 2 +c 3 +# +# Bug fix: LIMIT offset,count — handle all occurrences +# Was: rfind processed only the last LIMIT, missing subqueries +# Fix: forward loop rewrites every LIMIT offset,count +# +SELECT * FROM (SELECT * FROM t1 ORDER BY id LIMIT 1, 2) sub; +id val +2 b +3 c +# +# Regression: plain LIMIT (no comma) must not be altered +# +SELECT * FROM t1 ORDER BY id LIMIT 2; +id val +1 a +2 b +# +# Regression: LEFT/RIGHT/INNER JOIN with ON must not be touched +# +SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; +id val id t1_id val +1 a 1 1 x +2 b 2 2 y +3 c 3 3 z +SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; +id val id t1_id val +1 a 1 1 x +2 b 2 2 y +3 c 3 3 z +DROP DATABASE pushdown_rewrite; diff --git a/storage/duckdb/mysql-test/duckdb/r/rename_duckdb_table.result b/storage/duckdb/mysql-test/duckdb/r/rename_duckdb_table.result new file mode 100644 index 0000000000000..dd5e7694d1124 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/rename_duckdb_table.result @@ -0,0 +1,170 @@ +# +# 1) Prepare +# +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE TABLE db1.t1(a INT PRIMARY KEY) ENGINE = DUCKDB; +CREATE TABLE db2.t2(a INT PRIMARY KEY) ENGINE = DUCKDB; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1 +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1 +db2 t2 + + +# +# 2) Simple Rename +# +Rename DuckDB tables +RENAME TABLE db1.t1 TO db1.t1_rename, db2.t2 TO db2.t2_rename; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1_rename +db2 t2_rename +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1_rename +db2 t2_rename + + +ALTER TABLE db1.t1_rename RENAME TO db1.t1; +ALTER TABLE db2.t2_rename RENAME TO db2.t2; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1 +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1 +db2 t2 + + +Failed to rename single DuckDB table for different schemas +RENAME TABLE db1.t1 TO db2.t1; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: cross-schema rename is not supported. Try renaming within the same schema, not to 'db2' +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1 +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1 +db2 t2 + + +Failed to rename DuckDB tables for different schemas +ALTER TABLE db1.t1 RENAME TO db2.t1, ALGORITHM = INPLACE; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: cross-schema rename is not supported. Try renaming within the same schema, not to 'db2' +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1 +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1 +db2 t2 + + +RENAME TABLE db1.t1 TO db1.t1_rename, db2.t2 TO db1.t2_rename; +ERROR HY000: [DuckDB] DuckDB table structure is invalid. Reason: cross-schema rename is not supported. Try renaming within the same schema, not to 'db1' +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1_rename +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 2] +db1 t1 +db2 t2 + + +# +# 3) Inplace rename +# +CREATE TABLE db1.t3(a INT PRIMARY KEY) ENGINE = DUCKDB; +ALTER TABLE db1.t3 RENAME TO db1.t3_rename, CHECKSUM = 1; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1_rename +db1 t3_rename +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 3] +db1 t1 +db1 t3_rename +db2 t2 + + +# +# 4) COPY rename +# +ALTER TABLE db1.t3_rename RENAME TO db1.t3, ALGORITHM = COPY; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1_rename +db1 t3 +db2 t2 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 3] +db1 t1 +db1 t3 +db2 t2 + + +ALTER TABLE db1.t3 RENAME TO db2.t3, ALGORITHM = COPY; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +db1 t1_rename +db2 t2 +db2 t3 +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 3] +db1 t1 +db2 t2 +db2 t3 + + +# +# 5) Cleanup +# +DROP DATABASE db1; +DROP DATABASE db2; +SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; +TABLE_SCHEMA TABLE_NAME +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'") +table_schema table_name +VARCHAR VARCHAR +[ Rows: 1] +db1 t1 + + diff --git a/storage/duckdb/mysql-test/duckdb/r/supported_copy_ddl.result b/storage/duckdb/mysql-test/duckdb/r/supported_copy_ddl.result new file mode 100644 index 0000000000000..d540fa1e500ec --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/supported_copy_ddl.result @@ -0,0 +1,60 @@ +# +# RENAME TABLE WITH DIFFERENT DATABASES +# +CREATE DATABASE db1; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +ALTER TABLE t RENAME TO db1.t, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. +ALTER TABLE t RENAME TO db1.t; +SHOW CREATE TABLE db1.t; +Table Create Table +t CREATE TABLE `t` ( + `a` int NOT NULL, + `b` int DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=DUCKDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci +CALL dbms_duckdb.query("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type FROM information_schema.columns WHERE table_name = 't'"); +RESULT +table_schema table_name column_name column_default is_nullable data_type +VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR VARCHAR +[ Rows: 2] +db1 t a NULL NO INTEGER +db1 t b NULL YES INTEGER + + +CALL dbms_duckdb.query("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +RESULT +table_name constraint_type constraint_text +VARCHAR VARCHAR VARCHAR +[ Rows: 1] +t NOT NULL NOT NULL + + +DROP DATABASE db1; +# +# ALTER COLUMN ORDER +# +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +ALTER TABLE t ADD COLUMN c INT FIRST, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. +ALTER TABLE t ADD COLUMN c INT FIRST; +ALTER TABLE t ADD COLUMN d INT AFTER a, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. +ALTER TABLE t ADD COLUMN d INT AFTER a; +ALTER TABLE t MODIFY COLUMN c INT AFTER a, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. +ALTER TABLE t MODIFY COLUMN c INT AFTER a; +ALTER TABLE t MODIFY COLUMN d INT FIRST, ALGORITHM = INPLACE; +ERROR 0A000: ALGORITHM=INPLACE is not supported for this operation. Try ALGORITHM=COPY. +ALTER TABLE t MODIFY COLUMN d INT FIRST; +DROP TABLE t; +# +# BUGFIX: Interrupt fetch query when copy ddl from DuckDB to DuckDB. +# +CREATE TABLE t1(id INT AUTO_INCREMENT KEY, a INT) ENGINE = InnoDB; +ALTER TABLE t1 ENGINE = DuckDB; +SET GLOBAL duckdb_copy_ddl_in_batch = OFF; +ALTER TABLE t1 ADD COLUMN b INT, ALGORITHM = COPY; +include/assert.inc [Insert data in batch] +SET GLOBAL duckdb_copy_ddl_in_batch = default; +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/r/system_timezone.result b/storage/duckdb/mysql-test/duckdb/r/system_timezone.result new file mode 100644 index 0000000000000..df6b369459547 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/system_timezone.result @@ -0,0 +1,25 @@ +SHOW VARIABLES LIKE '%time%zone%'; +Variable_name Value +system_time_zone GMT +time_zone SYSTEM +SET time_zone='+00:00'; +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a timestamp, b datetime) ENGINE=duckdb; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a timestamp, b datetime) ENGINE=innodb; +INSERT INTO t_duckdb VALUES (1, '2020-01-01 00:00:00', '1970-01-01 00:00:00'); +INSERT INTO t_innodb VALUES (1, '2020-01-01 00:00:00', '1970-01-01 00:00:00'); +SET time_zone=system; +SELECT * FROM t_innodb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +id a b +1 2020-01-01 11:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +id a b +1 2020-01-01 11:00:00 1970-01-01 00:00:00 +SET time_zone=system; +SELECT * FROM t_innodb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +id a b +1 2020-01-01 00:00:00 1970-01-01 00:00:00 +SELECT * FROM t_duckdb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +id a b +1 2020-01-01 00:00:00 1970-01-01 00:00:00 +DROP TABLE t_duckdb; +DROP TABLE t_innodb; diff --git a/storage/duckdb/mysql-test/duckdb/r/transaction.result b/storage/duckdb/mysql-test/duckdb/r/transaction.result new file mode 100644 index 0000000000000..1fb4d6d6d30b9 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/transaction.result @@ -0,0 +1,225 @@ +# +# DuckDB transaction tests +# +# Suite 1: Basic transactions (binlog ON) +# Test 1: Atomicity +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +INSERT INTO t1 (id, name) VALUES (1, '1'); +BEGIN; +INSERT INTO t1 (id, name) VALUES (2, '2'); +INSERT INTO t1 (id, name) VALUES (3, '3'); +ROLLBACK; +SELECT * FROM t1; +id name +1 1 +# Test 2: Durability after restart +# restart +SELECT * FROM t1; +id name +1 1 +# Test 3: Restart rolls back uncommitted transaction +BEGIN; +INSERT INTO t1 (id, name) VALUES (4, '4'); +INSERT INTO t1 (id, name) VALUES (5, '5'); +# restart +SELECT * FROM t1; +id name +1 1 +# Test 4: Autocommit persists through restart +SET autocommit = 1; +INSERT INTO t1 (id, name) VALUES (3, '3'); +# restart +SELECT * FROM t1; +id name +1 1 +3 3 +# Test 5: Isolation +connect con1,localhost,root,,db_transaction; +connect con2,localhost,root,,db_transaction; +connection con1; +BEGIN; +INSERT INTO t1 (id, name) VALUES (1, '1'); +connection con2; +# Should be empty +SELECT * FROM t1; +id name +connection con1; +COMMIT; +connection con2; +# Should show 1 row +SELECT * FROM t1; +id name +1 1 +# Test 6: Mixed InnoDB+DuckDB atomicity +DROP TABLE IF EXISTS t_duck; +DROP TABLE IF EXISTS t_inno; +CREATE TABLE t_duck ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +CREATE TABLE t_inno ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = InnoDB; +INSERT INTO t_duck (id, name) VALUES (1, '1'); +INSERT INTO t_inno (id, name) VALUES (1, '1'); +BEGIN; +INSERT INTO t_duck (id, name) VALUES (2, '2'); +INSERT INTO t_inno (id, name) VALUES (2, '2'); +ROLLBACK; +SELECT * FROM t_duck; +id name +1 1 +SELECT * FROM t_inno; +id name +1 1 +# Test 7: Mixed InnoDB+DuckDB isolation +connection con1; +BEGIN; +INSERT INTO t_duck (id, name) VALUES (3, '3'); +INSERT INTO t_inno (id, name) VALUES (3, '3'); +connection con2; +SELECT * FROM t_duck; +id name +SELECT * FROM t_inno; +id name +connection con1; +COMMIT; +connection con2; +SELECT * FROM t_duck; +id name +3 3 +SELECT * FROM t_inno; +id name +3 3 +# Test 8: Mixed InnoDB+DuckDB durability +connection default; +INSERT INTO t_duck (id, name) VALUES (4, '4'); +INSERT INTO t_inno (id, name) VALUES (4, '4'); +disconnect con1; +disconnect con2; +# restart +SELECT * FROM t_duck; +id name +3 3 +4 4 +SELECT * FROM t_inno; +id name +3 3 +4 4 +# Suite 2: Basic transactions (binlog OFF) +# restart: --skip-log-bin +# Test 1: Atomicity +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +INSERT INTO t1 (id, name) VALUES (1, '1'); +BEGIN; +INSERT INTO t1 (id, name) VALUES (2, '2'); +INSERT INTO t1 (id, name) VALUES (3, '3'); +ROLLBACK; +SELECT * FROM t1; +id name +1 1 +# Test 2: Durability after restart +# restart: --skip-log-bin +SELECT * FROM t1; +id name +1 1 +# Test 3: Restart rolls back uncommitted transaction +BEGIN; +INSERT INTO t1 (id, name) VALUES (4, '4'); +INSERT INTO t1 (id, name) VALUES (5, '5'); +# restart: --skip-log-bin +SELECT * FROM t1; +id name +1 1 +# Test 4: Autocommit persists through restart +SET autocommit = 1; +INSERT INTO t1 (id, name) VALUES (3, '3'); +# restart: --skip-log-bin +SELECT * FROM t1; +id name +1 1 +3 3 +# Test 5: Isolation +connect con1,localhost,root,,db_transaction; +connect con2,localhost,root,,db_transaction; +connection con1; +BEGIN; +INSERT INTO t1 (id, name) VALUES (1, '1'); +connection con2; +# Should be empty +SELECT * FROM t1; +id name +connection con1; +COMMIT; +connection con2; +# Should show 1 row +SELECT * FROM t1; +id name +1 1 +# Test 6: Mixed InnoDB+DuckDB atomicity +DROP TABLE IF EXISTS t_duck; +DROP TABLE IF EXISTS t_inno; +CREATE TABLE t_duck ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = DuckDB; +CREATE TABLE t_inno ( +id INT DEFAULT 1 PRIMARY KEY, +name VARCHAR(10) DEFAULT 'NAME' +) ENGINE = InnoDB; +INSERT INTO t_duck (id, name) VALUES (1, '1'); +INSERT INTO t_inno (id, name) VALUES (1, '1'); +BEGIN; +INSERT INTO t_duck (id, name) VALUES (2, '2'); +INSERT INTO t_inno (id, name) VALUES (2, '2'); +ROLLBACK; +SELECT * FROM t_duck; +id name +1 1 +SELECT * FROM t_inno; +id name +1 1 +# Test 7: Mixed InnoDB+DuckDB isolation +connection con1; +BEGIN; +INSERT INTO t_duck (id, name) VALUES (3, '3'); +INSERT INTO t_inno (id, name) VALUES (3, '3'); +connection con2; +SELECT * FROM t_duck; +id name +SELECT * FROM t_inno; +id name +connection con1; +COMMIT; +connection con2; +SELECT * FROM t_duck; +id name +3 3 +SELECT * FROM t_inno; +id name +3 3 +# Test 8: Mixed InnoDB+DuckDB durability +connection default; +INSERT INTO t_duck (id, name) VALUES (4, '4'); +INSERT INTO t_inno (id, name) VALUES (4, '4'); +disconnect con1; +disconnect con2; +# restart: --skip-log-bin +SELECT * FROM t_duck; +id name +3 3 +4 4 +SELECT * FROM t_inno; +id name +3 3 +4 4 +# restart diff --git a/storage/duckdb/mysql-test/duckdb/r/truncate_and_maintenance_duckdb_table.result b/storage/duckdb/mysql-test/duckdb/r/truncate_and_maintenance_duckdb_table.result new file mode 100644 index 0000000000000..41e521075e3ba --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/r/truncate_and_maintenance_duckdb_table.result @@ -0,0 +1,41 @@ +# +# 1) PREPARE +# +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES (1, 1, 1, 1); +INSERT INTO t VALUES (2, 2, 2, 2); +INSERT INTO t VALUES (3, 3, 3, 3); +# +# 2) MAINTENANCE STATEMENTS +# +ANALYZE TABLE t; +Table Op Msg_type Msg_text +test.t analyze status Engine-independent statistics collected +test.t analyze note The storage engine for the table doesn't support analyze +CHECK TABLE t; +Table Op Msg_type Msg_text +test.t check note The storage engine for the table doesn't support check +CHECKSUM TABLE t; +Table Checksum +test.t 932102713 +OPTIMIZE TABLE t; +Table Op Msg_type Msg_text +test.t optimize note The storage engine for the table doesn't support optimize +REPAIR TABLE t; +Table Op Msg_type Msg_text +test.t repair status OK +# +# 3) TRUNCATE TABLE +# +SELECT * FROM t; +id a b c +1 1 1 1 +2 2 2 2 +3 3 3 3 +TRUNCATE TABLE t; +SELECT * FROM t; +id a b c +# +# 4) CLEANUP +# +DROP TABLE t; diff --git a/storage/duckdb/mysql-test/duckdb/suite.opt b/storage/duckdb/mysql-test/duckdb/suite.opt new file mode 100644 index 0000000000000..5a3ab036fd164 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/suite.opt @@ -0,0 +1,4 @@ +--plugin-load-add=ha_duckdb.so +--duckdb_dml_in_batch=OFF +--character-set-server=utf8mb4 +--character-set-collations=utf8mb4=utf8mb4_uca1400_ai_ci diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_default_debug.test b/storage/duckdb/mysql-test/duckdb/t/alter_default_debug.test new file mode 100644 index 0000000000000..a1a5cec984c74 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_default_debug.test @@ -0,0 +1,27 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES(1, 1, 1, 1); +SELECT * FROM t; + +# Breakpoint: ha_duckdb::commit_inplace_alter_table +# Check: handler_flags, sql_flags, query string +ALTER TABLE t ALTER COLUMN b SET DEFAULT 100, + ALTER COLUMN c SET DEFAULT 100, ALGORITHM = INSTANT; + +SELECT run_in_duckdb("SELECT column_name, column_default FROM information_schema.columns WHERE table_name = 't'"); + +INSERT INTO t(id) VALUES(2); +SELECT * FROM t; + +ALTER TABLE t ALTER COLUMN b DROP DEFAULT, ALGORITHM = INSTANT; + +SELECT run_in_duckdb("SELECT column_name, column_default FROM information_schema.columns WHERE table_name = 't'"); + +--error ER_NO_DEFAULT_FOR_FIELD +INSERT INTO t(id) VALUES(3); + +INSERT INTO t(id, b) VALUES(3, 3); +SELECT * FROM t; + +DROP TABLE t; diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column-master.opt b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column-master.opt new file mode 100644 index 0000000000000..0d064eabcd40c --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column-master.opt @@ -0,0 +1 @@ +--default-time-zone=+00:00 diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column.test b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column.test new file mode 100644 index 0000000000000..0ae239d698c48 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column.test @@ -0,0 +1,15 @@ +--echo #################### +--echo # TEST FOR INSTANT # +--echo #################### +--let $algorithm = INSTANT +--let $copy_ddl = 0 +--source ../include/alter_duckdb_column.inc + + +--echo #################### +--echo # TEST FOR INPLACE # +--echo #################### +--let $algorithm = INPLACE +--let $copy_ddl = 0 +--source ../include/alter_duckdb_column.inc + diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy-master.opt b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy-master.opt new file mode 100644 index 0000000000000..0d064eabcd40c --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy-master.opt @@ -0,0 +1 @@ +--default-time-zone=+00:00 diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy.test b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy.test new file mode 100644 index 0000000000000..2a88f36f9c890 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_column_copy.test @@ -0,0 +1,7 @@ +--echo ################# +--echo # TEST FOR COPY # +--echo ################# +--let $algorithm = COPY +--let $copy_ddl = 1 +--source ../include/alter_duckdb_column.inc + diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_index.test b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_index.test new file mode 100644 index 0000000000000..8df5bdd951bc5 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_duckdb_index.test @@ -0,0 +1,20 @@ +--echo #################### +--echo # TEST FOR INSTANT # +--echo #################### +--let $algorithm = INSTANT +--source ../include/alter_duckdb_index.inc + + +--echo #################### +--echo # TEST FOR INPLACE # +--echo #################### +--let $algorithm = INPLACE +--source ../include/alter_duckdb_index.inc + + +--echo ################# +--echo # TEST FOR COPY # +--echo ################# +--let $algorithm = COPY +--let $copy_ddl = 1 +--source ../include/alter_duckdb_index.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/alter_engine_duckdb.test b/storage/duckdb/mysql-test/duckdb/t/alter_engine_duckdb.test new file mode 100644 index 0000000000000..7d047686055cd --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/alter_engine_duckdb.test @@ -0,0 +1,17 @@ +SET GLOBAL duckdb_copy_ddl_in_batch=ON; +--source ../include/alter_engine_duckdb.inc + +SET GLOBAL duckdb_copy_ddl_in_batch=OFF; +--source ../include/alter_engine_duckdb.inc + +# Test for temporary table +CREATE TEMPORARY TABLE t1(id INT PRIMARY KEY) ENGINE = InnoDB; +--error ER_ILLEGAL_HA_CREATE_OPTION +ALTER TABLE t1 ENGINE = DuckDB; + +SET SESSION sql_mode = ''; +CREATE TEMPORARY TABLE t2(id INT PRIMARY KEY) ENGINE = DuckDB; +--error ER_ILLEGAL_HA_CREATE_OPTION +ALTER TABLE t2 ENGINE = DuckDB; + +SET GLOBAL duckdb_copy_ddl_in_batch=default; diff --git a/storage/duckdb/mysql-test/duckdb/t/bugfix_crash_after_commit_error.test b/storage/duckdb/mysql-test/duckdb/t/bugfix_crash_after_commit_error.test new file mode 100644 index 0000000000000..bc6757d240197 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/bugfix_crash_after_commit_error.test @@ -0,0 +1,28 @@ +--source ../include/have_duckdb.inc + +--skip TODO +create table t1(id int primary key) engine = duckdb; + +set global duckdb_dml_in_batch = on; +begin; +insert into t1 values (1); +insert into t1 values (1); + +--error ER_GET_ERRMSG +commit; +drop table t1; + + +--let $restart_parameters = "restart: --skip-log-bin" +--source include/restart_mysqld.inc + +create table t1(id int primary key) engine = duckdb; + +set global duckdb_dml_in_batch = on; +begin; +insert into t1 values (1); +insert into t1 values (1); + +--error ER_GET_ERRMSG +commit; +drop table t1; diff --git a/storage/duckdb/mysql-test/duckdb/t/bugfix_temp_and_system_database.test b/storage/duckdb/mysql-test/duckdb/t/bugfix_temp_and_system_database.test new file mode 100644 index 0000000000000..e802f5c4c354f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/bugfix_temp_and_system_database.test @@ -0,0 +1,77 @@ +--source ../include/have_duckdb.inc + +# DuckDB has default databases named 'temp' and 'system', if we try to create +# schema named 'temp' or 'system', we get an error. + +--echo # +--echo # TEST FOR `temp` and `system` +--echo # +SELECT run_in_duckdb("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); + +# temp schema +CREATE DATABASE `temp`; +USE `temp`; +CREATE TABLE t1(a INT KEY) ENGINE = DuckDB; +SELECT run_in_duckdb("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); + +RENAME TABLE t1 TO t2; +ALTER TABLE t2 RENAME TO t1; +ALTER TABLE t1 ADD COLUMN b INT; +TRUNCATE TABLE t1; + +# TODO: DML on temp schema may be failed. +USE `test`; +SET GLOBAL duckdb_dml_in_batch = ON; +INSERT INTO temp.t1 VALUES (1, 1), (2, 1), (3, 1); +SET GLOBAL duckdb_dml_in_batch = OFF; +--error ER_GET_ERRMSG +INSERT INTO temp.t1 VALUES (4, 1); +--error ER_GET_ERRMSG +SELECT * FROM temp.t1; +--error ER_GET_ERRMSG +UPDATE temp.t1 SET b = 1; +--error ER_GET_ERRMSG +DELETE FROM temp.t1; + +USE `temp`; +DROP TABLE t1; + +# system schema +CREATE DATABASE `system`; +USE `system`; +CREATE TABLE t1(a INT KEY) ENGINE = DuckDB; +SELECT run_in_duckdb("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); + +RENAME TABLE t1 TO t2; +ALTER TABLE t2 RENAME TO t1; +ALTER TABLE t1 ADD COLUMN b INT; +TRUNCATE TABLE t1; + +# TODO: DML on system schema may be failed. +USE `test`; +SET GLOBAL duckdb_dml_in_batch = ON; +INSERT INTO system.t1 VALUES (1, 1), (2, 1), (3, 1); +SET GLOBAL duckdb_dml_in_batch = OFF; +--error ER_GET_ERRMSG +INSERT INTO system.t1 VALUES (4, 1); +--error ER_GET_ERRMSG +SELECT * FROM system.t1; +--error ER_GET_ERRMSG +UPDATE system.t1 SET b = 1; +--error ER_GET_ERRMSG +DELETE FROM system.t1; + +USE `system`; +DROP TABLE t1; + +# Duckdb not support database name with ` +--error ER_GET_ERRMSG +CREATE DATABASE `d``b`; + +--echo # +--echo # CLEANUP +--echo # +SET GLOBAL duckdb_dml_in_batch = default; +DROP DATABASE `temp`; +DROP DATABASE `system`; +SELECT run_in_duckdb("SELECT * FROM information_schema.schemata WHERE catalog_name in (\"system\", \"temp\") or schema_name in (\"system\", \"temp\")"); diff --git a/storage/duckdb/mysql-test/duckdb/t/charset_and_collation-master.opt b/storage/duckdb/mysql-test/duckdb/t/charset_and_collation-master.opt new file mode 100644 index 0000000000000..6856ac296cf67 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/charset_and_collation-master.opt @@ -0,0 +1,2 @@ +--character_set_server=utf8mb4 +--collation_server=utf8mb4_uca1400_ai_ci diff --git a/storage/duckdb/mysql-test/duckdb/t/charset_and_collation.test b/storage/duckdb/mysql-test/duckdb/t/charset_and_collation.test new file mode 100644 index 0000000000000..38ee163154c6f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/charset_and_collation.test @@ -0,0 +1,369 @@ +--source ../include/have_duckdb.inc +--echo # +--echo # check collation config when execute +--echo # + +--disable_query_log +SET @saved_duckdb_dml_in_batch = @@GLOBAL.duckdb_dml_in_batch; +--enable_query_log + +create database if not exists charset_and_collation; +use charset_and_collation; + +# Test 1. SELECT 'a' = 'A' +# a. duckdb: uses the default_collation configuration of the session variable +# b. mysql: uses the collation_connection configuration of the sesison variable + +# Test 2. SELECT col = 'a' +# a. duckdb: uses the collate defined by the column. If the column attribute is not configured, use the session default_collation configuration; +# b. mysql: uses the collate defined by the column. If the column attribute is not configured, use the collate of the database; + +# For 2 we must set collation for duckdb column during create table and alter table + +CREATE TABLE t_duckdb (a varchar(32) PRIMARY KEY, b varchar(32), c varchar(32)) engine=duckdb; +CREATE TABLE t_innodb (a varchar(32) PRIMARY KEY, b varchar(32), c varchar(32)) engine=innodb; + +INSERT INTO t_duckdb VALUES ('a', 'A', 'á'); +INSERT INTO t_innodb VALUES ('a', 'A', 'á'); + +--echo # 1.1 Test for utf8mb4 + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_ai_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_0900_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_520_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_uca1400_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4' COLLATE 'utf8mb4_uca1400_as_cs'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +--echo # 1.2 Test for utf8mb3 +--disable_warnings + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_general_mysql500_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_uca1400_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_unicode_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_unicode_520_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_swedish_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8' COLLATE 'utf8_general_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8' COLLATE 'utf8_bin'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb3' COLLATE 'utf8mb3_uca1400_as_ci'; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +--enable_warnings + +--echo # 1.3 Test for latin1 +SET NAMES 'latin1' COLLATE 'latin1_general_ci'; +#--warning 7580 +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_duckdb; +SELECT 'a'='á' AS ai, 'a'='A' AS ci, 'A'='á' AS ai_ci FROM t_innodb; + +SET NAMES 'utf8mb4'; +DROP TABLE t_duckdb, t_innodb; + +--echo # +--echo # Test 2. SELECT col = 'a' +--echo # + +--echo # 2.1 define collation for table with utf8mb4 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb4_general_ci, c varchar(32) COLLATE utf8mb4_0900_as_cs, d varchar(32) COLLATE utf8mb4_0900_as_ci) engine=duckdb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb4_general_ci, c varchar(32) COLLATE utf8mb4_0900_as_cs, d varchar(32) COLLATE utf8mb4_0900_as_ci) engine=innodb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; + +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; +SELECT b FROM t_duckdb where b = 'a'; SELECT b FROM t_innodb where b = 'a'; +SELECT b FROM t_duckdb where b = 'A'; SELECT b FROM t_innodb where b = 'A'; +SELECT b FROM t_duckdb where b = 'á'; SELECT b FROM t_innodb where b = 'á'; +SELECT c FROM t_duckdb where c = 'a'; SELECT c FROM t_innodb where c = 'a'; +SELECT c FROM t_duckdb where c = 'A'; SELECT c FROM t_innodb where c = 'A'; +SELECT c FROM t_duckdb where c = 'á'; SELECT c FROM t_innodb where c = 'á'; +SELECT d FROM t_duckdb where d = 'a'; SELECT d FROM t_innodb where d = 'a'; +SELECT d FROM t_duckdb where d = 'A'; SELECT d FROM t_innodb where d = 'A'; +SELECT d FROM t_duckdb where d = 'á'; SELECT d FROM t_innodb where d = 'á'; + +DROP TABLE t_duckdb, t_innodb; + +--echo # 2.2 define collation for table with utf8mb3 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb3_general_mysql500_ci, c varchar(32) COLLATE utf8mb3_bin, d varchar(32) COLLATE utf8mb3_unicode_ci, e varchar(32) COLLATE utf8mb3_general_ci) engine=duckdb CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32), b varchar(32) COLLATE utf8mb3_general_mysql500_ci, c varchar(32) COLLATE utf8mb3_bin, d varchar(32) COLLATE utf8mb3_unicode_ci, e varchar(32) COLLATE utf8mb3_general_ci) engine=innodb CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a', 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; +SELECT b FROM t_duckdb where b = 'a'; SELECT b FROM t_innodb where b = 'a'; +SELECT b FROM t_duckdb where b = 'A'; SELECT b FROM t_innodb where b = 'A'; +SELECT b FROM t_duckdb where b = 'á'; SELECT b FROM t_innodb where b = 'á'; +SELECT c FROM t_duckdb where c = 'a'; SELECT c FROM t_innodb where c = 'a'; +SELECT c FROM t_duckdb where c = 'A'; SELECT c FROM t_innodb where c = 'A'; +SELECT c FROM t_duckdb where c = 'á'; +SELECT c FROM t_innodb where c = 'á'; +SELECT d FROM t_duckdb where d = 'a'; SELECT d FROM t_innodb where d = 'a'; +SELECT d FROM t_duckdb where d = 'A'; SELECT d FROM t_innodb where d = 'A'; +SELECT d FROM t_duckdb where d = 'á'; SELECT d FROM t_innodb where d = 'á'; +SELECT e FROM t_duckdb where e = 'a'; SELECT e FROM t_innodb where e = 'a'; +SELECT e FROM t_duckdb where e = 'A'; SELECT e FROM t_innodb where e = 'A'; +SELECT e FROM t_duckdb where e = 'á'; SELECT e FROM t_innodb where e = 'á'; + +DROP TABLE t_duckdb, t_innodb; + + +--echo # +--echo # Test 3. ALTER COLUMN COLLATION +--echo # +--echo # 3.1 define collation for table with utf8mb4 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8mb4_general_ci) engine=duckdb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8mb4_general_ci) engine=innodb CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs; + +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; + +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +DELETE FROM t_innodb; + +# ALTER TABLE +ALTER TABLE t_duckdb MODIFY COLUMN a varchar(32), + ADD COLUMN b varchar(32) COLLATE utf8mb4_general_ci, + ADD COLUMN c varchar(32) COLLATE utf8mb4_0900_as_cs, + ADD COLUMN d varchar(32) COLLATE utf8mb4_0900_as_ci; +ALTER TABLE t_innodb MODIFY COLUMN a varchar(32), + ADD COLUMN b varchar(32) COLLATE utf8mb4_general_ci, + ADD COLUMN c varchar(32) COLLATE utf8mb4_0900_as_cs, + ADD COLUMN d varchar(32) COLLATE utf8mb4_0900_as_ci; + +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; +SELECT b FROM t_duckdb where b = 'a'; SELECT b FROM t_innodb where b = 'a'; +SELECT b FROM t_duckdb where b = 'A'; SELECT b FROM t_innodb where b = 'A'; +SELECT b FROM t_duckdb where b = 'á'; SELECT b FROM t_innodb where b = 'á'; +SELECT c FROM t_duckdb where c = 'a'; SELECT c FROM t_innodb where c = 'a'; +SELECT c FROM t_duckdb where c = 'A'; SELECT c FROM t_innodb where c = 'A'; +SELECT c FROM t_duckdb where c = 'á'; SELECT c FROM t_innodb where c = 'á'; +SELECT d FROM t_duckdb where d = 'a'; SELECT d FROM t_innodb where d = 'a'; +SELECT d FROM t_duckdb where d = 'A'; SELECT d FROM t_innodb where d = 'A'; +SELECT d FROM t_duckdb where d = 'á'; SELECT d FROM t_innodb where d = 'á'; + +DROP TABLE t_duckdb, t_innodb; + + +--echo # 3.2 define collation for table with utf8mb3 +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8_general_ci) engine=duckdb CHARSET=utf8mb3 COLLATE=utf8_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE utf8_general_ci) engine=innodb CHARSET=utf8mb3 COLLATE=utf8_general_ci; + +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; + +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +DELETE FROM t_innodb; + +# ALTER TABLE +ALTER TABLE t_duckdb MODIFY a varchar(32), + ADD COLUMN b varchar(32) COLLATE utf8mb3_general_mysql500_ci, + ADD COLUMN c varchar(32) COLLATE utf8mb3_bin, + ADD COLUMN d varchar(32) COLLATE utf8mb3_unicode_ci, + ADD COLUMN e varchar(32) COLLATE utf8mb3_uca1400_as_ci; +ALTER TABLE t_innodb MODIFY a varchar(32), + ADD COLUMN b varchar(32) COLLATE utf8mb3_general_mysql500_ci, + ADD COLUMN c varchar(32) COLLATE utf8mb3_bin, + ADD COLUMN d varchar(32) COLLATE utf8mb3_unicode_ci, + ADD COLUMN e varchar(32) COLLATE utf8mb3_uca1400_as_ci; + +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a', 'a', 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT a FROM t_duckdb where a = 'á'; SELECT a FROM t_innodb where a = 'á'; +SELECT b FROM t_duckdb where b = 'a'; SELECT b FROM t_innodb where b = 'a'; +SELECT b FROM t_duckdb where b = 'A'; SELECT b FROM t_innodb where b = 'A'; +SELECT b FROM t_duckdb where b = 'á'; SELECT b FROM t_innodb where b = 'á'; +SELECT c FROM t_duckdb where c = 'a'; SELECT c FROM t_innodb where c = 'a'; +SELECT c FROM t_duckdb where c = 'A'; SELECT c FROM t_innodb where c = 'A'; +# SELECT c FROM t_duckdb where c = 'á'; # TODO: this query cannot executed +SELECT c FROM t_innodb where c = 'á'; +SELECT d FROM t_duckdb where d = 'a'; SELECT d FROM t_innodb where d = 'a'; +SELECT d FROM t_duckdb where d = 'A'; SELECT d FROM t_innodb where d = 'A'; +SELECT d FROM t_duckdb where d = 'á'; SELECT d FROM t_innodb where d = 'á'; +SELECT e FROM t_duckdb where e = 'a'; SELECT e FROM t_innodb where e = 'a'; +SELECT e FROM t_duckdb where e = 'A'; SELECT e FROM t_innodb where e = 'A'; +SELECT e FROM t_duckdb where e = 'á'; SELECT e FROM t_innodb where e = 'á'; + +DROP TABLE t_duckdb, t_innodb; + +--echo # 3.3 define collation for table with ascii +set names ascii; + +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE ascii_general_ci) engine=duckdb CHARSET=ascii COLLATE=ascii_general_ci; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a varchar(32) COLLATE ascii_general_ci) engine=innodb CHARSET=ascii COLLATE=ascii_general_ci; + +INSERT INTO t_duckdb VALUES (1, 'a'); +INSERT INTO t_innodb VALUES (1, 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; + +SELECT run_in_duckdb("DELETE FROM charset_and_collation.t_duckdb"); +DELETE FROM t_innodb; + +# ALTER TABLE +ALTER TABLE t_duckdb ADD COLUMN b varchar(32) COLLATE ascii_general_ci, + ADD COLUMN c varchar(32) COLLATE ascii_bin; + +ALTER TABLE t_innodb ADD COLUMN b varchar(32) COLLATE ascii_general_ci, + ADD COLUMN c varchar(32) COLLATE ascii_bin; + +INSERT INTO t_duckdb VALUES (1, 'a', 'a', 'a'); +INSERT INTO t_innodb VALUES (1, 'a', 'a', 'a'); + +SELECT a FROM t_duckdb where a = 'a'; SELECT a FROM t_innodb where a = 'a'; +SELECT a FROM t_duckdb where a = 'A'; SELECT a FROM t_innodb where a = 'A'; +SELECT b FROM t_duckdb where b = 'a'; SELECT b FROM t_innodb where b = 'a'; +SELECT b FROM t_duckdb where b = 'A'; SELECT b FROM t_innodb where b = 'A'; +SELECT c FROM t_duckdb where c = 'a'; SELECT c FROM t_innodb where c = 'a'; +SELECT c FROM t_duckdb where c = 'A'; SELECT c FROM t_innodb where c = 'A'; + +DROP TABLE t_duckdb, t_innodb; + +SET NAMES utf8mb4; + + +--echo # +--echo # Test 4. NON-UTF8 CHARSET +--echo # +--echo # 4.1 CREATE TABLE +--error ER_ILLEGAL_HA_CREATE_OPTION +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32)) engine=duckdb CHARSET=latin1; +--error ER_ILLEGAL_HA_CREATE_OPTION +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) CHARSET latin1) engine=duckdb CHARSET=utf8mb4; +--error ER_ILLEGAL_HA_CREATE_OPTION +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) COLLATE latin1_general_ci) engine=duckdb CHARSET=utf8mb4; + +--echo # 4.2 ALTER TABLE +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a varchar(32) CHARSET utf8mb3, b varchar(32) CHARSET utf8mb4) engine=duckdb CHARSET=latin1; + +--error ER_GET_ERRMSG +ALTER TABLE t_duckdb ADD COLUMN c varchar(32); +ALTER TABLE t_duckdb ADD COLUMN c varchar(32) CHARSET utf8mb3, ADD COLUMN d varchar(32) CHARSET utf8mb4; + +--error ER_GET_ERRMSG +ALTER TABLE t_duckdb MODIFY COLUMN a varchar(32); +--error ER_GET_ERRMSG +ALTER TABLE t_duckdb MODIFY COLUMN b varchar(32) CHARSET latin1; + + +--echo # +--echo # Test 5. UTF8MB4 with Emoji +--echo # + +CREATE TABLE t_mb4 ( + id int primary key, + a varchar(32) CHARSET utf8mb4, + b char(32) CHARSET utf8mb4, + c text(32) CHARSET utf8mb4, + d varchar(32) CHARSET utf8mb4 +) ENGINE=duckdb; + +# check emoji can be inserted successfully +INSERT INTO t_mb4 VALUES (1, 'a', 'b', 'c', 'd'); +INSERT INTO t_mb4 VALUES (2, UNHEX('F09F9884'), UNHEX('F09F9884'), UNHEX('F09F9884'), '😭h😄h😭'); + +SET GLOBAL duckdb_dml_in_batch = on; +INSERT INTO t_mb4 VALUES (3, UNHEX('F09F9884'), UNHEX('F09F9884'), UNHEX('F09F9884'), '😭h😄h😭'); + +# check emoji can be selected successfully +SELECT * from t_mb4; + +# check emoji can be read when copy-ddl +ALTER TABLE t_mb4 ADD e varchar(32) AFTER a, algorithm=copy; +SELECT * from t_mb4; + +ALTER TABLE t_mb4 ENGINE=innodb; +SELECT * from t_mb4; + +DROP DATABASE charset_and_collation; + +--disable_query_log +SET GLOBAL duckdb_dml_in_batch = @saved_duckdb_dml_in_batch; +--enable_query_log + + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/create_table_column-master.opt b/storage/duckdb/mysql-test/duckdb/t/create_table_column-master.opt new file mode 100644 index 0000000000000..0116e184c0a68 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/create_table_column-master.opt @@ -0,0 +1,2 @@ +--duckdb_require_primary_key=off +--default-time-zone=+00:00 \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/create_table_column.test b/storage/duckdb/mysql-test/duckdb/t/create_table_column.test new file mode 100644 index 0000000000000..4d56714c4845d --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/create_table_column.test @@ -0,0 +1,484 @@ +# file: duckdb_create_table.test +# +# Test case for creating tables with various MySQL field types in DuckDB. +# +#--source include/have_debug.inc + +--disable_query_log +SET @saved_duckdb_dml_in_batch = @@GLOBAL.duckdb_dml_in_batch; +SET @saved_duckdb_require_primary_key = @@GLOBAL.duckdb_require_primary_key; +SET GLOBAL duckdb_require_primary_key = OFF; +--enable_query_log + +--echo # 1) Prepare +--echo + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_create_col CHARACTER SET utf8mb4; +USE db_create_col; +--enable_query_log + +--echo # 2) Create table +--echo +--let $DB_NAME=db_create_col + +CREATE TABLE test_table ( + id INT PRIMARY KEY, + name VARCHAR(32), + index idx_id(name), + unique index uk_name(name), + unique index uk_id(id), + unique index uk_id_name(id,name) +) engine=duckdb; + +--let $table_name=test_table + +DROP TABLE test_table; + +# +# Numeric Types +# + +--echo # Range for each integer type from MySQL +# https://dev.mysql.com/doc/refman/8.4/en/integer-types.html +# a: Minimum Value Signed +# b: Maximum Value Signed +# c: Minimum Value Unsigned +# d: Maximum Value Unsigned + +--echo # +--echo # Tinyint +--echo # +--let $create_sql = create table t_tinyint (a tinyint, b tinyint, c tinyint unsigned, d tinyint unsigned) +--let $table_name = t_tinyint +--let $insert_sql = insert into t_tinyint values (-128, 127, 0, 255); +--let $select_sql = select a, hex(a), b, hex(b) from t_tinyint +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # smallint +--echo # +--let $create_sql = create table t_smallint (a smallint, b smallint, c smallint unsigned, d smallint unsigned) +--let $table_name = t_smallint +--let $insert_sql = insert into t_smallint values (-32768, 32767, 0, 65535); +--let $select_sql = select a, hex(a), b, hex(b) from t_smallint +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # mediumint +--echo # +--let $create_sql = create table t_mediumint (a mediumint, b mediumint, c mediumint unsigned, d mediumint unsigned) +--let $table_name = t_mediumint +--let $insert_sql = insert into t_mediumint values (-8388608, 8388607, 0, 16777215); +--let $select_sql = select a, hex(a), b, hex(b) from t_mediumint +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # int +--echo # +--let $create_sql = create table t_int (a int, b int, c int unsigned, d int unsigned) +--let $table_name = t_int +--let $insert_sql = insert into t_int values (-2147483648, 2147483647, 0, 4294967295); +--let $select_sql = select a, hex(a), b, hex(b) from t_int +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # bigint +--echo # +--let $create_sql = create table t_bigint (a bigint, b bigint, c bigint unsigned, d bigint unsigned) +--let $table_name = t_bigint +--let $insert_sql = insert into t_bigint values (-2147483648, 2147483647, 0, 4294967295); +--let $select_sql = select a, hex(a), b, hex(b) from t_bigint +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # bool +--echo # +--let $create_sql = create table t_bool (a bool, b bool, c bool, d bool) +--let $table_name = t_bool +--let $insert_sql = insert into t_bool values (1, true, 0, '1') +--let $select_sql = select a, hex(a), b, hex(b), c, hex(c), d, hex(d) from t_bool +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # float +--echo # +--let $create_sql = create table t_float (a float, b float(7,4), c float, d float) +--let $table_name = t_float +--let $insert_sql = insert into t_float values (1.01, 1.01, 99999, 3.40282E+20) +--let $select_sql = select a, b, c, d from t_float +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # float with high precision +--echo # +--let $create_sql = create table t_float (b float, c float, d float, e float) +--let $table_name = t_float +--let $insert_sql = insert into t_float values (3.402823466E+38, -3.402823466E+38, 1.175494351E-38, -1.175494351E-38) +--let $select_sql = select b,c,d, e from t_float +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # double +--echo # +--let $create_sql = create table t_double (a double, b double(7,4), c double, d double) +--let $table_name = t_double +--let $insert_sql = insert into t_double values (1.01, 1.01, 99999, 3.40282E+20) +--let $select_sql = select a,b,c,d from t_double +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # double with high precision +--echo # +--let $create_sql = create table t_double (b double, c double, d double, e double) +--let $table_name = t_double +--let $insert_sql = insert into t_double values (3.402823466E+38, -3.402823466E+38, 1.175494351E-38, -1.175494351E-38) +--let $select_sql = select b, c, d, e from t_double +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + + +--echo # +--echo # decimal +--echo # +--let $create_sql = create table t_decimal (g decimal(9,9),h decimal(9,4),i decimal(9,0)) +--let $table_name = t_decimal +--let $insert_sql = insert into t_decimal values (0.99999999,99999.9999,999999999) +--let $select_sql = select g, h, i from t_decimal +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # check all possibilities of decimal with kinds of precisions +--echo # + +--echo # +--echo # decimal(1): low precision(<=38) with low precision value and duckdb_use_double_for_decimal=on +--echo # +set global duckdb_use_double_for_decimal=on; +--let $create_sql = CREATE TABLE t1(id int primary key, c1 decimal(38, 5), c2 decimal(38, 5), c3 decimal(38, 5)) +--let $table_name = t1 +--let $insert_sql = INSERT INTO t1 values (1, 123456789012345678901234567890123.12345, 0, 0000000.0000000); +--let $select_sql = select id, c1,c2,c3 from t1 +--let $die_on_error=1 +--source ../include/check_field_correctness.inc +set global duckdb_use_double_for_decimal=default; + +--echo # +--echo # decimal(4): low precision(<=38) with low precision value and duckdb_use_double_for_decimal=off +--echo # +set global duckdb_use_double_for_decimal=off; +--let $create_sql = CREATE TABLE t1(id int primary key, c1 decimal(38, 5)) +--let $table_name = t1 +--let $insert_sql = INSERT INTO t1 values (1, 123456789012345678901234567890123.12345); +--let $select_sql = select id, c1 from t1 +--echo # use decimal +--let $die_on_error=1 +--source ../include/check_field_correctness.inc +set global duckdb_use_double_for_decimal=default; + +--echo # +--echo # decimal(5): high precision(>38) with low precision value(<=38) and duckdb_use_double_for_decimal=off +--echo # +set global duckdb_use_double_for_decimal=off; +--let $create_sql = CREATE TABLE t1(id int primary key, c1 decimal(40, 5)) +--let $table_name = t1 +--let $insert_sql = INSERT INTO t1 values (4, 123456789012345678901234567890.12345); +--let $select_sql = select id, c1 from t1 +--echo # use decimal(38, dec) +--let $die_on_error=1 +--source ../include/check_field_correctness.inc +set global duckdb_use_double_for_decimal=default; + +--echo # +--echo # bit +--echo # +--let $create_sql = create table t_bit (a bit(8), b bit(8), c bit(64), d bit(64)) +--let $table_name = t_bit +--let $insert_sql = insert into t_bit values(b'10101010', b'10111', b'1111111111111111111111111111111111111111111111111111111111111111', b'1111111111111111111111111111111111111111111111111111111111111011') +--let $select_sql = select hex(a), hex(b), hex(c), hex(d) from t_bit +# note: hex() display different in mysql and duckdb, (F and 0F) +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # char +--echo # +--let $create_sql = create table t_char (a char(1), b char(1), c char(10), d char(64), e char(64), f char(1), g char(32), h char(64)) +--let $table_name = t_char +--let $insert_sql = insert into t_char values(char(64),'A', b'10111', 'abcdefg0', '1111111111111111111111111111111111111111111111111111111111111011', 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select a, hex(a), b, hex(b), c, hex(c), d, hex(d), e, hex(e), f, g, h from t_char +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # varchar +--echo # +--let $create_sql = create table t_varchar (a varchar(1), b varchar(1), c varchar(10), d varchar(64), e varchar(64), f varchar(64), g varchar(32), h varchar(32)) +--let $table_name = t_varchar +--let $insert_sql = insert into t_varchar values(char(64),'A', b'10111', 'abcdefg0', '1111111111111111111111111111111111111111111111111111111111111011', 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select a, hex(a), b, hex(b), c, hex(c), d, hex(d), e, hex(e), f, g, h from t_varchar +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # tinytext +--echo # +--let $create_sql = create table t_varchar (a tinytext, b tinytext, d tinytext, e tinytext, f tinytext, g tinytext, h tinytext) +--let $table_name = t_varchar +--let $insert_sql = insert into t_varchar values(char(64),'A', 'abcdefg0', '1111111111111111111111111111111111111111111111111111111111111011', 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select a, hex(a), b, hex(b), d, hex(d), e, hex(e), f, g, h from t_varchar +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # text +--echo # +--let $create_sql = create table t_text (a text, b text, c text, d text, e text, f text) +--let $table_name = t_text +--let $insert_sql = insert into t_text values('0123456789ABCDEF', repeat('a', 65535), 0, 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), md5(hex(b)), hex(c), d, e, f from t_text +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # mediumtext +--echo # +--let $create_sql = create table t_mediumtext (a mediumtext, b mediumtext, c mediumtext, d mediumtext, e mediumtext) +--let $table_name = t_mediumtext +--let $insert_sql = insert into t_mediumtext values('0123456789ABCDEF', repeat('a', 167), 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), length(b), c, d, e from t_mediumtext +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # longtext +--echo # +--let $create_sql = create table t_longtext (a longtext, b longtext, c longtext, d longtext, e longtext, f longtext) +--let $table_name = t_longtext +--let $insert_sql = insert into t_longtext values('0123456789ABCDEF', repeat('a', 167), 0, 0xC3A6, "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), length(b), length(c), d, e, f from t_longtext +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # tinyblob +--echo # +--let $create_sql = create table t_tinyblob (a tinyblob, b tinyblob, c tinyblob, d tinyblob, e tinyblob, f tinyblob) +--let $table_name = t_tinyblob +--let $insert_sql = insert into t_tinyblob values('0123456789ABCDEF', repeat('a', 255), 0, X'48656C6C6F', "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), hex(b), hex(c), d, hex(d), e, f from t_tinyblob +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # blob +--echo # +--let $create_sql = create table t_blob (a blob, b blob, c blob, d blob, e blob, f blob) +--let $table_name = t_blob +--let $insert_sql = insert into t_blob values('0123456789ABCDEF', repeat('a', 65535), 0, X'48656C6C6F', "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), md5(hex(b)), hex(c), d, hex(d), e, f from t_blob +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # mediumblob +--echo # +--let $create_sql = create table t_mediumblob (a mediumblob, b mediumblob, c mediumblob, d mediumblob, e mediumblob) +--let $table_name = t_mediumblob +--let $insert_sql = insert into t_mediumblob values('0123456789ABCDEF', repeat('a', 167), X'48656C6C6F', "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), octet_length(b), c, hex(c), d, e from t_mediumblob +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # longblob +--echo # +--let $create_sql = create table t_longblob (a longblob, b longblob, c longblob, d longblob, e longblob, f longblob) +--let $table_name = t_longblob +--let $insert_sql = insert into t_longblob values('0123456789ABCDEF', repeat('a', 167), 0, X'48656C6C6F', "I'm duckdb", 0x48656C6C6F00776F726C64) +--let $select_sql = select hex(a), octet_length(b), octet_length(c), d, hex(d), e, f from t_longblob +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # binary +--echo # +--let $create_sql = CREATE TABLE t_binary (a BINARY(16), b BINARY(32)) +--let $table_name = t_binary +--let $insert_sql = INSERT INTO t_binary (a, b) VALUES(UNHEX(MD5('abcdef')), repeat('a', 32)) +--let $select_sql = select hex(a), octet_length(a) from t_binary +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # varbinary +--echo # +--let $create_sql = CREATE TABLE t_varbinary (a VARBINARY(16), b BINARY(32)) +--let $table_name = t_varbinary +--let $insert_sql = INSERT INTO t_varbinary (a, b) VALUES(UNHEX(MD5('abcdef')), repeat('a', 32)) +--let $select_sql = select hex(a), octet_length(a), hex(b), b from t_varbinary +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # year +--echo # + +--let $create_sql = CREATE TABLE t_year (a year, b year, c year) +--let $table_name = t_year +--let $insert_sql = insert into t_year values ('2020', '1970', '1969'); +--let $select_sql = select a, b, c from t_year +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # date +--echo # + +--let $create_sql = CREATE TABLE t_date (a date, b date, c date, d date, e date) +--let $table_name = t_date +--let $insert_sql = insert into t_date values ('2020-01-01', '1970-01-01', '1969-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1') +--let $select_sql = select a, b, c, d, e from t_date +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # time +--echo # + +--let $create_sql = CREATE TABLE t_time (a time, b time, c time, d time, e time) +--let $table_name = t_time +--let $insert_sql = insert into t_time values ('2020-01-01 12:00:00.1', '00:00:00', '23:59:59', '23:59:59.123456', '2020-01-01 12:00:00.1') +--let $select_sql = select a, b, c, d, e from t_time +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # time(6) +--echo # + + +--let $create_sql = CREATE TABLE t_time_6 (a time(6), b time(6), c time(6), d time(6), e time(6)) +--let $table_name = t_time_6 +--let $insert_sql = insert into t_time_6 values ('2020-01-01 12:00:00.1', '00:00:00', '23:59:59', '23:59:59.123456', '2020-01-01 12:00:00.1') +--let $select_sql = select a, b, c, d, e from t_time_6 +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # datetime +--echo # + +--let $create_sql = CREATE TABLE t_datetime (a datetime, b datetime, c datetime) +--let $table_name = t_datetime +--let $insert_sql = insert into t_datetime values ('2020-01-01 12:00:00', '1969-01-01 12:00:00', '2020-01-01 12:00:00.001') +--let $select_sql = select a, b, c from t_datetime +# Note: inconsistency occurs when '1969-01-01 12:00:00' +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # datetime(6) +--echo # + +--let $create_sql = CREATE TABLE t_datetime_6 (a datetime(6), b datetime(6), c datetime(6)) +--let $table_name = t_datetime_6 +--let $insert_sql = insert into t_datetime_6 values ('2020-01-01 12:00:00', '1969-01-01 12:00:00', '2020-01-01 12:00:00.001') +--let $select_sql = select a, b, c from t_datetime_6 +# Note: inconsistency occurs when '1969-01-01 12:00:00' +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # timestamp +--echo # +--let $create_sql = CREATE TABLE t_timestamp (a timestamp, b timestamp, c timestamp, d timestamp, e timestamp) +--let $table_name = t_timestamp +--let $insert_sql = insert into t_timestamp values ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '2020-01-01 12:00:00.001', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $select_sql = select a, b, c, d, e from t_timestamp +--let $die_on_error=1 +# Note: not inconsist +--source ../include/check_field_correctness.inc + +--echo # +--echo # timestamp(6) +--echo # +--let $create_sql = CREATE TABLE t_timestamp_6 (a timestamp(6), b timestamp(6), c timestamp(6), d timestamp(6), e timestamp(6)) +--let $table_name = t_timestamp_6 +--let $insert_sql = insert into t_timestamp_6 values ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '2020-01-01 12:00:00.001', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $select_sql = select a, b, c, d, e from t_timestamp_6 +--let $die_on_error=1 +# Note: not inconsist +--source ../include/check_field_correctness.inc + + +--echo # +--echo # json, enum, set +--echo # +--let $create_sql = CREATE TABLE t_json (a json, b enum('v1', 'v2', 'v3'), c set('v1', 'v2', 'v3')) +--let $table_name = t_json +--let $insert_sql = insert into t_json values ('{"id": 1, "value":"aaaa"}', 3, 'v1,v2,v3'); +--let $select_sql = select a, b, c from t_json +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # NULL +--echo # +--let $create_sql = CREATE TABLE t_null (a int, b int default null, c varchar(32) default null) +--let $table_name = t_null +--let $insert_sql = insert into t_null(a) values (1); +--let $select_sql = select a, b, c from t_null +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # column with charset ascii +--echo # +--let $create_sql = CREATE TABLE t_ascii (a varchar(32) CHARACTER SET ascii, b char(32) CHARACTER SET ascii, c text CHARACTER SET ascii) +--let $table_name = t_ascii +--let $insert_sql = insert into t_ascii(a, b, c) values ('ascii', 'ASCII', 'ascII'); +--let $select_sql = select a, b, c from t_ascii +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # column with utf8mb4 and emoji +--echo # +--let $create_sql = CREATE TABLE t_mb4_emoji (a varchar(32) CHARSET utf8mb4, b char(32) CHARSET utf8mb4, c text(32) CHARSET utf8mb4, d varchar(32) CHARSET utf8mb4) +--let $table_name = t_mb4_emoji +--let $insert_sql = insert into t_mb4_emoji(a, b, c, d) values (UNHEX('F09F9884'), UNHEX('F09F9884'), UNHEX('F09F9884'), '😭h😄h😭'); +--let $select_sql = select a, b, c, d from t_mb4_emoji +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--disable_query_log +SET GLOBAL duckdb_dml_in_batch = @saved_duckdb_dml_in_batch; +DROP DATABASE db_create_col; +USE test; +SET GLOBAL duckdb_require_primary_key = @saved_duckdb_require_primary_key; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp-master.opt b/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp-master.opt new file mode 100644 index 0000000000000..29c8f453eb6fd --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp-master.opt @@ -0,0 +1,2 @@ +--timezone=GMT +--duckdb_require_primary_key=off \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp.test b/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp.test new file mode 100644 index 0000000000000..1b9ecfc4bcde3 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/create_table_column_timestamp.test @@ -0,0 +1,271 @@ +--source include/have_debug.inc + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_ts CHARACTER SET utf8mb4; +--enable_query_log + +--echo # +--echo # datetime type, test insert and SELECT unchanged +--echo # + +--let $create_sql = CREATE TABLE t_datetime (a datetime, b datetime, c datetime) +--let $table_name = t_datetime +--let $insert_sql = insert into t_datetime values ('2020-01-01 12:00:00', '1969-01-01 12:00:00', '2020-01-01 12:00:00.001') +--let $select_sql = SELECT a, b, c FROM t_datetime +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # timestamp,test insert and SELECT unchanged +--echo # + +--let $create_sql = CREATE TABLE t_timestamp (a timestamp, b timestamp, c timestamp, d timestamp, e timestamp) +--let $table_name = t_timestamp +--let $insert_sql = INSERT INTO t_timestamp VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1970-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $select_sql = SELECT a, b, c, d, e FROM t_timestamp +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # Under system timezone, test linux `date` command and mysql `SELECT now()` get the same result +--echo # + +--source include/restart_mysqld.inc +--disable_query_log +USE db_ts; +--enable_query_log +SET TIME_ZONE = system; + +--let $_TMP_OUT_o= $MYSQLTEST_VARDIR/tmp/_t_os_time_o +--let $_TMP_OUT_n= $MYSQLTEST_VARDIR/tmp/_t_os_time_n + +--exec date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' > $_TMP_OUT_o +--exec date -d "+1 minutes" +"%Y-%m-%d %H:%M:%S" | tr -d '\n' > $_TMP_OUT_n + +--let $os_time_o = `SELECT LOAD_FILE('$_TMP_OUT_o')` +--let $os_time_n = `SELECT LOAD_FILE('$_TMP_OUT_n')` + +SELECT sleep(1); + +CREATE TABLE t(a timestamp) ENGINE=duckdb; +INSERT INTO t VALUES (now()); + +--let $sql=SELECT now() > '$os_time_o' FROM t +--let $ret=`$sql` +--if ($ret != 1) { +--die "[SELECT @should_be_1]" != "1" +} + +--let $sql=SELECT count(*) FROM t WHERE a > '$os_time_o' +--let $ret=`$sql` +--if ($ret != 1) { +--die "[SELECT count]" != "1" +} + +--let $sql=SELECT now() < '$os_time_n' FROM t +--let $ret=`$sql` +--if ($ret != 1) { +--die "[SELECT @should_be_1]" != "1" +} + +--let $sql=SELECT count(*) FROM t WHERE a < '$os_time_n' +--let $ret=`$sql` +--if ($ret != 1) { +--die "[SELECT count]" != "1" +} + +--remove_file $_TMP_OUT_o +--remove_file $_TMP_OUT_n +DROP TABLE t; + + +--echo # +--echo # For unsupported timezone +--echo # + +--enable_warnings + +CREATE TABLE t_time_zone_unsupported (a timestamp) ENGINE=duckdb; +INSERT INTO t_time_zone_unsupported VALUES (from_unixtime(1740117061)); + +SET time_zone = '+02:00'; +SELECT a FROM t_time_zone_unsupported; + +SET time_zone = '+02:30'; +SELECT a FROM t_time_zone_unsupported; + +SET time_zone = '+03:00'; +SELECT a FROM t_time_zone_unsupported; + +DROP TABLE t_time_zone_unsupported; + + +--echo # +--echo # Insert and Select under the same timezone, check duckdb and innodb are the same +--echo # + +SET time_zone = 'UTC'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=0 +--source ../include/check_field_correctness.inc + +SET time_zone = 'Europe/Moscow'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +SET time_zone = system; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +SET time_zone = '+10:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # +--echo # Timezone changed between Insert and Select, check duckdb and innodb are the same +--echo # + +--echo # Time zone changed from '+10:00' to '+09:00' +SET time_zone = '+10:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = SET time_zone = '+09:00'; +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # Time zone changed from SYSTEM to 'Europe/Moscow' +SET time_zone = SYSTEM; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = SET time_zone = 'Europe/Moscow'; +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # Time zone changed from '+02:00' to Japan +SET time_zone = '+02:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b timestamp(6), c datetime, d datetime(6), e timestamp(6)) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00', '1969-01-01 12:00:00', from_unixtime(1740117061), from_unixtime(1740117061.1234)); +--let $config_sql = SET time_zone = Japan; +--let $select_sql = SELECT a, b, c, d, e FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + +--echo # +--echo # Use time colum to do compare with string, check duckdb and innodb are the same +--echo # It test the timezone setting of duckdb connection is the consistent with mysql thd +--echo # + +--echo # test1 +SET time_zone = '+08:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b datetime) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00'); +--let $config_sql = +--let $select_sql = SELECT * FROM t_time_zone WHERE a < '2020-01-01 12:00:01' and a > '2020-01-01 11:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00' +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # test2 +SET time_zone = '+08:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b datetime) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00'); +--let $config_sql = SET time_zone = UTC; +--let $select_sql = SELECT * FROM t_time_zone WHERE a < '2020-01-01 04:00:01' and a > '2020-01-01 03:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00' +--let $select_sql = SELECT * FROM t_time_zone +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # test3 +SET time_zone = '+07:00'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b datetime) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00') +--let $config_sql = SET time_zone = 'MET' +--let $select_sql = SELECT * FROM t_time_zone WHERE a < '2020-01-01 06:00:01' and a > '2020-01-01 05:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00' +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + +--echo # test4 +SET time_zone = 'MET'; +--let $create_sql = CREATE TABLE t_time_zone (a timestamp, b datetime) +--let $table_name = t_time_zone +--let $insert_sql = INSERT INTO t_time_zone VALUES ('2020-01-01 12:00:00', '1970-01-01 12:00:00') +--let $config_sql = SET time_zone = '+07:00' +--let $select_sql = SELECT * FROM t_time_zone WHERE a < '2020-01-01 18:00:01' and a > '2020-01-01 17:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00' +--let $die_on_error=1 +--source ../include/check_field_correctness.inc + + + +SET time_zone='+00:00'; +--disable_query_log +USE db_ts; +--enable_query_log +CREATE TABLE t_duckdb (a timestamp, b datetime) ENGINE=duckdb; +CREATE TABLE t_innodb (a timestamp, b datetime) ENGINE=innodb; +INSERT INTO t_duckdb VALUES ('2020-01-01 00:00:00', '1970-01-01 00:00:00'); +INSERT INTO t_innodb VALUES ('2020-01-01 00:00:00', '1970-01-01 00:00:00'); + +SET time_zone='-11:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 13:00:01' and a > '2019-12-31 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 13:00:01' and a > '2019-12-31 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-10:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 14:00:01' and a > '2019-12-31 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 14:00:01' and a > '2019-12-31 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-09:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 15:00:01' and a > '2019-12-31 14:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 15:00:01' and a > '2019-12-31 14:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-08:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 16:00:01' and a > '2019-12-31 15:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 16:00:01' and a > '2019-12-31 15:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-07:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 17:00:01' and a > '2019-12-31 16:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 17:00:01' and a > '2019-12-31 16:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-06:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 18:00:01' and a > '2019-12-31 17:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 18:00:01' and a > '2019-12-31 17:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-05:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 19:00:01' and a > '2019-12-31 18:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 19:00:01' and a > '2019-12-31 18:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-04:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 20:00:01' and a > '2019-12-31 19:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 20:00:01' and a > '2019-12-31 19:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-03:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 21:00:01' and a > '2019-12-31 20:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 21:00:01' and a > '2019-12-31 20:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-02:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 22:00:01' and a > '2019-12-31 21:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 22:00:01' and a > '2019-12-31 21:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='-01:00'; SELECT * FROM t_innodb WHERE a < '2019-12-31 23:00:01' and a > '2019-12-31 22:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2019-12-31 23:00:01' and a > '2019-12-31 22:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+00:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 23:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 23:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+01:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 01:00:01' and a > '2020-01-01 00:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 01:00:01' and a > '2020-01-01 00:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+02:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 02:00:01' and a > '2020-01-01 01:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 02:00:01' and a > '2020-01-01 01:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+03:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 03:00:01' and a > '2020-01-01 02:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 03:00:01' and a > '2020-01-01 02:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+04:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 04:00:01' and a > '2020-01-01 03:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 04:00:01' and a > '2020-01-01 03:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+05:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 05:00:01' and a > '2020-01-01 04:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 05:00:01' and a > '2020-01-01 04:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+06:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 06:00:01' and a > '2020-01-01 05:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 06:00:01' and a > '2020-01-01 05:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+07:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 07:00:01' and a > '2020-01-01 06:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 07:00:01' and a > '2020-01-01 06:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+08:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 08:00:01' and a > '2020-01-01 07:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 08:00:01' and a > '2020-01-01 07:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+09:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 09:00:01' and a > '2020-01-01 08:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 09:00:01' and a > '2020-01-01 08:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+10:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 10:00:01' and a > '2020-01-01 09:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 10:00:01' and a > '2020-01-01 09:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+11:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+12:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 12:00:01' and a > '2020-01-01 11:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 12:00:01' and a > '2020-01-01 11:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+13:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 13:00:01' and a > '2020-01-01 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 13:00:01' and a > '2020-01-01 12:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SET time_zone='+14:00'; SELECT * FROM t_innodb WHERE a < '2020-01-01 14:00:01' and a > '2020-01-01 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; SELECT * FROM t_duckdb WHERE a < '2020-01-01 14:00:01' and a > '2020-01-01 13:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; + +DROP TABLE t_innodb; +DROP TABLE t_duckdb; + +SET time_zone=default; + +--disable_query_log +DROP DATABASE db_ts; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/create_table_constraint.test b/storage/duckdb/mysql-test/duckdb/t/create_table_constraint.test new file mode 100644 index 0000000000000..cccf1e06d35fb --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/create_table_constraint.test @@ -0,0 +1,35 @@ +--source ../include/have_duckdb.inc +--source include/have_debug.inc + + +--disable_query_log +CREATE DATABASE IF NOT EXISTS create_table_constraint CHARACTER SET utf8mb4; +USE create_table_constraint; +SET NAMES utf8mb4; + --enable_query_log + +--echo # 1) Prepare +--echo + +--echo # 2) Create table +--echo +--let $DB_NAME=test + +CREATE TABLE test_table ( + id INT PRIMARY KEY, + name VARCHAR(32), + index idx_id(name), + unique index uk_name(name), + unique index uk_id(id), + unique index uk_id_name(id,name) +) engine=duckdb; + +--let $table_name=test_table +--let $db_name=create_table_constraint +--source ../include/show_duckdb_table_structure.inc + +--source ../include/cleanup_duckdb.inc + +--disable_query_log +DROP DATABASE create_table_constraint; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_join-master.opt b/storage/duckdb/mysql-test/duckdb/t/cross_engine_join-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_join-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_join.test b/storage/duckdb/mysql-test/duckdb/t/cross_engine_join.test new file mode 100644 index 0000000000000..8fb4ea50aac30 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_join.test @@ -0,0 +1,132 @@ +--echo # +--echo # Cross-engine join: DuckDB + InnoDB via _mdb_scan replacement scan +--echo # + +--echo +--echo # Setup: create tables in both engines +--echo + +CREATE DATABASE IF NOT EXISTS cross_engine_join CHARACTER SET utf8mb4; +USE cross_engine_join; + +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, name VARCHAR(50)) ENGINE=InnoDB; + +INSERT INTO t_duck VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'); +INSERT INTO t_inno VALUES (1, 'Alice'), (2, 'Bob'), (4, 'Dave'); + +--echo +--echo # (1) Basic INNER JOIN +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + ORDER BY d.id; + +--echo +--echo # (2) LEFT JOIN — DuckDB left, InnoDB right +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d LEFT JOIN t_inno i ON d.id = i.id + ORDER BY d.id; + +--echo +--echo # (3) RIGHT JOIN — InnoDB left, DuckDB right +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d RIGHT JOIN t_inno i ON d.id = i.id + ORDER BY i.id; + +--echo +--echo # (4) Cross join (cartesian product) +--echo + +--sorted_result +SELECT d.id AS did, i.id AS iid + FROM t_duck d, t_inno i + ORDER BY did, iid; + +--echo +--echo # (5) Aggregation over cross-engine join +--echo + +--sorted_result +SELECT COUNT(*), SUM(d.id) + FROM t_duck d JOIN t_inno i ON d.id = i.id; + +--echo +--echo # (6) Subquery with InnoDB table in WHERE +--echo + +--sorted_result +SELECT d.id, d.val + FROM t_duck d + WHERE d.id IN (SELECT id FROM t_inno) + ORDER BY d.id; + +--echo +--echo # (7) Multiple data types +--echo + +CREATE TABLE t_duck_types ( + id INT PRIMARY KEY, + ti TINYINT, + bi BIGINT, + f FLOAT, + d DOUBLE, + dc DECIMAL(10,2), + dt DATE, + ts TIMESTAMP, + txt VARCHAR(100) +) ENGINE=DuckDB; + +CREATE TABLE t_inno_types ( + id INT PRIMARY KEY, + ti TINYINT, + bi BIGINT, + f FLOAT, + d DOUBLE, + dc DECIMAL(10,2), + dt DATE, + ts TIMESTAMP, + txt VARCHAR(100) +) ENGINE=InnoDB; + +INSERT INTO t_duck_types VALUES (1, 10, 1000000, 1.5, 2.5, 99.99, '2025-01-15', '2025-01-15 10:30:00', 'duck'); +INSERT INTO t_inno_types VALUES (1, 20, 2000000, 3.5, 4.5, 88.88, '2025-06-20', '2025-06-20 14:00:00', 'inno'); + +--sorted_result +SELECT d.id, d.ti, d.bi, d.f, d.dc, d.txt, i.ti, i.bi, i.f, i.dc, i.txt + FROM t_duck_types d JOIN t_inno_types i ON d.id = i.id; + +--echo +--echo # (8) NULL handling across engines +--echo + +CREATE TABLE t_duck_null (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno_null (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=InnoDB; + +INSERT INTO t_duck_null VALUES (1, 'a'), (2, NULL); +INSERT INTO t_inno_null VALUES (1, NULL), (2, 'b'); + +--sorted_result +SELECT d.id, d.val AS dval, i.val AS ival + FROM t_duck_null d JOIN t_inno_null i ON d.id = i.id + ORDER BY d.id; + +--echo +--echo # Cleanup +--echo + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_duck_types; +DROP TABLE t_inno_types; +DROP TABLE t_duck_null; +DROP TABLE t_inno_null; +DROP DATABASE cross_engine_join; diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_union-master.opt b/storage/duckdb/mysql-test/duckdb/t/cross_engine_union-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_union-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_union.test b/storage/duckdb/mysql-test/duckdb/t/cross_engine_union.test new file mode 100644 index 0000000000000..d303b974cb0f9 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_union.test @@ -0,0 +1,108 @@ +--echo # +--echo # Cross-engine UNION: DuckDB + InnoDB via select_handler unit pushdown +--echo # + +--echo +--echo # Setup +--echo + +CREATE DATABASE IF NOT EXISTS cross_engine_union CHARACTER SET utf8mb4; +USE cross_engine_union; + +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=InnoDB; + +INSERT INTO t_duck VALUES (1, 'duck_a'), (2, 'duck_b'), (3, 'duck_c'); +INSERT INTO t_inno VALUES (2, 'inno_b'), (3, 'inno_c'), (4, 'inno_d'); + +--echo +--echo # (1) UNION ALL — keeps duplicates +--echo +--sorted_result +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_inno +ORDER BY id, val; + +--echo +--echo # (2) UNION — removes duplicates +--echo +--sorted_result +SELECT id FROM t_duck +UNION +SELECT id FROM t_inno +ORDER BY id; + +--echo +--echo # (3) UNION ALL with WHERE filters +--echo +--sorted_result +SELECT id, val FROM t_duck WHERE id >= 2 +UNION ALL +SELECT id, val FROM t_inno WHERE id <= 3 +ORDER BY id, val; + +--echo +--echo # (4) Three-way UNION across engines +--echo + +CREATE TABLE t_duck2 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t_duck2 VALUES (10, 'extra'); + +--sorted_result +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_inno +UNION ALL +SELECT id, val FROM t_duck2 +ORDER BY id; + +--echo +--echo # (5) UNION with aggregation +--echo + +--sorted_result +SELECT COUNT(*) AS cnt FROM ( + SELECT id FROM t_duck + UNION ALL + SELECT id FROM t_inno +) AS combined; + +--echo +--echo # (6) UNION between two DuckDB tables (pure DuckDB, no cross-engine) +--echo + +--sorted_result +SELECT id, val FROM t_duck +UNION ALL +SELECT id, val FROM t_duck2 +ORDER BY id; + +--echo +--echo # (7) EXCEPT +--echo + +--sorted_result +SELECT id FROM t_duck +EXCEPT +SELECT id FROM t_inno +ORDER BY id; + +--echo +--echo # (8) INTERSECT +--echo + +--sorted_result +SELECT id FROM t_duck +INTERSECT +SELECT id FROM t_inno +ORDER BY id; + +--echo +--echo # Cleanup +--echo + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_duck2; +DROP DATABASE cross_engine_union; diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_where-master.opt b/storage/duckdb/mysql-test/duckdb/t/cross_engine_where-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_where-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/cross_engine_where.test b/storage/duckdb/mysql-test/duckdb/t/cross_engine_where.test new file mode 100644 index 0000000000000..4736fb64a97d3 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/cross_engine_where.test @@ -0,0 +1,149 @@ +--source include/have_sequence.inc + +--echo # +--echo # Cross-engine: WHERE pushdown, LIMIT, EXPLAIN, error cases +--echo # + +--echo +--echo # Setup +--echo + +DROP DATABASE IF EXISTS cross_engine_where; +CREATE DATABASE cross_engine_where CHARACTER SET utf8mb4; +USE cross_engine_where; + +CREATE TABLE t_duck (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +CREATE TABLE t_inno (id INT PRIMARY KEY, name VARCHAR(50), score INT) ENGINE=InnoDB; + +INSERT INTO t_duck VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma'), (4, 'delta'), (5, 'epsilon'); +INSERT INTO t_inno VALUES (1, 'Alice', 90), (2, 'Bob', 80), (3, 'Carol', 70), (4, 'Dave', 60), (5, 'Eve', 50); + +--echo +--echo # (1) WHERE on InnoDB table — equality +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.name = 'Bob'; + +--echo +--echo # (2) WHERE on InnoDB table — range +--echo + +--sorted_result +SELECT d.id, d.val, i.name, i.score + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.score >= 70 + ORDER BY d.id; + +--echo +--echo # (3) WHERE on InnoDB table — compound AND +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.score > 50 AND i.name < 'Dave' + ORDER BY d.id; + +--echo +--echo # (4) WHERE on InnoDB table — OR +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.name = 'Alice' OR i.name = 'Eve' + ORDER BY d.id; + +--echo +--echo # (5) WHERE on InnoDB table — IN list +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.id IN (1, 3, 5) + ORDER BY d.id; + +--echo +--echo # (6) WHERE on InnoDB table — BETWEEN +--echo + +--sorted_result +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.score BETWEEN 60 AND 80 + ORDER BY d.id; + +--echo +--echo # (7) LIMIT on cross-engine join +--echo + +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + ORDER BY d.id + LIMIT 2; + +--echo +--echo # (8) LIMIT with OFFSET +--echo + +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + ORDER BY d.id + LIMIT 2 OFFSET 1; + +--echo +--echo # (9) WHERE + LIMIT combined +--echo + +SELECT d.id, d.val, i.name + FROM t_duck d JOIN t_inno i ON d.id = i.id + WHERE i.score >= 60 + ORDER BY d.id + LIMIT 2; + +--echo +--echo # (10) Large-ish scan to check multi-chunk fiber yield +--echo + +CREATE TABLE t_inno_big (id INT PRIMARY KEY, val INT) ENGINE=InnoDB; +INSERT INTO t_inno_big SELECT seq, seq * 10 FROM test.seq_1_to_3000; + +CREATE TABLE t_duck_big (id INT PRIMARY KEY, label VARCHAR(20)) ENGINE=DuckDB; +INSERT INTO t_duck_big VALUES (1, 'start'), (1500, 'mid'), (3000, 'end'); + +--sorted_result +SELECT d.id, d.label, i.val + FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id + ORDER BY d.id; + +--echo +--echo # (11) COUNT over large InnoDB scan +--echo + +SELECT COUNT(*) FROM ( + SELECT d.id FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id +) AS sub; + +--echo +--echo # (12) WHERE on large table — should push down +--echo + +--sorted_result +SELECT d.id, d.label, i.val + FROM t_duck_big d JOIN t_inno_big i ON d.id = i.id + WHERE i.val > 20000 + ORDER BY d.id; + +--echo +--echo # Cleanup +--echo + +DROP TABLE t_duck; +DROP TABLE t_inno; +DROP TABLE t_inno_big; +DROP TABLE t_duck_big; +DROP DATABASE cross_engine_where; diff --git a/storage/duckdb/mysql-test/duckdb/t/decimal_high_precision.test b/storage/duckdb/mysql-test/duckdb/t/decimal_high_precision.test new file mode 100644 index 0000000000000..ec7bb24c7bd26 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/decimal_high_precision.test @@ -0,0 +1,155 @@ +--source ../include/have_duckdb.inc + + +--echo # +--echo # decimal with high precision +--echo # decimal precision more than 38 is not supported by DuckDB, use double by default +--echo # + +--disable_query_log +CREATE DATABASE IF NOT EXISTS innodb_db; +CREATE DATABASE IF NOT EXISTS duckdb_db; +--enable_query_log + +--echo # Create tables +USE innodb_db; +create table t_decimal (id int primary key, a decimal(65, 30),b decimal(65, 15),c decimal(65, 0),d decimal(38,30),e decimal(38,18),f decimal(38,0),g decimal(9,9),h decimal(9,4),i decimal(9,0)) engine = innodb; + +USE duckdb_db; +create table t_decimal (id int primary key, a decimal(65, 30),b decimal(65, 15),c decimal(65, 0),d decimal(38,30),e decimal(38,18),f decimal(38,0),g decimal(9,9),h decimal(9,4),i decimal(9,0)) engine = duckdb; + +--echo # display innodb table structure +desc innodb_db.t_decimal; + +--echo # display duckdb table structure +desc duckdb_db.t_decimal; + +--echo # Insert data into innodb +USE innodb_db; +insert into t_decimal values (1, 99999999999999999999999999999999999.999999999999999999999999999999,99999999999999999999999999999999999999999999999999.999999999999999,99999999999999999999999999999999999999999999999999999999999999999,99999999.999999999999999999999999999999,99999999999999999999.999999999999999999,99999999999999999999999999999999999999,0.99999999,99999.9999,999999999); + +--echo # Insert data into duckdb +USE duckdb_db; +SET GLOBAL duckdb_dml_in_batch = OFF; +insert into t_decimal values (1, 99999999999999999999999999999999999.999999999999999999999999999999,99999999999999999999999999999999999999999999999999.999999999999999,99999999999999999999999999999999999999999999999999999999999999999,99999999.999999999999999999999999999999,99999999999999999999.999999999999999999,99999999999999999999999999999999999999,0.99999999,99999.9999,999999999); + +--echo # Compare results +--disable_query_log +USE innodb_db; +--let $innodb_result=`select id, a, b, c, d, e, f, g, h, i from t_decimal` + +USE duckdb_db; +--let $duckdb_result=`select id, a, b, c, d, e, f, g, h, i from t_decimal` +--enable_query_log + +--echo innodb_result : $innodb_result +--echo duckdb_result : $duckdb_result + +--echo # Note: precision differences are expected for columns with precision > 38 + +--echo # cleanup +--disable_query_log +USE test; +DROP DATABASE innodb_db; +DROP DATABASE duckdb_db; +--enable_query_log + +--echo # +--echo # decimal(2): high precision(>38) with low precision value(<=38) and duckdb_use_double_for_decimal=on +--echo # +set global duckdb_use_double_for_decimal=on; + +--disable_query_log +CREATE DATABASE IF NOT EXISTS innodb_db; +CREATE DATABASE IF NOT EXISTS duckdb_db; + +USE innodb_db; +CREATE TABLE t1(id int primary key, c1 decimal(40, 5), c2 decimal(40, 5), c3 decimal(40, 5)) engine = innodb; + +USE duckdb_db; +CREATE TABLE t1(id int primary key, c1 decimal(40, 5), c2 decimal(40, 5), c3 decimal(40, 5)) engine = duckdb; +--enable_query_log + +--echo # display innodb table structure +desc innodb_db.t1; + +--echo # display duckdb table structure +desc duckdb_db.t1; + +--echo # use double +--disable_query_log +USE innodb_db; +INSERT INTO t1 values (4, 123456789012345678901234567890.12345, 0, 0000000.0000000); + +USE duckdb_db; +SET GLOBAL duckdb_dml_in_batch = OFF; +INSERT INTO t1 values (4, 123456789012345678901234567890.12345, 0, 0000000.0000000); + +USE innodb_db; +--let $innodb_result=`select id, c1 from t1` + +USE duckdb_db; +--let $duckdb_result=`select id, c1 from t1` +--enable_query_log + +--echo innodb_result : $innodb_result +--echo duckdb_result : $duckdb_result + +--disable_query_log +USE test; +DROP DATABASE innodb_db; +DROP DATABASE duckdb_db; +--enable_query_log + +set global duckdb_use_double_for_decimal=default; + +--echo # +--echo # decimal(3): high precision(>38) with high precision value(>38) and duckdb_use_double_for_decimal=on +--echo # +set global duckdb_use_double_for_decimal=on; + +--disable_query_log +CREATE DATABASE IF NOT EXISTS innodb_db; +CREATE DATABASE IF NOT EXISTS duckdb_db; + +USE innodb_db; +CREATE TABLE t1(id int primary key, c1 decimal(40, 5),c2 decimal(40, 5), c3 decimal(40, 5)) engine = innodb; + +USE duckdb_db; +CREATE TABLE t1(id int primary key, c1 decimal(40, 5),c2 decimal(40, 5), c3 decimal(40, 5)) engine = duckdb; +--enable_query_log + +--echo # display innodb table structure +desc innodb_db.t1; + +--echo # display duckdb table structure +desc duckdb_db.t1; + +--echo # use double +--disable_query_log +USE innodb_db; +INSERT INTO t1 values (4, 12345678901234567890123456789012345.12345, 0, 0000000.0000000); + +USE duckdb_db; +SET GLOBAL duckdb_dml_in_batch = OFF; +INSERT INTO t1 values (4, 12345678901234567890123456789012345.12345, 0, 0000000.0000000); + +USE innodb_db; +--let $innodb_result=`select id, c1 from t1` + +USE duckdb_db; +--let $duckdb_result=`select id, c1 from t1` +--enable_query_log + +--echo innodb_result : $innodb_result +--echo duckdb_result : $duckdb_result + +--disable_query_log +USE test; +DROP DATABASE innodb_db; +DROP DATABASE duckdb_db; +--enable_query_log + +set global duckdb_use_double_for_decimal=default; + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/decimal_precision_all_possibilities.test b/storage/duckdb/mysql-test/duckdb/t/decimal_precision_all_possibilities.test new file mode 100644 index 0000000000000..2ea4459947590 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/decimal_precision_all_possibilities.test @@ -0,0 +1,7 @@ +set global duckdb_dml_in_batch = OFF; +--source ../include/decimal_precision_all_possibilities.inc + +set global duckdb_dml_in_batch = ON; +--source ../include/decimal_precision_all_possibilities.inc + +set global duckdb_dml_in_batch = default; diff --git a/storage/duckdb/mysql-test/duckdb/t/dml_delete-master.opt b/storage/duckdb/mysql-test/duckdb/t/dml_delete-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/dml_delete-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/dml_delete.test b/storage/duckdb/mysql-test/duckdb/t/dml_delete.test new file mode 100644 index 0000000000000..9d8e4322cd7a1 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/dml_delete.test @@ -0,0 +1,133 @@ +--echo # +--echo # DuckDB DELETE operations test +--echo # + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_dml_delete CHARACTER SET utf8mb4; +USE db_dml_delete; +SET GLOBAL duckdb_dml_in_batch = OFF; +--enable_query_log + +# ----------------------------------------------------------------- +# Setup: create and populate test tables +# ----------------------------------------------------------------- +--echo # Setup + +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; + +CREATE TABLE t1 ( + id INT PRIMARY KEY, + val VARCHAR(50) +) ENGINE = DuckDB; + +CREATE TABLE t2 ( + id INT PRIMARY KEY, + val VARCHAR(50) +) ENGINE = DuckDB; + +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); + +INSERT INTO t2 VALUES (1, 'alpha'); +INSERT INTO t2 VALUES (3, 'beta'); +INSERT INTO t2 VALUES (5, 'gamma'); + +# ----------------------------------------------------------------- +# Test 1: DELETE single row by PK +# ----------------------------------------------------------------- +--echo # Test 1: DELETE single row by PK + +DELETE FROM t1 WHERE id = 1; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 2: DELETE by non-PK condition +# ----------------------------------------------------------------- +--echo # Test 2: DELETE by non-PK condition + +DELETE FROM t1 WHERE val = 'three'; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 3: DELETE with range condition +# ----------------------------------------------------------------- +--echo # Test 3: DELETE with range condition + +DELETE FROM t1 WHERE id >= 4; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 4: DELETE all rows +# ----------------------------------------------------------------- +--echo # Test 4: DELETE all rows + +DELETE FROM t1; +SELECT * FROM t1 ORDER BY id; +SELECT COUNT(*) FROM t1; + +# ----------------------------------------------------------------- +# Test 5: DELETE with subquery +# ----------------------------------------------------------------- +--echo # Test 5: DELETE with subquery + +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); + +# Subquery DELETE requires rnd_pos on t2 which DuckDB doesn't support +--error ER_ILLEGAL_HA +DELETE FROM t1 WHERE id IN (SELECT id FROM t2); +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 6: DELETE in transaction — COMMIT +# ----------------------------------------------------------------- +--echo # Test 6: DELETE in transaction — COMMIT + +--disable_query_log +DELETE FROM t1; +DELETE FROM t2; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +--enable_query_log + +BEGIN; +DELETE FROM t1 WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +COMMIT; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 7: DELETE in transaction — ROLLBACK +# ----------------------------------------------------------------- +--echo # Test 7: DELETE in transaction — ROLLBACK + +--disable_query_log +DELETE FROM t1; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +--enable_query_log + +BEGIN; +DELETE FROM t1 WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +ROLLBACK; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Cleanup +# ----------------------------------------------------------------- +--disable_query_log +DROP TABLE t1; +DROP TABLE t2; +DROP DATABASE db_dml_delete; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/dml_update-master.opt b/storage/duckdb/mysql-test/duckdb/t/dml_update-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/dml_update-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/dml_update.test b/storage/duckdb/mysql-test/duckdb/t/dml_update.test new file mode 100644 index 0000000000000..360dcfc14361f --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/dml_update.test @@ -0,0 +1,128 @@ +--echo # +--echo # DuckDB UPDATE operations test +--echo # + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_dml_update CHARACTER SET utf8mb4; +USE db_dml_update; +SET GLOBAL duckdb_dml_in_batch = OFF; +--enable_query_log + +# ----------------------------------------------------------------- +# Setup: create and populate test table +# ----------------------------------------------------------------- +--echo # Setup + +DROP TABLE IF EXISTS t1; + +CREATE TABLE t1 ( + id INT PRIMARY KEY, + val VARCHAR(50) +) ENGINE = DuckDB; + +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +INSERT INTO t1 VALUES (4, 'four'); +INSERT INTO t1 VALUES (5, 'five'); + +# ----------------------------------------------------------------- +# Test 1: UPDATE single row by PK +# ----------------------------------------------------------------- +--echo # Test 1: UPDATE single row by PK + +UPDATE t1 SET val = 'ONE' WHERE id = 1; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 2: UPDATE by non-PK condition +# ----------------------------------------------------------------- +--echo # Test 2: UPDATE by non-PK condition + +UPDATE t1 SET val = 'THREE' WHERE val = 'three'; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 3: UPDATE with range condition +# ----------------------------------------------------------------- +--echo # Test 3: UPDATE with range condition + +UPDATE t1 SET val = 'big' WHERE id >= 4; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 4: UPDATE multiple columns +# ----------------------------------------------------------------- +--echo # Test 4: UPDATE multiple columns + +UPDATE t1 SET id = 10, val = 'ten' WHERE id = 5; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 5: UPDATE all rows +# ----------------------------------------------------------------- +--echo # Test 5: UPDATE all rows + +UPDATE t1 SET val = 'reset'; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 6: UPDATE with expression +# ----------------------------------------------------------------- +--echo # Test 6: UPDATE with expression + +UPDATE t1 SET val = CONCAT('id_', id); +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 7: UPDATE in transaction — COMMIT +# ----------------------------------------------------------------- +--echo # Test 7: UPDATE in transaction — COMMIT + +--disable_query_log +DELETE FROM t1; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +--enable_query_log + +BEGIN; +UPDATE t1 SET val = 'UPDATED' WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +COMMIT; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 8: UPDATE in transaction — ROLLBACK +# ----------------------------------------------------------------- +--echo # Test 8: UPDATE in transaction — ROLLBACK + +--disable_query_log +DELETE FROM t1; +INSERT INTO t1 VALUES (1, 'one'); +INSERT INTO t1 VALUES (2, 'two'); +INSERT INTO t1 VALUES (3, 'three'); +--enable_query_log + +BEGIN; +UPDATE t1 SET val = 'UPDATED' WHERE id = 2; +SELECT * FROM t1 ORDER BY id; +ROLLBACK; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Test 9: UPDATE — no matching rows +# ----------------------------------------------------------------- +--echo # Test 9: UPDATE — no matching rows + +UPDATE t1 SET val = 'ghost' WHERE id = 999; +SELECT * FROM t1 ORDER BY id; + +# ----------------------------------------------------------------- +# Cleanup +# ----------------------------------------------------------------- +--disable_query_log +DROP TABLE t1; +DROP DATABASE db_dml_update; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/drop_database-master.opt b/storage/duckdb/mysql-test/duckdb/t/drop_database-master.opt new file mode 100644 index 0000000000000..8337e890040bf --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/drop_database-master.opt @@ -0,0 +1 @@ +--character_set_server=utf8mb4 diff --git a/storage/duckdb/mysql-test/duckdb/t/drop_database.test b/storage/duckdb/mysql-test/duckdb/t/drop_database.test new file mode 100644 index 0000000000000..75877300a67ec --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/drop_database.test @@ -0,0 +1,119 @@ +--echo # +--echo # DuckDB DROP DATABASE operations test +--echo # + +# Cleanup from possible previous failed run +--disable_query_log +--disable_warnings +DROP DATABASE IF EXISTS db_duck1; +DROP DATABASE IF EXISTS db_duck2; +DROP DATABASE IF EXISTS db_duck3; +DROP DATABASE IF EXISTS `my-duck-db`; +--enable_warnings +--enable_query_log + +# ----------------------------------------------------------------- +# Test 1: CREATE DATABASE, create DuckDB table, DROP DATABASE +# ----------------------------------------------------------------- +--echo # Test 1: Basic CREATE/DROP DATABASE with DuckDB table + +CREATE DATABASE IF NOT EXISTS db_duck1 CHARACTER SET utf8mb4; +USE db_duck1; + +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three'); +SELECT * FROM t1 ORDER BY id; + +USE test; +DROP DATABASE db_duck1; + +# Verify the database is gone +--error ER_BAD_DB_ERROR +USE db_duck1; + +# ----------------------------------------------------------------- +# Test 2: Re-create same database after DROP — no orphan schema +# ----------------------------------------------------------------- +--echo # Test 2: Re-create same database after DROP + +CREATE DATABASE db_duck1 CHARACTER SET utf8mb4; +USE db_duck1; + +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (10, 'ten'); +SELECT * FROM t1 ORDER BY id; + +USE test; +DROP DATABASE db_duck1; + +# ----------------------------------------------------------------- +# Test 3: DROP DATABASE with multiple DuckDB tables +# ----------------------------------------------------------------- +--echo # Test 3: DROP DATABASE with multiple DuckDB tables + +CREATE DATABASE db_duck2 CHARACTER SET utf8mb4; +USE db_duck2; + +CREATE TABLE t1 (id INT PRIMARY KEY, a INT) ENGINE = DuckDB; +CREATE TABLE t2 (id INT PRIMARY KEY, b VARCHAR(20)) ENGINE = DuckDB; +CREATE TABLE t3 (id INT PRIMARY KEY, c DECIMAL(10,2)) ENGINE = DuckDB; + +INSERT INTO t1 VALUES (1, 100), (2, 200); +INSERT INTO t2 VALUES (1, 'alpha'), (2, 'beta'); +INSERT INTO t3 VALUES (1, 1.11), (2, 2.22); + +SELECT * FROM t1 ORDER BY id; +SELECT * FROM t2 ORDER BY id; +SELECT * FROM t3 ORDER BY id; + +USE test; +DROP DATABASE db_duck2; + +# Re-create and verify clean state +CREATE DATABASE db_duck2 CHARACTER SET utf8mb4; +USE db_duck2; +CREATE TABLE t1 (id INT PRIMARY KEY, x INT) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 999); +SELECT * FROM t1; + +USE test; +DROP DATABASE db_duck2; + +# ----------------------------------------------------------------- +# Test 4: DROP DATABASE with backtick / special name +# ----------------------------------------------------------------- +--echo # Test 4: DROP DATABASE with special name + +CREATE DATABASE `my-duck-db` CHARACTER SET utf8mb4; +USE `my-duck-db`; + +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 'special'); +SELECT * FROM t1; + +USE test; +DROP DATABASE `my-duck-db`; + +# ----------------------------------------------------------------- +# Test 5: DROP DATABASE IF EXISTS (non-existent) +# ----------------------------------------------------------------- +--echo # Test 5: DROP DATABASE IF EXISTS on non-existent DB + +DROP DATABASE IF EXISTS db_nonexistent_duck; + +# ----------------------------------------------------------------- +# Test 6: DROP DATABASE within a transaction context +# ----------------------------------------------------------------- +--echo # Test 6: CREATE/DROP DATABASE interleaved with DML on test + +CREATE DATABASE db_duck3 CHARACTER SET utf8mb4; +USE db_duck3; + +CREATE TABLE t1 (id INT PRIMARY KEY, val INT) ENGINE = DuckDB; +INSERT INTO t1 VALUES (1, 10); +SELECT * FROM t1; + +USE test; +DROP DATABASE db_duck3; + +--echo # All tests passed diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_add_backticks.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_add_backticks.test new file mode 100644 index 0000000000000..e354231ed18e5 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_add_backticks.test @@ -0,0 +1,158 @@ +SET GLOBAL duckdb_require_primary_key=OFF; + +--echo # +--echo # Test whether the convertor accurately adds backticks to schema names, +--echo # table names, and column names in DDL statements +--echo # and correctly passes them to the DuckDB engine for execution. +--echo # + +--echo # +--echo # Test: drop and create databases with a name starting with a digit or containing only digits. +--echo # +--disable_warnings +DROP DATABASE IF EXISTS `09898141`; +--enable_warnings +CREATE DATABASE `09898141`; +--disable_warnings +DROP DATABASE IF EXISTS `011fq123`; +--enable_warnings +CREATE DATABASE `011fq123`; +USE `09898141`; +SELECT schema_name FROM information_schema.schemata WHERE schema_name = '09898141'; +SELECT run_in_duckdb("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '09898141'"); +SELECT schema_name FROM information_schema.schemata WHERE schema_name = '011fq123'; +SELECT run_in_duckdb("SELECT schema_name FROM information_schema.schemata WHERE schema_name = '011fq123'"); + + +--echo # +--echo # Test: drop and create tables with a name starting with a digit or containing only digits. +--echo # +--disable_warnings +DROP TABLE IF EXISTS `001`; +--enable_warnings +CREATE TABLE `001` ( + `00000000000` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY, + `0a01131` VARCHAR(50) +) ENGINE=DuckDB; +--disable_warnings +DROP TABLE IF EXISTS `111a`; +--enable_warnings +CREATE TABLE `111a` ( + `#0x1141` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY +) ENGINE=DuckDB; +CREATE TABLE `321` ( + `009` BIGINT NOT NULL DEFAULT '0' PRIMARY KEY +) ENGINE=DuckDB; +SELECT table_name FROM information_schema.tables WHERE table_schema='09898141'; +SELECT run_in_duckdb("SELECT table_name FROM information_schema.tables WHERE table_schema='09898141'"); +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='001'; +SELECT run_in_duckdb("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='001'"); +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='111a'; +SELECT run_in_duckdb("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='111a'"); +SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='321'; +SELECT run_in_duckdb("SELECT column_name FROM information_schema.columns WHERE table_schema='09898141' and table_name='321'"); + +--echo # +--echo # Test: insert(delete and update are not currently supported) +--echo # +USE `09898141`; +INSERT INTO `001` VALUES (1, 'a'),(2, 'b'),(3, 'c'),(4, 'd'),(5, 'e'); +INSERT INTO `111a` VALUES (1), (2), (3), (4), (5); +SELECT * from `09898141`.`001`; +SELECT run_in_duckdb("SELECT * FROM `09898141`.`001`"); +SELECT * from `09898141`.`111a`; +SELECT run_in_duckdb("SELECT * from `09898141`.`111a`"); + + +--echo # +--echo # Test: select +--echo # +SELECT * FROM `001` ORDER BY `00000000000` DESC; +SELECT run_in_duckdb("SELECT * FROM `09898141`.`001` ORDER BY `00000000000` DESC"); +SELECT COUNT(*) FROM `001` WHERE `00000000000` > 2 GROUP BY `00000000000`; +SELECT run_in_duckdb("SELECT COUNT(*) FROM `09898141`.`001` WHERE `00000000000` > 2 GROUP BY `00000000000`"); + +USE `011fq123`; +SELECT `#0x1141` FROM `09898141`.`111a` ORDER BY `#0x1141` DESC; +SELECT run_in_duckdb("SELECT `#0x1141` FROM `09898141`.`111a` ORDER BY `#0x1141` DESC"); +SELECT `0a01131` FROM `09898141`.`001` ORDER BY `0a01131` DESC; +SELECT run_in_duckdb("SELECT `0a01131` FROM `09898141`.`001` ORDER BY `0a01131` DESC"); + +--echo # +--echo # Test: alter table +--echo # +USE `09898141`; +ALTER TABLE `001` ADD COLUMN `0A` BIGINT DEFAULT 0; +SHOW CREATE TABLE `001`; +SELECT run_in_duckdb("PRAGMA table_info(`09898141`.`001`)"); +SELECT * FROM `001`; +SELECT run_in_duckdb("SELECT * FROM `09898141`.`001`"); +ALTER TABLE `001` MODIFY `0A` INT; +ALTER TABLE `001` RENAME COLUMN `0A` TO `0B`; +ALTER TABLE `001` MODIFY `0B` INT DEFAULT '0'; +ALTER TABLE `001` MODIFY `0B` INT NOT NULL; +ALTER TABLE `09898141`.`001` MODIFY `0B` INT NULL; +ALTER TABLE `09898141`.`001` MODIFY `0B` INT; +ALTER TABLE `09898141`.`001` RENAME TO `101`; +SHOW CREATE TABLE `101`; +SELECT run_in_duckdb("PRAGMA table_info(`09898141`.`101`)"); +SELECT * FROM `101`; +SELECT run_in_duckdb("SELECT * FROM `09898141`.`101`"); +ALTER TABLE `101` DROP COLUMN `0B`; +SELECT * FROM `101`; +SELECT run_in_duckdb("SELECT * FROM `09898141`.`101`"); + + +--echo # +--echo # Test: truncate tables +--echo # +TRUNCATE TABLE `09898141`.`111a`; + + +--echo # +--echo # Test: create tables with special characters +--echo # +--disable_warnings +DROP DATABASE IF EXISTS mydb1; +--enable_warnings +CREATE DATABASE mydb1; +CREATE TABLE mydb1.t (i INT, d DECIMAL, f FLOAT); +INSERT INTO mydb1.t VALUES(1,1,1); +CREATE TABLE mydb1.y ENGINE=DuckDB AS SELECT AVG(i), AVG(d), AVG(f) FROM mydb1.t; +SHOW CREATE TABLE mydb1.y; +SELECT run_in_duckdb("PRAGMA table_info(mydb1.y)"); + + +--echo # +--echo # Test: drop databases(cascade) +--echo # +DROP DATABASE IF EXISTS `09898141`; +DROP DATABASE IF EXISTS `011fq123`; +DROP DATABASE IF EXISTS mydb1; + + +--echo # +--echo # Test: database, table and column names with space. +--echo # +CREATE DATABASE `my db`; +CREATE TABLE `my db`.`my table` ( + `my column` INT +) ENGINE=DuckDB; +INSERT INTO `my db`.`my table` VALUES(1); +SELECT `my column` FROM `my db`.`my table`; + +SELECT run_in_duckdb("SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'"); +SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'; + +SELECT run_in_duckdb("PRAGMA table_info(`my db`.`my table`)"); +SHOW CREATE TABLE `my db`.`my table`; + +DROP DATABASE `my db`; +SELECT run_in_duckdb("SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'my db'"); + +# +# CLEANUP +# +--disable_warnings +SET GLOBAL duckdb_require_primary_key=ON; +--enable_warnings diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_agg_func.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_agg_func.test new file mode 100644 index 0000000000000..bc50c1bf8cd11 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_agg_func.test @@ -0,0 +1,348 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE t_innodb ( + id INT PRIMARY KEY, + col1 TINYINT, + col2 INT, + col3 BIGINT, + col4 DOUBLE, + col5 DECIMAL(38, 10), + col6 TIME, + col7 DATE, + col8 DATETIME, + col9 TIMESTAMP, + col10 VARCHAR(100), + col11 BLOB +); + +CREATE TABLE t_duckdb ( + id INT PRIMARY KEY, + col1 TINYINT, + col2 INT, + col3 BIGINT, + col4 DOUBLE, + col5 DECIMAL(38, 10), + col6 TIME, + col7 DATE, + col8 DATETIME, + col9 TIMESTAMP, + col10 VARCHAR(100), + col11 BLOB +) ENGINE=DuckDB; + +INSERT INTO t_innodb VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (2, 10, 123456789, 123456789011121314, 123456789011121314.12345678910, 1234567890111213141516171819.123456789, '23:59:59', '2025-12-31', '9999-12-31 23:59:59', '2025-12-31 23:59:59', '1234567890111213141516171819.123456789', '1234567890111213141516171819.123456789'), + (3, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', '-1234567890111213141516171819.123456789', '-1234567890111213141516171819.123456789'), + (4, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', REPEAT('a', 100), REPEAT('a', 65535)); + + +INSERT INTO t_duckdb VALUES (1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (2, 10, 123456789, 123456789011121314, 123456789011121314.12345678910, 1234567890111213141516171819.123456789, '23:59:59', '2025-12-31', '9999-12-31 23:59:59', '2025-12-31 23:59:59', '1234567890111213141516171819.123456789', '1234567890111213141516171819.123456789'), + (3, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', '-1234567890111213141516171819.123456789', '-1234567890111213141516171819.123456789'), + (4, -10, -123456789, -123456789011121314, -123456789011121314.12345678910, -1234567890111213141516171819.123456789, '00:00:00', '1970-01-01', '0001-01-01 00:00:00', '1970-01-02 00:00:00', REPEAT('a', 100), REPEAT('a', 65535)); + +--echo -------------------------- +--echo 1. AVG() +--echo -------------------------- + +SELECT AVG(col1), AVG(col2), AVG(col3), AVG(col4), + AVG(col5), AVG(col6), AVG(col7), AVG(col8), + AVG(col9), AVG(col10) +FROM t_innodb; + +SELECT AVG(col1), AVG(col2), AVG(col3), AVG(col4), + AVG(col5), AVG(col6), AVG(col7), AVG(col8), + AVG(col9), AVG(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT AVG(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 2. BIT_AND() +--echo -------------------------- + +SELECT BIT_AND(col1), BIT_AND(col2), BIT_AND(col3) +FROM t_innodb; + +SELECT BIT_AND(col1), BIT_AND(col2), BIT_AND(col3) +FROM t_duckdb; + +# DECIMAL, DOUBLE, TIME, DATE, TIMESTAMP, VARCHAR, BLOB is not supoorted yet. +--error ER_GET_ERRMSG +SELECT BIT_AND(col4), BIT_AND(col5), BIT_AND(col6), + BIT_AND(col7), BIT_AND(col8), BIT_AND(col9), + BIT_AND(col10) +FROM t_duckdb; + +--echo -------------------------- +--echo 3. BIT_OR() +--echo -------------------------- + +SELECT BIT_OR(col1), BIT_OR(col2), BIT_OR(col3) +FROM t_innodb; + +SELECT BIT_OR(col1), BIT_OR(col2), BIT_OR(col3) +FROM t_duckdb; + +# DECIMAL, DOUBLE, TIME, DATE, TIMESTAMP, VARCHAR, BLOB is not supoorted yet. +--error ER_GET_ERRMSG +SELECT BIT_OR(col4), BIT_OR(col5), BIT_OR(col6), + BIT_OR(col7), BIT_OR(col8), BIT_OR(col9), + BIT_OR(col10) +FROM t_duckdb; + +--echo -------------------------- +--echo 4. BIT_XOR() +--echo -------------------------- + +SELECT BIT_XOR(col1), BIT_XOR(col2), BIT_XOR(col3) +FROM t_innodb; + +SELECT BIT_XOR(col1), BIT_XOR(col2), BIT_XOR(col3) +FROM t_duckdb; + +# DECIMAL, DOUBLE, TIME, DATE, TIMESTAMP, VARCHAR, BLOB is not supoorted yet. +--error ER_GET_ERRMSG +SELECT BIT_XOR(col4), BIT_XOR(col5), BIT_XOR(col6), + BIT_XOR(col7), BIT_XOR(col8), BIT_XOR(col9), + BIT_XOR(col10) +FROM t_duckdb; + +--echo -------------------------- +--echo 5. COUNT() +--echo -------------------------- + +SELECT COUNT(col1), COUNT(col2), COUNT(col3), COUNT(col4), + COUNT(col5), COUNT(col6), COUNT(col7), COUNT(col8), + COUNT(col9), COUNT(col10), COUNT(col11) +FROM t_innodb; + +SELECT COUNT(col1), COUNT(col2), COUNT(col3), COUNT(col4), + COUNT(col5), COUNT(col6), COUNT(col7), COUNT(col8), + COUNT(col9), COUNT(col10), COUNT(col11) +FROM t_duckdb; + +SELECT COUNT(DISTINCT col1), COUNT(DISTINCT col2), COUNT(DISTINCT col3), COUNT(DISTINCT col4), + COUNT(DISTINCT col5), COUNT(DISTINCT col6), COUNT(DISTINCT col7), COUNT(DISTINCT col8), + COUNT(DISTINCT col9), COUNT(DISTINCT col10), COUNT(DISTINCT col11) +FROM t_innodb; + +SELECT COUNT(DISTINCT col1), COUNT(DISTINCT col2), COUNT(DISTINCT col3), COUNT(DISTINCT col4), + COUNT(DISTINCT col5), COUNT(DISTINCT col6), COUNT(DISTINCT col7), COUNT(DISTINCT col8), + COUNT(DISTINCT col9), COUNT(DISTINCT col10), COUNT(DISTINCT col11) +FROM t_duckdb; + +--echo -------------------------- +--echo 6. GROUP_CONCAT() +--echo -------------------------- + +SET group_concat_max_len = 100000; + +SELECT GROUP_CONCAT(col1), GROUP_CONCAT(col2), GROUP_CONCAT(col3), GROUP_CONCAT(col4), + GROUP_CONCAT(col5), GROUP_CONCAT(col6), GROUP_CONCAT(col7), GROUP_CONCAT(col8), + GROUP_CONCAT(col9), LENGTH(GROUP_CONCAT(col10)), LENGTH(GROUP_CONCAT(col11)) +FROM t_innodb; + +# DuckDB is not affected by group_concat_max_len parameter +SELECT GROUP_CONCAT(col1), GROUP_CONCAT(col2), GROUP_CONCAT(col3), GROUP_CONCAT(col4), + GROUP_CONCAT(col5), GROUP_CONCAT(col6), GROUP_CONCAT(col7), GROUP_CONCAT(col8), + GROUP_CONCAT(col9), LENGTH(GROUP_CONCAT(col10)), LENGTH(GROUP_CONCAT(col11)) +FROM t_duckdb; + +--echo -------------------------- +--echo 7. JSON_ARRAYAGG() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 8. JSON_OBJECTAGG() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 9. MIN() +--echo -------------------------- + +SELECT MIN(col1), MIN(col2), MIN(col3), MIN(col4), + MIN(col5), MIN(col6), MIN(col7), MIN(col8), + MIN(col9), MIN(col10), MIN(col11) +FROM t_innodb; + +SELECT MIN(col1), MIN(col2), MIN(col3), MIN(col4), + MIN(col5), MIN(col6), MIN(col7), MIN(col8), + MIN(col9), MIN(col10), MIN(col11) +FROM t_duckdb; + +--echo -------------------------- +--echo 10. MAX() +--echo -------------------------- + +SELECT MAX(col1), MAX(col2), MAX(col3), MAX(col4), + MAX(col5), MAX(col6), MAX(col7), MAX(col8), + MAX(col9), LENGTH(MAX(col10)), LENGTH(MAX(col11)) +FROM t_innodb; + +SELECT MAX(col1), MAX(col2), MAX(col3), MAX(col4), + MAX(col5), MAX(col6), MAX(col7), MAX(col8), + MAX(col9), LENGTH(MAX(col10)), LENGTH(MAX(col11)) +FROM t_duckdb; + +--echo -------------------------- +--echo 11. STD() +--echo -------------------------- + +SELECT STD(col1), STD(col2), STD(col3), STD(col4), + STD(col5), STD(col6), STD(col7), STD(col8), + STD(col9), STD(col10) +FROM t_innodb; + +SELECT STD(col1), STD(col2), STD(col3), STD(col4), + STD(col5), STD(col6), STD(col7), STD(col8), + STD(col9), STD(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT STD(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 12. STDDEV() +--echo -------------------------- + +SELECT STDDEV(col1), STDDEV(col2), STDDEV(col3), STDDEV(col4), + STDDEV(col5), STDDEV(col6), STDDEV(col7), STDDEV(col8), + STDDEV(col9), STDDEV(col10) +FROM t_innodb; + +SELECT STDDEV(col1), STDDEV(col2), STDDEV(col3), STDDEV(col4), + STDDEV(col5), STDDEV(col6), STDDEV(col7), STDDEV(col8), + STDDEV(col9), STDDEV(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT STDDEV(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 13. STDDEV_POP() +--echo -------------------------- + +SELECT STDDEV_POP(col1), STDDEV_POP(col2), STDDEV_POP(col3), STDDEV_POP(col4), + STDDEV_POP(col5), STDDEV_POP(col6), STDDEV_POP(col7), STDDEV_POP(col8), + STDDEV_POP(col9), STDDEV_POP(col10) +FROM t_innodb; + +SELECT STDDEV_POP(col1), STDDEV_POP(col2), STDDEV_POP(col3), STDDEV_POP(col4), + STDDEV_POP(col5), STDDEV_POP(col6), STDDEV_POP(col7), STDDEV_POP(col8), + STDDEV_POP(col9), STDDEV_POP(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT STDDEV_POP(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 14. STDDEV_POP() +--echo -------------------------- + +SELECT STDDEV_SAMP(col1), STDDEV_SAMP(col2), STDDEV_SAMP(col3), STDDEV_SAMP(col4), + STDDEV_SAMP(col5), STDDEV_SAMP(col6), STDDEV_SAMP(col7), STDDEV_SAMP(col8), + STDDEV_SAMP(col9), STDDEV_SAMP(col10) +FROM t_innodb; + +SELECT STDDEV_SAMP(col1), STDDEV_SAMP(col2), STDDEV_SAMP(col3), STDDEV_SAMP(col4), + STDDEV_SAMP(col5), STDDEV_SAMP(col6), STDDEV_SAMP(col7), STDDEV_SAMP(col8), + STDDEV_SAMP(col9), STDDEV_SAMP(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT STDDEV_SAMP(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 15. SUM() +--echo -------------------------- + +SELECT SUM(col1), SUM(col2), SUM(col3), SUM(col4), + SUM(col5), SUM(col6), SUM(col7), SUM(col8), + SUM(col9), SUM(col10) +FROM t_innodb; + +SELECT SUM(col1), SUM(col2), SUM(col3), SUM(col4), + SUM(col5), SUM(col6), SUM(col7), SUM(col8), + SUM(col9), SUM(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT SUM(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 16. VAR_POP() +--echo -------------------------- + +SELECT VAR_POP(col1), VAR_POP(col2), VAR_POP(col3), VAR_POP(col4), + VAR_POP(col5), VAR_POP(col6), VAR_POP(col7), VAR_POP(col8), + VAR_POP(col9), VAR_POP(col10) +FROM t_innodb; + +SELECT VAR_POP(col1), VAR_POP(col2), VAR_POP(col3), VAR_POP(col4), + VAR_POP(col5), VAR_POP(col6), VAR_POP(col7), VAR_POP(col8), + VAR_POP(col9), VAR_POP(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT VAR_POP(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 17. VAR_SAMP() +--echo -------------------------- + +SELECT VAR_SAMP(col1), VAR_SAMP(col2), VAR_SAMP(col3), VAR_SAMP(col4), + VAR_SAMP(col5), VAR_SAMP(col6), VAR_SAMP(col7), VAR_SAMP(col8), + VAR_SAMP(col9), VAR_SAMP(col10) +FROM t_innodb; + +SELECT VAR_SAMP(col1), VAR_SAMP(col2), VAR_SAMP(col3), VAR_SAMP(col4), + VAR_SAMP(col5), VAR_SAMP(col6), VAR_SAMP(col7), VAR_SAMP(col8), + VAR_SAMP(col9), VAR_SAMP(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT VAR_SAMP(col11) +FROM t_duckdb WHERE id <= 3; + +--echo -------------------------- +--echo 18. VARIANCE() +--echo -------------------------- + +SELECT VARIANCE(col1), VARIANCE(col2), VARIANCE(col3), VARIANCE(col4), + VARIANCE(col5), VARIANCE(col6), VARIANCE(col7), VARIANCE(col8), + VARIANCE(col9), VARIANCE(col10) +FROM t_innodb; + +SELECT VARIANCE(col1), VARIANCE(col2), VARIANCE(col3), VARIANCE(col4), + VARIANCE(col5), VARIANCE(col6), VARIANCE(col7), VARIANCE(col8), + VARIANCE(col9), VARIANCE(col10) +FROM t_duckdb; + +# AVG(Blob) is not supported yet. +--error ER_GET_ERRMSG +SELECT VARIANCE(col11) +FROM t_duckdb WHERE id <= 3; + +DROP TABLE t_innodb; +DROP TABLE t_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption-master.opt b/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption-master.opt new file mode 100644 index 0000000000000..d1f7ad8f47858 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption-master.opt @@ -0,0 +1,5 @@ +--early-plugin-load="keyring_file=$KEYRING_PLUGIN" +--keyring_file_data=$MYSQL_TMP_DIR/mysecret_keyring +$KEYRING_PLUGIN_OPT +--default-table-encryption=OFF +--table-encryption-privilege-check=OFF \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption.test new file mode 100644 index 0000000000000..8f7ba7574ed86 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_allow_encryption.test @@ -0,0 +1,60 @@ + +--disable_warnings +DROP DATABASE IF EXISTS encryption_test; +--enable_warnings +CREATE DATABASE encryption_test; +USE encryption_test; + +--echo # +--echo # Test: create duckdb table with encryption option +--echo # +CREATE TABLE string_table ( + id INT PRIMARY KEY, + bool_field BOOLEAN, + decimal_field DECIMAL(10,2), + enum_field ENUM('small', 'medium', 'large'), + set_field SET('red', 'green', 'blue'), + varchar_field VARCHAR(255), + json_field JSON , + text_field TEXT, + new_decimal_field DECIMAL(20,6) +) ENGINE=DuckDB ENCRYPTION = 'Y'; +INSERT INTO string_table VALUES (1, TRUE, 123.45, 'small', 'red,green', 'varchar_sample','{"name": "Alice", "age": 30}', 'This is a sample text.', 123456.789012); +INSERT INTO string_table VALUES (2, NULL, NULL, NULL, NULL, NULL, NULL,NULL,NULL); +INSERT INTO string_table VALUES (3, FALSE, 100.00, 'medium', 'red,blue', 'varchar_final', '{"animal": "dog", "legs": 4}', 'End of this test text.', 778899.001122); +SHOW CREATE TABLE string_table; +CHECKSUM TABLE string_table; + +--echo # +--echo # alter table to innodb and check whether innodb is encrypted +--echo # +ALTER TABLE string_table ENGINE = InnoDB; +SHOW CREATE TABLE string_table; +CHECKSUM TABLE string_table; + +--echo # +--echo # Test: create innodb table with encryption option +--echo # +CREATE TABLE blob_table ( + id INT PRIMARY KEY, + tiny_blob_field TINYBLOB, + medium_blob_field MEDIUMBLOB, + long_blob_field LONGBLOB, + blob_field BLOB, + bit_field BIT(64) +) ENGINE=InnoDB ENCRYPTION = 'Y'; +INSERT INTO blob_table VALUES (1, 'tinyblob_data', 'mediumblob_data', 'longblob_data', 'blob_data', b'00000001'); +INSERT INTO blob_table VALUES (2, NULL, NULL, 'longblob_data3', NULL, b'00000010'); +INSERT INTO blob_table VALUES (3, 'tinyblob_data4', 'mediumblob_data4', NULL, 'blob_data2', NULL); +SHOW CREATE TABLE blob_table; +CHECKSUM TABLE blob_table; + +--echo # +--echo # Test: alter table to duckdb and check whether duckdb is encrypted +--echo # +ALTER TABLE blob_table ENGINE = DuckDB; +SHOW CREATE TABLE blob_table; +CHECKSUM TABLE blob_table; + + +DROP DATABASE encryption_test; \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine-master.opt b/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine-master.opt new file mode 100644 index 0000000000000..9a966f9160e2e --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine-master.opt @@ -0,0 +1 @@ +--max_allowed_packet=128M diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine.test new file mode 100644 index 0000000000000..80ef329792733 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_alter_table_engine.test @@ -0,0 +1,279 @@ +--source ../include/have_duckdb.inc + +--echo # +--echo # Test DDL: alter table engine = innodb from duckdb tables +--echo # check whether changing storage engine from duckdb to innodb is supported +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS alter_engine_test; +--enable_warnings +CREATE DATABASE alter_engine_test; +USE alter_engine_test; + +--echo # +--echo # 1. test blob types +--echo # +CREATE TABLE blob_table ( + id INT PRIMARY KEY, + tiny_blob_field TINYBLOB, + medium_blob_field MEDIUMBLOB, + long_blob_field LONGBLOB, + blob_field BLOB, + bit_field BIT(64) +) ENGINE=DUCKDB; +INSERT INTO blob_table VALUES (1, 'tinyblob_data', 'mediumblob_data', 'longblob_data', 'blob_data', b'00000001'); +INSERT INTO blob_table VALUES (2, 'tinyblob_data2', 'mediumblob_data2', 'longblob_data2', 'blob_data2', b'00000010'); +INSERT INTO blob_table VALUES (3, NULL, NULL, 'longblob_data3', NULL, b'00000010'); +INSERT INTO blob_table VALUES (4, 'tinyblob_data4', 'mediumblob_data4', NULL, 'blob_data2', NULL); +INSERT INTO blob_table VALUES (5, 'tinyblob_data5', 'mediumblob_data5', 'longblob_data5', 'blob_data5', b'00000011'); + +SHOW CREATE TABLE blob_table; +SELECT id, tiny_blob_field, medium_blob_field, long_blob_field, blob_field, hex(bit_field) FROM blob_table; +CHECKSUM TABLE blob_table; +ALTER TABLE blob_table ENGINE = InnoDB; +SHOW CREATE TABLE blob_table; +CHECKSUM TABLE blob_table; +ALTER TABLE blob_table ENGINE = DUCKDB; +SHOW CREATE TABLE blob_table; +CHECKSUM TABLE blob_table; +ALTER TABLE blob_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE blob_table; +CHECKSUM TABLE blob_table; + + +--echo # +--echo # 2. test string types +--echo # +CREATE TABLE string_table ( + id INT PRIMARY KEY, + null_field VARCHAR(255), + bool_field BOOLEAN, + decimal_field DECIMAL(10,2), + enum_field ENUM('small', 'medium', 'large'), + set_field SET('red', 'green', 'blue'), + string_field VARCHAR(255), + var_string_field VARCHAR(255), + varchar_field VARCHAR(255), + json_field JSON , + text_field TEXT, + new_decimal_field DECIMAL(20,6) +) ENGINE=DUCKDB; +INSERT INTO string_table VALUES (1, NULL, TRUE, 123.45, 'small', 'red,green', 'sample_string', 'var_string_sample', 'varchar_sample','{"name": "Alice", "age": 30}', 'This is a sample text.', 123456.789012); +INSERT INTO string_table VALUES (2, 'another_value', FALSE, 678.90, 'medium', 'blue', 'another_string', 'another_var_string', NULL, NULL, NULL, 345678.901234); +INSERT INTO string_table VALUES (3, 'test_value', TRUE, 50.25, 'large', NULL, 'test_string', 'var_string_test', 'varchar_test', '{"city": "Beijing", "population": 2154}', 'Some additional text content.', 987654.321098); +INSERT INTO string_table VALUES (4, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL,NULL); +INSERT INTO string_table VALUES (5, NULL, FALSE, 999.99, 'small', 'red', 'another test', 'var_string_another', 'varchar_another', '{"country": "USA", "states": 50}', 'More sample text data.', NULL); +INSERT INTO string_table VALUES (6, 'final_value', TRUE, 100.00, 'medium', 'red,blue', 'final_string', 'var_string_final', 'varchar_final', '{"animal": "dog", "legs": 4}', 'End of this test text.', 778899.001122); + +SHOW CREATE TABLE string_table; +SELECT * FROM string_table; +CHECKSUM TABLE string_table; +ALTER TABLE string_table ENGINE = InnoDB; +SHOW CREATE TABLE string_table; +CHECKSUM TABLE string_table; +ALTER TABLE string_table ENGINE = DUCKDB; +SHOW CREATE TABLE string_table; +CHECKSUM TABLE string_table; +ALTER TABLE string_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE string_table; +CHECKSUM TABLE string_table; + + +--echo # +--echo # 3. test numeric types +--echo # +CREATE TABLE numeric_table ( + id INT PRIMARY KEY, + tinyint_field TINYINT, + smallint_field SMALLINT, + mediumint_field MEDIUMINT, + int_field INT, + bigint_field BIGINT, + float_field FLOAT, + double_field DOUBLE, + decimal_field DECIMAL(20,10) +)ENGINE=DUCKDB; +INSERT INTO numeric_table VALUES +(1, 127, 32767, 8388607, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 123456789.1234567890), +(2, -128, -32768, -8388608, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -1234501234.1234567890), +(3, NULL, 0, 0, 0, 0, 0.0, 0.0,NULL), +(4, 1, 2, NULL, 4, 1844674407370955161, NULL, 2.2, 1234567890.1234567890), +(5, -1, -2, -3, -4, -1844674407370955161, NULL, -2.2, -1234567890.1234567890); + +SHOW CREATE TABLE numeric_table; +SELECT * FROM numeric_table; +CHECKSUM TABLE numeric_table; +ALTER TABLE numeric_table ENGINE = InnoDB; +SHOW CREATE TABLE numeric_table; +CHECKSUM TABLE numeric_table; +ALTER TABLE numeric_table ENGINE = DUCKDB; +SHOW CREATE TABLE numeric_table; +CHECKSUM TABLE numeric_table; +ALTER TABLE numeric_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE numeric_table; +CHECKSUM TABLE numeric_table; + + + +--echo # +--echo # 4. test unsigned numeric types +--echo # +CREATE TABLE unsigned_numeric_table ( + id INT PRIMARY KEY, + tinyint_field TINYINT UNSIGNED, + smallint_field SMALLINT UNSIGNED, + mediumint_field MEDIUMINT UNSIGNED, + int_field INT UNSIGNED, + bigint_field BIGINT UNSIGNED, + float_field FLOAT, + double_field DOUBLE, + decimal_field DECIMAL(20,10) +)ENGINE=DUCKDB; +--echo # Inserting test data into unsigned_numeric_table +INSERT INTO unsigned_numeric_table VALUES (1, 255, 65535, 16777215, 4294967295, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, '9999999999.9999999999'); +INSERT INTO unsigned_numeric_table VALUES (2, 254, 65534, 16777214, 4294967294, 18446744073709551614, 3.402823465E+38, NULL, '9999999998.9999999998'); +INSERT INTO unsigned_numeric_table VALUES (3, 128, 32768, 8388607, 2147483647, 9223372036854775807, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); +INSERT INTO unsigned_numeric_table VALUES (4, NULL, 32767, NULL, 2147483647, NULL, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); +INSERT INTO unsigned_numeric_table VALUES (5, 127, 32767, NULL, NULL, NULL, 123456789.123456789, 9876543210.0123456789, '1234567890.0123456789'); + +SHOW CREATE TABLE unsigned_numeric_table; +SELECT * FROM unsigned_numeric_table; +CHECKSUM TABLE unsigned_numeric_table; +ALTER TABLE unsigned_numeric_table ENGINE = InnoDB; +SHOW CREATE TABLE unsigned_numeric_table; +CHECKSUM TABLE unsigned_numeric_table; +ALTER TABLE unsigned_numeric_table ENGINE = DUCKDB; +SHOW CREATE TABLE unsigned_numeric_table; +CHECKSUM TABLE unsigned_numeric_table; +ALTER TABLE unsigned_numeric_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE unsigned_numeric_table; +CHECKSUM TABLE unsigned_numeric_table; + + +--echo # +--echo # 5. test date types +--echo # +CREATE TABLE date_table ( + id INT PRIMARY KEY, + date_field DATE, + datetime_field DATETIME, + timestamp_field TIMESTAMP, + time_field TIME +)ENGINE=DUCKDB; +INSERT INTO date_table VALUES +(1, '2023-01-01', '2023-01-01 12:00:00', '2023-01-01 12:00:00', '12:00:00'), +(2, '1999-12-31', '1999-12-31 23:59:59', '1999-12-31 23:59:59', '23:59:59'), +(3, NULL, NULL, NULL, '00:00:00'), +(4, '2024-02-29', '2024-02-29 00:00:00', '2024-02-29 00:00:00', '00:00:00'), +(5, '2023-04-05', NULL, '2023-04-05 14:30:45', NULL), +(6, '2023-04-05', '2023-04-05 14:30:45', '2023-04-05 14:30:45', '14:30:45'); + +SHOW CREATE TABLE date_table; +SELECT * FROM date_table; +CHECKSUM TABLE date_table; +ALTER TABLE date_table ENGINE = InnoDB; +SHOW CREATE TABLE date_table; +CHECKSUM TABLE date_table; +ALTER TABLE date_table ENGINE = DUCKDB; +SHOW CREATE TABLE date_table; +CHECKSUM TABLE date_table; +ALTER TABLE date_table ENGINE = DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE date_table; +CHECKSUM TABLE date_table; + + + +--echo # +--echo # 6. test large field +--echo # +CREATE TABLE large_field_table1 ( + id INT PRIMARY KEY, + tiny_blob_field TINYBLOB, + blob_field BLOB, + medium_blob_field MEDIUMBLOB, + long_blob_field LONGBLOB +) ENGINE=DUCKDB; +INSERT INTO large_field_table1 values(1, 'a', 'b', 'ccc', repeat('d', 1024 * 1024)); +INSERT INTO large_field_table1 values(2, repeat('a', 255), repeat('b', 65535), repeat('c',16777215), repeat('d', 67108864)); +INSERT INTO large_field_table1 values(3, 'aa', 'bb', 'ccc', repeat('d', 1024 * 1024)); +INSERT INTO large_field_table1 values(4, repeat('a', 254), repeat('b', 65533), repeat('c',8000000), repeat('d', 67108864)); +INSERT INTO large_field_table1 values(5, 'aa', 'bb', 'ccc', repeat('d', 1024 * 1024)); + +SHOW CREATE TABLE large_field_table1; +CHECKSUM TABLE large_field_table1; +ALTER TABLE large_field_table1 ENGINE=INNODB; +SHOW CREATE TABLE large_field_table1; +CHECKSUM TABLE large_field_table1; +ALTER TABLE large_field_table1 ENGINE=DUCKDB; +SHOW CREATE TABLE large_field_table1; +CHECKSUM TABLE large_field_table1; +ALTER TABLE large_field_table1 ENGINE=DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE large_field_table1; +CHECKSUM TABLE large_field_table1; + + +CREATE TABLE large_field_table2 ( + id INT PRIMARY KEY, + bit_field BIT(64), + char_field CHAR(255), + string_field VARCHAR(8192), + json_field JSON, + text_field TEXT +) ENGINE=DUCKDB; +SET @long_str = repeat('a',67107864); +SET @json_val = JSON_OBJECT('key1', 'static_value', 'key2', @long_str); +INSERT INTO large_field_table2 values(1, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); +INSERT INTO large_field_table2 values(2, X'FFFFFFFFFFFFFFFF', repeat('C', 255), repeat('D',8192), @json_val, repeat('E', 65535)); +INSERT INTO large_field_table2 values(3, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); +INSERT INTO large_field_table2 values(4, X'FFFFFFFFFFFFFFFF', repeat('w', 255), repeat('v',4096), @json_val, repeat('o', 65535)); +INSERT INTO large_field_table2 values(5, X'ff', 'aaa', 'bbbbb', '{"country": "USA", "states": 50}', 'eeeee'); + +SHOW CREATE TABLE large_field_table2; +CHECKSUM TABLE large_field_table2; +ALTER TABLE large_field_table2 ENGINE=INNODB; +SHOW CREATE TABLE large_field_table2; +CHECKSUM TABLE large_field_table2; +ALTER TABLE large_field_table2 ENGINE=DUCKDB; +SHOW CREATE TABLE large_field_table2; +CHECKSUM TABLE large_field_table2; +ALTER TABLE large_field_table2 ENGINE=DUCKDB, ALGORITHM = COPY; +SHOW CREATE TABLE large_field_table2; +CHECKSUM TABLE large_field_table2; + + +--echo # +--echo # 7. test CHECKSUM +--echo # special case: when NULL in fields with default value +--echo # +CREATE TABLE `t1` ( + `id` bigint NOT NULL, + `c0` varchar(20) NOT NULL, + `c1` char(10) DEFAULT 'duckdb', + `c2` decimal(5,2) DEFAULT NULL, + `c3` datetime DEFAULT NULL, + `c4` timestamp NULL DEFAULT NULL, + `c5` blob, + PRIMARY KEY (`id`) +) ENGINE=InnoDB; + +CREATE TABLE `t2` ( + `id` bigint NOT NULL, + `c0` varchar(20) NOT NULL, + `c1` char(10) DEFAULT 'duckdb', + `c2` decimal(5,2) DEFAULT NULL, + `c3` datetime DEFAULT NULL, + `c4` timestamp NULL DEFAULT NULL, + `c5` blob, + PRIMARY KEY (`id`) +) ENGINE=DuckDB; +INSERT INTO t1 (id, c0, c1, c2, c3, c4, c5) VALUES (1, 'abc', NULL, 1.23, '2020-11-20', NULL, X'e18080ff8a'); +INSERT INTO t2 (id, c0, c1, c2, c3, c4, c5) VALUES (1, 'abc', NULL, 1.23, '2020-11-20', NULL, X'e18080ff8a'); + +CHECKSUM TABLE t1; +CHECKSUM TABLE t2; + + +SET @long_str = NULL; +SET @json_val = NULL; +DROP DATABASE IF EXISTS alter_engine_test; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_appender_allocator_flush_threshold.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_appender_allocator_flush_threshold.test new file mode 100644 index 0000000000000..3a154d82679a6 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_appender_allocator_flush_threshold.test @@ -0,0 +1,15 @@ +--source ../include/have_duckdb.inc + +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); + +SET GLOBAL duckdb_appender_allocator_flush_threshold = 1048576; +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); + +SET GLOBAL duckdb_appender_allocator_flush_threshold = 1048576 * 1024; +SHOW GLOBAL VARIABLES LIKE "duckdb_appender_allocator_flush_threshold"; +SELECT run_in_duckdb("FROM duckdb_settings() WHERE name = 'allocator_flush_threshold'"); + +SET GLOBAL duckdb_appender_allocator_flush_threshold = default; +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_bit_string.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_bit_string.test new file mode 100644 index 0000000000000..22e82e4c3e5e6 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_bit_string.test @@ -0,0 +1,33 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE t1 (id INT PRIMARY KEY, col1 VARCHAR(100)) ENGINE=DuckDB; +CREATE TABLE t2 (id INT PRIMARY KEY, col1 BLOB) ENGINE=DuckDB; + +INSERT INTO t1 VALUES (1, 'A'), (2, x'41'), (3, b'01000001'); +INSERT INTO t2 VALUES (1, 'A'), (2, x'41'), (3, b'01000001'), (4, 0xFF), (5, x'FF'), (6, b'11111111'); + +SELECT * FROM t1 WHERE col1 = 'A'; +SELECT * FROM t1 WHERE col1 = x'41'; +SELECT * FROM t1 WHERE col1 = b'01000001'; + +SELECT * FROM t2 WHERE col1 = 'A'; +SELECT * FROM t2 WHERE col1 = x'41'; +SELECT * FROM t2 WHERE col1 = b'01000001'; + +# Bit strings starting with 0x are not currently supported. +--error ER_GET_ERRMSG +SELECT id, hex(col1) FROM t2 WHERE col1 = 0xFF; + +SELECT id, hex(col1) FROM t2 WHERE col1 = x'FF'; +SELECT id, hex(col1) FROM t2 WHERE col1 = b'11111111'; + +DELETE FROM t1 WHERE col1 = x'41'; +SELECT * FROM t1; + +DELETE FROM t2 WHERE col1 = x'41'; +SELECT id, hex(col1) FROM t2; +DELETE FROM t2 WHERE col1 = x'FF'; +SELECT id, hex(col1) FROM t2; + +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_collate.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_collate.test new file mode 100644 index 0000000000000..c575cbc92015d --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_collate.test @@ -0,0 +1,50 @@ +--disable_query_log +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +--enable_query_log + +CREATE TABLE t1 (col1 VARCHAR(20) COLLATE utf8mb4_0900_ai_ci PRIMARY KEY) ENGINE=DuckDB; +INSERT INTO t1 VALUES ('B'), ('a'), ('b'); + +--echo # +--echo # 1) modify the scope of default_collation to local +--echo # + +connect (con1, localhost, root,,); +--echo [ connection default ] +--connection default +SET collation_connection = 'utf8mb4_0900_as_ci'; + +--echo [ connection con1 ] +--connection con1 +SELECT 'a' = 'A' FROM t1 LIMIT 1; + +--echo [ connection default ] +--connection default +SET collation_connection = 'utf8mb4_0900_as_cs'; + +--echo [ connection con1 ] +--connection con1 +SELECT 'a' = 'A' FROM t1 LIMIT 1; +SET collation_connection = 'utf8mb4_0900_as_ci'; +SELECT 'a' = 'A' FROM t1 LIMIT 1; +SET collation_connection = 'utf8mb4_0900_as_cs'; +SELECT 'a' = 'A' FROM t1 LIMIT 1; + +--echo # +--echo # 2) force_no_collation +--echo # + +--connection default +SET duckdb_force_no_collation = false; +SELECT * FROM t1 WHERE col1 = 'A'; +SELECT min(col1) FROM t1; +SELECT * FROM t1 ORDER BY col1; + +SET duckdb_force_no_collation = true; +SELECT * FROM t1 WHERE col1 = 'A'; +SELECT min(col1) FROM t1; +SELECT * FROM t1 ORDER BY col1; + +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_cte.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_cte.test new file mode 100644 index 0000000000000..b0210d0b55808 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_cte.test @@ -0,0 +1,44 @@ +CREATE DATABASE IF NOT EXISTS duckdb_cte; +USE duckdb_cte; + +CREATE TABLE duckdb_table (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +CREATE TABLE innodb_table (id INT PRIMARY KEY, col1 INT) ENGINE=InnoDB; +INSERT INTO duckdb_table VALUES (1, 1); +INSERT INTO innodb_table VALUES (1, 1); + +--echo +--echo (1) CTE +--echo + +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp; +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp, duckdb_table; +WITH tmp AS (SELECT MAX(col1) FROM duckdb_table) SELECT * FROM tmp, innodb_table; + +--echo +--echo (2) Recursive CTE +--echo + +WITH RECURSIVE tmp AS ( + SELECT 1 AS n + UNION ALL + SELECT n + 1 FROM tmp WHERE n < 3 +) +SELECT * FROM duckdb_table, tmp; + +WITH RECURSIVE tmp AS ( + SELECT 1 AS n + UNION ALL + SELECT n + 1 FROM tmp, duckdb_table WHERE n = duckdb_table.col1 +) +SELECT * FROM duckdb_table, tmp; + +WITH RECURSIVE tmp AS ( + SELECT 1 AS n + UNION ALL + SELECT n + 1 FROM tmp, innodb_table WHERE n = innodb_table.col1 +) +SELECT * FROM duckdb_table, tmp; + +DROP TABLE duckdb_table; +DROP TABLE innodb_table; +DROP DATABASE duckdb_cte; \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_db_table_strconvert.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_db_table_strconvert.test new file mode 100644 index 0000000000000..0d50b5a1c9bbe --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_db_table_strconvert.test @@ -0,0 +1,64 @@ +# Test for escape characters in the table name or database name. + +--echo # +--echo # 1) PREPARE +--echo # +CREATE DATABASE `my-db`; +USE `my-db`; +CREATE TABLE `my-table` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB; +INSERT INTO `my-table` (`id`, `name`) VALUES (1, 'John'), (2, 'Jane'), (3, 'Jim'); +--let $checksum_innodb_1 = query_get_value(CHECKSUM TABLE `my-table`, Checksum, 1) + + +--echo # +--echo # 2) ALTER TO DUCKDB +--echo # +ALTER TABLE `my-table` ENGINE = DuckDB; +--let $checksum_duckdb_1 = query_get_value(CHECKSUM TABLE `my-table`, Checksum, 1) +--let $assert_cond = "$checksum_duckdb_1" = "$checksum_innodb_1" +--let $assert_text= CHECKSUM is the same +--source include/assert.inc + +# DO RENAME +ALTER TABLE `my-table` RENAME TO `my-table-renamed`; +RENAME TABLE `my-table-renamed` TO `my-table`; + +# DO INPLACE/INSTANT DDL +ALTER TABLE `my-table` ADD COLUMN `col1` INT; + +# DO COPY DDL +ALTER TABLE `my-table` ADD COLUMN `col2` INT, ALGORITHM = COPY; + +# DO DML +SELECT COUNT(*) FROM `my-table`; +INSERT INTO `my-table` (`id`, `name`) VALUES (4, 'Joe'); +UPDATE `my-table` SET `name` = 'Jack' WHERE `id` = 4; +DELETE FROM `my-table` WHERE `id` = 4; + +SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'my-db'"); +SELECT run_in_duckdb("DESC `my-db`.`my-table`"); +--let $checksum_duckdb_2 = query_get_value(CHECKSUM TABLE `my-table`, Checksum, 1) + + +--echo # +--echo # 3) ALTER TO INNODB +--echo # +ALTER TABLE `my-table` ENGINE = InnoDB; +SELECT run_in_duckdb("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'my-db'"); +--let $checksum_innodb_2 = query_get_value(CHECKSUM TABLE `my-table`, Checksum, 1) +--let $assert_cond = "$checksum_duckdb_2" = "$checksum_innodb_2" +--let $assert_text= CHECKSUM is the same +--source include/assert.inc + + +--echo # +--echo # 4) CLEANUP +--echo # +DROP DATABASE `my-db`; +SELECT run_in_duckdb("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = 'my-db'"); + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_ddl_during_transaction.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_ddl_during_transaction.test new file mode 100644 index 0000000000000..b0146bf54cb86 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_ddl_during_transaction.test @@ -0,0 +1,71 @@ +--source ../include/have_duckdb.inc + +--echo ########################################################################### +--echo # Prepare +--echo ########################################################################### +CREATE TABLE t1 ( + id BIGINT NOT NULL DEFAULT 1, + c0 SMALLINT NOT NULL DEFAULT 2, + c1 VARCHAR(20) NOT NULL DEFAULT 'abc', + PRIMARY KEY (id) +) ENGINE=DuckDB; + +CREATE TABLE t2 ( + id BIGINT NOT NULL DEFAULT 1, + c0 SMALLINT NOT NULL DEFAULT 2, + c1 VARCHAR(20) NOT NULL DEFAULT 'abc', + PRIMARY KEY (id) +) ENGINE=DuckDB; + +SHOW CREATE TABLE t1; +SHOW CREATE TABLE t2; + +INSERT INTO t1 VALUES (3, 4, 'zzz'), (5, 6, 'xxx'); +SELECT * FROM t1; + +--connect(con1,localhost,root) +--connect(con2,localhost,root) + +--echo ########################################################################### +--echo # Test executing DDL during a DuckDB transaction +--echo ########################################################################### +--echo # 1. Test for DuckDB Appender +--connection con1 +BEGIN; +INSERT INTO t2 (id) VALUES (7); + +--connection con2 +ALTER TABLE t1 DROP COLUMN c0; + +--connection con1 +CALL mtr.add_suppression("Call to EndRow before all columns have been appended to"); +--error ER_GET_ERRMSG +INSERT INTO t1 (c1) VALUES ('xyz'); +--error ER_GET_ERRMSG +COMMIT; + +SELECT * FROM t1; + +--let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.1.err +--let $assert_text = Check DuckDB Warning +--let $assert_select = Call to EndRow before all columns have been appended to +--let $assert_count = 1 +--source include/assert_grep.inc + +--echo # 2. Test for SELECT +--connection con1 +BEGIN; +SELECT * FROM t2; + +--connection con2 +ALTER TABLE t1 DROP COLUMN c1, ADD COLUMN c2 DATE; + +--connection con1 +--error ER_GET_ERRMSG +SELECT * FROM t1; +COMMIT; + +--echo ########################################################################### +--echo # Cleanup +--echo ########################################################################### +DROP TABLE t1, t2; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_fix_sql.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_fix_sql.test new file mode 100644 index 0000000000000..a005b8027034d --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_fix_sql.test @@ -0,0 +1,305 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE fake_innodb_table (id INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO fake_innodb_table VALUES (0); +CREATE TABLE fake_duckdb_table (id INT PRIMARY KEY) ENGINE=DuckDB; +INSERT INTO fake_duckdb_table VALUES (0); + +# Modify the default value of the bit_xxx aggregate function +# when there is no data in the table +CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; +SELECT BIT_AND(id), BIT_OR(id), BIT_XOR(id) FROM t1; +ALTER TABLE t1 ENGINE = DuckDB; +SELECT BIT_AND(id), BIT_OR(id), BIT_XOR(id) FROM t1; +DROP TABLE t1; + +# Fix microsecond function +SELECT microsecond(TIME '12:12:12.123456') FROM fake_innodb_table; +SELECT microsecond(TIME '12:12:12.123456') FROM fake_duckdb_table; + +# Fix the behavior of least function when there is a null value in it +SELECT LEAST('a', NULL) FROM fake_innodb_table; +SELECT LEAST('a', NULL) FROM fake_duckdb_table; + +# Fix the behavior of oct/bin/hex function when the argument is float/varchar +SELECT oct(123.123) FROM fake_innodb_table; +SELECT oct(123.123) FROM fake_duckdb_table; +SELECT oct('123.123a') FROM fake_innodb_table; +SELECT oct('123.123a') FROM fake_duckdb_table; +SELECT bin(123.123) FROM fake_innodb_table; +SELECT bin(123.123) FROM fake_duckdb_table; +SELECT bin('123.123a') FROM fake_innodb_table; +SELECT bin('123.123a') FROM fake_duckdb_table; +SELECT hex(123.123) FROM fake_innodb_table; +SELECT hex(123.123) FROM fake_duckdb_table; +SELECT hex('123.123a') FROM fake_innodb_table; +SELECT hex('123.123a') FROM fake_duckdb_table; + +# Fix the behavior of left/right function when the argument len is less than 0 +SELECT left('abc', -1) FROM fake_innodb_table; +SELECT left('abc', -1) FROM fake_duckdb_table; +SELECT right('abc', -1) FROM fake_innodb_table; +SELECT right('abc', -1) FROM fake_duckdb_table; + +# Fix the behavior of trim function when the argument remstr is a string +SELECT trim('ab' from 'aaab') FROM fake_innodb_table; +SELECT trim('ab' from 'aaab') FROM fake_duckdb_table; + +# Add a type conversion function from timestamptz to time +CREATE TABLE t1 (id TIMESTAMP PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES('2020-01-01 12:12:12.123'); +SELECT CONVERT(id, TIME) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CONVERT(id, TIME) FROM t1; +DROP TABLE t1; + +# Fix time_to_sec(TIMESTAMP) function +SELECT time_to_sec('2020-01-01 12:12:12') FROM fake_innodb_table; +SELECT time_to_sec('2020-01-01 12:12:12') FROM fake_duckdb_table; + +# Fix strcmp function +SELECT strcmp('a', NULL) FROM fake_innodb_table; +SELECT strcmp('a', NULL) FROM fake_duckdb_table; + +# Fix substring_index function +SELECT substring_index(NULL, 'ab', 2) FROM fake_innodb_table; +SELECT substring_index(NULL, 'ab', 2) FROM fake_duckdb_table; +SELECT substring_index('ddabddabcc', NULL, 2) FROM fake_innodb_table; +SELECT substring_index('ddabddabcc', NULL, 2) FROM fake_duckdb_table; +SELECT substring_index('ddabddabcc', 'ab', NULL) FROM fake_innodb_table; +SELECT substring_index('ddabddabcc', 'ab', NULL) FROM fake_duckdb_table; +SELECT substring_index('ddabddabcc', 'ab', 2) FROM fake_innodb_table; +SELECT substring_index('ddabddabcc', 'ab', 2) FROM fake_duckdb_table; + +# Fix weekday(TIMESTAMPTZ) function +CREATE TABLE t1 (id TIMESTAMP PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('2020-01-01 12:12:12'); +SELECT weekday(id) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT weekday(id) FROM t1; +DROP TABLE t1; + +# Modify type conversion function from varchar to time +SELECT CONVERT('2020-01-01', TIME) FROM fake_innodb_table; +SELECT CONVERT('2020-01-01', TIME) FROM fake_duckdb_table; + +# Modify type conversion function from varchar to date +SELECT CONVERT('20-01-01', DATE) FROM fake_innodb_table; +SELECT CONVERT('20-01-01', DATE) FROM fake_duckdb_table; +SELECT CONVERT('90-01-01', DATE) FROM fake_innodb_table; +SELECT CONVERT('90-01-01', DATE) FROM fake_duckdb_table; + +# Fix the behavior of rollup operator when there is no data in table +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT) ENGINE=InnoDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +DROP TABLE t1; + +# Fix the behavior of DIV operator when the argument is float +SELECT 5.6 DIV 2.2 FROM fake_innodb_table; +SELECT 5.6 DIV 2.2 FROM fake_duckdb_table; + +# Fix the behavior of concat function when there is null in argument +SELECT concat('a', NULL) FROM fake_innodb_table; +SELECT concat('a', NULL) FROM fake_duckdb_table; + +# Fix the behavior of like operator +CREATE TABLE t1 (id VARCHAR(20) PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES ('abc\\b'); +SELECT id LIKE 'abc\\b', id LIKE 'abc\\\\b' FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id LIKE 'abc\\b', id LIKE 'abc\\\\b' FROM t1; +DROP TABLE t1; + +# Fix the behavior of substring function when argument offset is zero +SELECT substring('abcde', 0, 2) FROM fake_innodb_table; +SELECT substring('abcde', 0, 2) FROM fake_duckdb_table; + +# Fix the behavior of position/instr function when the argument str is nocase +CREATE TABLE t1 (id VARCHAR(20) collate utf8mb3_general_ci PRIMARY KEY); +INSERT INTO t1 VALUES ('abc'); +SELECT position('C' IN id) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT position('C' IN id) FROM t1; +DROP TABLE t1; + +CREATE TABLE t1 (id VARCHAR(20) collate utf8mb3_bin PRIMARY KEY); +INSERT INTO t1 VALUES ('abc'); +SELECT position('C' IN id) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT position('C' IN id) FROM t1; +DROP TABLE t1; + +# Modify type conversion function from varchar to double +SELECT CONVERT('', DOUBLE), CONVERT('123.123a', DOUBLE), CONVERT('a123', DOUBLE), CONVERT('1_23', DOUBLE) FROM fake_innodb_table; +SELECT CONVERT('', DOUBLE), CONVERT('123.123a', DOUBLE), CONVERT('a123', DOUBLE), CONVERT('1_23', DOUBLE) FROM fake_duckdb_table; + +# Disable DUPLICATE_GROUPS Optimization +CREATE TABLE t1 (col1 INT PRIMARY KEY, col2 INT) ENGINE=InnoDB; +CREATE TABLE t2 (col3 INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (1); +SELECT col1, col2, col3 FROM t1 JOIN t2 ON t1.col1 = t2.col3 GROUP BY col1, col2, col3 WITH ROLLUP ORDER BY 1, 2, 3; +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT col1, col2, col3 FROM t1 JOIN t2 ON t1.col1 = t2.col3 GROUP BY col1, col2, col3 WITH ROLLUP ORDER BY 1, 2, 3; +DROP TABLE t1; +DROP TABLE t2; + +# Modify type conversion function from ... to char(width) +SELECT CONVERT('abcde', CHAR(3)) FROM fake_innodb_table; +SELECT CONVERT('abcde', CHAR(3)) FROM fake_duckdb_table; + +# Modify type conversion function from ... to decimal +SELECT CONVERT(12345.12345, DECIMAL) FROM fake_innodb_table; +SELECT CONVERT(12345.12345, DECIMAL) FROM fake_duckdb_table; + +# Fix the behavior of union two column with different collation +CREATE TABLE t1(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +CREATE TABLE t2(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +INSERT INTO t1 VALUES ('A'); +INSERT INTO t2 VALUES ('a'); +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +DROP TABLE t1; +DROP TABLE t2; + +CREATE TABLE t1(id VARCHAR(20) COLLATE utf8mb3_bin PRIMARY KEY); +CREATE TABLE t2(id VARCHAR(20) COLLATE utf8mb3_general_ci PRIMARY KEY); +INSERT INTO t1 VALUES ('A'); +INSERT INTO t2 VALUES ('a'); +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT COUNT(*) FROM (SELECT * FROM t1 UNION SELECT * FROM t2) d; +DROP TABLE t1; +DROP TABLE t2; + +# NOCASE.NOACCENT +CREATE TABLE t1(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_ai_ci) ENGINE=InnoDB; +# NOACCENT +CREATE TABLE t2(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_as_ci) ENGINE=InnoDB; +# POSIX +CREATE TABLE t3(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_0900_as_cs) ENGINE=InnoDB; +# POSIX +CREATE TABLE t4(id INT PRIMARY KEY, col1 VARCHAR(20) COLLATE utf8mb4_bin) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t2 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t3 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +INSERT INTO t4 VALUES (1, 'a'), (2, 'A'), (3, 'ä'); +SELECT * FROM t1 d1, t1 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t2 d1, t2 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t3 d1, t3 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t4 d1, t4 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t1 d1, (SELECT * FROM t1 UNION SELECT * FROM t1) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t2 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t3 d1, (SELECT * FROM t3 UNION SELECT * FROM t3) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t4 d1, (SELECT * FROM t4 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +# Union or comparison of columns with different collations is not allowed unless one of the collation is bin. +--error ER_CANT_AGGREGATE_2COLLATIONS +SELECT * FROM t1, t2 WHERE t1.col1 = t2.col1; +--error ER_CANT_AGGREGATE_NCOLLATIONS +SELECT * FROM t1 UNION SELECT * FROM t2; +--error ER_CANT_AGGREGATE_2COLLATIONS +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1; + +SELECT * FROM t1, t4 WHERE t1.col1 = t4.col1 ORDER BY t1.id, t4.id; +SELECT * FROM t1 UNION SELECT * FROM t4 ORDER BY id, col1; +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; + +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +ALTER TABLE t3 ENGINE=DuckDB; +ALTER TABLE t4 ENGINE=DuckDB; +SELECT * FROM t1 d1, t1 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t2 d1, t2 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t3 d1, t3 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t4 d1, t4 d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t1 d1, (SELECT * FROM t1 UNION SELECT * FROM t1) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t2 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t3 d1, (SELECT * FROM t3 UNION SELECT * FROM t3) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +SELECT * FROM t4 d1, (SELECT * FROM t4 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; +--error ER_CANT_AGGREGATE_2COLLATIONS +SELECT * FROM t1, t2 WHERE t1.col1 = t2.col1; +--error ER_CANT_AGGREGATE_NCOLLATIONS +SELECT * FROM t1 UNION SELECT * FROM t2; +--error ER_CANT_AGGREGATE_2COLLATIONS +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t2) d2 WHERE d1.col1 = d2.col1; + +SELECT * FROM t1, t4 WHERE t1.col1 = t4.col1 ORDER BY t1.id, t4.id; +SELECT * FROM t1 UNION SELECT * FROM t4 ORDER BY id, col1; +SELECT * FROM t1 d1, (SELECT * FROM t2 UNION SELECT * FROM t4) d2 WHERE d1.col1 = d2.col1 ORDER BY d1.id, d2.id; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +DROP TABLE t4; + +# Fix the behavior of aggregate(column order by numeric) +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 2), (2, 1); +SELECT GROUP_CONCAT(col1 ORDER BY 1) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT GROUP_CONCAT(col1 ORDER BY 1) FROM t1; +DROP TABLE t1; + +# Fix the behavior of case when ... then ... else ... end +SELECT CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END FROM fake_innodb_table; +SELECT CASE WHEN 1 = 2 THEN 1 WHEN 1 = 1 THEN '1a' ELSE NULL END FROM fake_duckdb_table; +CREATE TABLE t1 (id INT PRIMARY KEY); +INSERT INTO t1 VALUES (1); +SELECT CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CASE WHEN 1 = 2 THEN id WHEN 1 = 1 THEN '1a' ELSE NULL END FROM t1; +DROP TABLE t1; + +# Fix the behavior of COALESCE/IFNULL +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1), (2, NULL); +SELECT IFNULL(col1, '1a') FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT IFNULL(col1, '1a') FROM t1; +DROP TABLE t1; + +# Fix the behavior of JOIN ... USING ... +CREATE TABLE t1 (id1 INT PRIMARY KEY, col1 INT); +CREATE TABLE t2 (id2 INT PRIMARY KEY, col1 INT); +SELECT * FROM t1 JOIN t2 USING(col1); +SELECT * FROM t1 LEFT JOIN t2 USING(col1); +SELECT * FROM t1 RIGHT JOIN t2 USING(col1); +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 JOIN t2 USING(col1); +SELECT * FROM t1 LEFT JOIN t2 USING(col1); +SELECT * FROM t1 RIGHT JOIN t2 USING(col1); +DROP TABLE t1; +DROP TABLE t2; + +# Modify type conversion function from double to varchar +CREATE TABLE t1 (id INT PRIMARY KEY, col1 DOUBLE); +INSERT INTO t1 VALUES (1, 1e30); +INSERT INTO t1 VALUES (2, 0); +SELECT CONVERT(id, CHAR) FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CONVERT(id, CHAR) FROM t1; +DROP TABLE t1; + + +DROP TABLE fake_innodb_table; +DROP TABLE fake_duckdb_table; + +# Modify type conversion function from blob to varchar +CREATE TABLE t1 (id INT PRIMARY KEY, col1 BLOB); +INSERT INTO t1 VALUES (1, 0xFF), (2, 0x61), (3, 0xE695B0E68DAEE5BA93); +SELECT CAST(col1 AS CHAR) FROM t1; +SELECT CAST(col1 AS CHAR) like '%据%' FROM t1; +SELECT FROM_BASE64(TO_BASE64('数据库')), CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' FROM t1 LIMIT 1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT CAST(col1 AS CHAR) FROM t1; +SELECT CAST(col1 AS CHAR) like '%据%' FROM t1; +SELECT FROM_BASE64(TO_BASE64('数据库')), CAST(FROM_BASE64(TO_BASE64('数据库')) AS CHAR) LIKE '%据%' FROM t1 LIMIT 1; + +DROP TABLE t1; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_json.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_json.test new file mode 100644 index 0000000000000..f607d971e4fa1 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_json.test @@ -0,0 +1,342 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE t1_innodb (id INT PRIMARY KEY, col1 JSON) ENGINE=InnoDB; +CREATE TABLE t1_duckdb (id INT PRIMARY KEY, col1 JSON) ENGINE=DuckDB; +CREATE TABLE t2_innodb (id INT PRIMARY KEY, col1 VARCHAR(200)) ENGINE=InnoDB; +CREATE TABLE t2_duckdb (id INT PRIMARY KEY, col1 VARCHAR(200)) ENGINE=DuckDB; + +--echo +--echo 1. JSON_ARRAY +--echo + +INSERT INTO t1_innodb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t1_duckdb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t2_innodb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +INSERT INTO t2_duckdb VALUES (1, JSON_OBJECT("test", JSON_ARRAY(1, 1.2, "json", true))); +SELECT * FROM t1_innodb; +SELECT * FROM t1_duckdb; +SELECT t1_innodb.col1 = t2_innodb.col1 FROM t1_innodb, t2_innodb; +--error ER_GET_ERRMSG +SELECT t1_duckdb.col1 = t2_duckdb.col1 FROM t1_duckdb, t2_duckdb; + +--echo +--echo 2. JSON_ARRAY_APPEND() +--echo + +--echo +--echo 3. JSON_ARRAY_INSERT() +--echo + +--echo +--echo 4. JSON_CONTAINS() +--echo + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.b') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c.d') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '{"d": 4}', '$.c') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[1, 2]', '$.c.e') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '[3, 1]', '$.c.e') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": 1, "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.c.e') FROM t1_duckdb; + +SELECT JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_innodb; +SELECT JSON_CONTAINS('{"a": [1], "b": 2, "c": {"d": 4, "e":[1, 2, 3]}}', '1', '$.a') FROM t1_duckdb; + +--echo +--echo 5. JSON_CONTAINS_PATH() +--echo + +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') FROM t1_innodb; +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a', '$.e') FROM t1_duckdb; + +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') FROM t1_innodb; +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'all', '$.a', '$.e') FROM t1_duckdb; + +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') FROM t1_innodb; +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.c.d') FROM t1_duckdb; + +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') FROM t1_innodb; +SELECT JSON_CONTAINS_PATH('{"a": 1, "b": 2, "c": {"d": 4}}', 'one', '$.a.d') FROM t1_duckdb; + +--echo +--echo 6. JSON_DEPTH() +--echo + +SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true') FROM t1_innodb; +SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true') FROM t1_duckdb; + +SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]') FROM t1_innodb; +SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]') FROM t1_duckdb; + +SELECT JSON_DEPTH('[10, {"a": 20}]') FROM t1_innodb; +SELECT JSON_DEPTH('[10, {"a": 20}]') FROM t1_duckdb; + +--echo +--echo 7. JSON_EXTRACT() +--echo + +SELECT JSON_EXTRACT(col1, '$.test[0]') = 1 FROM t1_innodb; +SELECT JSON_EXTRACT(col1, '$.test[1]') = 1.2 FROM t1_innodb; +SELECT JSON_EXTRACT(col1, '$.test[2]') = 'json' FROM t1_innodb; + +SELECT JSON_EXTRACT(col1, '$.test[0]') = 1 FROM t1_duckdb; +SELECT JSON_EXTRACT(col1, '$.test[1]') = 1.2 FROM t1_duckdb; +SELECT JSON_EXTRACT(col1, '$.test[2]') = 'json' FROM t1_duckdb; + +--echo +--echo 8. JSON_INSERT() +--echo + +--echo +--echo 9. JSON_KEYS() +--echo + +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}') FROM t1_innodb; +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}') FROM t1_duckdb; + +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') FROM t1_innodb; +SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b') FROM t1_duckdb; + +--echo +--echo 10. JSON_LENGTH() +--echo + +SELECT JSON_LENGTH(col1, '$'), JSON_LENGTH(col1, '$.test'), JSON_LENGTH(col1, '$.test[0]') FROM t1_innodb; +SELECT JSON_LENGTH(col1, '$'), JSON_LENGTH(col1, '$.test'), JSON_LENGTH(col1, '$.test[0]') FROM t1_duckdb; + +--echo +--echo 11. JSON_MERGE() +--echo + +--echo +--echo 12. JSON_MERGE_PATCH() +--echo + +# Only support two arguments + +SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]') FROM t1_duckdb; + +SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}') FROM t1_duckdb; + +SELECT JSON_MERGE_PATCH('1', 'true') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('1', 'true') FROM t1_duckdb; + +SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}') FROM t1_duckdb; + +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }', '{ "a": 3, "c":4 }') FROM t1_duckdb; + +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') FROM t1_innodb; +SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }') FROM t1_duckdb; + +--echo +--echo 13. JSON_MERGE_PRESERVE() +--echo + +--echo +--echo 14. JSON_OBJECT() +--echo + +SELECT JSON_OBJECT('id', 87, 'name', 'carrot') FROM t1_innodb; +SELECT JSON_OBJECT('id', 87, 'name', 'carrot') FROM t1_duckdb; + +--echo +--echo 15. JSON_OVERLAPS() +--echo + +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") FROM t1_innodb; +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,5,7]") FROM t1_duckdb; + +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") FROM t1_innodb; +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,7]") FROM t1_duckdb; + +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") FROM t1_innodb; +SELECT JSON_OVERLAPS("[1,3,5,7]", "[2,6,8]") FROM t1_duckdb; + +SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') FROM t1_innodb; +SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') FROM t1_innodb; +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') FROM t1_innodb; +SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('5', '5') FROM t1_innodb; +SELECT JSON_OVERLAPS('5', '5') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('5', '6') FROM t1_innodb; +SELECT JSON_OVERLAPS('5', '6') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('[4,5,"6",7]', '6') FROM t1_innodb; +SELECT JSON_OVERLAPS('[4,5,"6",7]', '6') FROM t1_duckdb; + +SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"') FROM t1_innodb; +SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"') FROM t1_duckdb; + +--echo +--echo 16. JSON_PRETTY() +--echo + +SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}') FROM t1_innodb; +SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}') FROM t1_duckdb; + +--echo +--echo 17. JSON_QUOTE() +--echo + +SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"') FROM t1_innodb; +SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"') FROM t1_duckdb; + +SELECT JSON_QUOTE('[1, 2, 3]') FROM t1_innodb; +SELECT JSON_QUOTE('[1, 2, 3]') FROM t1_duckdb; + +SELECT JSON_QUOTE('test') FROM t1_innodb; +SELECT JSON_QUOTE('test') FROM t1_duckdb; + +--echo +--echo 18. JSON_REMOVE() +--echo + +--echo +--echo 19. JSON_REPLACE() +--echo + +--echo +--echo 20. JSON_SCHEMA_VALID() +--echo + +--echo +--echo 21. JSON_SCHEMA_VALIDATION_REPORT() +--echo + +--echo +--echo 22. JSON_SEARCH() +--echo + +--echo +--echo 23. JSON_SET() +--echo + +--echo +--echo 24. JSON_STORAGE_FREE() +--echo + +--echo +--echo 25. JSON_STORAGE_SIZE() +--echo + +--echo +--echo 26. JSON_TABLE() +--echo + +--echo +--echo 27. JSON_TYPE() +--echo + +# The definition of json_type in duckdb and mysql is different, +# so this function is not fully compatible. + +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$')) FROM t1_duckdb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test')) FROM t1_duckdb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_duckdb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_duckdb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_duckdb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_innodb; +SELECT JSON_TYPE(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_duckdb; + +--echo +--echo 28. JSON_UNQUOTE() +--echo + +SELECT JSON_UNQUOTE('"test"') FROM t1_innodb; +SELECT JSON_UNQUOTE('"test"') FROM t1_duckdb; + +SELECT JSON_UNQUOTE(NULL) FROM t1_innodb; +SELECT JSON_UNQUOTE(NULL) FROM t1_duckdb; + +SELECT JSON_UNQUOTE('[1, 2, 3]') FROM t1_innodb; +SELECT JSON_UNQUOTE('[1, 2, 3]') FROM t1_duckdb; + +SELECT JSON_UNQUOTE('{"a": 1}') FROM t1_innodb; +SELECT JSON_UNQUOTE('{"a": 1}') FROM t1_duckdb; + +--echo +--echo 29. JSON_VALID() +--echo + +SELECT JSON_VALID('test') FROM t1_innodb; +SELECT JSON_VALID('test') FROM t1_duckdb; +SELECT JSON_VALID('"test"') FROM t1_innodb; +SELECT JSON_VALID('"test"') FROM t1_duckdb; + +SELECT JSON_VALID(JSON_EXTRACT(col1, '$')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$')) FROM t1_duckdb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test')) FROM t1_duckdb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[0]')) FROM t1_duckdb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[1]')) FROM t1_duckdb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[2]')) FROM t1_duckdb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_innodb; +SELECT JSON_VALID(JSON_EXTRACT(col1, '$.test[3]')) FROM t1_duckdb; + +--echo +--echo 30. JSON_VALUE() +--echo + +# Only support JSON_VALUE(json_doc, path) + +SELECT JSON_VALUE(col1, '$') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$') FROM t1_duckdb; +SELECT JSON_VALUE(col1, '$.test') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$.test') FROM t1_duckdb; +SELECT JSON_VALUE(col1, '$.test[0]') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$.test[0]') FROM t1_duckdb; +SELECT JSON_VALUE(col1, '$.test[1]') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$.test[1]') FROM t1_duckdb; +SELECT JSON_VALUE(col1, '$.test[2]') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$.test[2]') FROM t1_duckdb; +SELECT JSON_VALUE(col1, '$.test[3]') FROM t1_innodb; +SELECT JSON_VALUE(col1, '$.test[3]') FROM t1_duckdb; + +--echo +--echo 31. MEMBER OF() +--echo + +DROP TABLE t1_innodb; +DROP TABLE t1_duckdb; +DROP TABLE t2_innodb; +DROP TABLE t2_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_kill.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_kill.test new file mode 100644 index 0000000000000..2915977740bb0 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_kill.test @@ -0,0 +1,227 @@ +--source ../include/have_duckdb.inc + +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--echo # +--echo # Prepare the test +--echo # + +delimiter |; +# Helper function used to repeatedly kill a session. +CREATE FUNCTION MY_KILL_QUERY(tid INT) RETURNS INT +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; + KILL QUERY tid; + RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid AND Info like '%SELECT%'); +END| + +CREATE FUNCTION MY_KILL_CONNECTION(tid INT) RETURNS INT +BEGIN + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; + KILL CONNECTION tid; + RETURN (SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = tid); +END| +delimiter ;| + +CREATE TABLE integers(i int primary key) ENGINE=duckdb; +SELECT run_in_duckdb("INSERT INTO test.integers FROM range(10000)"); + +connect (con1, localhost, root,,); +connect (con2, localhost, root,,); + +--echo # +--echo # 1. Kill query during executing duckdb query +--echo # + +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +# Let con1 execute a long query +connection con1; +--send SELECT i1.i FROM integers i1, integers i2, integers i3, integers i4, integers i5, integers i6, integers i7, integers i8, integers i9, integers i10, integers i11, integers i12, integers i13 + +# con2 wait con1 begin query +connection con2; +let $wait_condition = SELECT count(*) = 1 FROM information_schema.processlist WHERE id = @id and Time > 1 and command != 'Sleep' and Info like 'SELECT%integers%'; +--source include/wait_condition.inc + +# con2 kill con1's query +let $wait_condition= SELECT MY_KILL_QUERY(@id); +--source include/wait_condition.inc + +# con1 check itself's query was killed +connection con1; +--error ER_GET_ERRMSG +--reap + +# con1's connection is not killed +SELECT 1; + +--echo # +--echo # 2. Kill query before executing duckdb query +--echo # + +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +# Let con2 kill con1 +connection con2; +let $wait_condition= SELECT MY_KILL_QUERY(@id); +--source include/wait_condition.inc + +# con1 should not be influcenced +connection con1; +SELECT COUNT(*) FROM integers; + +--echo # +--echo # 3. support MAX_EXECUTION_TIME for duckdb query +--echo # + +connection con1; + +SET MAX_EXECUTION_TIME=100; +--error ER_GET_ERRMSG +SELECT i1.i FROM integers i1, integers i2, integers i3, integers i4, integers i5, integers i6, integers i7, integers i8, integers i9, integers i10, integers i11, integers i12, integers i13; + +SET MAX_EXECUTION_TIME=0; +--error ER_GET_ERRMSG +SELECT /*+ MAX_EXECUTION_TIME(100) */ i1.i FROM integers i1, integers i2, integers i3, integers i4, integers i5, integers i6, integers i7, integers i8, integers i9, integers i10, integers i11, integers i12, integers i13; + +SELECT COUNT(*) FROM integers; + +connection default; +# bugfix return partial result event if interrupt +--exec $MYSQL -e "SELECT * FROM test.integers" > $MYSQLTEST_VARDIR/log/1.log + +SET max_execution_time = 1000; +SET DEBUG = '+d, simulate_interrupt_duckdb_row'; +--error ER_GET_ERRMSG +SELECT * FROM integers; +SET DEBUG = 'reset'; + +SET DEBUG = '+d, simulate_interrupt_duckdb_chunk'; +--error ER_GET_ERRMSG +SELECT * FROM integers; +SET DEBUG = 'reset'; + +--let $assert_file = $MYSQLTEST_VARDIR/log/mysqld.1.err +--let $assert_text = Found interrupt when fetching rows. +--let $assert_select = Interrupt when fetching rows +--let $assert_count = 1 +--source include/assert_grep.inc + +--let $assert_text = Found interrupt when fetching chunks. +--let $assert_select = Interrupt when fetching chunks +--let $assert_count = 1 +--source include/assert_grep.inc + +connection con1; + + +--echo # +--echo # 4. Kill connection when executing duckdb query +--echo # + +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +# Let con1 execute a long query +connection con1; +--send SELECT i1.i FROM integers i1, integers i2, integers i3, integers i4, integers i5, integers i6, integers i7, integers i8, integers i9, integers i10, integers i11, integers i12, integers i13 + +# con2 wait con1 begin query +connection con2; +let $wait_condition = SELECT count(*) = 1 FROM information_schema.processlist WHERE id = @id and Time > 1 and command != 'Sleep' and Info like 'SELECT%intergers%'; +--source include/wait_condition.inc + +# con2 kill con1's connection +let $wait_condition= SELECT MY_KILL_CONNECTION(@id); +--source include/wait_condition.inc + +# con1 check itself's query was killed +connection con1; +--error CR_SERVER_LOST +--reap + +# con1's connection is killed, need reconnect +--enable_reconnect +SELECT COUNT(*) FROM integers; + + +--echo # +--echo # 5. Kill connection before executing duckdb query +--echo # + +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +# Let con2 kill con1 +connection con2; +let $wait_condition= SELECT MY_KILL_CONNECTION(@id); +--source include/wait_condition.inc + +# con1 should failed, need to reconnect +connection con1; +--error CR_SERVER_LOST +SELECT COUNT(*) FROM integers; + +--enable_reconnect +SELECT COUNT(*) FROM integers; + + +--echo # +--echo # 6. Kill query beween mysql prepare and run_in_duckdb +--echo # + +connection con1; +--disable_reconnect +let $ID= `SELECT @id := CONNECTION_ID()`; +connection con2; +let $ignore= `SELECT @id := $ID`; + +connection con1; +SET DEBUG_SYNC= 'before_run_in_duckdb SIGNAL con1_prepared WAIT_FOR con2_killed_con1'; +--send_eval SELECT COUNT(*) FROM integers + +connection con2; +SET DEBUG_SYNC='now WAIT_FOR con1_prepared'; + +connection con2; +let $wait_condition= SELECT MY_KILL_QUERY(@id); +--source include/wait_condition.inc + +set debug_sync='now SIGNAL con2_killed_con1'; + +connection con1; +--error ER_GET_ERRMSG +--reap + +connection con1; +SET DEBUG_SYNC='RESET'; +connection con2; +SET DEBUG_SYNC='RESET'; + +--echo # +--echo # Cleanup the test +--echo # + +--remove_file $MYSQLTEST_VARDIR/log/1.log +DROP TABLE IF EXISTS integers; +DROP FUNCTION IF EXISTS MY_KILL_QUERY; +DROP FUNCTION IF EXISTS MY_KILL_CONNECTION; + +--disconnect con1 +--disconnect con2 diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_monitor.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_monitor.test new file mode 100644 index 0000000000000..c4c4883be127d --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_monitor.test @@ -0,0 +1,94 @@ +--source ../include/have_duckdb.inc + +# +# DuckDB monitoring status variables test. +# +# We test plugin-level status variables (Duckdb_*). +# AliSQL Com_duckdb_* counters are server-side and not available in MariaDB. +# + +# restart to reset status counters +--source include/restart_mysqld.inc + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_duckdb_monitor CHARACTER SET utf8mb4; +USE db_duckdb_monitor; +--enable_query_log + +CREATE TABLE t1(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=duckdb; +CREATE TABLE t2(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=duckdb; +CREATE TABLE t3(id INT PRIMARY KEY, v VARCHAR(255)) ENGINE=innodb; + +# Save baseline commit count (CREATE TABLEs cause autocommits) +--let $base_commit = query_get_value(SHOW STATUS LIKE 'Duckdb_commit', Value, 1) + +--echo # +--echo # 1) INSERT: rows_insert increments per row (dml_in_batch=OFF) +--echo # +SHOW STATUS LIKE 'Duckdb_rows_insert'; +INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'); +INSERT INTO t2 VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'); +SHOW STATUS LIKE 'Duckdb_rows_insert'; + +--echo # +--echo # 2) DELETE: rows_delete increments via direct_delete_rows +--echo # +SHOW STATUS LIKE 'Duckdb_rows_delete'; +DELETE FROM t1 WHERE id IN (1,2); +SHOW STATUS LIKE 'Duckdb_rows_delete'; + +--echo # +--echo # 3) UPDATE: rows_update increments via direct_update_rows +--echo # +SHOW STATUS LIKE 'Duckdb_rows_update'; +UPDATE t1 SET v = 'x' WHERE id IN (3,4); +SHOW STATUS LIKE 'Duckdb_rows_update'; + +--echo # +--echo # 4) INSERT SELECT: rows_insert increments +--echo # +SHOW STATUS LIKE 'Duckdb_rows_insert'; +INSERT INTO t1 SELECT * FROM t2; +SHOW STATUS LIKE 'Duckdb_rows_insert'; + +--echo # +--echo # 5) Duckdb_commit increments on explicit COMMIT +--echo # +--disable_query_log +--eval SELECT @commits_before := $base_commit +--let $commits_before = query_get_value(SHOW STATUS LIKE 'Duckdb_commit', Value, 1) +--enable_query_log +BEGIN; +INSERT INTO t1 VALUES(6,'f'); +COMMIT; +--let $commits_after = query_get_value(SHOW STATUS LIKE 'Duckdb_commit', Value, 1) +--disable_query_log +--eval SELECT $commits_after - $commits_before AS commit_delta +--enable_query_log + +--echo # +--echo # 6) Duckdb_rollback increments on explicit ROLLBACK +--echo # +SHOW STATUS LIKE 'Duckdb_rollback'; +BEGIN; +INSERT INTO t1 VALUES(7,'g'); +ROLLBACK; +SHOW STATUS LIKE 'Duckdb_rollback'; + +--echo # +--echo # 7) Final state: batch counters should be 0 (dml_in_batch=OFF) +--echo # +SHOW STATUS LIKE 'Duckdb_rows_insert_in_batch'; +SHOW STATUS LIKE 'Duckdb_rows_update_in_batch'; +SHOW STATUS LIKE 'Duckdb_rows_delete_in_batch'; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; + + +--disable_query_log +DROP DATABASE db_duckdb_monitor; +--enable_query_log + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_numeric_func.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_numeric_func.test new file mode 100644 index 0000000000000..5f992cb23b505 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_numeric_func.test @@ -0,0 +1,201 @@ +--source ../include/have_duckdb.inc + +# 1. CONV(), CRC32(), MOD, DIV, TRUNCATE() is not support yet. +# 2. The RAND() function in duckdb and mysql uses different random number generation algorithms, so it cannot support the seed argument. + +CREATE DATABASE test_duckdb; +USE test_duckdb; +CREATE TABLE t_innodb (id int PRIMARY KEY, col1 TINYINT, col2 INT, col3 BIGINT, col4 DECIMAL(38, 10), col5 DOUBLE ) ENGINE=Innodb; +CREATE TABLE t_duckdb (id int PRIMARY KEY, col1 TINYINT, col2 INT, col3 BIGINT, col4 DECIMAL(38, 10), col5 DOUBLE ) ENGINE=DuckDB; +INSERT INTO t_innodb VALUES (1, 10, 12345678, 23372036854775808, 123456789101112131415161718.1234567891, 123456789101112131415161718.1234567891); +INSERT INTO t_innodb VALUES (2, -10, -12345678, -12345678910111213, -123456789101112131415161718.1234567891, -123456789101112131415161718.1234567891); +INSERT INTO t_duckdb VALUES (1, 10, 12345678, 23372036854775808, 123456789101112131415161718.1234567891, 123456789101112131415161718.1234567891); +INSERT INTO t_duckdb VALUES (2, -10, -12345678, -12345678910111213, -123456789101112131415161718.1234567891, -123456789101112131415161718.1234567891); + +--echo --------------------------- +--echo 1. ABS() +--echo --------------------------- + +SELECT ABS(col1), ABS(col2), ABS(col3), ABS(col4), ABS(col5) FROM t_innodb; +SELECT ABS(col1), ABS(col2), ABS(col3), ABS(col4), ABS(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 2. ACOS() +--echo --------------------------- + +SELECT ACOS(COS(col1)), ACOS(COS(col2)), ACOS(COS(col3)), ACOS(COS(col4)), ACOS(COS(col5)), ACOS(col1) FROM t_innodb; +SELECT ACOS(COS(col1)), ACOS(COS(col2)), ACOS(COS(col3)), ACOS(COS(col4)), ACOS(COS(col5)), ACOS(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 3. ASIN() +--echo --------------------------- + +SELECT ASIN(SIN(col1)), ASIN(SIN(col2)), ASIN(SIN(col3)), ASIN(SIN(col4)), ASIN(SIN(col5)), ASIN(col1) FROM t_innodb; +SELECT ASIN(SIN(col1)), ASIN(SIN(col2)), ASIN(SIN(col3)), ASIN(SIN(col4)), ASIN(SIN(col5)), ASIN(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 3. ATAN() +--echo --------------------------- + +SELECT ATAN(TAN(col1)), ATAN(TAN(col2)), ATAN(TAN(col3)), ATAN(TAN(col4)), ATAN(TAN(col5)) FROM t_innodb; +SELECT ATAN(TAN(col1)), ATAN(TAN(col2)), ATAN(TAN(col3)), ATAN(TAN(col4)), ATAN(TAN(col5)) FROM t_duckdb; + +--echo --------------------------- +--echo 4. ATAN2() +--echo --------------------------- + +SELECT ATAN2(TAN(col1), 2), ATAN2(TAN(col2), 2), ATAN2(TAN(col3), 2), ATAN2(TAN(col4), 2), ATAN2(TAN(col5), 2) FROM t_innodb; +SELECT ATAN2(TAN(col1), 2), ATAN2(TAN(col2), 2), ATAN2(TAN(col3), 2), ATAN2(TAN(col4), 2), ATAN2(TAN(col5), 2) FROM t_duckdb; + +--echo --------------------------- +--echo 5. CEIL(), CEILING() +--echo --------------------------- + +SELECT CEIL(col1), CEIL(col2), CEIL(col3), CEIL(col4), CEIL(col5) FROM t_innodb; +SELECT CEIL(col1), CEIL(col2), CEIL(col3), CEIL(col4), CEIL(col5) FROM t_duckdb; + +SELECT CEILING(col1), CEILING(col2), CEILING(col3), CEILING(col4), CEILING(col5) FROM t_innodb; +SELECT CEILING(col1), CEILING(col2), CEILING(col3), CEILING(col4), CEILING(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 6. COS() +--echo --------------------------- + +SELECT COS(col1), COS(col2), COS(col3), COS(col4), COS(col5) FROM t_innodb; +SELECT COS(col1), COS(col2), COS(col3), COS(col4), COS(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 7. COT() +--echo --------------------------- + +SELECT COT(col1), COT(col2), COT(col3), COT(col4), COT(col5) FROM t_innodb; +SELECT COT(col1), COT(col2), COT(col3), COT(col4), COT(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 8. DEGREES() +--echo --------------------------- + +SELECT DEGREES(col1), DEGREES(col2), DEGREES(col3), DEGREES(col4), DEGREES(col5) FROM t_innodb; +SELECT DEGREES(col1), DEGREES(col2), DEGREES(col3), DEGREES(col4), DEGREES(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 9. EXP() +--echo --------------------------- + +SELECT EXP(LN(ABS(col1))), EXP(LN(ABS(col2))), EXP(LN(ABS(col3))), EXP(LN(ABS(col4))), EXP(LN(ABS(col5))) FROM t_innodb; +SELECT EXP(LN(ABS(col1))), EXP(LN(ABS(col2))), EXP(LN(ABS(col3))), EXP(LN(ABS(col4))), EXP(LN(ABS(col5))) FROM t_duckdb; + +--echo --------------------------- +--echo 10. FLOOR() +--echo --------------------------- + +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_innodb; +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 11. FLOOR() +--echo --------------------------- + +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_innodb; +SELECT FLOOR(col1), FLOOR(col2), FLOOR(col3), FLOOR(col4), FLOOR(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 12. LN() +--echo --------------------------- + +SELECT LN(ABS(col1)), LN(ABS(col2)), LN(ABS(col3)), LN(ABS(col4)), LN(ABS(col5)), LN(col1) FROM t_innodb; +SELECT LN(ABS(col1)), LN(ABS(col2)), LN(ABS(col3)), LN(ABS(col4)), LN(ABS(col5)), LN(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 13. LOG() +--echo --------------------------- + +SELECT LOG(ABS(col1)), LOG(ABS(col2)), LOG(ABS(col3)), LOG(ABS(col4)), LOG(ABS(col5)), LOG(col1) FROM t_innodb; +SELECT LOG(ABS(col1)), LOG(ABS(col2)), LOG(ABS(col3)), LOG(ABS(col4)), LOG(ABS(col5)), LOG(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 14. LOG10() +--echo --------------------------- + +SELECT LOG10(ABS(col1)), LOG10(ABS(col2)), LOG10(ABS(col3)), LOG10(ABS(col4)), LOG10(ABS(col5)), LOG10(col1) FROM t_innodb; +SELECT LOG10(ABS(col1)), LOG10(ABS(col2)), LOG10(ABS(col3)), LOG10(ABS(col4)), LOG10(ABS(col5)), LOG10(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 15. LOG2() +--echo --------------------------- + +SELECT LOG2(ABS(col1)), LOG2(ABS(col2)), LOG2(ABS(col3)), LOG2(ABS(col4)), LOG2(ABS(col5)), LOG2(col1) FROM t_innodb; +SELECT LOG2(ABS(col1)), LOG2(ABS(col2)), LOG2(ABS(col3)), LOG2(ABS(col4)), LOG2(ABS(col5)), LOG2(col1) FROM t_duckdb; + +--echo --------------------------- +--echo 16. PI() +--echo --------------------------- + +SELECT PI() FROM t_innodb; +SELECT PI() FROM t_duckdb; + +--echo --------------------------- +--echo 17. POW(), POWER() +--echo --------------------------- + +SELECT POW(ABS(col1), 0.5), POW(ABS(col2), 0.5), POW(ABS(col3), 0.5), POW(ABS(col4), 0.5), POW(ABS(col5), 0.5) FROM t_innodb; +SELECT POW(ABS(col1), 0.5), POW(ABS(col2), 0.5), POW(ABS(col3), 0.5), POW(ABS(col4), 0.5), POW(ABS(col5), 0.5) FROM t_duckdb; + +SELECT POWER(ABS(col1), 0.5), POWER(ABS(col2), 0.5), POWER(ABS(col3), 0.5), POWER(ABS(col4), 0.5), POWER(ABS(col5), 0.5) FROM t_innodb; +SELECT POWER(ABS(col1), 0.5), POWER(ABS(col2), 0.5), POWER(ABS(col3), 0.5), POWER(ABS(col4), 0.5), POWER(ABS(col5), 0.5) FROM t_duckdb; + +--echo --------------------------- +--echo 18. RADIANS() +--echo --------------------------- + +SELECT RADIANS(col1), RADIANS(col2), RADIANS(col3), RADIANS(col4), RADIANS(col5) FROM t_innodb; +SELECT RADIANS(col1), RADIANS(col2), RADIANS(col3), RADIANS(col4), RADIANS(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 19. RAND() +--echo --------------------------- + +# SELECT RAND() FROM t_innodb; +# SELECT RAND() FROM t_duckdb; + +--echo --------------------------- +--echo 20. ROUND() +--echo --------------------------- + +SELECT ROUND(col1), ROUND(col1, 0), ROUND(col2), ROUND(col2, 0), ROUND(col3), ROUND(col3, 0), ROUND(col4), ROUND(col4, 0), ROUND(col5), ROUND(col5, 0) FROM t_innodb; +SELECT ROUND(col1), ROUND(col1, 0), ROUND(col2), ROUND(col2, 0), ROUND(col3), ROUND(col3, 0), ROUND(col4), ROUND(col4, 0), ROUND(col5), ROUND(col5, 0) FROM t_duckdb; + +SELECT ROUND(col1, 3), ROUND(col1, -3), ROUND(col2, 6), ROUND(col2, -6), ROUND(col3, 6), ROUND(col3, -6), ROUND(col4, 6), ROUND(col4, 6), ROUND(col5, 6), ROUND(col5, 6) FROM t_innodb; +SELECT ROUND(col1, 3), ROUND(col1, -3), ROUND(col2, 6), ROUND(col2, -6), ROUND(col3, 6), ROUND(col3, -6), ROUND(col4, 6), ROUND(col4, 6), ROUND(col5, 6), ROUND(col5, 6) FROM t_duckdb; + +--echo --------------------------- +--echo 21. SIGN() +--echo --------------------------- + +SELECT SIGN(col1), SIGN(col2), SIGN(col3), SIGN(col4), SIGN(col5) FROM t_innodb; +SELECT SIGN(col1), SIGN(col2), SIGN(col3), SIGN(col4), SIGN(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 22. SIN() +--echo --------------------------- + +SELECT SIN(col1), SIN(col2), SIN(col3), SIN(col4), SIN(col5) FROM t_innodb; +SELECT SIN(col1), SIN(col2), SIN(col3), SIN(col4), SIN(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 23. SQRT() +--echo --------------------------- + +SELECT SQRT(col1), SQRT(col2), SQRT(col3), SQRT(col4), SQRT(col5) FROM t_innodb; +SELECT SQRT(col1), SQRT(col2), SQRT(col3), SQRT(col4), SQRT(col5) FROM t_duckdb; + +--echo --------------------------- +--echo 24. TAN() +--echo --------------------------- + +SELECT TAN(col1), TAN(col2), TAN(col3), TAN(col4), TAN(col5) FROM t_innodb; +SELECT TAN(col1), TAN(col2), TAN(col3), TAN(col4), TAN(col5) FROM t_duckdb; + +DROP TABLE t_innodb; +DROP TABLE t_duckdb; +DROP DATABASE test_duckdb; \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_refuse_xa.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_refuse_xa.test new file mode 100644 index 0000000000000..0e34bd12c6125 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_refuse_xa.test @@ -0,0 +1,59 @@ +--source ../include/have_duckdb.inc + +CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; + +# xa start +XA START 'xa_1'; + +--error ER_XAER_RMFAIL +CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; + +--error ER_XAER_RMFAIL +INSERT INTO t1 (c2) VALUES ('a'); + +--error ER_XAER_RMFAIL +ALTER TABLE t1 ADD COLUMN c3 INT; + +--error ER_XAER_RMFAIL +ALTER TABLE t1 RENAME TO t2; + +--error ER_XAER_RMFAIL +DROP TABLE t1; + +--error ER_XAER_RMFAIL +TRUNCATE TABLE t1; + +# xa end + +XA END 'xa_1'; +--error ER_XAER_RMFAIL +INSERT INTO t1 (c1, c2) VALUES (1, 'a'); + +--error ER_XAER_RMFAIL +ALTER TABLE t1 ADD COLUMN c3 INT; + +--error ER_XAER_RMFAIL +ALTER TABLE t1 RENAME TO t2; + +--error ER_XAER_RMFAIL +TRUNCATE TABLE t1; + +--error ER_XAER_RMFAIL +DROP TABLE t1; + +# xa prepare + +XA PREPARE 'xa_1'; +INSERT INTO t1 (c1, c2) VALUES (1, 'a'); + +# xa commit +XA COMMIT 'xa_1'; +CREATE TABLE t2 (c1 INT PRIMARY KEY, c2 VARCHAR(5)) ENGINE=duckdb; + +INSERT INTO t1 (c1, c2) VALUES (2, 'a'); +TRUNCATE TABLE t1; +ALTER TABLE t1 ADD COLUMN c3 INT; +ALTER TABLE t1 RENAME TO t3; + +DROP TABLE t2; +DROP TABLE t3; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_require_primary_key.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_require_primary_key.test new file mode 100644 index 0000000000000..cc8ffb15acb31 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_require_primary_key.test @@ -0,0 +1,57 @@ +--source ../include/have_duckdb.inc + + + + +--disable_query_log +CREATE DATABASE IF NOT EXISTS require_primary_key CHARACTER SET utf8mb4; +USE require_primary_key; +--enable_query_log +ALTER DATABASE require_primary_key CHARACTER SET utf8mb4; +SET NAMES utf8mb4; + +--echo # +--echo # 1) duckdb_require_primary_key is ON, CREATE without PK is not allowed +--echo # +SET GLOBAL duckdb_require_primary_key = ON; +--error ER_REQUIRES_PRIMARY_KEY +CREATE TABLE t(id INT, a VARCHAR(10), b VARCHAR(10)) ENGINE = DuckDB; + + +--echo # +--echo # 2) duckdb_require_primary_key is OFF, CREATE without PK is allowed +--echo # +SET GLOBAL duckdb_require_primary_key = OFF; +CREATE TABLE t(id INT, a VARCHAR(10), b VARCHAR(10)) ENGINE = DuckDB; +SHOW CREATE TABLE t; + + +--echo # +--echo # 3) duckdb_require_primary_key is ON, ALTER without PK is not allowed +--echo # +SET GLOBAL duckdb_require_primary_key = ON; +--error ER_REQUIRES_PRIMARY_KEY +ALTER TABLE t ADD COLUMN c VARCHAR(10); +SHOW CREATE TABLE t; + + +--echo # +--echo # 4) duckdb_require_primary_key is OFF, CREATE without PK is allowed +--echo # +SET GLOBAL duckdb_require_primary_key = OFF; +ALTER TABLE t ADD COLUMN c VARCHAR(10); +SHOW CREATE TABLE t; + + +--echo # +--echo # 5) Cleanup +--echo # +SET GLOBAL duckdb_require_primary_key = default; +DROP TABLE t; + +--disable_query_log +DROP DATABASE require_primary_key; +--enable_query_log + + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_set_operation.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_set_operation.test new file mode 100644 index 0000000000000..e245e7c9575fc --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_set_operation.test @@ -0,0 +1,18 @@ +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 1), (2, 1), (3, 2), (4, 2); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t2 VALUES (1, 2), (2, 2), (3, 3), (4, 3); + +# 1. UNION, INTERSECT, EXCEPT +--sorted_result +SELECT col1 FROM t1 UNION SELECT col1 FROM t2; +SELECT col1 FROM t1 INTERSECT SELECT col1 FROM t2; +SELECT col1 FROM t1 EXCEPT SELECT col1 FROM t2; + +# 2. UNION ALL, INTERSECT ALL, EXCEPT ALL +SELECT col1 FROM t1 UNION ALL SELECT col1 FROM t2; +SELECT col1 FROM t1 INTERSECT ALL SELECT col1 FROM t2; +SELECT col1 FROM t1 EXCEPT ALL SELECT col1 FROM t2; + +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_mode.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_mode.test new file mode 100644 index 0000000000000..baeca66b9dbc9 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_mode.test @@ -0,0 +1,34 @@ +--source ../include/have_duckdb.inc + +--echo +--echo 1. ONLY_FULL_GROUP_BY +--echo + +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 2); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1, 1), (2, 2), (3, 2); + +SET sql_mode= ''; +SELECT id, sum(id) FROM t1; +SELECT id, sum(id) FROM t2; +SELECT id, sum(id) FROM t1 GROUP BY col1; +SELECT id, sum(id) FROM t2 GROUP BY col1; +SELECT col1 FROM t1 GROUP BY id; +SELECT col1 FROM t2 GROUP BY id; +SET sql_mode='ONLY_FULL_GROUP_BY'; +--error ER_MIX_OF_GROUP_FUNC_AND_FIELDS +SELECT id, sum(id) FROM t1; +--error ER_MIX_OF_GROUP_FUNC_AND_FIELDS +SELECT id, sum(id) FROM t2; +--error ER_WRONG_FIELD_WITH_GROUP +SELECT id, sum(id) FROM t1 GROUP BY col1; +--error ER_WRONG_FIELD_WITH_GROUP +SELECT id, sum(id) FROM t2 GROUP BY col1; + +--error ER_GET_ERRMSG +SELECT col1 FROM t1 GROUP BY id; +SELECT col1 FROM t2 GROUP BY id; + +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_syntax.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_syntax.test new file mode 100644 index 0000000000000..c9db95ddd25ec --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_sql_syntax.test @@ -0,0 +1,71 @@ +# WITH ROLLUP +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT); +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT id, col1, col2 FROM t1 GROUP BY id, col1, col2 WITH ROLLUP; +DROP TABLE t1; + +# CONDITIONLESS JOIN; +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT, col2 INT); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT, col2 INT); +INSERT INTO t1 VALUES (1, 1, 1); +INSERT INTO t2 VALUES (2, 2, 2); +SELECT * FROM t1 JOIN t2; +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 JOIN t2; +DROP TABLE t1; +DROP TABLE t2; + +# select options +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1), (2, 1); +SELECT DISTINCT col1 FROM t1; +SELECT ALL col1 FROM t1; +SELECT HIGH_PRIORITY * FROM t1; +SELECT SQL_SMALL_RESULT * FROM t1; +SELECT SQL_BIG_RESULT * FROM t1; +SELECT SQL_BUFFER_RESULT * FROM t1; +SELECT SQL_CALC_FOUND_ROWS * FROM t1; +SELECT SQL_NO_CACHE * FROM t1; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT DISTINCT col1 FROM t1; +SELECT ALL col1 FROM t1; +SELECT HIGH_PRIORITY * FROM t1; +SELECT SQL_SMALL_RESULT * FROM t1; +SELECT SQL_BIG_RESULT * FROM t1; +SELECT SQL_BUFFER_RESULT * FROM t1; +SELECT SQL_CALC_FOUND_ROWS * FROM t1; +SELECT SQL_NO_CACHE * FROM t1; +DROP TABLE t1; + +# LIMIT limit, offset +CREATE TABLE t1 (id INT PRIMARY KEY); +INSERT INTO t1 VALUES (1), (2), (3), (4), (5); +SELECT * FROM t1 LIMIT 2,2; +ALTER TABLE t1 ENGINE=DuckDB; +SELECT * FROM t1 LIMIT 2,2; +DROP TABLE t1; + +# Index hint +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +SELECT * FROM t1 FORCE INDEX(PRI); +SELECT * FROM t1 IGNORE INDEX(PRI); +SELECT * FROM t1 USE INDEX(PRI); +ALTER TABLE t1 ENGINE=DuckDB; +SELECT * FROM t1 FORCE INDEX(PRI); +SELECT * FROM t1 IGNORE INDEX(PRI); +SELECT * FROM t1 USE INDEX(PRI); +DROP TABLE t1; + +# STRAIGHT_JOIN +CREATE TABLE t1 (id INT PRIMARY KEY, col1 INT); +CREATE TABLE t2 (id INT PRIMARY KEY, col1 INT); +INSERT INTO t1 VALUES (1, 1); +INSERT INTO t2 VALUES (2, 2); +SELECT * FROM t1 STRAIGHT_JOIN t2; +ALTER TABLE t1 ENGINE=DuckDB; +ALTER TABLE t2 ENGINE=DuckDB; +SELECT * FROM t1 STRAIGHT_JOIN t2; +DROP TABLE t1; +DROP TABLE t2; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_string_func.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_string_func.test new file mode 100644 index 0000000000000..a14478edc6578 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_string_func.test @@ -0,0 +1,518 @@ +--source ../include/have_duckdb.inc + +# 1. In Duckdb, some string function does not support blob fields as input +# 2. Regex-related functions are basically incompatible, and regex functions will be processed uniformly in the future. +# 3. Some set functions are not support yet, such as CHAR(), ELT(), EXPORT_SET(), FIELD(), FORMAT(), MAKE_SET(). +# 4. Character set and collation related functions are not supported, such as SOUNDEX(), WEIGHT_STRING(). + + +--disable_query_log +SET @saved_duckdb_dml_in_batch = @@GLOBAL.duckdb_dml_in_batch; +--enable_query_log + +CREATE DATABASE test_duckdb; +USE test_duckdb; + +SET NAMES utf8mb4; + +CREATE TABLE t_innodb(col1 VARCHAR(20) PRIMARY KEY, col2 BLOB); +CREATE TABLE t_duckdb(col1 VARCHAR(20) PRIMARY KEY, col2 BLOB) ENGINE=DuckDB; +insert into t_innodb values ('MySQL', 0x4D7953514CF09FA686); +insert into t_duckdb values ('MySQL', 0x4D7953514CF09FA686); +insert into t_innodb values ('数据库', 0xE695B0E68DAEE5BA93); +insert into t_duckdb values ('数据库', 0xE695B0E68DAEE5BA93); + +--echo -------------------------- +--echo 1. ASCII() +--echo -------------------------- + +SELECT ASCII('MySQL'), ASCII('数据库'), ASCII(col1) FROM t_innodb; +SELECT ASCII('MySQL'), ASCII('数据库'), ASCII(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 2. BIN() +--echo -------------------------- + +SELECT BIN('MySQL'), BIN('数据库'), BIN(123), BIN(col1) FROM t_innodb; +SELECT BIN('MySQL'), BIN('数据库'), BIN(123), BIN(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 3. BIT_LENGTH() +--echo -------------------------- + +SELECT BIT_LENGTH('MySQL'), BIT_LENGTH('数据库'), BIT_LENGTH(col1) FROM t_innodb; +SELECT BIT_LENGTH('MySQL'), BIT_LENGTH('数据库'), BIT_LENGTH(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 4. CHAR() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 5. CHAR_LENGTH() +--echo -------------------------- + +SELECT CHAR_LENGTH('MySQL'), CHAR_LENGTH('数据库'), CHAR_LENGTH(col1) FROM t_innodb; +SELECT CHAR_LENGTH('MySQL'), CHAR_LENGTH('数据库'), CHAR_LENGTH(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 6. CHARACTER_LENGTH() +--echo -------------------------- + +SELECT CHARACTER_LENGTH('MySQL'), CHARACTER_LENGTH('数据库'), CHARACTER_LENGTH(col1) FROM t_innodb; +SELECT CHARACTER_LENGTH('MySQL'), CHARACTER_LENGTH('数据库'), CHARACTER_LENGTH(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 7. CONCAT() +--echo -------------------------- + +SELECT CONCAT('MySQL', '数据库'), CONCAT(col1, 'RDS') FROM t_innodb; +SELECT CONCAT('MySQL', '数据库'), CONCAT(col1, 'RDS') FROM t_duckdb; + +--echo -------------------------- +--echo 8. CONCAT_WS() +--echo -------------------------- + +SELECT CONCAT_WS(',', 'MySQL', '数据库'), CONCAT_WS(',', col1, 'RDS') FROM t_innodb; +SELECT CONCAT_WS(',', 'MySQL', '数据库'), CONCAT_WS(',', col1, 'RDS') FROM t_duckdb; + +--echo -------------------------- +--echo 9. ELT() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 10. EXPORT_SET() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 11. FIELD() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 12. FIND_IN_SET() +--echo -------------------------- + +SELECT FIND_IN_SET('MySQL', 'MySQL,RDS'), FIND_IN_SET('数据库', 'MySQL,RDS'), FIND_IN_SET(col1, 'MySQL,RDS') FROM t_innodb; +SELECT FIND_IN_SET('MySQL', 'MySQL,RDS'), FIND_IN_SET('数据库', 'MySQL,RDS'), FIND_IN_SET(col1, 'MySQL,RDS') FROM t_innodb; + +--echo -------------------------- +--echo 13. FORMAT() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 14. HEX() +--echo -------------------------- + +SELECT HEX('MySQL'), HEX(70), HEX(col1) FROM t_innodb; +SELECT HEX('MySQL'), HEX(70), HEX(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 15. INSERT() +--echo -------------------------- + +SELECT INSERT('Quadratic', 3, 4, 'What'), INSERT('Quadratic', -1, 4, 'What'), INSERT('Quadratic', 3, 100, 'What') FROM t_innodb; +SELECT INSERT('Quadratic', 3, 4, 'What'), INSERT('Quadratic', -1, 4, 'What'), INSERT('Quadratic', 3, 100, 'What') FROM t_duckdb; + +--echo -------------------------- +--echo 16. INSTR() +--echo -------------------------- + +SELECT INSTR('MySQL数据库', '数据库'), INSTR('MySQL数据库', col1) FROM t_innodb; +SELECT INSTR('MySQL数据库', '数据库'), INSTR('MySQL数据库', col1) FROM t_duckdb; + +--echo -------------------------- +--echo 17. LCASE() +--echo -------------------------- + +SELECT LCASE('MySQL'), LCASE('数据库'), LCASE(col1) FROM t_innodb; +SELECT LCASE('MySQL'), LCASE('数据库'), LCASE(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 18. LEFT() +--echo -------------------------- + +SELECT LEFT('MySQL', 2), LEFT('数据库', 2), LEFT(col1, 2) FROM t_innodb; +SELECT LEFT('MySQL', 2), LEFT('数据库', 2), LEFT(col1, 2) FROM t_duckdb; + +--echo -------------------------- +--echo 19. LENGTH() +--echo -------------------------- + +SELECT LENGTH('MySQL'), LENGTH('数据库'), LENGTH(col1), LENGTH(col2) FROM t_innodb; +SELECT LENGTH('MySQL'), LENGTH('数据库'), LENGTH(col1), LENGTH(col2) FROM t_duckdb; + +--echo -------------------------- +--echo 20. LIKE +--echo -------------------------- + +SELECT 'MySQL' LIKE '%SQL', col1 LIKE '%SQL' FROM t_innodb; +SELECT 'MySQL' LIKE '%SQL', col1 LIKE '%SQL' FROM t_duckdb; + +--echo -------------------------- +--echo 21. LOAD_FILE() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 22. LOCATE() +--echo -------------------------- + +SELECT LOCATE('SQL', 'MySQLSQL'), LOCATE('SQL', 'MySQLSQL', 3), LOCATE('SQL', 'MySQLSQL', 7) FROM t_innodb; +SELECT LOCATE('SQL', 'MySQLSQL'), LOCATE('SQL', 'MySQLSQL', 3), LOCATE('SQL', 'MySQLSQL', 7) FROM t_duckdb; + +--echo -------------------------- +--echo 23. LOWER() +--echo -------------------------- + +SELECT LOWER('MySQL'), LOWER('数据库'), LOWER(col1) FROM t_innodb; +SELECT LOWER('MySQL'), LOWER('数据库'), LOWER(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 24. LPAD() +--echo -------------------------- + +SELECT LPAD('MySQL', '3', 'My'), LPAD('MySQL', '10', 'My'), LPAD(col1, '10', 'My') FROM t_innodb; +SELECT LPAD('MySQL', '3', 'My'), LPAD('MySQL', '10', 'My'), LPAD(col1, '10', 'My') FROM t_duckdb; + +--echo -------------------------- +--echo 25. LTRIM() +--echo -------------------------- + +SELECT LTRIM(' MySQL'), LTRIM(' 数据库') FROM t_innodb; +SELECT LTRIM(' MySQL'), LTRIM(' 数据库') FROM t_duckdb; + +--echo -------------------------- +--echo 26. MAKE_SET() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 27. MATCH() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 28. MID() +--echo -------------------------- + +SELECT MID('MySQL数据库', 3), MID('MySQL数据库', 3, 10), MID('MySQL数据库', 3, 5) FROM t_innodb; +SELECT MID('MySQL数据库', 3), MID('MySQL数据库', 3, 10), MID('MySQL数据库', 3, 5) FROM t_duckdb; + +--echo -------------------------- +--echo 29. NOT LIKE +--echo -------------------------- + +SELECT 'MySQL' NOT LIKE '%SQL', col1 LIKE '%SQL' FROM t_innodb; +SELECT 'MySQL' NOT LIKE '%SQL', col1 LIKE '%SQL' FROM t_duckdb; + +--echo -------------------------- +--echo 30. NOT REGEXP +--echo -------------------------- + +SELECT 'Michael!' NOT REGEXP '.*' FROM t_innodb; +SELECT 'Michael!' NOT REGEXP '.*' FROM t_duckdb; + +SELECT 'new*\n*line' NOT REGEXP 'new\\*.\\*line' FROM t_innodb; +SELECT 'new*\n*line' NOT REGEXP 'new\\*.\\*line' FROM t_duckdb; + +SELECT 'a' NOT REGEXP '^[a-d]' FROM t_innodb; +SELECT 'a' NOT REGEXP '^[a-d]' FROM t_duckdb; + +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'Michael!', '.*'), (2, 'new*\n*line', 'new\\*.\\*line'), + (3, 'a', '^[a-d]'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT col1 NOT REGEXP col2 FROM t_regexp; +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT col1 NOT REGEXP col2 FROM t_regexp; +DROP TABLE t_regexp; + +--echo -------------------------- +--echo 31. OCT() +--echo -------------------------- + +SELECT OCT('MySQL'), OCT(20250328), OCT(col1) FROM t_innodb; +SELECT OCT('MySQL'), OCT(20250328), OCT(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 32. OCTET_LENGTH() +--echo -------------------------- + +SELECT OCTET_LENGTH('MySQL'), OCTET_LENGTH('数据库'), OCTET_LENGTH(col1), OCTET_LENGTH(col2) FROM t_innodb; +SELECT OCTET_LENGTH('MySQL'), OCTET_LENGTH('数据库'), OCTET_LENGTH(col1), OCTET_LENGTH(col2) FROM t_duckdb; + +--echo -------------------------- +--echo 33. ORD() +--echo -------------------------- + +SELECT ORD('MySQL'), ORD('数据库'), ORD(col1) FROM t_innodb; +SELECT ORD('MySQL'), ORD('数据库'), ORD(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 34. POSITION() +--echo -------------------------- + +SELECT POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL') FROM t_innodb; +SELECT POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL'), POSITION('SQL' IN 'MySQLSQL') FROM t_duckdb; + +--echo -------------------------- +--echo 35. QUOTE() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 36. REGEXP +--echo -------------------------- + +SELECT 'Michael!' REGEXP '.*' FROM t_innodb; +SELECT 'Michael!' REGEXP '.*' FROM t_duckdb; + +SELECT 'new*\n*line' REGEXP 'new\\*.\\*line' FROM t_innodb; +SELECT 'new*\n*line' REGEXP 'new\\*.\\*line' FROM t_duckdb; + +SELECT 'a' REGEXP '^[a-d]' FROM t_innodb; +SELECT 'a' REGEXP '^[a-d]' FROM t_duckdb; + +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'Michael!', '.*'), (2, 'new*\n*line', 'new\\*.\\*line'), + (3, 'a', '^[a-d]'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT col1 REGEXP col2 FROM t_regexp; +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT col1 REGEXP col2 FROM t_regexp; +DROP TABLE t_regexp; + +--echo -------------------------- +--echo 37. REGEXP_INSTR() +--echo -------------------------- + +SELECT REGEXP_INSTR('dog cat dog', 'dog') FROM t_innodb; +SELECT REGEXP_INSTR('dog cat dog', 'dog') FROM t_duckdb; + +# MariaDB 11.4 does not support 3+ arg REGEXP_INSTR (MySQL 8.0 feature) +# SELECT REGEXP_INSTR('dog cat dog', 'dog', 2) FROM t_innodb; +# SELECT REGEXP_INSTR('dog cat dog', 'dog', 2) FROM t_duckdb; + +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{2}') FROM t_innodb; +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{2}') FROM t_duckdb; + +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{4}') FROM t_innodb; +SELECT REGEXP_INSTR('aa aaa aaaa', 'a{4}') FROM t_duckdb; + +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'dog cat dog', 'dog'), (2, 'aa aaa aaaa', 'a{2}'), + (3, 'aa aaa aaaa', 'a{4}'), (4, NULL, 'abc'), (5, 'abc', NULL); +SELECT REGEXP_INSTR(col1, col2) FROM t_regexp; +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_INSTR(col1, col2) FROM t_regexp; +DROP TABLE t_regexp; + +--echo -------------------------- +--echo 38. REGEXP_LIKE() — skipped, not available in MariaDB 11.4 +--echo -------------------------- + +# REGEXP_LIKE is MySQL 8.0 only. MariaDB uses REGEXP / RLIKE instead. + +--echo -------------------------- +--echo 39. REGEXP_REPLACE() +--echo -------------------------- + +SELECT REGEXP_REPLACE('a b c', 'b', 'X') FROM t_innodb; +SELECT REGEXP_REPLACE('a b c', 'b', 'X') FROM t_duckdb; + +# 5-arg REGEXP_REPLACE is MySQL 8.0 only +# SELECT REGEXP_REPLACE('abc def ghi', '[a-z]+', 'X', 1, 3) FROM t_innodb; +# SELECT REGEXP_REPLACE('abc def ghi', '[a-z]+', 'X', 1, 3) FROM t_duckdb; + +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100), col3 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'a b c', 'b', 'X'), (2, 'abc def ghi', '[a-z]+', 'X'), + (3, NULL, 'a{4}', 'X'), (4, 'abc def ghi', NULL, 'X'), (5, 'abc def ghi', '[a-z]+', NULL); +SELECT REGEXP_REPLACE(col1, col2, col3) FROM t_regexp; +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_REPLACE(col1, col2, col3) FROM t_regexp; +DROP TABLE t_regexp; + +--echo -------------------------- +--echo 40. REGEXP_SUBSTR() +--echo -------------------------- + +SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+') FROM t_innodb; +SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+') FROM t_duckdb; + +# 4-arg REGEXP_SUBSTR is MySQL 8.0 only +# SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+', 1, 3) FROM t_innodb; +# SELECT REGEXP_SUBSTR('abc def ghi', '[a-z]+', 1, 3) FROM t_duckdb; + +CREATE TABLE t_regexp (id INT PRIMARY KEY, col1 VARCHAR(100), col2 VARCHAR(100)); +INSERT INTO t_regexp VALUES (1, 'a b c', 'b'), (2, 'abc def ghi', '[a-z]+'); +SELECT REGEXP_SUBSTR(col1, col2) FROM t_regexp; +ALTER TABLE t_regexp ENGINE=DuckDB; +SELECT REGEXP_SUBSTR(col1, col2) FROM t_regexp; +DROP TABLE t_regexp; + +--echo -------------------------- +--echo 41. REPEAT() +--echo -------------------------- + +SELECT REPEAT('MySQL', 2), REPEAT('数据库', 2), REPEAT(col1, 2) FROM t_innodb; +SELECT REPEAT('MySQL', 2), REPEAT('数据库', 2), REPEAT(col1, 2) FROM t_duckdb; + +--echo -------------------------- +--echo 42. REPLACE() +--echo -------------------------- + +SELECT REPLACE('MySQL', 'SQL', 'Database'), REPLACE('数据库', '库', '管理'), REPLACE(col1, 'SQL', 'Database') FROM t_innodb; +SELECT REPLACE('MySQL', 'SQL', 'Database'), REPLACE('数据库', '库', '管理'), REPLACE(col1, 'SQL', 'Database') FROM t_duckdb; + +--echo -------------------------- +--echo 43. REVERSE() +--echo -------------------------- + +SELECT REVERSE('MySQL'), REVERSE('数据库'), REVERSE(col1) FROM t_innodb; +SELECT REVERSE('MySQL'), REVERSE('数据库'), REVERSE(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 44. RIGHT() +--echo -------------------------- + +SELECT RIGHT('MySQL', 2), RIGHT('数据库', 2), RIGHT(col1, 2) FROM t_innodb; +SELECT RIGHT('MySQL', 2), RIGHT('数据库', 2), RIGHT(col1, 2) FROM t_duckdb; + +--echo -------------------------- +--echo 45. RLIKE +--echo -------------------------- + +SELECT 'Michael!' RLIKE '.*' FROM t_innodb; +SELECT 'Michael!' RLIKE '.*' FROM t_duckdb; + +SELECT 'new*\n*line' RLIKE 'new\\*.\\*line' FROM t_innodb; +SELECT 'new*\n*line' RLIKE 'new\\*.\\*line' FROM t_duckdb; + +SELECT 'a' RLIKE '^[a-d]' FROM t_innodb; +SELECT 'a' RLIKE '^[a-d]' FROM t_duckdb; + +--echo -------------------------- +--echo 46. RPAD() +--echo -------------------------- + +SELECT RPAD('MySQL', '3', 'My'), RPAD('MySQL', '10', 'My'), RPAD(col1, '10', 'My') FROM t_innodb; +SELECT RPAD('MySQL', '3', 'My'), RPAD('MySQL', '10', 'My'), RPAD(col1, '10', 'My') FROM t_duckdb; + +--echo -------------------------- +--echo 47. RTRIM() +--echo -------------------------- + +SELECT RTRIM('MySQL '), RTRIM('数据库 ') FROM t_innodb; +SELECT RTRIM('MySQL '), RTRIM('数据库 ') FROM t_duckdb; + +--echo -------------------------- +--echo 48. SOUNDEX() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 49. SOUNDS LIKE +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 50. SPACE() +--echo -------------------------- + +# SELECT SPACE(6) FROM t_innodb; +# SELECT SPACE(6) FROM t_duckdb; + +--echo -------------------------- +--echo 51. STRCMP() +--echo -------------------------- + +SELECT STRCMP('MySQL', 'mysql'), STRCMP('MySQL', 'DuckDB') FROM t_innodb; +SELECT STRCMP('MySQL', 'mysql'), STRCMP('MySQL', 'DuckDB') FROM t_duckdb; + +--echo -------------------------- +--echo 52. SUBSTR() +--echo -------------------------- + +SELECT SUBSTR('MySQL数据库', 3), SUBSTR('MySQL数据库', 3, 10), SUBSTR('MySQL数据库', 3, 5) FROM t_innodb; +SELECT SUBSTR('MySQL数据库', 3), SUBSTR('MySQL数据库', 3, 10), SUBSTR('MySQL数据库', 3, 5) FROM t_duckdb; + +--echo -------------------------- +--echo 53. SUBSTRING() +--echo -------------------------- + +SELECT SUBSTRING('MySQL数据库', 3), SUBSTRING('MySQL数据库', 3, 10), SUBSTRING('MySQL数据库', 3, 5) FROM t_innodb; +SELECT SUBSTRING('MySQL数据库', 3), SUBSTRING('MySQL数据库', 3, 10), SUBSTRING('MySQL数据库', 3, 5) FROM t_duckdb; + +--echo -------------------------- +--echo 54. SUBSTRING_INDEX() +--echo -------------------------- + +SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) FROM t_innodb; +SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2) FROM t_duckdb; + +--echo -------------------------- +--echo 55. TRIM() +--echo -------------------------- + +SELECT TRIM(' bar '), TRIM(LEADING 'x' FROM 'xxxbarxxx'), TRIM(BOTH 'x' FROM 'xxxbarxxx'), TRIM(TRAILING 'xyz' FROM 'barxxyz') FROM t_innodb; +SELECT TRIM(' bar '), TRIM(LEADING 'x' FROM 'xxxbarxxx'), TRIM(BOTH 'x' FROM 'xxxbarxxx'), TRIM(TRAILING 'xyz' FROM 'barxxyz') FROM t_duckdb; + +--echo -------------------------- +--echo 56. UCASE() +--echo -------------------------- + +SELECT UCASE('MySQL'), UCASE('数据库'), UCASE(col1) FROM t_innodb; +SELECT UCASE('MySQL'), UCASE('数据库'), UCASE(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 57. UNHEX() +--echo -------------------------- + +SELECT UNHEX('4D7953514C'), UNHEX('E695B0E68DAEE5BA93'), UNHEX(HEX(col1)) FROM t_innodb; +SELECT UNHEX('4D7953514C'), UNHEX('E695B0E68DAEE5BA93'), UNHEX(HEX(col1)) FROM t_duckdb; + +--echo -------------------------- +--echo 58. UPPER() +--echo -------------------------- + +SELECT UPPER('MySQL'), UPPER('数据库'), UPPER(col1) FROM t_innodb; +SELECT UPPER('MySQL'), UPPER('数据库'), UPPER(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 59. WEIGHT_STRING() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 59. TO_BASE64() +--echo -------------------------- + +SELECT TO_BASE64('MySQL'), TO_BASE64('数据库'), TO_BASE64(col1) FROM t_innodb; +SELECT TO_BASE64('MySQL'), TO_BASE64('数据库'), TO_BASE64(col1) FROM t_duckdb; + +--echo -------------------------- +--echo 59. FROM_BASE64() +--echo -------------------------- + +SELECT FROM_BASE64(TO_BASE64('MySQL')), FROM_BASE64(TO_BASE64('数据库')), FROM_BASE64(TO_BASE64(col1)) FROM t_innodb; +SELECT FROM_BASE64(TO_BASE64('MySQL')), FROM_BASE64(TO_BASE64('数据库')), FROM_BASE64(TO_BASE64(col1)) FROM t_duckdb; + +DROP TABLE t_innodb; +DROP TABLE t_duckdb; +DROP DATABASE test_duckdb; + +SET GLOBAL duckdb_dml_in_batch = @saved_duckdb_dml_in_batch; diff --git a/storage/duckdb/mysql-test/duckdb/t/duckdb_time_func.test b/storage/duckdb/mysql-test/duckdb/t/duckdb_time_func.test new file mode 100644 index 0000000000000..d74a7af3d6139 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/duckdb_time_func.test @@ -0,0 +1,847 @@ +--source ../include/have_duckdb.inc + +# Compared with MySQL, duckdb's date functions generally have the following incompatibilities: +# 1. Conversion between integer type and date/timestamp/time type +# 2. When invalid date is used as a parameter, MySQL will return NULL and DuckDB will report an error. +# 3. MySQL allows a larger time range : -838:59:59 ~ 838:59:59, but duckdb : 00:00:00 ~ 23:59:59 +# 4. DuckDB does not support some interval unit such as SECOND_MICROSECOND, MINUTE_MICROSECOND, MINUTE_SECOND, etc. +# 5. DuckDB does not supoort compound expression unit, such as 'interval -1 day'. +# 6. [Partially Solved]Duckdb does not support week format, which will affect week(), yearweek(), str_to_date(), date_format() functions. +# 7. [Partially Solved] For a time type field, when implicitly converted to TIMESTAMP, MySQL will be current_date + time, while duckdb will be 1970-01-01 + time or cannot convert. +# 8. [Solved] For CURRENT_TIME(), CURRENT_TIMESTAMP(), CURTIME(), LOCALTIME(), LOCALTIMESTAMP(), NOW(), UTC_TIME(), UTC_TIMESTAMP(), the fsp argument is not support. +# 9. MySQL accepts NULL as function input but DuckDB does not. +# The above scenarios cannot guarantee compatibility and therefore will not appear in this test case. + +CREATE DATABASE test_duckdb; +USE test_duckdb; +CREATE TABLE t (id INT PRIMARY KEY) ENGINE = DuckDB; +INSERT INTO t VALUES (1); + +--echo --------------------------- +--echo 1. ADDDATE() +--echo --------------------------- +SELECT ADDDATE(DATE '2020-01-01', interval 1 day); +SELECT ADDDATE(DATE '2020-01-01', interval 1 day) FROM t; +SELECT ADDDATE(DATE '2020-01-01', interval 1 hour); +SELECT ADDDATE(DATE '2020-01-01', interval 1 hour) FROM t; +SELECT ADDDATE(DATE '2020-01-01', 1); +SELECT ADDDATE(DATE '2020-01-01', 1) FROM t; +SELECT ADDDATE(TIMESTAMP '2020-01-01 12:00:00', interval 1 day); +SELECT ADDDATE(TIMESTAMP '2020-01-01 12:00:00', interval 1 day) FROM t; +SET timestamp = 1753226301; +SELECT ADDDATE(TIME '12:00:00', interval 1 hour); +SELECT ADDDATE(TIME '12:00:00', interval 1 hour) FROM t; + +# Incompatibilities 7 +SELECT ADDDATE(TIME '12:00:00', interval 1 day); +SELECT ADDDATE(TIME '12:00:00', interval 1 day) FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 2. ADDTIME() +--echo --------------------------- + +SELECT addtime(TIMESTAMP '2007-12-31 23:59:59.999999', '1 1:1:1.000002'); +SELECT addtime(TIMESTAMP '2007-12-31 23:59:59.999999', '1 1:1:1.000002') FROM t; +SELECT addtime(TIMESTAMP '2007-12-31 23:59:59.999999', '1:1:1.000002'); +SELECT addtime(TIMESTAMP '2007-12-31 23:59:59.999999', '1:1:1.000002') FROM t; + +# Incompatibilities 3 +SET timestamp = 1753226301; +SELECT addtime(TIME '23:59:59.999999', '1 1:1:1.000002'); +SELECT addtime(TIME '23:59:59.999999', '1 1:1:1.000002') FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 3. CONVERT_TZ() +--echo --------------------------- + +SELECT CONVERT_TZ(TIMESTAMP '2004-01-01 12:00:00','GMT','MET'); +SELECT CONVERT_TZ(TIMESTAMP '2004-01-01 12:00:00','GMT','MET') FROM t; + +# timezone +SELECT CONVERT_TZ(TIMESTAMP '2004-01-01 12:00:00','+00:00','+10:00'); +--error ER_GET_ERRMSG +SELECT CONVERT_TZ(TIMESTAMP '2004-01-01 12:00:00','+00:00','+10:00') FROM t; + +--echo --------------------------- +--echo 4. CURDATE() +--echo --------------------------- + +SET timestamp = 1753226301; +SELECT CURDATE(); +SELECT CURDATE() FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 5. CURRENT_DATE, CURRENT_TIME() +--echo --------------------------- + +SET timestamp = 1753226301; +SELECT CURRENT_DATE; +SELECT CURRENT_DATE FROM t; +SELECT CURRENT_DATE(); +SELECT CURRENT_DATE() FROM t; + +SELECT CURRENT_TIME; +SELECT CURRENT_TIME FROM t; +SELECT CURRENT_TIME(); +SELECT CURRENT_TIME() FROM t; + +SELECT CURRENT_TIME(1); +SELECT CURRENT_TIME(1) FROM t; + +SELECT CURRENT_TIME(6); +SELECT CURRENT_TIME(6) FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 6. CURRENT_TIMESTAMP, CURRENT_TIMESTAMP() +--echo --------------------------- + +SET timestamp = 1753226301; +SELECT CURRENT_TIMESTAMP; +SELECT CURRENT_TIMESTAMP FROM t; +SELECT CURRENT_TIMESTAMP(); +SELECT CURRENT_TIMESTAMP() FROM t; + +SELECT CURRENT_TIMESTAMP(1); +SELECT CURRENT_TIMESTAMP(1) FROM t; +SELECT CURRENT_TIMESTAMP(6); +SELECT CURRENT_TIMESTAMP(6) FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 7. CURTIME() +--echo --------------------------- + +SET timestamp = 1753226301; +SELECT CURTIME(); +SELECT CURTIME() FROM t; + +SELECT CURTIME(1); +SELECT CURTIME(1) FROM t; +SELECT CURTIME(6); +SELECT CURTIME(6) FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 8. DATE() +--echo --------------------------- + +SELECT DATE(TIMESTAMP '2003-12-31 01:02:03'); +SELECT DATE(TIMESTAMP '2003-12-31 01:02:03') FROM t; +SELECT DATE(DATE '2003-12-31'); +SELECT DATE(DATE '2003-12-31') FROM t; + +# Incompatibilities 7 +SET timestamp = 1753226301; +SELECT DATE(TIME '01:02:03'); +--error ER_GET_ERRMSG +SELECT DATE(TIME '01:02:03') FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 9. DATEDIFF() +--echo --------------------------- + +SELECT DATEDIFF(TIMESTAMP '2007-12-31 23:59:59', DATE '2007-12-30'); +SELECT DATEDIFF(TIMESTAMP '2007-12-31 23:59:59', DATE '2007-12-30') FROM t; +SELECT DATEDIFF(TIMESTAMP '2010-11-30 23:59:59', DATE '2010-12-31'); +SELECT DATEDIFF(TIMESTAMP '2010-11-30 23:59:59', DATE '2010-12-31') FROM t; + +SET timestamp = 1753226301; +SELECT DATEDIFF(TIMESTAMP '2007-12-31 23:59:59', TIME '23:59:59'); +SELECT DATEDIFF(TIMESTAMP '2007-12-31 23:59:59', TIME '23:59:59') FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 10. DATE_ADD(),DATE_SUB() +--echo --------------------------- + +SELECT DATE_ADD(DATE '2020-01-01', interval 1 day); +SELECT DATE_ADD(DATE '2020-01-01', interval 1 day) FROM t; +SELECT DATE_ADD(DATE '2020-01-01', interval 1 hour); +SELECT DATE_ADD(DATE '2020-01-01', interval 1 hour) FROM t; +SELECT DATE_ADD(TIMESTAMP '2020-01-01 12:00:00', interval 1 day); +SELECT DATE_ADD(TIMESTAMP '2020-01-01 12:00:00', interval 1 day) FROM t; +SELECT DATE_ADD(TIME '12:00:00', interval 1 hour); +SELECT DATE_ADD(TIME '12:00:00', interval 1 hour) FROM t; + +# Incompatibilities 3 +SET timestamp = 1753226301; +SELECT DATE_ADD(TIME '12:00:00', interval 1 day); +SELECT DATE_ADD(TIME '12:00:00', interval 1 day) FROM t; +SET timestamp = default; + +SELECT DATE_SUB(DATE '2020-01-01', interval 1 day); +SELECT DATE_SUB(DATE '2020-01-01', interval 1 day) FROM t; +SELECT DATE_SUB(DATE '2020-01-01', interval 1 hour); +SELECT DATE_SUB(DATE '2020-01-01', interval 1 hour) FROM t; +SELECT DATE_SUB(TIMESTAMP '2020-01-01 12:00:00', interval 1 day); +SELECT DATE_SUB(TIMESTAMP '2020-01-01 12:00:00', interval 1 day) FROM t; +SET timestamp = 1753226301; +SELECT DATE_SUB(TIME '12:00:00', interval 1 hour); +SELECT DATE_SUB(TIME '12:00:00', interval 1 hour) FROM t; + +# Incompatibilities 7 +SELECT DATE_SUB(TIME '12:00:00', interval 1 day); +SELECT DATE_SUB(TIME '12:00:00', interval 1 day) FROM t; +SET timestamp = default; + +--echo --------------------------- +--echo 11. DATE_FORMAT() +--echo --------------------------- + +SELECT DATE_FORMAT(TIMESTAMP '2009-10-04 22:23:00.123456', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v'); +SELECT DATE_FORMAT(TIMESTAMP '2009-10-04 22:23:00.123456', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v') FROM t; + +SELECT DATE_FORMAT(DATE '2009-10-04', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v'); +SELECT DATE_FORMAT(DATE '2009-10-04', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v') FROM t; + +# Incompatibilities 3 +SET timestamp = 1753226301; +SELECT DATE_FORMAT(TIME '22:23:00.123456', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v'); +SELECT DATE_FORMAT(TIME '22:23:00.123456', '%a %b %c %D %d %e %f %H %h %I %i %j %k %l %M %m %p %r %S %s %T %W %w %Y %y %U %x %v') FROM t; +SET timestamp = default; + +# Incompatibilities 6: %u, %V, %X is not supoorted now. +SELECT DATE_FORMAT(TIMESTAMP '2009-10-04 22:23:00.123456', '%V, %X, %u'); +--error ER_GET_ERRMSG +SELECT DATE_FORMAT(TIMESTAMP '2009-10-04 22:23:00.123456', '%V, %X, %u') FROM t; + +--echo --------------------------- +--echo 12. DAY() +--echo --------------------------- + +SELECT DAY(DATE '2007-02-03'); +SELECT DAY(DATE '2007-02-03') FROM t; +SELECT DAY(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT DAY(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT DAY(TIME '22:23:00.123456'); +SELECT DAY(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 13. DAYNAME() +--echo -------------------------- + +SELECT DAYNAME(DATE '2007-02-03'); +SELECT DAYNAME(DATE '2007-02-03') FROM t; +SELECT DAYNAME(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT DAYNAME(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT DAYNAME(TIME '22:23:00.123456'); +SELECT DAYNAME(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 14. DAYOFMONTH() +--echo -------------------------- + +SELECT DAYOFMONTH(DATE '2007-02-03'); +SELECT DAYOFMONTH(DATE '2007-02-03') FROM t; +SELECT DAYOFMONTH(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT DAYOFMONTH(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT DAYOFMONTH(TIME '22:23:00.123456'); +SELECT DAYOFMONTH(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 15. DAYOFWEEK() +--echo -------------------------- + +SELECT DAYOFWEEK(DATE '2007-02-03'); +SELECT DAYOFWEEK(DATE '2007-02-03') FROM t; +SELECT DAYOFWEEK(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT DAYOFWEEK(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT DAYOFWEEK(TIME '22:23:00.123456'); +SELECT DAYOFWEEK(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 16. DAYOFYEAR() +--echo -------------------------- + +SELECT DAYOFYEAR(DATE '2007-02-03'); +SELECT DAYOFYEAR(DATE '2007-02-03') FROM t; +SELECT DAYOFYEAR(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT DAYOFYEAR(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT DAYOFYEAR(TIME '22:23:00.123456'); +SELECT DAYOFYEAR(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 17. EXTRACT() +--echo -------------------------- + +SELECT EXTRACT(YEAR FROM DATE '2019-07-02'); +SELECT EXTRACT(HOUR FROM DATE '2019-07-02'); +SELECT EXTRACT(YEAR FROM DATE '2019-07-02') FROM t; +SELECT EXTRACT(HOUR FROM DATE '2019-07-02') FROM t; + +SELECT EXTRACT(YEAR FROM TIMESTAMP '2019-07-02 01:02:03'); +SELECT EXTRACT(HOUR FROM TIMESTAMP '2019-07-02 01:02:03'); +SELECT EXTRACT(YEAR FROM TIMESTAMP '2019-07-02 01:02:03') FROM t; +SELECT EXTRACT(HOUR FROM TIMESTAMP '2019-07-02 01:02:03') FROM t; + +SELECT EXTRACT(HOUR FROM TIME '01:02:03'); +SELECT EXTRACT(HOUR FROM TIME '01:02:03') FROM t; + +SET timestamp = 1753226301; +SELECT EXTRACT(YEAR FROM TIME '01:02:03'); +SELECT EXTRACT(YEAR FROM TIME '01:02:03') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 18. FROM_DAYS() +--echo -------------------------- + +SELECT FROM_DAYS(0); +SELECT FROM_DAYS(0) FROM t; +SELECT FROM_DAYS(730669); +SELECT FROM_DAYS(730669) FROM t; + +--echo -------------------------- +--echo 19. FROM_UNIXTIME() +--echo -------------------------- + +SELECT FROM_UNIXTIME(1447430881); +SELECT FROM_UNIXTIME(1447430881) FROM t; + +SELECT FROM_UNIXTIME(1447430881, '%Y %D %M %h:%i:%s'); +SELECT FROM_UNIXTIME(1447430881, '%Y %D %M %h:%i:%s') FROM t; + +--echo -------------------------- +--echo 20. GET_FORMAT() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 21. HOUR() +--echo -------------------------- + +SELECT HOUR(TIME '10:05:03'); +SELECT HOUR(TIME '10:05:03') FROM t; + +SELECT HOUR(DATE '2019-07-02'); +SELECT HOUR(DATE '2019-07-02') FROM t; + +--echo -------------------------- +--echo 22. LAST_DAY() +--echo -------------------------- + +SELECT LAST_DAY(DATE '2003-02-05'); +SELECT LAST_DAY(DATE '2003-02-05') FROM t; + +SELECT LAST_DAY(DATE '2004-02-05'); +SELECT LAST_DAY(DATE '2004-02-05') FROM t; + +SELECT LAST_DAY(TIMESTAMP '2004-01-01 01:01:01'); +SELECT LAST_DAY(TIMESTAMP '2004-01-01 01:01:01') FROM t; + +SET timestamp = 1753226301; +SELECT LAST_DAY(TIME '01:01:01'); +SELECT LAST_DAY(TIME '01:01:01') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 23. LOCALTIME, LOCALTIME() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT LOCALTIME; +SELECT LOCALTIME FROM t; +SELECT LOCALTIME(); +SELECT LOCALTIME() FROM t; + +SELECT LOCALTIME(1); +SELECT LOCALTIME(1) FROM t; +SELECT LOCALTIME(6); +SELECT LOCALTIME(6) FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 24. LOCALTIMESTAMP, LOCALTIMESTAMP() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT LOCALTIMESTAMP; +SELECT LOCALTIMESTAMP FROM t; +SELECT LOCALTIMESTAMP(); +SELECT LOCALTIMESTAMP() FROM t; + +SELECT LOCALTIMESTAMP(1); +SELECT LOCALTIMESTAMP(1) FROM t; +SELECT LOCALTIMESTAMP(6); +SELECT LOCALTIMESTAMP(6) FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 25. MAKEDATE() +--echo -------------------------- + +SELECT MAKEDATE(2011,31), MAKEDATE(2011,32); +SELECT MAKEDATE(2011,31), MAKEDATE(2011,32) FROM t; +SELECT MAKEDATE(2011,365), MAKEDATE(2014,365); +SELECT MAKEDATE(2011,365), MAKEDATE(2014,365) FROM t; +SELECT MAKEDATE(2011,0); +SELECT MAKEDATE(2011,0) FROM t; + +--echo -------------------------- +--echo 26. MAKETIME() +--echo -------------------------- + +SELECT MAKETIME(12,15,30); +SELECT MAKETIME(12,15,30) FROM t; + +# Incompatibilities 3 +SELECT MAKETIME(838,59,59); +--error ER_GET_ERRMSG +SELECT MAKETIME(838,59,59) FROM t; + +--echo -------------------------- +--echo 27. MICROSECOND() +--echo -------------------------- + +SELECT MICROSECOND(DATE '2007-02-03'); +SELECT MICROSECOND(DATE '2007-02-03') FROM t; +SELECT MICROSECOND(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT MICROSECOND(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SELECT MICROSECOND(TIME '22:23:00.123456'); +SELECT MICROSECOND(TIME '22:23:00.123456') FROM t; + +--echo -------------------------- +--echo 28. MINUTE() +--echo -------------------------- + +SELECT MINUTE(DATE '2007-02-03'); +SELECT MINUTE(DATE '2007-02-03') FROM t; +SELECT MINUTE(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT MINUTE(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SELECT MINUTE(TIME '22:23:00.123456'); +SELECT MINUTE(TIME '22:23:00.123456') FROM t; + +--echo -------------------------- +--echo 29. MONTHNAME() +--echo -------------------------- + +SELECT MONTHNAME(DATE '2007-02-03'); +SELECT MONTHNAME(DATE '2007-02-03') FROM t; +SELECT MONTHNAME(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT MONTHNAME(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT MONTHNAME(TIME '22:23:00.123456'); +SELECT MONTHNAME(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 30. NOW() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT NOW(); +SELECT NOW() FROM t; + +SELECT NOW(1); +SELECT NOW(1) FROM t; + +SELECT NOW(6); +SELECT NOW(6) FROM t; + +SET timestamp = default; + +--echo -------------------------- +--echo 31. PERIOD_ADD() +--echo -------------------------- + +SELECT PERIOD_ADD(200801,2); +SELECT PERIOD_ADD(200801,2) FROM t; +SELECT PERIOD_ADD(801,2); +SELECT PERIOD_ADD(801,2) FROM t; +SELECT PERIOD_ADD(8801,2); +SELECT PERIOD_ADD(8801,2) FROM t; +SELECT PERIOD_ADD(1,2); +SELECT PERIOD_ADD(1,2) FROM t; +SELECT PERIOD_ADD(10101, 2); +SELECT PERIOD_ADD(10101, 2) FROM t; + +# The year range of duckdb only supports 0000~9999 +SELECT PERIOD_ADD(12340101, 2); +SELECT PERIOD_ADD(12340101, 2) FROM t; + +--echo -------------------------- +--echo 32. PERIOD_DIFF() +--echo -------------------------- + +SELECT PERIOD_DIFF(200801,801); +SELECT PERIOD_DIFF(200801,801) FROM t; +SELECT PERIOD_DIFF(200801,8801); +SELECT PERIOD_DIFF(200801,8801) FROM t; +SELECT PERIOD_DIFF(200801,1); +SELECT PERIOD_DIFF(200801,1) FROM t; +SELECT PERIOD_DIFF(200801,10101); +SELECT PERIOD_DIFF(200801,10101) FROM t; + +# The year range of duckdb only supports 0000~9999 +SELECT PERIOD_DIFF(200801, 12340101); +SELECT PERIOD_DIFF(200801, 12340101) FROM t; + +--echo -------------------------- +--echo 33. QUARTER() +--echo -------------------------- + +SELECT QUARTER(DATE '2007-02-03'); +SELECT QUARTER(DATE '2007-02-03') FROM t; +SELECT QUARTER(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT QUARTER(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SET timestamp = 1753226301; +SELECT QUARTER(TIME '22:23:00.123456'); +SELECT QUARTER(TIME '22:23:00.123456') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 34. SECOND() +--echo -------------------------- + +SELECT SECOND(DATE '2007-02-03'); +SELECT SECOND(DATE '2007-02-03') FROM t; +SELECT SECOND(TIMESTAMP '2007-02-03 22:23:00.123456'); +SELECT SECOND(TIMESTAMP '2007-02-03 22:23:00.123456') FROM t; + +SELECT SECOND(TIME '22:23:00.123456'); +SELECT SECOND(TIME '22:23:00.123456') FROM t; + +--echo -------------------------- +--echo 35. SEC_TO_TIME() +--echo -------------------------- + +SELECT SEC_TO_TIME(10000); +SELECT SEC_TO_TIME(10000) FROM t; + +# Incompatibilities 3 +SELECT SEC_TO_TIME(100000); +SELECT SEC_TO_TIME(100000) FROM t; + +--echo -------------------------- +--echo 36. STR_TO_DATE() +--echo -------------------------- + +SELECT STR_TO_DATE('01,5,2013','%d,%m,%Y'); +SELECT STR_TO_DATE('01,5,2013','%d,%m,%Y') FROM t; + +SELECT STR_TO_DATE('a09:30:17','a%h:%i:%s'); +SELECT STR_TO_DATE('a09:30:17','a%h:%i:%s') FROM t; + +SELECT STR_TO_DATE('May 1, 2013','%M %d,%Y'); +SELECT STR_TO_DATE('May 1, 2013','%M %d,%Y') FROM t; + +SELECT STR_TO_DATE('a09:30:17','%h:%i:%s'); +SELECT STR_TO_DATE('a09:30:17','%h:%i:%s') FROM t; + +SELECT STR_TO_DATE('abc','abc'); +SELECT STR_TO_DATE('abc','abc') FROM t; + +SELECT STR_TO_DATE('9','%m'); +SELECT STR_TO_DATE('9','%m') FROM t; + +SELECT STR_TO_DATE('09:30:17a','%h:%i:%s'); +SELECT STR_TO_DATE('09:30:17a','%h:%i:%s') FROM t; + +# Besides, the sql_mode will affect sql results in MySQL. + +--echo -------------------------- +--echo 37. SUBDATE() +--echo -------------------------- + +SELECT SUBDATE(DATE '2020-01-01', interval 1 day); +SELECT SUBDATE(DATE '2020-01-01', interval 1 day) FROM t; +SELECT SUBDATE(DATE '2020-01-01', interval 1 hour); +SELECT SUBDATE(DATE '2020-01-01', interval 1 hour) FROM t; +SELECT SUBDATE(DATE '2020-01-01', 1); +SELECT SUBDATE(DATE '2020-01-01', 1) FROM t; +SELECT SUBDATE(TIMESTAMP '2020-01-01 12:00:00', interval 1 day); +SELECT SUBDATE(TIMESTAMP '2020-01-01 12:00:00', interval 1 day) FROM t; +SET timestamp = 1753226301; +SELECT SUBDATE(TIME '12:00:00', interval 1 hour); +SELECT SUBDATE(TIME '12:00:00', interval 1 hour) FROM t; + +# Incompatibilities 7 +SELECT SUBDATE(TIME '12:00:00', interval 1 day); +SELECT SUBDATE(TIME '12:00:00', interval 1 day) FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 38. SUBTIME() +--echo -------------------------- + +SELECT SUBTIME(TIMESTAMP '2007-12-31 23:59:59.999999', '1 1:1:1.000002'); +SELECT SUBTIME(TIMESTAMP '2007-12-31 23:59:59.999999', '1 1:1:1.000002') FROM t; +SELECT SUBTIME(TIMESTAMP '2007-12-31 23:59:59.999999', '1:1:1.000002'); +SELECT SUBTIME(TIMESTAMP '2007-12-31 23:59:59.999999', '1:1:1.000002') FROM t; + +# Incompatibilities 3 +SELECT SUBTIME(TIME '23:59:59.999999', '1 1:1:1.000002'); +SELECT SUBTIME(TIME '23:59:59.999999', '1 1:1:1.000002') FROM t; + +--echo -------------------------- +--echo 39. SYSDATE() +--echo -------------------------- + +# SELECT SYSDATE(); +# SELECT SYSDATE() FROM t; + +--echo -------------------------- +--echo 40. TIME() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 41. TIMEDIFF() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 42. TIMESTAMP() +--echo -------------------------- + +# Not support yet + +--echo -------------------------- +--echo 42. TIMESTAMPADD() +--echo -------------------------- + +SELECT TIMESTAMPADD(MINUTE, 1, DATE '2003-01-02'); +SELECT TIMESTAMPADD(MINUTE, 1, DATE '2003-01-02') FROM t; + +SELECT TIMESTAMPADD(WEEK, 1, DATE '2003-01-02'); +SELECT TIMESTAMPADD(WEEK, 1, DATE '2003-01-02') FROM t; + +--echo -------------------------- +--echo 43. TIMESTAMPDIFF() +--echo -------------------------- + +SELECT TIMESTAMPDIFF(MONTH, DATE '2003-02-01', DATE '2003-05-01'); +SELECT TIMESTAMPDIFF(MONTH, DATE '2003-02-01', DATE '2003-05-01') FROM t; + +SELECT TIMESTAMPDIFF(YEAR, DATE '2002-05-01', DATE '2001-01-01'); +SELECT TIMESTAMPDIFF(YEAR, DATE '2002-05-01', DATE '2001-01-01') FROM t; + +SELECT TIMESTAMPDIFF(MINUTE, DATE '2003-02-01', TIMESTAMP '2003-05-01 12:05:55'); +SELECT TIMESTAMPDIFF(MINUTE, DATE '2003-02-01', TIMESTAMP '2003-05-01 12:05:55') FROM t; + +--echo -------------------------- +--echo 44. TIME_FORMAT() +--echo -------------------------- + +SELECT TIME_FORMAT(TIME '01:02:03', '%H %k %h %I %l'); +SELECT TIME_FORMAT(TIME '01:02:03', '%H %k %h %I %l') FROM t; + +# Incompatibilities 3 +SELECT TIME_FORMAT(TIME '100:02:03', '%H %k %h %I %l'); +--error ER_GET_ERRMSG +SELECT TIME_FORMAT(TIME '100:02:03', '%H %k %h %I %l') FROM t; + +--echo -------------------------- +--echo 44. TIME_TO_SEC() +--echo -------------------------- + +SELECT TIME_TO_SEC(TIME '22:23:00'); +SELECT TIME_TO_SEC(TIME '22:23:00') FROM t; + +# Incompatibilities 3 +SELECT TIME_TO_SEC(TIME '220:23:00'); +--error ER_GET_ERRMSG +SELECT TIME_TO_SEC(TIME '220:23:00') FROM t; + +--echo -------------------------- +--echo 45. TO_DAYS() +--echo -------------------------- + +SELECT TO_DAYS('2007-10-07'); +SELECT TO_DAYS('2007-10-07') FROM t; + +# Incompatibilities 7 +SET timestamp = 1753226301; +SELECT TO_DAYS(TIME '12:00:00'); +--error ER_GET_ERRMSG +SELECT TO_DAYS(TIME '12:00:00') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 46. TO_SECONDS() +--echo -------------------------- + +SELECT TO_SECONDS(DATE '2009-11-29'); +SELECT TO_SECONDS(DATE '2009-11-29') FROM t; + +SELECT TO_SECONDS(TIMESTAMP '2009-11-29 13:43:32'); +SELECT TO_SECONDS(TIMESTAMP '2009-11-29 13:43:32') FROM t; + +SET timestamp = 1753226301; +SELECT TO_SECONDS(TIME '12:00:00'); +SELECT TO_SECONDS(TIME '12:00:00') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 47. UNIX_TIMESTAMP() +--echo -------------------------- + +SELECT UNIX_TIMESTAMP(TIMESTAMP '2015-11-13 10:20:19'); +SELECT UNIX_TIMESTAMP(TIMESTAMP '2015-11-13 10:20:19') FROM t; + +SET timestamp = 1753226301; +SELECT UNIX_TIMESTAMP(); +SELECT UNIX_TIMESTAMP() FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 48. UTC_DATE() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT UTC_DATE(); +SELECT UTC_DATE() FROM t; +SELECT UTC_DATE; +SELECT UTC_DATE FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 49. UTC_TIME() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT UTC_TIME(); +SELECT UTC_TIME() FROM t; +SELECT UTC_TIME; +SELECT UTC_TIME FROM t; +SELECT UTC_TIME(1); +SELECT UTC_TIME(1) FROM t; +SELECT UTC_TIME(6); +SELECT UTC_TIME(6) FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 50. UTC_TIMESTAMP() +--echo -------------------------- + +SET timestamp = 1753226301; +SELECT UTC_TIMESTAMP(); +SELECT UTC_TIMESTAMP() FROM t; +SELECT UTC_TIMESTAMP; +SELECT UTC_TIMESTAMP FROM t; + +SELECT UTC_TIMESTAMP(1); +SELECT UTC_TIMESTAMP(1) FROM t; +SELECT UTC_TIMESTAMP(6); +SELECT UTC_TIMESTAMP(6) FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 51. WEEK() +--echo -------------------------- + +SELECT WEEK(DATE '2009-01-01'); +SELECT WEEK(DATE '2009-01-01') FROM t; +set @old_default_week_format = @@default_week_format; +set default_week_format = 1; +SELECT WEEK(DATE '2009-01-01'); +SELECT WEEK(DATE '2009-01-01') FROM t; +set default_week_format = 2; +SELECT WEEK(DATE '2009-01-01'); +SELECT WEEK(DATE '2009-01-01') FROM t; +set default_week_format = 3; +SELECT WEEK(DATE '2009-01-01'); +SELECT WEEK(DATE '2009-01-01') FROM t; +set default_week_format = @old_default_week_format; + +SELECT WEEK(DATE '2009-01-01', 4); +SELECT WEEK(DATE '2009-01-01', 4) FROM t; +SELECT WEEK(DATE '2009-01-01', 5); +SELECT WEEK(DATE '2009-01-01', 5) FROM t; +SELECT WEEK(DATE '2009-01-01', 6); +SELECT WEEK(DATE '2009-01-01', 6) FROM t; +SELECT WEEK(DATE '2009-01-01', 7); +SELECT WEEK(DATE '2009-01-01', 7) FROM t; + +--echo -------------------------- +--echo 52. WEEKDAY() +--echo -------------------------- + +SELECT WEEKDAY(TIMESTAMP '2008-02-03 22:23:00'); +SELECT WEEKDAY(TIMESTAMP '2008-02-03 22:23:00') FROM t; + +--echo -------------------------- +--echo 53. WEEKOFYEAR() +--echo -------------------------- + +SELECT WEEKOFYEAR(DATE '2008-02-20'); +SELECT WEEKOFYEAR(DATE '2008-02-20') FROM t; +SELECT WEEKOFYEAR(TIMESTAMP '2008-02-03 22:23:00'); +SELECT WEEKOFYEAR(TIMESTAMP '2008-02-03 22:23:00') FROM t; + +SET timestamp = 1753226301; +SELECT WEEKOFYEAR(TIME '12:20:22'); +SELECT WEEKOFYEAR(TIME '12:20:22') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 54. YEAR() +--echo -------------------------- + +SELECT YEAR(DATE '1987-01-01'); +SELECT YEAR(DATE '1987-01-01') FROM t; +SELECT YEAR(TIMESTAMP '1987-01-01 13:12:14'); +SELECT YEAR(TIMESTAMP '1987-01-01 13:12:14') FROM t; + +SET timestamp = 1753226301; +SELECT YEAR(TIME '12:20:22'); +SELECT YEAR(TIME '12:20:22') FROM t; +SET timestamp = default; + +--echo -------------------------- +--echo 55. YEARWEEK() +--echo -------------------------- + +SELECT YEARWEEK(DATE '2009-01-01'); +SELECT YEARWEEK(DATE '2009-01-01') FROM t; + +set @old_default_week_format = @@default_week_format; +set default_week_format = 1; +SELECT YEARWEEK(DATE '2009-01-01'); +SELECT YEARWEEK(DATE '2009-01-01') FROM t; +set default_week_format = @old_default_week_format; + +SELECT YEARWEEK(DATE '2009-01-01', 1); +SELECT YEARWEEK(DATE '2009-01-01', 1) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 2); +SELECT YEARWEEK(DATE '2009-01-01', 2) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 3); +SELECT YEARWEEK(DATE '2009-01-01', 3) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 4); +SELECT YEARWEEK(DATE '2009-01-01', 4) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 5); +SELECT YEARWEEK(DATE '2009-01-01', 5) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 6); +SELECT YEARWEEK(DATE '2009-01-01', 6) FROM t; +SELECT YEARWEEK(DATE '2009-01-01', 7); +SELECT YEARWEEK(DATE '2009-01-01', 7) FROM t; + +DROP TABLE t; +DROP DATABASE test_duckdb; diff --git a/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type-master.opt b/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type-master.opt new file mode 100644 index 0000000000000..cd4976292dddb --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type-master.opt @@ -0,0 +1,2 @@ +--max_allowed_packet=128M +--default-time-zone=+00:00 diff --git a/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type.test b/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type.test new file mode 100644 index 0000000000000..e21b2cb311e89 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/feature_duckdb_data_type.test @@ -0,0 +1,152 @@ +--source ../include/have_duckdb.inc +# Feature ORC data type test + +# Prepare + +create database mytest; +use mytest; + +# 1. Numeric data types +-- echo +-- echo Numeric data type test +-- echo signed Numeric data type +-- echo check data consistency +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 int, + col2 bigint, + col3 float, + col4 double, + col5 decimal(10,4), + col6 tinyint, + col7 smallint, + col8 mediumint +) ENGINE = DuckDB; + +insert into t1 values (1, 1, -1, 1.01, -1.001, 1.0001, 1, 1, 1), + (2, -2, 2, -2.02, 2.002, -2.0002, 2, 2, 2), + (-3, 3, 3, 3.03, 3.003, 3.0003, 3, 3, 3), + (-2147483648, -2147483648, -9223372036854775808, -3.402823466E+38, -1.7976931348623157E+308, -999999.9999, -128, -32768, -8388608), + (2147483647, 2147483647, 9223372036854775807, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, 127, 32767, 8388607); + +select * from mytest.t1; +drop table t1; + +-- echo +-- echo unsigned Numeric data type +-- echo check data consistency +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 int unsigned, + col2 bigint unsigned, + col3 float unsigned, + col4 double unsigned, + col5 decimal(10,4) unsigned, + col6 bit(10), + col7 tinyint unsigned, + col8 smallint unsigned, + col9 mediumint unsigned +) ENGINE = DuckDB; + +insert into t1 values (1, 1, 1, 1.01, 1.001, 1.0001, B'111111111', 1, 1, 1), + (2, 2, 2, 2.02, 2.002, 2.0002, B'0000000000', 1, 1, 1), + (3, 3, 3, 3.03, 3.003, 3.0003, B'0001100110', 1, 1, 1), + (-2147483648, 0, 0, 0, 0, 0, B'00000000000', 0, 0, 0), + (2147483647, 255, 18446744073709551615, 3.402823466E+38, 1.7976931348623157E+308, 999999.9999, B'1111111111', 255, 65535, 8388607); + +select id, col1, col2, col3, col4, col5, hex(col6), col7, col8, col9 from mytest.t1; +drop table t1; + +-- echo +-- echo decimal precision test, decimal precision more than 38 is not supported. +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 decimal(65, 30), + col2 decimal(65, 15), + col3 decimal(65, 0), + col4 decimal(38,30), + col5 decimal(38,18), + col6 decimal(38,0), + col7 decimal(9,9), + col8 decimal(9,4), + col9 decimal(9,0) +) ENGINE = DuckDB; + +insert into t1 values (1, + 99999999999999999999999999999999999.999999999999999999999999999999, + 99999999999999999999999999999999999999999999999999.999999999999999, + 99999999999999999999999999999999999999999999999999999999999999999, + 99999999.999999999999999999999999999999, + 99999999999999999999.999999999999999999, + 99999999999999999999999999999999999999, + 0.99999999, + 99999.9999, + 999999999); + +select * from mytest.t1; +drop table t1; + +# 2. Date data types +-- echo +-- echo Date data type test +-- echo check data consistency +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 date, + col2 datetime, + col3 timestamp, + col4 datetime(6), + col5 time, + col6 time(6), + col7 year +) ENGINE = DuckDB; + +insert into t1 values (1, '2020-01-01', '2020-01-01 12:00:00', '2020-01-01 12:00:00', '2020-01-01 12:00:00.1', '12:00:00', '12:00:00.1', 2020), + (2, '2020-12-31', '2020-12-31 00:00:00', '2020-12-31 00:00:00', '2020-12-31 00:00:00.123456789', '00:00:00', '00:00:00.123456789', 1970), + (3, '1970-01-01', '1970-01-01 23:59:59', '1970-01-01 23:59:59', '2020-12-31 23:59:59.123456', '23:59:59', '23:59:59.123456', 2050), + (4, '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '2000-12-31 23:59:59.123456', '23:59:59.123456', '23:59:59.123456', 2100); + +select * from mytest.t1; +drop table t1; + +# 3. String data types +-- echo +-- echo String data type test +-- echo check data consistency +CREATE TABLE t1 ( + id int PRIMARY KEY, + col1 char(100), + col2 varchar(100), + col3 binary(4), + col4 varbinary(100), + col5 tinyblob, + col6 blob, + col7 mediumblob, + col8 longblob, + col9 tinytext, + col10 text, + col11 mediumtext, + col12 longtext, + col13 json, + col14 enum('v1', 'v2', 'v3'), + col15 set('v1', 'v2', 'v3') +) ENGINE = DuckDB; + +insert into t1 values (1, 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', 'aaaa', '{"id": 1, "value":"aaaa"}', 1, 1); +insert into t1 values (2, 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', 'bbbb', '{"id": 1, "value":"aaaa"}', 'v2', 'v2'); +insert into t1 values (3, 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', 'cccc', '{"id": 1, "value":"aaaa"}', 3, 'v1,v2,v3'); +insert into t1 values (4, repeat('d', 100), repeat('d', 100), repeat('d', 4), repeat('d', 100), + repeat('d', 255), repeat('d', 65535), repeat('d', 16777215), repeat('d', 60000000), + repeat('d', 255), repeat('d', 65535), repeat('d', 16777215), repeat('d', 60000000), + '{}', 3, 'v3'); + +select * from mytest.t1 where id <= 3; +select length(col1), length(col2), octet_length(col3), + octet_length(col4), octet_length(col5), octet_length(col6), + octet_length(col7), octet_length(col8), length(col9), + length(col10),length(col11),length(col12), octet_length(col13), + length(col14), length(col15) +from mytest.t1; +drop table t1; + +drop database mytest; \ No newline at end of file diff --git a/storage/duckdb/mysql-test/duckdb/t/ha_duckdb.test b/storage/duckdb/mysql-test/duckdb/t/ha_duckdb.test new file mode 100644 index 0000000000000..ffdfff0e07e92 --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/ha_duckdb.test @@ -0,0 +1,47 @@ +--echo # +--echo # Basic DuckDB handler test: DDL and DML operations +--echo # + +--disable_query_log +CREATE DATABASE IF NOT EXISTS db_ha_duckdb CHARACTER SET utf8mb4; +USE db_ha_duckdb; +--enable_query_log + +# ----------------------------------------------------------------- +# Test 1: CREATE TABLE with indexes +# ----------------------------------------------------------------- +--echo # Test 1: CREATE TABLE with various index types + +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 ( + id BIGINT NOT NULL, + v VARCHAR(4) NOT NULL, + INDEX ind_v(v), + UNIQUE INDEX uk(v), + PRIMARY KEY (id) +) ENGINE = DuckDB; + +# ----------------------------------------------------------------- +# Test 2: Basic INSERT operations +# ----------------------------------------------------------------- +--echo # Test 2: INSERT rows + +INSERT INTO t1 VALUES (1, '1'); +# DuckDB has no primary key enforcement, so duplicate PK insert succeeds +INSERT INTO t1 VALUES (1, '1'); + +# ----------------------------------------------------------------- +# Test 3: SELECT data +# ----------------------------------------------------------------- +--echo # Test 3: Verify inserted data + +SELECT * FROM t1; + +# ----------------------------------------------------------------- +# Cleanup +# ----------------------------------------------------------------- +--disable_query_log +DROP TABLE t1; +DROP DATABASE db_ha_duckdb; +USE test; +--enable_query_log diff --git a/storage/duckdb/mysql-test/duckdb/t/pushdown_rewrite.test b/storage/duckdb/mysql-test/duckdb/t/pushdown_rewrite.test new file mode 100644 index 0000000000000..bfc8cbf47506e --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/pushdown_rewrite.test @@ -0,0 +1,74 @@ +--source ../include/have_duckdb.inc + +--disable_warnings +DROP DATABASE IF EXISTS pushdown_rewrite; +--enable_warnings +CREATE DATABASE pushdown_rewrite CHARACTER SET utf8mb4; +USE pushdown_rewrite; + +CREATE TABLE t1 (id INT PRIMARY KEY, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t1 VALUES (1, 'a'), (2, 'b'), (3, 'c'); + +CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, val VARCHAR(50)) ENGINE=DuckDB; +INSERT INTO t2 VALUES (1, 1, 'x'), (2, 2, 'y'), (3, 3, 'z'); + +--echo # +--echo # Bug fix: STRAIGHT_JOIN as join keyword (with ON clause) +--echo # Was: STRAIGHT_JOIN → CROSS JOIN → DuckDB rejects "CROSS JOIN ... ON" +--echo # Fix: STRAIGHT_JOIN → JOIN (preserves ON clause) +--echo # +SELECT * FROM t1 STRAIGHT_JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; + +--echo # +--echo # Bug fix: STRAIGHT_JOIN as SELECT hint +--echo # Was: SELECT STRAIGHT_JOIN → SELECT CROSS JOIN → syntax error +--echo # Fix: hint is removed, query passes through +--echo # +SELECT STRAIGHT_JOIN t1.id, t2.val FROM t1 JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; + +--echo # +--echo # Bug fix: NATURAL JOIN must not be converted to CROSS JOIN +--echo # Was: scanner found no ON/USING after NATURAL JOIN → replaced with CROSS JOIN +--echo # Fix: NATURAL (and LEFT/RIGHT/INNER/FULL/OUTER) recognized before JOIN +--echo # +CREATE TABLE nat1 (shared_col INT PRIMARY KEY, val1 VARCHAR(10)) ENGINE=DuckDB; +CREATE TABLE nat2 (shared_col INT PRIMARY KEY, val2 VARCHAR(10)) ENGINE=DuckDB; +INSERT INTO nat1 VALUES (1, 'a'), (2, 'b'); +INSERT INTO nat2 VALUES (1, 'x'), (3, 'y'); + +SELECT * FROM nat1 NATURAL JOIN nat2 ORDER BY shared_col; + +--echo # +--echo # Bug fix: CONVERT word boundary +--echo # Was: find("CONVERT(") matched MYCONVERT( inside identifiers +--echo # Fix: check preceding character is not alphanumeric/_ +--echo # +SELECT CONVERT(123, CHAR) AS c FROM t1 LIMIT 1; + +--echo # +--echo # Bug fix: GROUP BY ... WITH ROLLUP — handle multiple occurrences +--echo # Was: only first WITH ROLLUP was rewritten (no loop) +--echo # Fix: loop rewrites all WITH ROLLUP in query (including subqueries) +--echo # +--sorted_result +SELECT val, SUM(id) s FROM t1 GROUP BY val WITH ROLLUP; + +--echo # +--echo # Bug fix: LIMIT offset,count — handle all occurrences +--echo # Was: rfind processed only the last LIMIT, missing subqueries +--echo # Fix: forward loop rewrites every LIMIT offset,count +--echo # +SELECT * FROM (SELECT * FROM t1 ORDER BY id LIMIT 1, 2) sub; + +--echo # +--echo # Regression: plain LIMIT (no comma) must not be altered +--echo # +SELECT * FROM t1 ORDER BY id LIMIT 2; + +--echo # +--echo # Regression: LEFT/RIGHT/INNER JOIN with ON must not be touched +--echo # +SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; +SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.t1_id ORDER BY t1.id; + +DROP DATABASE pushdown_rewrite; diff --git a/storage/duckdb/mysql-test/duckdb/t/rename_duckdb_table.test b/storage/duckdb/mysql-test/duckdb/t/rename_duckdb_table.test new file mode 100644 index 0000000000000..6ea744b0fd0fb --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/rename_duckdb_table.test @@ -0,0 +1,69 @@ +--source ../include/have_duckdb.inc + +--echo # +--echo # 1) Prepare +--echo # +CREATE DATABASE db1; +CREATE DATABASE db2; + +CREATE TABLE db1.t1(a INT PRIMARY KEY) ENGINE = DUCKDB; +CREATE TABLE db2.t2(a INT PRIMARY KEY) ENGINE = DUCKDB; + +--write_file $MYSQL_TMP_DIR/duckdb_meta.inc + --sorted_result + SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE = "DuckDB"; + SELECT run_in_duckdb("SELECT table_schema, table_name FROM information_schema.tables WHERE table_catalog = 'duckdb' AND table_schema != 'mysql'"); +EOF +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +--echo # +--echo # 2) Simple Rename +--echo # +--echo Rename DuckDB tables +RENAME TABLE db1.t1 TO db1.t1_rename, db2.t2 TO db2.t2_rename; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +ALTER TABLE db1.t1_rename RENAME TO db1.t1; +ALTER TABLE db2.t2_rename RENAME TO db2.t2; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +--echo Failed to rename single DuckDB table for different schemas +--error ER_GET_ERRMSG +RENAME TABLE db1.t1 TO db2.t1; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +--echo Failed to rename DuckDB tables for different schemas +--error ER_GET_ERRMSG +ALTER TABLE db1.t1 RENAME TO db2.t1, ALGORITHM = INPLACE; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +--error ER_GET_ERRMSG +RENAME TABLE db1.t1 TO db1.t1_rename, db2.t2 TO db1.t2_rename; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + + +--echo # +--echo # 3) Inplace rename +--echo # +CREATE TABLE db1.t3(a INT PRIMARY KEY) ENGINE = DUCKDB; +ALTER TABLE db1.t3 RENAME TO db1.t3_rename, CHECKSUM = 1; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + + +--echo # +--echo # 4) COPY rename +--echo # +ALTER TABLE db1.t3_rename RENAME TO db1.t3, ALGORITHM = COPY; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + +ALTER TABLE db1.t3 RENAME TO db2.t3, ALGORITHM = COPY; +--source $MYSQL_TMP_DIR/duckdb_meta.inc + + +--echo # +--echo # 5) Cleanup +--echo # +DROP DATABASE db1; +DROP DATABASE db2; +--source $MYSQL_TMP_DIR/duckdb_meta.inc +--remove_file $MYSQL_TMP_DIR/duckdb_meta.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/supported_copy_ddl.test b/storage/duckdb/mysql-test/duckdb/t/supported_copy_ddl.test new file mode 100644 index 0000000000000..97069c1b09f4a --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/supported_copy_ddl.test @@ -0,0 +1,61 @@ +--source ../include/have_duckdb.inc +# Test for DDL which are supported by DuckDB using COPY algorithm +--echo # +--echo # RENAME TABLE WITH DIFFERENT DATABASES +--echo # +CREATE DATABASE db1; +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +--error ER_GET_ERRMSG +ALTER TABLE t RENAME TO db1.t, ALGORITHM = INPLACE; +ALTER TABLE t RENAME TO db1.t; + +SHOW CREATE TABLE db1.t; +SELECT run_in_duckdb("SELECT table_schema, table_name, column_name, column_default, is_nullable, data_type FROM information_schema.columns WHERE table_name = 't'"); +SELECT run_in_duckdb("SELECT table_name, constraint_type, constraint_text FROM duckdb_constraints WHERE table_name = 't'"); +DROP DATABASE db1; + + +--echo # +--echo # ALTER COLUMN ORDER +--echo # +CREATE TABLE t(a INT PRIMARY KEY, b INT) ENGINE = DuckDB; +--error ER_ALTER_OPERATION_NOT_SUPPORTED +ALTER TABLE t ADD COLUMN c INT FIRST, ALGORITHM = INPLACE; +ALTER TABLE t ADD COLUMN c INT FIRST; +--error ER_ALTER_OPERATION_NOT_SUPPORTED +ALTER TABLE t ADD COLUMN d INT AFTER a, ALGORITHM = INPLACE; +ALTER TABLE t ADD COLUMN d INT AFTER a; +--error ER_ALTER_OPERATION_NOT_SUPPORTED +ALTER TABLE t MODIFY COLUMN c INT AFTER a, ALGORITHM = INPLACE; +ALTER TABLE t MODIFY COLUMN c INT AFTER a; +--error ER_ALTER_OPERATION_NOT_SUPPORTED +ALTER TABLE t MODIFY COLUMN d INT FIRST, ALGORITHM = INPLACE; +ALTER TABLE t MODIFY COLUMN d INT FIRST; +DROP TABLE t; + + +--echo # +--echo # BUGFIX: Interrupt fetch query when copy ddl from DuckDB to DuckDB. +--echo # +CREATE TABLE t1(id INT AUTO_INCREMENT KEY, a INT) ENGINE = InnoDB; +--disable_query_log +INSERT INTO t1(a) VALUES(1); +--let $i = 15 +while ($i) +{ + INSERT INTO t1(a) SELECT a FROM t1; + --dec $i +} +--enable_query_log +ALTER TABLE t1 ENGINE = DuckDB; +SET GLOBAL duckdb_copy_ddl_in_batch = OFF; +--let $value1 = query_get_value(SHOW GLOBAL STATUS LIKE "duckdb_rows_insert_in_batch", Value, 1) +ALTER TABLE t1 ADD COLUMN b INT, ALGORITHM = COPY; +--let $value2 = query_get_value(SHOW GLOBAL STATUS LIKE "duckdb_rows_insert_in_batch", Value, 1) +--let $assert_text = Insert data in batch +--let $assert_cond = $value2 > $value1 +--source include/assert.inc +SET GLOBAL duckdb_copy_ddl_in_batch = default; +DROP TABLE t1; + +--source ../include/cleanup_duckdb.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/system_timezone.test b/storage/duckdb/mysql-test/duckdb/t/system_timezone.test new file mode 100644 index 0000000000000..59aa9d106a51c --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/system_timezone.test @@ -0,0 +1,87 @@ +--source ../include/have_mysqld_safe.inc + +# 1) Set valiables to be used in parameters of mysqld_safe. +let $MYSQLD_DATADIR= `SELECT @@datadir`; +let $MYSQL_BASEDIR= `SELECT @@basedir`; +let $MYSQL_SOCKET= `SELECT @@socket`; +let $MYSQL_TIMEZONE= `SELECT @@time_zone`; +let $MYSQL_PIDFILE= `SELECT @@pid_file`; +let $MYSQL_PORT= `SELECT @@port`; +let $MYSQL_MESSAGESDIR= `SELECT @@lc_messages_dir`; +let $start_page_size= `select @@innodb_page_size`; +let $other_page_size_k= `SELECT $start_page_size DIV 1024`; +let $other_page_size_nk= `SELECT CONCAT($other_page_size_k,'k')`; + +# mysqld_path to be passed to --ledir +# use test; +perl; + my $dir = $ENV{'MYSQLTEST_VARDIR'}; + open ( OUTPUT, ">$dir/tmp/mysqld_path_file.inc") ; + my $path = $ENV{MYSQLD}; + $path =~ /^(.*)\/([^\/]*)$/; + print OUTPUT "let \$mysqld_path = $1;\n"; + print OUTPUT "let \$mysqld_bin = $2;\n"; + close (OUTPUT); +EOF + +#Get the value of the variable from to MTR, from perl +--source $MYSQLTEST_VARDIR/tmp/mysqld_path_file.inc + +#Remove the temp file +--remove_file $MYSQLTEST_VARDIR/tmp/mysqld_path_file.inc + +# 2) Shutdown mysqld which is started by mtr. +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect +--exec echo "wait" > $_expect_file_name +--shutdown_server +--source include/wait_until_disconnected.inc + +# # 3) Run the mysqld_safe script with exec. +--exec sh $MYSQLD_SAFE --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$MYSQLTEST_VARDIR/log/err.log --basedir=$MYSQL_BASEDIR --ledir=$mysqld_path --mysqld=$mysqld_bin --datadir=$MYSQLD_DATADIR --socket=$MYSQL_SOCKET --pid-file=$MYSQL_PIDFILE --port=$MYSQL_PORT --timezone=GMT-11 --log-output=file --loose-debug-sync-timeout=600 --default-storage-engine=InnoDB --default-tmp-storage-engine=InnoDB --secure-file-priv="" --core-file --lc-messages-dir=$MYSQL_MESSAGESDIR --innodb-page-size=$other_page_size_nk < /dev/null > /dev/null 2>&1 & +# mysqld_safe takes some time to start mysqld +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect + +connection default; + + +# 4) Check the timezone settings. +SHOW VARIABLES LIKE '%time%zone%'; + +SET time_zone='+00:00'; +CREATE TABLE t_duckdb (id INT PRIMARY KEY, a timestamp, b datetime) ENGINE=duckdb; +CREATE TABLE t_innodb (id INT PRIMARY KEY, a timestamp, b datetime) ENGINE=innodb; +INSERT INTO t_duckdb VALUES (1, '2020-01-01 00:00:00', '1970-01-01 00:00:00'); +INSERT INTO t_innodb VALUES (1, '2020-01-01 00:00:00', '1970-01-01 00:00:00'); + +SET time_zone=system; # system timezone is configured --timezone=GMT-11 by mysqld_safe +SELECT * FROM t_innodb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SELECT * FROM t_duckdb WHERE a < '2020-01-01 11:00:01' and a > '2020-01-01 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; + + +--exec $MYSQLADMIN -h localhost -S $MYSQL_SOCKET -P $MYSQL_PORT -u root shutdown 2>&1 +--source include/wait_until_disconnected.inc + +--exec echo "wait" > $_expect_file_name +--exec sh $MYSQLD_SAFE --defaults-file=$MYSQLTEST_VARDIR/my.cnf --log-error=$MYSQLTEST_VARDIR/log/err.log --basedir=$MYSQL_BASEDIR --ledir=$mysqld_path --mysqld=$mysqld_bin --datadir=$MYSQLD_DATADIR --socket=$MYSQL_SOCKET --pid-file=$MYSQL_PIDFILE --port=$MYSQL_PORT --timezone=CST --log-output=file --loose-debug-sync-timeout=600 --default-storage-engine=InnoDB --default-tmp-storage-engine=InnoDB --secure-file-priv="" --core-file --lc-messages-dir=$MYSQL_MESSAGESDIR --innodb-page-size=$other_page_size_nk < /dev/null > /dev/null 2>&1 & +# mysqld_safe takes some time to start mysqld +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect +connection default; + +SET time_zone=system; # system timezone is configured --timezone=CST by mysqld_safe +SELECT * FROM t_innodb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; +SELECT * FROM t_duckdb WHERE a < '2020-01-01 00:00:01' and a > '2019-12-31 10:59:59' and b < '1970-01-01 12:00:01' and b > '1969-12-31 23:59:00'; + +DROP TABLE t_duckdb; +DROP TABLE t_innodb; + +--exec $MYSQLADMIN -h localhost -S $MYSQL_SOCKET -P $MYSQL_PORT -u root shutdown 2>&1 +--source include/wait_until_disconnected.inc + +--exec echo "restart" > $_expect_file_name +--enable_reconnect +--source include/wait_until_connected_again.inc diff --git a/storage/duckdb/mysql-test/duckdb/t/transaction.test b/storage/duckdb/mysql-test/duckdb/t/transaction.test new file mode 100644 index 0000000000000..7f080ce7456ca --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/transaction.test @@ -0,0 +1,27 @@ +--echo # +--echo # DuckDB transaction tests +--echo # + +# ----------------------------------------------------------------- +# Suite 1: binlog ON, basic transaction ability +# ----------------------------------------------------------------- +--echo # Suite 1: Basic transactions (binlog ON) + +--source ../include/transaction_basic.inc + +# ----------------------------------------------------------------- +# Suite 2: binlog OFF, basic transaction ability +# ----------------------------------------------------------------- +--echo # Suite 2: Basic transactions (binlog OFF) + +--let $restart_parameters=--skip-log-bin +--source include/restart_mysqld.inc + +--source ../include/transaction_basic.inc + +# ----------------------------------------------------------------- +# Restore default parameters +# ----------------------------------------------------------------- +--let $restart_parameters= +--source include/restart_mysqld.inc + diff --git a/storage/duckdb/mysql-test/duckdb/t/truncate_and_maintenance_duckdb_table.test b/storage/duckdb/mysql-test/duckdb/t/truncate_and_maintenance_duckdb_table.test new file mode 100644 index 0000000000000..3c2108ff356ca --- /dev/null +++ b/storage/duckdb/mysql-test/duckdb/t/truncate_and_maintenance_duckdb_table.test @@ -0,0 +1,31 @@ +--echo # +--echo # 1) PREPARE +--echo # +CREATE TABLE t(id INT PRIMARY KEY, a INT, b INT, c INT) ENGINE = DuckDB; +INSERT INTO t VALUES (1, 1, 1, 1); +INSERT INTO t VALUES (2, 2, 2, 2); +INSERT INTO t VALUES (3, 3, 3, 3); + + +--echo # +--echo # 2) MAINTENANCE STATEMENTS +--echo # +ANALYZE TABLE t; +CHECK TABLE t; +CHECKSUM TABLE t; +OPTIMIZE TABLE t; +REPAIR TABLE t; + + +--echo # +--echo # 3) TRUNCATE TABLE +--echo # +SELECT * FROM t; +TRUNCATE TABLE t; +SELECT * FROM t; + + +--echo # +--echo # 4) CLEANUP +--echo # +DROP TABLE t; diff --git a/storage/duckdb/run_mtr.sh b/storage/duckdb/run_mtr.sh new file mode 100755 index 0000000000000..db07df95bbee8 --- /dev/null +++ b/storage/duckdb/run_mtr.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +set -e +set -o pipefail + +SCRIPT_LOCATION=$(dirname "$0") +source "$SCRIPT_LOCATION/utils.sh" + +MDB_SOURCE_PATH=$(realpath "$SCRIPT_LOCATION"/../../../) +DUCKDB_SOURCE_PATH=$(realpath "$SCRIPT_LOCATION") +BUILD_PATH=$(realpath "$MDB_SOURCE_PATH"/../DuckdbBuildOf_$(basename "$MDB_SOURCE_PATH")) +MTR_PATH="$BUILD_PATH/mysql-test" +MTR="$MTR_PATH/mtr" +SUITE="duckdb" +SUITE_DIR="$DUCKDB_SOURCE_PATH/mysql-test" +BUILD_PLUGIN_DIR="$BUILD_PATH/storage/duckdb-engine/duck" + +# ─── Defaults ─────────────────────────────────────────────────────────────────── + +TEST_NAME="" +RECORD=false +EXTERN=false +EXTERN_SOCKET="" +RUN_ALL=false +SKIP_DISABLED=false +PARALLEL=4 +EXTRA_MTR_ARGS=() + +# ─── Usage ────────────────────────────────────────────────────────────────────── + +usage() { + echo "Usage: $0 [options] [test_name]" + echo "" + echo "Modes:" + echo " Run a single test (without .test extension)" + echo " -a, --all Run all tests in the suite" + echo "" + echo "Options:" + echo " -r, --record Record test result (update .result file)" + echo " -e, --extern Use externally running MariaDB (auto-detects socket)" + echo " --socket Socket path for extern (default: auto-detect)" + echo " -s, --skip-dis Skip disabled tests (run-all-with-disabled.def)" + echo " -j Parallel threads (default: $PARALLEL)" + echo " -v, --verbose Verbose MTR output" + echo " -d, --debug Run test under debugger (--debug)" + echo " --force Continue on failure (--force)" + echo " --retry=N Retry failed tests N times" + echo " -h, --help Show this help" + echo "" + echo "Examples:" + echo " $0 create_table_column # run one test" + echo " $0 -r create_table_column # run and record" + echo " $0 -a # run all tests" + echo " $0 -a -e # run all against extern server" + echo " $0 -e create_table_column # single test against extern server" + exit 0 +} + +# ─── Parse args ───────────────────────────────────────────────────────────────── + +while [[ $# -gt 0 ]]; do + case "$1" in + -a|--all) RUN_ALL=true; shift ;; + -r|--record) RECORD=true; shift ;; + -e|--extern) EXTERN=true; shift ;; + --socket) EXTERN_SOCKET="$2"; shift 2 ;; + -s|--skip-dis) SKIP_DISABLED=true; shift ;; + -j) PARALLEL="$2"; shift 2 ;; + -v|--verbose) EXTRA_MTR_ARGS+=("--verbose"); shift ;; + -d|--debug) EXTRA_MTR_ARGS+=("--debug"); shift ;; + --force) EXTRA_MTR_ARGS+=("--force"); shift ;; + --retry=*) EXTRA_MTR_ARGS+=("$1"); shift ;; + -h|--help) usage ;; + -*) warn "Unknown option: $1"; usage ;; + *) TEST_NAME="$1"; shift ;; + esac +done + +# ─── Validate ─────────────────────────────────────────────────────────────────── + +if [[ ! -x "$MTR" ]]; then + fail "MTR not found at $MTR. Build first with build.sh" +fi + +# Symlink source mysql-test into build tree so MTR discovers the suite +# via its storage/*/*/mysql-test search pattern +if [[ ! -d "$BUILD_PLUGIN_DIR/mysql-test" ]]; then + ln -sf "$SUITE_DIR" "$BUILD_PLUGIN_DIR/mysql-test" + info "Symlinked test suite into build tree" +fi + +# MTR discovers plugins via glob storage/*/*.so (one level). +# ha_duckdb.so lives in storage/duckdb-engine/duck/ (two levels). +# Symlink it one level up so MTR finds it for managed-server mode. +PARENT_PLUGIN_DIR="$BUILD_PATH/storage/duckdb-engine" +if [[ -f "$BUILD_PLUGIN_DIR/ha_duckdb.so" && ! -e "$PARENT_PLUGIN_DIR/ha_duckdb.so" ]]; then + ln -sf "$BUILD_PLUGIN_DIR/ha_duckdb.so" "$PARENT_PLUGIN_DIR/ha_duckdb.so" + info "Symlinked ha_duckdb.so for MTR plugin discovery" +fi + +if [[ "$RUN_ALL" == false && -z "$TEST_NAME" ]]; then + # Interactive: show menu of available tests + mapfile -t available_tests < <( + find "$SUITE_DIR/duckdb/t" -name "*.test" -printf "%f\n" | sed 's/\.test$//' | sort + ) + if [[ ${#available_tests[@]} -eq 0 ]]; then + fail "No tests found in $SUITE_DIR/duckdb/t/" + fi + menu_choice "Select test to run:" available_tests + TEST_NAME="$MENU_RESULT" +fi + +# ─── Resolve extern early (affects parallel) ──────────────────────────────────── + +if [[ "$EXTERN" == true ]]; then + if [[ -z "$EXTERN_SOCKET" ]]; then + EXTERN_SOCKET=$(mariadb -BNe "SELECT @@socket" 2>/dev/null || echo "/run/mysqld/mysqld.sock") + fi + if [[ ! -S "$EXTERN_SOCKET" ]]; then + fail "Socket not found: $EXTERN_SOCKET. Is MariaDB running?" + fi + # Force serial execution with extern — all tests share one server, + # parallel workers would collide on table names and global variables + PARALLEL=1 +fi + +# ─── Build MTR command ────────────────────────────────────────────────────────── + +MTR_CMD=("perl" "$MTR" + --suite="$SUITE" + --parallel="$PARALLEL" + --force + --max-test-fail=0 +) + +if [[ "$RECORD" == true ]]; then + MTR_CMD+=("--record") +fi + +if [[ "$EXTERN" == true ]]; then + MTR_CMD+=("--extern" "socket=$EXTERN_SOCKET") +fi + +if [[ "$SKIP_DISABLED" == true ]]; then + MTR_CMD+=("--skip-disabled") +fi + +MTR_CMD+=("${EXTRA_MTR_ARGS[@]}") + +if [[ "$RUN_ALL" == false && -n "$TEST_NAME" ]]; then + # Strip .test extension if provided + TEST_NAME="${TEST_NAME%.test}" + MTR_CMD+=("$SUITE.$TEST_NAME") +fi + +# ─── Print summary ────────────────────────────────────────────────────────────── + +header "DuckDB MTR Test Runner" +if [[ "$RUN_ALL" == true ]]; then + info "Mode: ${_CLR_YELLOW}all tests" +else + info "Test: ${_CLR_YELLOW}$TEST_NAME" +fi +if [[ "$RECORD" == true ]]; then + warn "Recording: ${_CLR_YELLOW}ON (will update .result files)" +fi +if [[ "$EXTERN" == true ]]; then + info "Server: ${_CLR_YELLOW}extern ($EXTERN_SOCKET)" +else + info "Server: ${_CLR_YELLOW}MTR-managed" +fi +info "Parallel: ${_CLR_YELLOW}$PARALLEL" +info "Suite dir: ${_CLR_YELLOW}$SUITE_DIR/duckdb" +info "Build dir: ${_CLR_YELLOW}$BUILD_PATH" +separator +echo "" + +# ─── Run ──────────────────────────────────────────────────────────────────────── + +info "Running: ${_CLR_DARKGRAY}${MTR_CMD[*]}" +echo "" + +set +e +cd "$MTR_PATH" +"${MTR_CMD[@]}" +rc=$? +set -e + +echo "" +if [[ $rc -eq 0 ]]; then + success "All tests passed" +else + error "Tests failed (exit code $rc)" + # Show log location for failed tests + if [[ -d "$MTR_PATH/var/log" ]]; then + info "Logs: ${_CLR_YELLOW}$MTR_PATH/var/log/" + fi + exit $rc +fi diff --git a/storage/duckdb/runtime/CMakeLists.txt b/storage/duckdb/runtime/CMakeLists.txt new file mode 100644 index 0000000000000..9cb150f058a5c --- /dev/null +++ b/storage/duckdb/runtime/CMakeLists.txt @@ -0,0 +1,28 @@ +INCLUDE(${CMAKE_SOURCE_DIR}/storage/duckdb/cmake/duckdb_target_setup.cmake) + +SET(DUCKDB_RUNTIME_SOURCES + duckdb_config.cc + duckdb_manager.cc + duckdb_query.cc + duckdb_context.cc + delta_appender.cc + cross_engine_scan.cc + duckdb_mysql_compat.cc + fiber_context.c + fiber_scan.cc +) + +ADD_LIBRARY(duckdb_runtime STATIC ${DUCKDB_RUNTIME_SOURCES}) +duckdb_setup_target(duckdb_runtime) + +TARGET_INCLUDE_DIRECTORIES(duckdb_runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +TARGET_LINK_LIBRARIES(duckdb_runtime PUBLIC duckdb_convertor duckdb_common) + +IF(DUCKDB_UNIT_TESTS) + ADD_EXECUTABLE(fiber_context_test fiber_context_test.cc fiber_context.c) + SET_TARGET_PROPERTIES(fiber_context_test PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON) + TARGET_INCLUDE_DIRECTORIES(fiber_context_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + TARGET_COMPILE_OPTIONS(fiber_context_test PRIVATE -Wno-frame-larger-than=) +ENDIF() diff --git a/storage/duckdb/runtime/cross_engine_scan.cc b/storage/duckdb/runtime/cross_engine_scan.cc new file mode 100644 index 0000000000000..a453867d59b8d --- /dev/null +++ b/storage/duckdb/runtime/cross_engine_scan.cc @@ -0,0 +1,555 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "field.h" +#include "handler.h" +#include "mysqld.h" +#include "log.h" + +#undef UNKNOWN + +#include "cross_engine_scan.h" +#include "fiber_scan.h" +#include "ddl_convertor.h" +#include "duckdb_log.h" + +#include "duckdb/main/database.hpp" +#include "duckdb/catalog/catalog.hpp" +#include "duckdb/catalog/catalog_transaction.hpp" +#include "duckdb/function/table_function.hpp" +#include "duckdb/parser/parsed_data/create_table_function_info.hpp" +#include "duckdb/parser/tableref/table_function_ref.hpp" +#include "duckdb/parser/expression/constant_expression.hpp" +#include "duckdb/parser/expression/function_expression.hpp" + +extern "C" pthread_key(struct st_my_thread_var *, THR_KEY_mysys); + +namespace myduck +{ + +/* ---------------------------------------------------------------- + Thread-local registry of external (non-DuckDB) tables + ---------------------------------------------------------------- */ + +static thread_local std::unordered_map + tls_external_tables; +static thread_local std::unordered_map + tls_external_where; + +void register_external_table(const std::string &name, TABLE *table) +{ + tls_external_tables[name]= table; +} + +void register_external_where(const std::string &name, + const std::string &where_sql) +{ + tls_external_where[name]= where_sql; +} + +std::string find_external_where(const std::string &name) +{ + auto it= tls_external_where.find(name); + if (it != tls_external_where.end()) + return it->second; + return std::string(); +} + +void clear_external_tables() +{ + tls_external_tables.clear(); + tls_external_where.clear(); +} + +TABLE *find_external_table(const std::string &name) +{ + auto it= tls_external_tables.find(name); + if (it != tls_external_tables.end()) + return it->second; + return nullptr; +} + +/* ---------------------------------------------------------------- + MariaDB Field → DuckDB LogicalType mapping + ---------------------------------------------------------------- */ + +static duckdb::LogicalType field_to_logical_type(const Field *field) +{ + bool is_unsigned= (field->flags & UNSIGNED_FLAG) != 0; + + switch (field->real_type()) + { + case MYSQL_TYPE_TINY: + return is_unsigned ? duckdb::LogicalType::UTINYINT + : duckdb::LogicalType::TINYINT; + case MYSQL_TYPE_SHORT: + return is_unsigned ? duckdb::LogicalType::USMALLINT + : duckdb::LogicalType::SMALLINT; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + return is_unsigned ? duckdb::LogicalType::UINTEGER + : duckdb::LogicalType::INTEGER; + case MYSQL_TYPE_LONGLONG: + return is_unsigned ? duckdb::LogicalType::UBIGINT + : duckdb::LogicalType::BIGINT; + case MYSQL_TYPE_FLOAT: + return duckdb::LogicalType::FLOAT; + case MYSQL_TYPE_DOUBLE: + return duckdb::LogicalType::DOUBLE; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: { + auto *df= static_cast(field); + uint prec= df->precision > 38 ? 38 : df->precision; + return duckdb::LogicalType::DECIMAL(prec, df->dec); + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + return duckdb::LogicalType::DATE; + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + return duckdb::LogicalType::TIME; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: + return duckdb::LogicalType::TIMESTAMP; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: + return duckdb::LogicalType::TIMESTAMP_TZ; + case MYSQL_TYPE_YEAR: + return duckdb::LogicalType::INTEGER; + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: + return duckdb::LogicalType::BLOB; + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + return field->has_charset() ? duckdb::LogicalType::VARCHAR + : duckdb::LogicalType::BLOB; + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + return duckdb::LogicalType::VARCHAR; + default: + return duckdb::LogicalType::VARCHAR; + } +} + +/* ---------------------------------------------------------------- + Read a MariaDB Field value → duckdb::Value + ---------------------------------------------------------------- */ + +static duckdb::Value field_to_duckdb_value(Field *field) +{ + if (field->is_null()) + return duckdb::Value(); + + switch (field->real_type()) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_YEAR: { + if (field->is_unsigned()) + return duckdb::Value::UBIGINT(field->val_uint()); + return duckdb::Value::BIGINT(field->val_int()); + } + case MYSQL_TYPE_FLOAT: { + float v; + v= static_cast(field->val_real()); + return duckdb::Value::FLOAT(v); + } + case MYSQL_TYPE_DOUBLE: + return duckdb::Value::DOUBLE(field->val_real()); + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: { + String buf; + field->val_str(&buf); + return duckdb::Value(std::string(buf.ptr(), buf.length())); + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: { + String buf; + field->val_str(&buf); + return duckdb::Value(std::string(buf.ptr(), buf.length())); + } + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: { + String buf; + field->val_str(&buf); + return duckdb::Value::BLOB(std::string(buf.ptr(), buf.length())); + } + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: { + String buf; + field->val_str(&buf); + if (field->has_charset()) + return duckdb::Value(std::string(buf.ptr(), buf.length())); + return duckdb::Value::BLOB(std::string(buf.ptr(), buf.length())); + } + default: { + String buf; + field->val_str(&buf); + return duckdb::Value(std::string(buf.ptr(), buf.length())); + } + } +} + +/* ---------------------------------------------------------------- + DuckDB table function: _mdb_scan + Wraps ha_rnd_init / ha_rnd_next on a MariaDB TABLE*. + ---------------------------------------------------------------- */ + +struct MdbScanBindData : duckdb::FunctionData +{ + std::string table_key; + std::string where_sql; + TABLE *table= nullptr; + + duckdb::unique_ptr Copy() const override + { + auto copy= duckdb::make_uniq(); + copy->table_key= table_key; + copy->where_sql= where_sql; + copy->table= table; + return duckdb::unique_ptr(std::move(copy)); + } + + bool Equals(const duckdb::FunctionData &other) const override + { + return table_key == other.Cast().table_key; + } +}; + +struct MdbScanGlobalState : duckdb::GlobalTableFunctionState +{ + bool scan_started= false; + bool rnd_inited= false; + bool finished= false; + TABLE *table= nullptr; + duckdb::vector column_ids; + std::string table_key; + std::string where_sql; + + /* Fiber-based scan for predicate pushdown */ + std::unique_ptr fiber; + + idx_t MaxThreads() const override { return 1; } +}; + +static duckdb::unique_ptr +mdb_scan_bind(duckdb::ClientContext &context, + duckdb::TableFunctionBindInput &input, + duckdb::vector &return_types, + duckdb::vector &names) +{ + auto key= input.inputs[0].GetValue(); + + TABLE *tbl= find_external_table(key); + if (!tbl) + throw duckdb::BinderException("_mdb_scan: table '%s' not found in " + "external table registry", + key.c_str()); + + for (Field **f= tbl->field; *f; f++) + { + names.push_back((*f)->field_name.str); + return_types.push_back(field_to_logical_type(*f)); + } + + auto data= duckdb::make_uniq(); + data->table_key= key; + data->where_sql= find_external_where(key); + data->table= tbl; + return duckdb::unique_ptr(std::move(data)); +} + +static duckdb::unique_ptr +mdb_scan_init_global(duckdb::ClientContext &context, + duckdb::TableFunctionInitInput &input) +{ + auto &bind_data= input.bind_data->Cast(); + auto state= duckdb::make_uniq(); + state->table= bind_data.table; + state->column_ids= input.column_ids; + state->table_key= bind_data.table_key; + state->where_sql= bind_data.where_sql; + + if ((myduck::duckdb_log_options & LOG_DUCKDB_QUERY) && input.filters) + { + TABLE *tbl= bind_data.table; + for (auto &entry : input.filters->filters) + { + duckdb::idx_t proj_idx= entry.first; + std::string col_name= "?"; + /* Map projected index → original column via column_ids → field name */ + if (proj_idx < input.column_ids.size()) + { + duckdb::idx_t orig_idx= input.column_ids[proj_idx]; + uint i= 0; + for (Field **f= tbl->field; *f; f++, i++) + { + if (i == orig_idx) + { + col_name= (*f)->field_name.str; + break; + } + } + } + std::string desc= entry.second->ToString(col_name); + sql_print_information( + "CROSS-ENGINE: DuckDB filter[%llu] col='%s': %s", + (ulonglong) proj_idx, col_name.c_str(), desc.c_str()); + } + } + + return duckdb::unique_ptr( + std::move(state)); +} + +static void mdb_scan_function(duckdb::ClientContext &context, + duckdb::TableFunctionInput &input, + duckdb::DataChunk &output) +{ + auto &state= input.global_state->Cast(); + + if (state.finished) + { + output.SetCardinality(0); + return; + } + + TABLE *tbl= state.table; + if (!tbl) + { + output.SetCardinality(0); + state.finished= true; + return; + } + + /* ---- Lazy fiber initialization on first call ---- */ + if (!state.scan_started) + { + const std::string &where_sql= state.where_sql; + if (!where_sql.empty() || true) /* Always use fiber for MVP */ + { + duckdb::vector col_types; + uint nfields= tbl->s->fields; + for (auto col_idx : state.column_ids) + { + if (col_idx >= nfields) + col_types.push_back(duckdb::LogicalType::BIGINT); + else + col_types.push_back(field_to_logical_type(tbl->field[col_idx])); + } + + state.fiber= std::make_unique(); + if (state.fiber->init(tbl, state.column_ids, col_types, where_sql)) + { + state.finished= true; + output.SetCardinality(0); + state.fiber.reset(); + return; + } + } + state.scan_started= true; + } + + /* ---- Fiber-based scan path ---- */ + if (state.fiber) + { + /* + Save caller TLS and install fiber THD context. + Fibers share the OS thread, so we must swap current_thd and + THR_KEY_mysys manually around spawn/continue. + */ + THD *prev_thd= _current_thd(); + void *prev_mysys= pthread_getspecific(THR_KEY_mysys); + THD *fiber_thd= state.fiber->fiber_thd; + + set_current_thd(fiber_thd); + if (fiber_thd && fiber_thd->mysys_var) + pthread_setspecific(THR_KEY_mysys, fiber_thd->mysys_var); + + if (!state.fiber->fiber_started) + { + fiber_context_spawn(&state.fiber->ctx, fiber_scan_func, + state.fiber.get()); + state.fiber->fiber_started= true; + } + else + { + state.fiber->buffer.Reset(); + fiber_context_continue(&state.fiber->ctx); + } + + /* Restore caller TLS */ + set_current_thd(prev_thd); + pthread_setspecific(THR_KEY_mysys, prev_mysys); + + if (state.fiber->error) + { + state.finished= true; + output.SetCardinality(0); + return; + } + + if (state.fiber->buffer.size() > 0) + { + output.Reference(state.fiber->buffer); + if (state.fiber->finished) + state.finished= true; + } + else + { + output.SetCardinality(0); + state.finished= true; + } + return; + } + + /* ---- Fallback: direct ha_rnd_next (no fiber) ---- */ + THD *prev_thd= _current_thd(); + if (tbl->in_use && tbl->in_use != prev_thd) + set_current_thd(tbl->in_use); + + if (!state.rnd_inited) + { + uint nf= tbl->s->fields; + bitmap_clear_all(tbl->read_set); + for (auto col_idx : state.column_ids) + { + if (col_idx < nf) + bitmap_set_bit(tbl->read_set, static_cast(col_idx)); + } + + if (tbl->file->ha_rnd_init(true)) + { + state.finished= true; + output.SetCardinality(0); + if (_current_thd() != prev_thd) + set_current_thd(prev_thd); + return; + } + state.rnd_inited= true; + } + + duckdb::idx_t count= 0; + duckdb::idx_t ncols= state.column_ids.size(); + uint nfields= tbl->s->fields; + + while (count < STANDARD_VECTOR_SIZE) + { + int err= tbl->file->ha_rnd_next(tbl->record[0]); + if (err) + { + tbl->file->ha_rnd_end(); + state.finished= true; + break; + } + + for (duckdb::idx_t i= 0; i < ncols; i++) + { + if (state.column_ids[i] >= nfields) + { + output.data[i].SetValue(count, duckdb::Value()); + continue; + } + Field *field= tbl->field[state.column_ids[i]]; + duckdb::Value val= field_to_duckdb_value(field); + output.data[i].SetValue(count, val); + } + count++; + } + + output.SetCardinality(count); + + if (_current_thd() != prev_thd) + set_current_thd(prev_thd); +} + +/* ---------------------------------------------------------------- + Replacement scan callback + ---------------------------------------------------------------- */ + +duckdb::unique_ptr mariadb_replacement_scan( + duckdb::ClientContext &context, duckdb::ReplacementScanInput &input, + duckdb::optional_ptr data) +{ + TABLE *tbl= find_external_table(input.table_name); + if (!tbl) + return nullptr; + + auto ref= duckdb::make_uniq(); + + duckdb::vector> children; + children.push_back(duckdb::make_uniq( + duckdb::Value(input.table_name))); + + ref->function= duckdb::make_uniq( + "_mdb_scan", std::move(children)); + ref->alias= input.table_name; + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information( + "DuckDB cross-engine: replacement scan redirected '%s' to _mdb_scan", + input.table_name.c_str()); + + return duckdb::unique_ptr(std::move(ref)); +} + +/* ---------------------------------------------------------------- + Registration + ---------------------------------------------------------------- */ + +void register_cross_engine_scan(duckdb::DatabaseInstance &db) +{ + duckdb::TableFunction mdb_scan("_mdb_scan", {duckdb::LogicalType::VARCHAR}, + mdb_scan_function, mdb_scan_bind, + mdb_scan_init_global); + mdb_scan.projection_pushdown= true; + mdb_scan.filter_pushdown= true; + + duckdb::CreateTableFunctionInfo info(std::move(mdb_scan)); + auto &catalog= duckdb::Catalog::GetSystemCatalog(db); + auto transaction= duckdb::CatalogTransaction::GetSystemTransaction(db); + catalog.CreateFunction(transaction, info); + + auto &config= duckdb::DBConfig::GetConfig(db); + config.replacement_scans.emplace_back(mariadb_replacement_scan); + + sql_print_information("DuckDB: cross-engine scan registered " + "(_mdb_scan + replacement scan)"); +} + +} /* namespace myduck */ diff --git a/storage/duckdb/runtime/cross_engine_scan.h b/storage/duckdb/runtime/cross_engine_scan.h new file mode 100644 index 0000000000000..0de92ec92f85f --- /dev/null +++ b/storage/duckdb/runtime/cross_engine_scan.h @@ -0,0 +1,76 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include "duckdb/common/types.hpp" +#include "duckdb/function/replacement_scan.hpp" + +#include + +struct TABLE; + +namespace duckdb +{ +class DatabaseInstance; +} + +namespace myduck +{ + +/** + Describes a non-DuckDB table that participates in a cross-engine query. + Populated by the select_handler before executing the DuckDB query. +*/ +struct ExternalTableInfo +{ + TABLE *table; /* opened MariaDB TABLE with valid handler */ + std::string table_name; /* unqualified table name */ +}; + +/** + Thread-local registry of external tables available for the current query. + Set before Connection::Query(), cleared after the query completes. + The replacement scan callback reads this to decide which tables to redirect + to the _mdb_scan table function. +*/ +void register_external_table(const std::string &name, TABLE *table); +void register_external_where(const std::string &name, + const std::string &where_sql); +std::string find_external_where(const std::string &name); +void clear_external_tables(); +TABLE *find_external_table(const std::string &name); + +/** + DuckDB replacement scan callback. + When DuckDB cannot find a table in its catalog, this callback checks the + thread-local registry. If the table is registered, it returns a + TableFunctionRef pointing to _mdb_scan('table_name'). +*/ +duckdb::unique_ptr mariadb_replacement_scan( + duckdb::ClientContext &context, duckdb::ReplacementScanInput &input, + duckdb::optional_ptr data); + +/** + Register the _mdb_scan table function and the replacement scan callback + with the DuckDB instance. Called once during DuckdbManager::Initialize(). +*/ +void register_cross_engine_scan(duckdb::DatabaseInstance &db); + +} /* namespace myduck */ diff --git a/storage/duckdb/runtime/delta_appender.cc b/storage/duckdb/runtime/delta_appender.cc new file mode 100644 index 0000000000000..b269250bb070a --- /dev/null +++ b/storage/duckdb/runtime/delta_appender.cc @@ -0,0 +1,601 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include +#include "sql_class.h" +#include "log.h" + +#undef UNKNOWN + +#include "delta_appender.h" +#include "duckdb_query.h" +#include "duckdb_config.h" +#include "ddl_convertor.h" +#include "duckdb_timezone.h" +#include "duckdb_handler_errors.h" +#include "tztime.h" +#include "my_decimal.h" + +#include "duckdb/common/hugeint.hpp" +#include "duckdb/common/types/decimal.hpp" + +#include + +#define DIG_PER_DEC1 9 +#define DIG_BASE 1000000000 +#define ROUND_UP(X) (((X) + DIG_PER_DEC1 - 1) / DIG_PER_DEC1) +static const decimal_digit_t powers10[DIG_PER_DEC1 + 1]= { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + +static int my_decimal_actual_intg(const my_decimal *from) +{ + int intg= from->intg; + const decimal_digit_t *buf= from->buf; + if (intg == 0) + return 0; + int complete_groups= ROUND_UP(intg); + int i= 0; + while (i < complete_groups && buf[i] == 0) + i++; + if (i >= complete_groups) + return 0; + int digits= 0; + decimal_digit_t temp= buf[i]; + while (temp > 0) + { + temp/= 10; + digits++; + } + return digits + (complete_groups - i - 1) * DIG_PER_DEC1; +} + +template +static T get_duckdb_decimal(const my_decimal &from, int fixed_decimal) +{ + T res{0}; + decimal_digit_t *buf= from.buf; + int intg= from.intg, frac= from.frac, fill= fixed_decimal - frac; + bool sign= from.sign(); + + auto update_res= [&](longlong digit) { + if (sign) + res-= digit; + else + res+= digit; + }; + + for (; intg > 0; intg-= DIG_PER_DEC1) + { + res*= DIG_BASE; + update_res(*buf++); + } + + for (; frac >= DIG_PER_DEC1; frac-= DIG_PER_DEC1) + { + res*= DIG_BASE; + update_res(*buf++); + } + + if (frac > 0) + { + res*= powers10[frac]; + update_res(*buf / powers10[DIG_PER_DEC1 - frac]); + } + + if (fill > 0) + res*= powers10[fill]; + + return res; +} + +int DeltaAppender::append_row_insert(TABLE *table, ulonglong trx_no, + const MY_BITMAP *blob_type_map) +{ + ++m_row_count; + m_has_insert= true; + + try + { + m_appender->BeginRow(); + + for (uint i= 0; i < table->s->fields; i++) + { + int ret= append_mysql_field(table->field[i], blob_type_map); + if (ret) + return HA_DUCKDB_APPEND_ERROR; + } + + if (m_use_tmp_table) + { + m_appender->Append(0); + m_appender->Append(m_row_count); + m_appender->Append(trx_no); + } + + m_appender->EndRow(); + } + catch (std::exception &ex) + { + sql_print_error("DuckDB: Appender error: %s", ex.what()); + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, ex.what(), "DuckDB Appender"); + return HA_DUCKDB_APPEND_ERROR; + } + + return 0; +} + +int DeltaAppender::append_row_update(TABLE *table, ulonglong trx_no, + const uchar *old_row) +{ + m_has_update= true; + return (append_row_delete(table, trx_no, old_row) || + append_row_insert(table, trx_no, nullptr)) + ? HA_DUCKDB_APPEND_ERROR + : 0; +} + +int DeltaAppender::append_row_delete(TABLE *table, ulonglong trx_no, + const uchar *old_row) +{ + ++m_row_count; + m_has_delete= true; + + try + { + m_appender->BeginRow(); + + for (uint i= 0; i < table->s->fields; i++) + { + Field *field= table->field[i]; + + if (bitmap_is_set(&m_pk_bitmap, field->field_index)) + { + int ret= 0; + if (!old_row) + { + ret= append_mysql_field(field); + } + else + { + uchar *saved_ptr= field->ptr; + field->ptr= + const_cast(old_row + field->offset(table->record[0])); + ret= append_mysql_field(field); + field->ptr= saved_ptr; + } + if (ret) + return HA_DUCKDB_APPEND_ERROR; + } + else + { + m_appender->Append(duckdb::Value(duckdb::LogicalType::SQLNULL)); + } + } + + if (m_use_tmp_table) + { + m_appender->Append(1); + m_appender->Append(m_row_count); + m_appender->Append(trx_no); + } + + m_appender->EndRow(); + } + catch (std::exception &ex) + { + sql_print_error("DuckDB: Appender error: %s", ex.what()); + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, ex.what(), "DuckDB Appender"); + return HA_DUCKDB_APPEND_ERROR; + } + + return 0; +} + +bool DeltaAppender::Initialize(TABLE *table) +{ + if (m_use_tmp_table) + { + m_tmp_table_name= buf_table_name(m_schema_name, m_table_name); + + std::stringstream ss; + ss << "CREATE TEMPORARY TABLE IF NOT EXISTS main.\"" << m_tmp_table_name + << "\" AS FROM \"" << m_schema_name << "\".\"" << m_table_name + << "\" LIMIT 0;"; + ss << "ALTER TABLE main.\"" << m_tmp_table_name + << "\" ADD COLUMN \"#mdb_delete_flag\" BOOL;"; + ss << "ALTER TABLE main.\"" << m_tmp_table_name + << "\" ADD COLUMN \"#mdb_row_no\" INT;"; + ss << "ALTER TABLE main.\"" << m_tmp_table_name + << "\" ADD COLUMN \"#mdb_trx_no\" INT;"; + + auto ret= myduck::duckdb_query(*m_con, ss.str()); + if (ret->HasError()) + return true; + + std::string schema_name("main"); + try + { + m_appender= std::make_unique(*m_con, schema_name, + m_tmp_table_name); + } + catch (std::exception &ex) + { + sql_print_error("DuckDB: Appender init error (tmp): %s", ex.what()); + return true; + } + + KEY *key_info= table->key_info; + if (!key_info) + return true; + my_bitmap_init(&m_pk_bitmap, nullptr, table->s->fields); + KEY_PART_INFO *key_part= key_info->key_part; + for (uint i= 0; i < key_info->user_defined_key_parts; i++, key_part++) + { + if (i) + m_pk_list+= ", "; + m_pk_list+= "\""; + m_pk_list+= key_part->field->field_name.str; + m_pk_list+= "\""; + bitmap_set_bit(&m_pk_bitmap, key_part->field->field_index); + } + + for (uint i= 0; i < table->s->fields; i++) + { + if (i) + m_col_list+= ", "; + m_col_list+= "\""; + m_col_list+= table->field[i]->field_name.str; + m_col_list+= "\""; + } + } + else + { + try + { + m_appender= std::make_unique(*m_con, m_schema_name, + m_table_name); + } + catch (std::exception &ex) + { + sql_print_error("DuckDB: Appender init error: %s", ex.what()); + return true; + } + } + + return false; +} + +int DeltaAppender::append_mysql_field(const Field *field_arg, + const MY_BITMAP *blob_type_map) +{ + Field *field= const_cast(field_arg); + auto appender= m_appender.get(); + + if (field->is_real_null()) + { + appender->Append(duckdb::Value(duckdb::LogicalType::SQLNULL)); + return 0; + } + + enum_field_types type= field->real_type(); + + switch (type) + { + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: { + longlong value= field->val_int(); + appender->Append(value); + break; + } + case MYSQL_TYPE_LONGLONG: { + longlong value= field->val_int(); + if (field->is_unsigned()) + appender->Append(value); + else + appender->Append(value); + break; + } + case MYSQL_TYPE_FLOAT: { + float value= static_cast(field->val_real()); + appender->Append(value); + break; + } + case MYSQL_TYPE_DOUBLE: { + double value= field->val_real(); + appender->Append(value); + break; + } + case MYSQL_TYPE_NEWDECIMAL: { + my_decimal value; + Field_new_decimal *decimal_field= static_cast(field); + uint precision_val= decimal_field->precision; + uint8 dec= decimal_field->dec; + + if (precision_val <= 38) + { + decimal_field->val_decimal(&value); + if (value.intg + value.frac > (int) precision_val || + value.frac > (int) dec) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, "Append DECIMAL field failed", "DuckDB Appender"); + return HA_DUCKDB_APPEND_ERROR; + } + + if (precision_val <= duckdb::Decimal::MAX_WIDTH_INT16) + appender->Append(duckdb::Value::DECIMAL( + get_duckdb_decimal(value, dec), precision_val, dec)); + else if (precision_val <= duckdb::Decimal::MAX_WIDTH_INT32) + appender->Append(duckdb::Value::DECIMAL( + get_duckdb_decimal(value, dec), precision_val, dec)); + else if (precision_val <= duckdb::Decimal::MAX_WIDTH_INT64) + appender->Append(duckdb::Value::DECIMAL( + get_duckdb_decimal(value, dec), precision_val, dec)); + else + appender->Append(duckdb::Value::DECIMAL( + get_duckdb_decimal(value, dec), precision_val, + dec)); + } + else if (myduck::use_double_for_decimal) + { + double dval= decimal_field->val_real(); + appender->Append(dval); + } + else + { + /* Append as decimal(38, dec) — truncate intg to fit */ + decimal_field->val_decimal(&value); + int real_intg= my_decimal_actual_intg(&value); + if (real_intg + (int) dec > 38) + { + my_error(ER_GET_ERRMSG, MYF(0), HA_DUCKDB_APPEND_ERROR, + "Decimal value out of range for DECIMAL(38,...)", "DuckDB Appender"); + return HA_DUCKDB_APPEND_ERROR; + } + appender->Append(duckdb::Value::DECIMAL( + get_duckdb_decimal(value, dec), 38, dec)); + } + break; + } + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: { + MYSQL_TIME tm; + field->get_date(&tm, date_mode_t(0)); + long date= + calc_daynr(tm.year, tm.month, tm.day) - myduck::days_at_timestart; + appender->Append(static_cast(date)); + break; + } + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_DATETIME2: { + MYSQL_TIME tm; + field->get_date(&tm, date_mode_t(0)); + /* Compute microseconds since Unix epoch directly, supporting + dates before 1970 (which TIME_to_gmt_sec cannot handle). */ + long days= calc_daynr(tm.year, tm.month, tm.day) - calc_daynr(1970, 1, 1); + longlong secs= + days * 86400LL + tm.hour * 3600LL + tm.minute * 60LL + tm.second; + appender->Append( + static_cast(secs * 1000000LL + tm.second_part)); + break; + } + case MYSQL_TYPE_YEAR: { + longlong value= field->val_int(); + appender->Append(value); + break; + } + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_TIME2: { + MYSQL_TIME tm; + field->get_date(&tm, date_mode_t(0)); + appender->Append(static_cast( + (tm.hour * 3600LL + tm.minute * 60LL + tm.second) * 1000000LL + + tm.second_part)); + break; + } + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_TIMESTAMP2: { + /* Use get_date() to get local-time representation, matching the + non-batch SQL path which stores val_str() (local time string). + get_timestamp() returns UTC, but DuckDB read path does not + apply timezone conversion, so we must store local time. */ + MYSQL_TIME tm; + field->get_date(&tm, date_mode_t(0)); + long days= calc_daynr(tm.year, tm.month, tm.day) - calc_daynr(1970, 1, 1); + longlong secs= + days * 86400LL + tm.hour * 3600LL + tm.minute * 60LL + tm.second; + appender->Append( + static_cast(secs * 1000000LL + tm.second_part)); + break; + } + case MYSQL_TYPE_SET: + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_BIT: + case MYSQL_TYPE_GEOMETRY: + case MYSQL_TYPE_VARCHAR: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: { + char buf[128]; + String tmp(buf, sizeof(buf), &my_charset_bin); + field->val_str(&tmp); + + bool is_blob= false; + if (blob_type_map != nullptr) + is_blob= bitmap_is_set(blob_type_map, field->field_index); + else + is_blob= (FieldConvertor::convert_type(field) == "BLOB"); + + if (is_blob) + { + auto value= duckdb::Value::BLOB((duckdb::const_data_ptr_t) tmp.ptr(), + tmp.length()); + appender->Append(value); + } + else + { + appender->Append( + duckdb::string_t(tmp.ptr(), tmp.length())); + } + break; + } + default: + return HA_DUCKDB_APPEND_ERROR; + } + return 0; +} + +static void appendSelectQuery(std::stringstream &ss, + const std::string &select_list, + const std::string &pk_list, + const std::string &table_name, int delete_flag) +{ + ss << "SELECT UNNEST(r) FROM (SELECT LAST(ROW(" << select_list + << ") ORDER BY \"#mdb_row_no\") AS r, " + "LAST(\"#mdb_delete_flag\" ORDER BY \"#mdb_row_no\") AS " + "\"#mdb_delete_flag\" FROM main.\"" + << table_name << "\" GROUP BY " << pk_list << ")"; + if (!delete_flag) + ss << " WHERE \"#mdb_delete_flag\" = " << delete_flag; +} + +void DeltaAppender::generateQuery(std::stringstream &ss, bool delete_flag) +{ + ss.str(""); + ss << "USE \"" << m_schema_name << "\"; "; + + if (!delete_flag) + { + ss << "INSERT INTO \"" << m_schema_name << "\".\"" << m_table_name + << "\" "; + appendSelectQuery(ss, m_col_list, m_pk_list, m_tmp_table_name, + delete_flag); + ss << ";"; + } + else + { + ss << "DELETE FROM \"" << m_schema_name << "\".\"" << m_table_name + << "\" WHERE (" << m_pk_list << ") IN ("; + appendSelectQuery(ss, m_pk_list, m_pk_list, m_tmp_table_name, delete_flag); + ss << ");"; + } +} + +bool DeltaAppender::flush(bool idempotent_flag) +{ + m_appender->Flush(); + + if (m_use_tmp_table) + { + std::stringstream ss; + + if (m_has_delete || idempotent_flag) + { + generateQuery(ss, true); + auto ret= myduck::duckdb_query(*m_con, ss.str()); + if (ret->HasError()) + return true; + } + + if (m_has_insert) + { + generateQuery(ss, false); + auto ret= myduck::duckdb_query(*m_con, ss.str()); + if (ret->HasError()) + return true; + } + + ss.str(""); + ss << "DROP TABLE main.\"" << m_tmp_table_name << "\""; + auto ret= myduck::duckdb_query(*m_con, ss.str()); + if (ret->HasError()) + return true; + } + + return false; +} + +void DeltaAppender::cleanup() +{ + if (m_use_tmp_table) + { + my_bitmap_free(&m_pk_bitmap); + std::stringstream ss; + ss << "DROP TABLE IF EXISTS main.\"" << m_tmp_table_name << "\";"; + myduck::duckdb_query(*m_con, ss.str()); + } +} + +void DeltaAppenders::delete_appender(std::string &db, std::string &tb) +{ + auto key= std::make_pair(db, tb); + m_append_infos.erase(key); +} + +bool DeltaAppenders::flush_all(bool idempotent_flag, std::string &error_msg) +{ + try + { + for (auto &pair : m_append_infos) + { + if (pair.second->flush(idempotent_flag)) + { + error_msg= "DeltaAppender flush failed"; + return true; + } + } + } + catch (std::exception &ex) + { + error_msg= ex.what(); + sql_print_error("DuckDB: DeltaAppender flush error: %s", + error_msg.c_str()); + return true; + } + m_append_infos.clear(); + return false; +} + +DeltaAppender *DeltaAppenders::get_appender(std::string &db, std::string &tb, + bool insert_only, TABLE *table) +{ + auto key= std::make_pair(db, tb); + auto it= m_append_infos.find(key); + if (it != m_append_infos.end()) + return it->second.get(); + + auto appender= std::make_unique(m_con, db, tb, !insert_only); + try + { + if (appender->Initialize(table)) + return nullptr; + } + catch (std::exception &ex) + { + sql_print_error("DuckDB: DeltaAppender init error: %s", ex.what()); + return nullptr; + } + + auto *raw= appender.get(); + m_append_infos[key]= std::move(appender); + return raw; +} diff --git a/storage/duckdb/runtime/delta_appender.h b/storage/duckdb/runtime/delta_appender.h new file mode 100644 index 0000000000000..c9c4b8aa302b8 --- /dev/null +++ b/storage/duckdb/runtime/delta_appender.h @@ -0,0 +1,110 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include +#include +#include "duckdb_manager.h" +#include "field.h" + +class DeltaAppender +{ +public: + int append_row_insert(TABLE *table, ulonglong trx_no, + const MY_BITMAP *blob_type_map); + + int append_row_update(TABLE *table, ulonglong trx_no, const uchar *old_row); + + int append_row_delete(TABLE *table, ulonglong trx_no, + const uchar *old_row= nullptr); + + static std::string buf_table_name(std::string db, std::string tb) + { + return db + "_rds_buf_" + tb; + } + + DeltaAppender(std::shared_ptr con, std::string db, + std::string tb, bool use_tmp_table) + : m_use_tmp_table(use_tmp_table), m_schema_name(db), m_table_name(tb), + m_con(con) + { + } + + bool Initialize(TABLE *table); + + int append_mysql_field(const Field *field, + const MY_BITMAP *blob_type_map= nullptr); + + DeltaAppender()= default; + + ~DeltaAppender() { cleanup(); } + + bool flush(bool idempotent_flag); + + void cleanup(); + +private: + void generateQuery(std::stringstream &ss, bool delete_flag); + + bool m_use_tmp_table; + + std::string m_schema_name; + std::string m_table_name; + std::string m_tmp_table_name; + + MY_BITMAP m_pk_bitmap; + std::string m_pk_list{""}; + std::string m_col_list{""}; + + uint64_t m_row_count{0}; + bool m_has_insert{false}; + bool m_has_update{false}; + bool m_has_delete{false}; + + std::shared_ptr m_con; + + std::unique_ptr m_appender; +}; + +class DeltaAppenders +{ +public: + DeltaAppenders(std::shared_ptr con) + : m_con(con), m_append_infos() + { + } + + ~DeltaAppenders()= default; + + void delete_appender(std::string &db, std::string &tb); + + bool flush_all(bool idempotent_flag, std::string &error_msg); + + bool is_empty() { return m_append_infos.empty(); } + + DeltaAppender *get_appender(std::string &db, std::string &tb, + bool insert_only, TABLE *table); + +private: + std::shared_ptr m_con; + + std::map, std::unique_ptr> + m_append_infos; +}; diff --git a/storage/duckdb/runtime/duckdb_config.cc b/storage/duckdb/runtime/duckdb_config.cc new file mode 100644 index 0000000000000..336dfaabc4312 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_config.cc @@ -0,0 +1,160 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "duckdb_config.h" +#include "duckdb_query.h" +#include "duckdb/common/string_util.hpp" + +#include + +namespace myduck +{ + +ulonglong global_memory_limit= 0; +char *global_duckdb_temp_directory= nullptr; +ulonglong global_max_temp_directory_size= 0; +ulonglong global_max_threads= 0; +ulonglong appender_allocator_flush_threshold= 0; +ulonglong checkpoint_threshold= 268435456; /* 256 MB */ +my_bool global_use_dio= FALSE; +my_bool global_scheduler_process_partial= TRUE; +my_bool use_double_for_decimal= TRUE; +my_bool require_primary_key= TRUE; + +const char *explain_output_names[]= {"ALL", "OPTIMIZED_ONLY", "PHYSICAL_ONLY", + NullS}; + +TYPELIB explain_output_typelib= CREATE_TYPELIB_FOR(explain_output_names); + +const char *disabled_optimizers_names[]= {"EXPRESSION_REWRITER", + "FILTER_PULLUP", + "FILTER_PUSHDOWN", + "EMPTY_RESULT_PULLUP", + "CTE_FILTER_PUSHER", + "REGEX_RANGE", + "IN_CLAUSE", + "JOIN_ORDER", + "DELIMINATOR", + "UNNEST_REWRITER", + "UNUSED_COLUMNS", + "STATISTICS_PROPAGATION", + "COMMON_SUBEXPRESSIONS", + "COMMON_AGGREGATE", + "COLUMN_LIFETIME", + "BUILD_SIDE_PROBE_SIDE", + "LIMIT_PUSHDOWN", + "TOP_N", + "COMPRESSED_MATERIALIZATION", + "DUPLICATE_GROUPS", + "REORDER_FILTER", + "SAMPLING_PUSHDOWN", + "JOIN_FILTER_PUSHDOWN", + "EXTENSION", + "MATERIALIZED_CTE", + "SUM_REWRITER", + "LATE_MATERIALIZATION", + NullS}; + +TYPELIB disabled_optimizers_typelib= + CREATE_TYPELIB_FOR(disabled_optimizers_names); + +std::string BytesToHumanReadableString(uint64_t bytes, uint64_t multiplier) +{ + return duckdb::StringUtil::BytesToHumanReadableString(bytes, multiplier); +} + +/* ---- ON_UPDATE callbacks for global proxy variables ---- */ + +void update_memory_limit_cb(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(ulonglong *) var_ptr= *(const ulonglong *) save; + std::ostringstream oss; + if (global_memory_limit == 0) + oss << "RESET GLOBAL memory_limit"; + else + { + oss << "SET GLOBAL memory_limit = '"; + oss << BytesToHumanReadableString(global_memory_limit) << "'"; + } + duckdb_query(oss.str()); +} + +void update_max_temp_directory_size_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(ulonglong *) var_ptr= *(const ulonglong *) save; + std::ostringstream oss; + if (global_max_temp_directory_size == 0) + oss << "RESET GLOBAL max_temp_directory_size"; + else + { + oss << "SET GLOBAL max_temp_directory_size = '"; + oss << BytesToHumanReadableString(global_max_temp_directory_size) << "'"; + } + duckdb_query(oss.str()); +} + +void update_threads_cb(MYSQL_THD thd, struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(ulonglong *) var_ptr= *(const ulonglong *) save; + std::ostringstream oss; + if (global_max_threads == 0) + oss << "RESET GLOBAL threads"; + else + oss << "SET GLOBAL threads = " << global_max_threads; + duckdb_query(oss.str()); +} + +void update_scheduler_process_partial_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(my_bool *) var_ptr= *(const my_bool *) save; + std::ostringstream oss; + oss << "SET scheduler_process_partial = " + << (global_scheduler_process_partial ? "true" : "false"); + duckdb_query(oss.str()); +} + +void update_appender_flush_threshold_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(ulonglong *) var_ptr= *(const ulonglong *) save; + std::ostringstream oss; + oss << "SET GLOBAL allocator_flush_threshold = '" + << BytesToHumanReadableString(appender_allocator_flush_threshold) << "'"; + duckdb_query(oss.str()); +} + +void update_checkpoint_threshold_cb(MYSQL_THD thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save) +{ + *(ulonglong *) var_ptr= *(const ulonglong *) save; + std::ostringstream oss; + oss << "SET GLOBAL checkpoint_threshold = '"; + oss << BytesToHumanReadableString(checkpoint_threshold) << "'"; + duckdb_query(oss.str()); +} + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_context.cc b/storage/duckdb/runtime/duckdb_context.cc new file mode 100644 index 0000000000000..9d1f05557c216 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_context.cc @@ -0,0 +1,194 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "log.h" + +#undef UNKNOWN + +#include "duckdb_context.h" +#include "duckdb_charset_collation.h" +#include "duckdb_config.h" +#include "duckdb_types.h" +#include "duckdb_handler_errors.h" +#include "duckdb_timezone.h" + +#include + +namespace myduck +{ + +static void push_duckdb_warning(THD *thd, std::string &warn_msg) +{ + if (warn_msg.empty()) + return; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "DuckDB: %s", warn_msg.c_str()); + warn_msg.clear(); +} + +static std::string disabled_optimizers_to_string(ulonglong val) +{ + std::string result; + for (uint i= 0; disabled_optimizers_names[i] != NullS; i++) + { + if (val & (1ULL << i)) + { + if (!result.empty()) + result+= ','; + result+= disabled_optimizers_names[i]; + } + } + return result; +} + +void DuckdbThdContext::config_duckdb_env(const std::string &schema) +{ + if (schema.empty() || schema == m_current_schema) + return; + + std::string sql1= "CREATE SCHEMA IF NOT EXISTS \"" + schema + "\""; + std::string sql2= "USE \"" + schema + "\""; + m_current_schema= schema; + + for (auto &sql : {sql1, sql2}) + { + auto res= duckdb_query(get_connection(), sql); + if (res && res->HasError()) + sql_print_warning("DuckDB: config_duckdb_env failed: %s (sql=%s)", + res->GetError().c_str(), sql.c_str()); + } +} + +void DuckdbThdContext::config_duckdb_session(THD *thd) +{ + std::vector config_sql; + + /* Timezone */ + std::string warn_msg; + std::string tz_name= get_timezone_according_thd(thd, warn_msg); + if (tz_name != m_current_timezone) + { + config_sql.push_back("SET TimeZone = '" + tz_name + "'"); + push_duckdb_warning(thd, warn_msg); + m_current_timezone= tz_name; + } + + /* Collation: force_no_collation overrides to binary (POSIX) */ + warn_msg.clear(); + std::string collation; + if (get_thd_force_no_collation(thd)) + collation= COLLATION_BINARY; + else + collation= + get_duckdb_collation(thd->variables.collation_connection, warn_msg); + if (collation != m_collation) + { + config_sql.push_back("SET default_collation = '" + collation + "'"); + /* Only warn when collation is explicitly changed, not on initial setup */ + if (!m_collation.empty()) + push_duckdb_warning(thd, warn_msg); + m_collation= collation; + } + + /* merge_join_threshold (session) */ + ulonglong mjt= get_thd_merge_join_threshold(thd); + if (mjt != m_merge_join_threshold) + { + config_sql.push_back("SET merge_join_threshold = " + std::to_string(mjt)); + m_merge_join_threshold= mjt; + } + + /* disabled_optimizers (session) */ + ulonglong dopt= get_thd_disabled_optimizers(thd); + if (dopt != m_disabled_optimizers) + { + std::string val_str= disabled_optimizers_to_string(dopt); + config_sql.push_back("SET disabled_optimizers = '" + val_str + "'"); + m_disabled_optimizers= dopt; + } + + /* explain_output (session, only when EXPLAIN) */ + ulong eo= get_thd_explain_output(thd); + std::string eo_str= explain_output_names[eo]; + if (eo_str != m_explain_output_str) + { + config_sql.push_back("SET explain_output = '" + eo_str + "'"); + m_explain_output_str= eo_str; + } + + /* Execute all config statements */ + for (auto &sql : config_sql) + { + auto res= duckdb_query(get_connection(), sql); + if (res && res->HasError()) + sql_print_warning("DuckDB: config_duckdb_session failed: %s (sql=%s)", + res->GetError().c_str(), sql.c_str()); + } +} + +DeltaAppender *DuckdbThdContext::get_appender(TABLE *table) +{ + if (!m_appenders) + m_appenders= std::make_unique(m_con); + + DatabaseTableNames dt(table->s->normalized_path.str); + std::string db= dt.db_name; + std::string tb= dt.table_name; + + return m_appenders->get_appender( + db, tb, batch_state == BatchState::IN_INSERT_ONLY_BATCH, table); +} + +int DuckdbThdContext::append_row_insert(TABLE *table, + const MY_BITMAP *blob_map) +{ + DeltaAppender *delta= get_appender(table); + return delta ? delta->append_row_insert(table, 0, blob_map) + : HA_DUCKDB_APPEND_ERROR; +} + +int DuckdbThdContext::append_row_update(TABLE *table, const uchar *old_row) +{ + DeltaAppender *delta= get_appender(table); + return delta ? delta->append_row_update(table, 0, old_row) + : HA_DUCKDB_APPEND_ERROR; +} + +int DuckdbThdContext::append_row_delete(TABLE *table) +{ + DeltaAppender *delta= get_appender(table); + return delta ? delta->append_row_delete(table, 0) : HA_DUCKDB_APPEND_ERROR; +} + +bool reject_xa_if_active(THD *thd) +{ + if (!thd->transaction->xid_state.is_explicit_XA()) + return false; + + static const char *xa_state_names[]= {"ACTIVE", "IDLE", "PREPARED", + "ROLLBACK ONLY"}; + my_error(ER_XAER_RMFAIL, MYF(0), + xa_state_names[thd->transaction->xid_state.get_state_code()]); + return true; +} + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_context.h b/storage/duckdb/runtime/duckdb_context.h new file mode 100644 index 0000000000000..bb7d00695df23 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_context.h @@ -0,0 +1,167 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include "duckdb_manager.h" +#include "duckdb_query.h" +#include "delta_appender.h" + +#include +#include + +class THD; +struct TABLE; + +namespace myduck +{ + +enum class BatchState +{ + UNDEFINED= 0, + NOT_IN_BATCH, + IN_INSERT_ONLY_BATCH, + IN_MIX_BATCH +}; + +class DuckdbThdContext +{ +public: + DuckdbThdContext() : batch_state(BatchState::UNDEFINED) + { + m_con= DuckdbManager::CreateConnection(); + } + + ~DuckdbThdContext() + { + if (has_transaction()) + { + std::string error_msg; + duckdb_trans_rollback(error_msg); + } + } + + bool has_transaction() { return m_con && m_con->HasActiveTransaction(); } + + bool duckdb_trans_begin() + { + if (!m_con || m_con->HasActiveTransaction()) + return true; + auto result= duckdb_query(*m_con, "BEGIN"); + return result->HasError(); + } + + bool duckdb_trans_commit(std::string &error_msg) + { + if (!m_con) + return true; + + if (m_con->HasActiveTransaction()) + { + auto result= duckdb_query(*m_con, "COMMIT"); + if (result->HasError()) + { + error_msg= result->GetError().c_str(); + return true; + } + } + set_batch_state(BatchState::UNDEFINED); + return false; + } + + bool duckdb_trans_rollback(std::string &error_msg) + { + if (!m_con) + return true; + + if (m_con->HasActiveTransaction()) + { + auto result= duckdb_query(*m_con, "ROLLBACK"); + if (result->HasError()) + { + error_msg= result->GetError().c_str(); + return true; + } + } + set_batch_state(BatchState::UNDEFINED); + return false; + } + + duckdb::Connection &get_connection() { return *m_con; } + + /** Set DuckDB current schema (CREATE SCHEMA IF NOT EXISTS + USE). */ + void config_duckdb_env(const std::string &schema); + + /** Configure DuckDB session variables (timezone, optimizer flags) from THD. + */ + void config_duckdb_session(THD *thd); + + void delete_appender(const std::string &schema, const std::string &table) + { + if (!m_appenders || m_appenders->is_empty()) + return; + std::string db= schema, tb= table; + m_appenders->delete_appender(db, tb); + } + + bool flush_appenders(std::string &error_msg) + { + if (m_appenders && !m_appenders->is_empty()) + { + if (m_appenders->flush_all(false, error_msg)) + return true; + } + set_batch_state(BatchState::UNDEFINED); + return false; + } + + DeltaAppender *get_appender(TABLE *table); + + int append_row_insert(TABLE *table, const MY_BITMAP *blob_map); + + int append_row_update(TABLE *table, const uchar *old_row); + + int append_row_delete(TABLE *table); + + void set_in_copy_ddl(bool in) { in_copy_ddl= in; } + bool is_in_copy_ddl() const { return in_copy_ddl; } + + void set_batch_state(BatchState state) { batch_state= state; } + BatchState get_batch_state() { return batch_state; } + +private: + std::shared_ptr m_con; + bool in_copy_ddl= false; + BatchState batch_state; + std::unique_ptr m_appenders; + + /* Cached session variable values — propagated to DuckDB on change */ + ulonglong m_merge_join_threshold= 4611686018427387904ULL; + ulonglong m_disabled_optimizers= 0; + std::string m_explain_output_str; + std::string m_current_schema; + std::string m_current_timezone; + std::string m_collation; +}; + +/** Return true (and push ER_XAER_RMFAIL) when THD is inside an XA + transaction. DuckDB does not support XA. */ +bool reject_xa_if_active(THD *thd); + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_manager.cc b/storage/duckdb/runtime/duckdb_manager.cc new file mode 100644 index 0000000000000..7232baf2dde37 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_manager.cc @@ -0,0 +1,228 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#include "duckdb_manager.h" + +#include +#include "mysqld.h" +#include "log.h" +#include "duckdb_config.h" + +#undef UNKNOWN + +#include "cross_engine_scan.h" +#include "duckdb_mysql_compat.h" + +namespace myduck +{ + +DuckdbManager *DuckdbManager::m_instance= nullptr; + +DuckdbManager::DuckdbManager() : m_database(nullptr) {} + +bool DuckdbManager::Initialize() +{ + if (m_database != nullptr) + return false; + + std::lock_guard lock(m_mutex); + + if (m_database != nullptr) + return false; + + duckdb::DBConfig config; + + config.options.use_direct_io= global_use_dio; + + if (global_max_threads != 0) + config.options.maximum_threads= global_max_threads; + + /* + When memory_limit sysvar is 0 (default), DuckDB tries to auto-detect + available RAM (80%). Inside some Docker/cgroup environments this + detection fails and returns 0, making DuckDB unable to allocate + anything. Use an explicit 1 GB fallback in that case. + */ + static constexpr uint64_t DUCKDB_DEFAULT_MEMORY_FALLBACK= 1ULL + << 30; /* 1 GB */ + if (global_memory_limit != 0) + config.options.maximum_memory= global_memory_limit; + else + config.options.maximum_memory= DUCKDB_DEFAULT_MEMORY_FALLBACK; + + if (global_max_temp_directory_size != 0) + config.options.maximum_swap_space= global_max_temp_directory_size; + + config.options.checkpoint_wal_size= checkpoint_threshold; + + /* Temp directory: user-specified or default (data directory) */ + { + char tmp_path[FN_REFLEN]; + if (global_duckdb_temp_directory && global_duckdb_temp_directory[0]) + config.options.temporary_directory= global_duckdb_temp_directory; + else + { + fn_format(tmp_path, DUCKDB_DEFAULT_TMP_NAME, mysql_real_data_home, "", + MYF(0)); + config.options.temporary_directory= tmp_path; + } + } + + /* Store all tables in one file in the data directory */ + char path[FN_REFLEN]; + fn_format(path, DUCKDB_FILE_NAME, mysql_real_data_home, "", MYF(0)); + + try + { + m_database= new duckdb::DuckDB(path, &config); + } + catch (const std::exception &e) + { + sql_print_error("DuckDB: failed to open database at '%s': %s", path, + e.what()); + m_database= nullptr; + return true; + } + catch (...) + { + sql_print_error("DuckDB: failed to open database at '%s': " + "unknown exception", + path); + m_database= nullptr; + return true; + } + + /* Enable autoloading of statically-linked extensions (core_functions etc.) */ + { + auto con= std::make_shared(*m_database); + con->Query("SET autoload_known_extensions=true"); + con->Query("SET autoinstall_known_extensions=true"); + + /* + Register MariaDB-compatible SQL macros for functions that DuckDB + lacks but MariaDB pushes down via the original query text. + */ + con->Query("CREATE OR REPLACE MACRO adddate(d, i) AS d + i"); + /* addtime/subtime registered as C++ UDFs */ + /* curdate/curtime — MariaDB aliases */ + /* datediff(d1, d2) — MariaDB returns days, DuckDB needs 3-arg form */ + con->Query("CREATE OR REPLACE MACRO datediff(d1, d2) AS " + "(d1::DATE - d2::DATE)"); + con->Query("CREATE OR REPLACE MACRO curdate() AS current_date"); + con->Query("CREATE OR REPLACE MACRO curtime(fsp := 0) AS current_time"); + /* convert_tz(ts, from_tz, to_tz) */ + con->Query("CREATE OR REPLACE MACRO convert_tz(ts, from_tz, to_tz) AS " + "timezone(to_tz, timezone(from_tz, ts))"); + con->Query("CREATE OR REPLACE MACRO subdate(d, i) AS d - i"); + con->Query("CREATE OR REPLACE MACRO insert(str, pos, len, newstr) AS " + "CASE WHEN pos < 1 OR pos > length(str) THEN str " + "ELSE substr(str, 1, pos - 1) || newstr || " + "substr(str, pos + len) END"); + /* to_base64 / from_base64 — DuckDB uses base64()/from_base64() */ + con->Query("CREATE OR REPLACE MACRO to_base64(x) AS " + "base64(encode(x))"); + /* substring_index(str, delim, count) */ + con->Query("CREATE OR REPLACE MACRO substring_index(s, d, c) AS " + "CASE WHEN c > 0 THEN " + "array_to_string(list_slice(string_split(s, d), 1, c), d) " + "WHEN c < 0 THEN " + "array_to_string(list_slice(string_split(s, d), c, NULL), d) " + "ELSE '' END"); + /* strcmp(s1, s2) — returns 0, -1 or 1 */ + con->Query("CREATE OR REPLACE MACRO strcmp(a, b) AS " + "CASE WHEN a = b THEN 0 WHEN a < b THEN -1 ELSE 1 END"); + /* MID() registered as C++ UDF in register_mysql_compat_functions() */ + /* oct, bin, hex, locate are now registered as native C++ scalar functions + in register_mysql_compat_functions() -- no SQL macros needed. */ + } + + /* Register MySQL-compatible function overloads */ + register_mysql_compat_functions(*m_database->instance); + + /* Register cross-engine scan support (_mdb_scan + replacement scan) */ + register_cross_engine_scan(*m_database->instance); + + sql_print_information("DuckDB: DuckdbManager::Initialize succeed, path=%s", + path); + + return false; +} + +bool DuckdbManager::CreateInstance() +{ + DBUG_ASSERT(m_instance == nullptr); + m_instance= new DuckdbManager(); + if (m_instance == nullptr) + { + sql_print_error("DuckDB: DuckdbManager::CreateInstance failed"); + return true; + } + + /* Eagerly initialize DuckDB so that errors are caught during plugin init + rather than later when the first query arrives. */ + if (m_instance->Initialize()) + { + sql_print_error("DuckDB: DuckdbManager::Initialize failed during " + "CreateInstance"); + delete m_instance; + m_instance= nullptr; + return true; + } + + return false; +} + +DuckdbManager::~DuckdbManager() +{ + if (m_database != nullptr) + { + try + { + delete m_database; + } + catch (...) + { + sql_print_error("DuckDB: exception during DuckDB database destruction"); + } + m_database= nullptr; + } +} + +void DuckdbManager::Cleanup() +{ + if (m_instance == nullptr) + return; + delete m_instance; + m_instance= nullptr; +} + +DuckdbManager &DuckdbManager::Get() +{ + DBUG_ASSERT(m_instance != nullptr); + return *m_instance; +} + +std::shared_ptr DuckdbManager::CreateConnection() +{ + auto &instance= Get(); + auto connection= std::make_shared(*instance.m_database); + return connection; +} + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_manager.h b/storage/duckdb/runtime/duckdb_manager.h new file mode 100644 index 0000000000000..07ad99be85457 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_manager.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include "duckdb.hpp" +#include "duckdb/common/types.hpp" +#include "duckdb/main/appender.hpp" +#include "duckdb/main/connection.hpp" +#include "duckdb/main/table_description.hpp" + +namespace myduck +{ + +constexpr char DUCKDB_FILE_NAME[]= "duckdb.db"; +constexpr char DUCKDB_DEFAULT_TMP_NAME[]= "duckdb_tmp"; + +class DuckdbManager +{ +public: + DuckdbManager(const DuckdbManager &)= delete; + DuckdbManager &operator=(const DuckdbManager &)= delete; + + static bool CreateInstance(); + static void Cleanup(); + static inline DuckdbManager &Get(); + static std::shared_ptr CreateConnection(); + +private: + static DuckdbManager *m_instance; + + DuckdbManager(); + ~DuckdbManager(); + + bool Initialize(); + + duckdb::DuckDB *m_database= nullptr; + std::mutex m_mutex; +}; + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_mysql_compat.cc b/storage/duckdb/runtime/duckdb_mysql_compat.cc new file mode 100644 index 0000000000000..af6e5afe7fc03 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_mysql_compat.cc @@ -0,0 +1,1413 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +/* + DuckDB scalar function overloads for MariaDB-compatible behavior. + + These add missing type overloads to DuckDB builtins so that pushdown + queries from MariaDB work without SQL text rewriting. Registered + once at DuckdbManager::Initialize() via register_mysql_compat_functions(). + + hex/oct/bin implementations ported from AliSQL's DuckDB fork. +*/ + +#include +#include "log.h" + +#undef UNKNOWN + +#include "duckdb_mysql_compat.h" + +#include "duckdb/catalog/catalog.hpp" +#include "duckdb/catalog/catalog_transaction.hpp" +#include "duckdb/common/bit_utils.hpp" +#include "duckdb/common/exception.hpp" +#include "duckdb/common/numeric_utils.hpp" +#include +#include +#include "duckdb/common/string_util.hpp" +#include "duckdb/common/types.hpp" +#include "duckdb/common/types/blob.hpp" +#include "duckdb/common/vector_operations/unary_executor.hpp" +#include "duckdb/common/vector_operations/vector_operations.hpp" +#include "duckdb/function/scalar_function.hpp" +#include "duckdb/function/function_set.hpp" +#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp" +#include "duckdb/common/types/timestamp.hpp" +#include "duckdb/main/database.hpp" +#include "duckdb/main/connection.hpp" + +#include "duckdb/common/types/string_type.hpp" +#include "re2/re2.h" + +namespace myduck +{ + +/* ================================================================ + octet_length(VARCHAR) -> BIGINT + DuckDB builtin only has octet_length(BLOB). + MariaDB OCTET_LENGTH() works on any string type. + ================================================================ */ + +static void octet_length_varchar_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + auto &input= args.data[0]; + auto count= args.size(); + + duckdb::UnaryExecutor::Execute( + input, result, count, + [](duckdb::string_t s) -> int64_t { return (int64_t) s.GetSize(); }); +} + +/* ================================================================ + length(VARCHAR) -> BIGINT (byte count, MariaDB semantics) + DuckDB builtin length(VARCHAR) returns character count. + MariaDB LENGTH() = OCTET_LENGTH() = byte count. + We override to match MariaDB behavior for pushdown queries. + ================================================================ */ + +static void length_varchar_byte_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + duckdb::UnaryExecutor::Execute( + args.data[0], result, args.size(), + [](duckdb::string_t s) -> int64_t { return (int64_t) s.GetSize(); }); +} + +/* ================================================================ + length(BLOB) -> BIGINT + DuckDB builtin length() only works on VARCHAR (returns char count). + MariaDB LENGTH() = OCTET_LENGTH() = byte count. + ================================================================ */ + +/* ================================================================ + ascii(VARCHAR) -> INTEGER (first byte, MariaDB semantics) + DuckDB builtin ascii() returns Unicode codepoint of first character. + MariaDB ASCII() returns the numeric value of the first byte. + ================================================================ */ + +static void ascii_byte_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + duckdb::UnaryExecutor::Execute( + args.data[0], result, args.size(), + [](duckdb::string_t s) -> int32_t { + return s.GetSize() > 0 ? (unsigned char) s.GetData()[0] : 0; + }); +} + +/* ================================================================ + ord(VARCHAR) -> BIGINT (multibyte byte-value, MariaDB semantics) + DuckDB builtin ord() returns Unicode codepoint. + MariaDB ORD() for multibyte characters returns + (byte1 * 256 + byte2) * 256 + byte3 ... etc. + For single-byte characters, same as ASCII(). + ================================================================ */ + +static void ord_byte_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + duckdb::UnaryExecutor::Execute( + args.data[0], result, args.size(), + [](duckdb::string_t s) -> int32_t { + auto data= (const unsigned char *) s.GetData(); + auto size= s.GetSize(); + if (size == 0) + return 0; + /* Determine UTF-8 character length from first byte */ + unsigned char c= data[0]; + int char_len= 1; + if (c >= 0xF0) + char_len= 4; + else if (c >= 0xE0) + char_len= 3; + else if (c >= 0xC0) + char_len= 2; + /* Single byte — same as ASCII */ + if (char_len == 1) + return (int32_t) c; + /* Multibyte: (b1 * 256 + b2) * 256 + b3 ... */ + int32_t val= 0; + for (int i= 0; i < char_len && i < (int) size; i++) + val= val * 256 + data[i]; + return val; + }); +} + +/* ================================================================ + Helper: parse MariaDB time interval string 'D H:M:S.us' into + microseconds. Supports formats: + 'HH:MM:SS', 'HH:MM:SS.uuuuuu', 'D HH:MM:SS', 'D HH:MM:SS.uuuuuu' + Returns total microseconds. Negative values supported via leading '-'. + ================================================================ */ + +static int64_t parse_mariadb_interval_us(const char *data, size_t len) +{ + if (len == 0) + return 0; + bool neg= false; + size_t i= 0; + if (data[0] == '-') + { + neg= true; + i++; + } + int days= 0, hours= 0, minutes= 0, seconds= 0, usec= 0; + + /* Check if there's a 'D ' prefix (day followed by space) */ + size_t space= std::string(data + i, len - i).find(' '); + if (space != std::string::npos) + { + days= atoi(std::string(data + i, space).c_str()); + i+= space + 1; + } + + /* Parse H:M:S */ + int parts[3]= {0, 0, 0}; + int pidx= 0; + size_t num_start= i; + for (; i <= len && pidx < 3; i++) + { + if (i == len || data[i] == ':' || data[i] == '.') + { + parts[pidx++]= atoi(std::string(data + num_start, i - num_start).c_str()); + num_start= i + 1; + if (i < len && data[i] == '.') + { + i++; + break; + } + } + } + hours= parts[0]; + minutes= parts[1]; + seconds= parts[2]; + + /* Parse fractional seconds */ + if (i < len) + { + std::string frac(data + i, len - i); + /* Pad to 6 digits */ + while (frac.size() < 6) + frac+= '0'; + frac= frac.substr(0, 6); + usec= atoi(frac.c_str()); + } + + int64_t total_us= ((int64_t) days * 86400 + (int64_t) hours * 3600 + + (int64_t) minutes * 60 + seconds) * + 1000000 + + usec; + return neg ? -total_us : total_us; +} + +/* ================================================================ + addtime(TIMESTAMP, VARCHAR) -> TIMESTAMP + subtime(TIMESTAMP, VARCHAR) -> TIMESTAMP + MariaDB ADDTIME/SUBTIME accepts time interval in 'D H:M:S.us' format. + DuckDB INTERVAL doesn't parse this format. + ================================================================ */ + +static void addtime_func(duckdb::DataChunk &args, + duckdb::ExpressionState &, + duckdb::Vector &result) +{ + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [](duckdb::timestamp_t ts, duckdb::string_t interval_str) + -> duckdb::timestamp_t { + int64_t us= parse_mariadb_interval_us(interval_str.GetData(), + interval_str.GetSize()); + return duckdb::timestamp_t(ts.value + us); + }); +} + +static void subtime_func(duckdb::DataChunk &args, + duckdb::ExpressionState &, + duckdb::Vector &result) +{ + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [](duckdb::timestamp_t ts, duckdb::string_t interval_str) + -> duckdb::timestamp_t { + int64_t us= parse_mariadb_interval_us(interval_str.GetData(), + interval_str.GetSize()); + return duckdb::timestamp_t(ts.value - us); + }); +} + +/* ================================================================ + rtrim(VARCHAR, VARCHAR), ltrim(VARCHAR, VARCHAR), trim(VARCHAR, VARCHAR) + DuckDB builtins remove individual characters from the set. + MariaDB TRIM removes a substring pattern (e.g. TRIM(TRAILING 'xyz' FROM s) + removes the trailing "xyz" substring, not individual x/y/z chars). + We override the 2-arg forms for substring semantics. + ================================================================ */ + +static void rtrim_substr_func(duckdb::DataChunk &args, + duckdb::ExpressionState &, + duckdb::Vector &result) +{ + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [&](duckdb::string_t s, duckdb::string_t pat) -> duckdb::string_t { + auto data= s.GetData(); + auto slen= (int64_t) s.GetSize(); + auto plen= (int64_t) pat.GetSize(); + if (plen == 0 || plen > slen) + return s; + /* Single char — same as DuckDB default behavior */ + if (plen == 1) + { + auto c= pat.GetData()[0]; + while (slen > 0 && data[slen - 1] == c) + slen--; + return duckdb::StringVector::AddString(result, data, slen); + } + /* Multi-char: remove trailing substring repeatedly */ + auto pdata= pat.GetData(); + while (slen >= plen && + memcmp(data + slen - plen, pdata, plen) == 0) + slen-= plen; + return duckdb::StringVector::AddString(result, data, slen); + }); +} + +static void ltrim_substr_func(duckdb::DataChunk &args, + duckdb::ExpressionState &, + duckdb::Vector &result) +{ + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [&](duckdb::string_t s, duckdb::string_t pat) -> duckdb::string_t { + auto data= s.GetData(); + auto slen= (int64_t) s.GetSize(); + auto plen= (int64_t) pat.GetSize(); + int64_t start= 0; + if (plen == 0 || plen > slen) + return s; + if (plen == 1) + { + auto c= pat.GetData()[0]; + while (start < slen && data[start] == c) + start++; + return duckdb::StringVector::AddString(result, data + start, + slen - start); + } + auto pdata= pat.GetData(); + while (start + plen <= slen && + memcmp(data + start, pdata, plen) == 0) + start+= plen; + return duckdb::StringVector::AddString(result, data + start, + slen - start); + }); +} + +static void length_blob_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + auto &input= args.data[0]; + auto count= args.size(); + + duckdb::UnaryExecutor::Execute( + input, result, count, + [](duckdb::string_t s) -> int64_t { return (int64_t) s.GetSize(); }); +} + +/* ================================================================ + json_contains(json, candidate, path) -> BOOLEAN + DuckDB has json_contains(json, candidate) -- 2-arg. + MariaDB JSON_CONTAINS(json, candidate, path) -- 3-arg, extracts + path first then checks containment. + Implemented as: json_contains(json_extract(json, path), candidate) + ================================================================ */ + +static void json_contains_3arg_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + auto &json_vec= args.data[0]; + auto &candidate_vec= args.data[1]; + auto &path_vec= args.data[2]; + auto count= args.size(); + + duckdb::TernaryExecutor::Execute( + json_vec, candidate_vec, path_vec, result, count, + [](duckdb::string_t json, duckdb::string_t candidate, + duckdb::string_t path) -> bool { + /* Minimal implementation: delegate to DuckDB's own functions + would require a ClientContext which we don't have here. + For now, return false -- placeholder for proper implementation. */ + (void) json; + (void) candidate; + (void) path; + return false; + }); +} + +/* ================================================================ + hex / oct / bin helper functions + Ported from AliSQL's DuckDB fork (core_functions/scalar/string/hex.cpp). + ================================================================ */ + +namespace { + +using namespace duckdb; + +/* ---- Hex byte writers ---- */ + +static void WriteHexBytes(uint64_t x, char *&output, idx_t buffer_size) +{ + idx_t offset= buffer_size * 4; + for (; offset >= 4; offset -= 4) + { + uint8_t byte= (x >> (offset - 4)) & 0x0F; + *output= Blob::HEX_TABLE[byte]; + output++; + } +} + +template +static void WriteHugeIntHexBytes(T x, char *&output, idx_t buffer_size) +{ + idx_t offset= buffer_size * 4; + auto upper= x.upper; + auto lower= x.lower; + + for (; offset >= 68; offset -= 4) + { + uint8_t byte= (upper >> (offset - 68)) & 0x0F; + *output= Blob::HEX_TABLE[byte]; + output++; + } + + for (; offset >= 4; offset -= 4) + { + uint8_t byte= (lower >> (offset - 4)) & 0x0F; + *output= Blob::HEX_TABLE[byte]; + output++; + } +} + +/* ---- Binary (bin) byte writers ---- */ + +static void WriteBinBytes(uint64_t x, char *&output, idx_t buffer_size) +{ + idx_t offset= buffer_size; + for (; offset >= 1; offset -= 1) + { + *output= NumericCast(((x >> (offset - 1)) & 0x01) + '0'); + output++; + } +} + +template +static void WriteHugeIntBinBytes(T x, char *&output, idx_t buffer_size) +{ + auto upper= x.upper; + auto lower= x.lower; + idx_t offset= buffer_size; + + for (; offset >= 65; offset -= 1) + { + *output= ((upper >> (offset - 65)) & 0x01) + '0'; + output++; + } + + for (; offset >= 1; offset -= 1) + { + *output= ((lower >> (offset - 1)) & 0x01) + '0'; + output++; + } +} + +/* ---- Octal byte writers ---- */ + +static void WriteOctBytes(uint64_t x, char *&output, idx_t buffer_size) +{ + idx_t offset= buffer_size * 3; + for (; offset >= 3; offset -= 3) + { + uint8_t byte= (x >> (offset - 3)) & 0x07; + *output= Blob::HEX_TABLE[byte]; + output++; + } +} + +template +static void WriteHugeIntOctBytes(T x, char *&output, idx_t buffer_size) +{ + idx_t offset= buffer_size * 3; + auto upper= x.upper; + auto lower= x.lower; + + for (; offset >= 69; offset -= 3) + { + uint8_t byte= (upper >> (offset - 66)) & 0x07; + *output= Blob::HEX_TABLE[byte]; + output++; + } + + { + uint8_t byte= ((upper & 0x03) << 1) + ((lower >> offset) & 0x01); + *output= Blob::HEX_TABLE[byte]; + output++; + offset -= 3; + } + + for (; offset >= 3; offset -= 3) + { + uint8_t byte= (lower >> (offset - 3)) & 0x07; + *output= Blob::HEX_TABLE[byte]; + output++; + } +} + +/* ================================================================ + Hex operator structs + ================================================================ */ + +struct HexStrOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto data= input.GetData(); + auto size= input.GetSize(); + + auto target= StringVector::EmptyString(result, size * 2); + auto output= target.GetDataWriteable(); + + for (idx_t i= 0; i < size; ++i) + { + *output= Blob::HEX_TABLE[(data[i] >> 4) & 0x0F]; + output++; + *output= Blob::HEX_TABLE[data[i] & 0x0F]; + output++; + } + + target.Finalize(); + return target; + } +}; + +struct HexIntegralOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto num_leading_zero= + CountZeros::Leading(static_cast(input)); + idx_t num_bits_to_check= 64 - num_leading_zero; + D_ASSERT(num_bits_to_check <= sizeof(INPUT_TYPE) * 8); + + idx_t buffer_size= (num_bits_to_check + 3) / 4; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHexBytes(static_cast(input), output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct HexHugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + idx_t num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= sizeof(INPUT_TYPE) * 2 - (num_leading_zero / 4); + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntHexBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct HexUhugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + idx_t num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= sizeof(INPUT_TYPE) * 2 - (num_leading_zero / 4); + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntHexBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct HexFloatOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + int64_t input_integer= std::round(input); + return HexIntegralOperator::Operation(input_integer, + result); + } +}; + +/* ================================================================ + Oct operator structs + ================================================================ */ + +struct OctIntegralOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto num_leading_zero= + CountZeros::Leading(static_cast(input)); + idx_t num_bits_to_check= 64 - num_leading_zero; + D_ASSERT(num_bits_to_check <= sizeof(INPUT_TYPE) * 8); + + idx_t buffer_size= (num_bits_to_check + 2) / 3; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteOctBytes(static_cast(input), output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct OctHugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + idx_t num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= + (sizeof(INPUT_TYPE) * 2 - num_leading_zero + 2) / 3; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntOctBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct OctUhugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + idx_t num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= + (sizeof(INPUT_TYPE) * 2 - num_leading_zero + 2) / 3; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntOctBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct OctFloatOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + int64_t input_integer= std::round(input); + return OctIntegralOperator::Operation(input_integer, + result); + } +}; + +struct OctStrOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + double d; + std::string tmp(input.GetData(), input.GetSize()); + char *end= nullptr; + errno= 0; + d= strtod(tmp.c_str(), &end); + bool success= (errno == 0 && end != tmp.c_str()); + if (!success) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + else + { + return OctFloatOperator::Operation(d, result); + } + } +}; + +/* ================================================================ + Bin (binary) operator structs + ================================================================ */ + +struct BinaryIntegralOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto num_leading_zero= + CountZeros::Leading(static_cast(input)); + idx_t num_bits_to_check= 64 - num_leading_zero; + D_ASSERT(num_bits_to_check <= sizeof(INPUT_TYPE) * 8); + + idx_t buffer_size= num_bits_to_check; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + D_ASSERT(buffer_size > 0); + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteBinBytes(static_cast(input), output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct BinaryHugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= sizeof(INPUT_TYPE) * 8 - num_leading_zero; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntBinBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct BinaryUhugeIntOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + auto num_leading_zero= + CountZeros::Leading(UnsafeNumericCast(input)); + idx_t buffer_size= sizeof(INPUT_TYPE) * 8 - num_leading_zero; + + if (buffer_size == 0) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + + auto target= StringVector::EmptyString(result, buffer_size); + auto output= target.GetDataWriteable(); + + WriteHugeIntBinBytes(input, output, buffer_size); + + target.Finalize(); + return target; + } +}; + +struct BinaryFloatOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + int64_t input_integer= std::round(input); + return BinaryIntegralOperator::Operation(input_integer, + result); + } +}; + +struct BinaryStrOperator { + template + static RESULT_TYPE Operation(INPUT_TYPE input, Vector &result) + { + double d; + std::string tmp(input.GetData(), input.GetSize()); + char *end= nullptr; + errno= 0; + d= strtod(tmp.c_str(), &end); + bool success= (errno == 0 && end != tmp.c_str()); + if (!success) + { + auto target= StringVector::EmptyString(result, 1); + auto output= target.GetDataWriteable(); + *output= '0'; + target.Finalize(); + return target; + } + else + { + return BinaryFloatOperator::Operation(d, result); + } + } +}; + +/* ================================================================ + Template wrapper functions for UnaryExecutor::ExecuteString + ================================================================ */ + +template +static void ToHexFunction(DataChunk &args, ExpressionState &state, + Vector &result) +{ + D_ASSERT(args.ColumnCount() == 1); + auto &input= args.data[0]; + idx_t count= args.size(); + UnaryExecutor::ExecuteString(input, result, count); +} + +template +static void ToBinaryFunction(DataChunk &args, ExpressionState &state, + Vector &result) +{ + D_ASSERT(args.ColumnCount() == 1); + auto &input= args.data[0]; + idx_t count= args.size(); + UnaryExecutor::ExecuteString(input, result, count); +} + +template +static void ToOctFunction(DataChunk &args, ExpressionState &state, + Vector &result) +{ + D_ASSERT(args.ColumnCount() == 1); + auto &input= args.data[0]; + idx_t count= args.size(); + UnaryExecutor::ExecuteString(input, result, count); +} + +} /* anonymous namespace */ + +/* ================================================================ + locate(substr, str) -> BIGINT + locate(substr, str, pos) -> BIGINT + MariaDB LOCATE(substr, str [, pos]) returns the position of the + first occurrence of substr in str, starting at position pos (1-based). + This is the reversed argument order of DuckDB's instr(str, substr). + ================================================================ */ + +static void locate_2arg_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + auto &needle_vec= args.data[0]; + auto &haystack_vec= args.data[1]; + auto count= args.size(); + + duckdb::BinaryExecutor::Execute( + needle_vec, haystack_vec, result, count, + [](duckdb::string_t needle, duckdb::string_t haystack) -> int64_t { + if (needle.GetSize() == 0) + return 1; + auto haystack_data= haystack.GetData(); + auto haystack_size= haystack.GetSize(); + auto needle_data= needle.GetData(); + auto needle_size= needle.GetSize(); + + if (needle_size > haystack_size) + return 0; + + for (duckdb::idx_t i= 0; i <= haystack_size - needle_size; i++) + { + if (memcmp(haystack_data + i, needle_data, needle_size) == 0) + return (int64_t)(i + 1); + } + return 0; + }); +} + +static void locate_3arg_func(duckdb::DataChunk &args, + duckdb::ExpressionState &state, + duckdb::Vector &result) +{ + auto &needle_vec= args.data[0]; + auto &haystack_vec= args.data[1]; + auto &pos_vec= args.data[2]; + auto count= args.size(); + + duckdb::TernaryExecutor::Execute( + needle_vec, haystack_vec, pos_vec, result, count, + [](duckdb::string_t needle, duckdb::string_t haystack, + int64_t pos) -> int64_t { + if (pos < 1) + return 0; + if (needle.GetSize() == 0) + return pos; + + auto haystack_data= haystack.GetData(); + auto haystack_size= (int64_t) haystack.GetSize(); + auto needle_data= needle.GetData(); + auto needle_size= (int64_t) needle.GetSize(); + + /* pos is 1-based; convert to 0-based offset */ + int64_t start= pos - 1; + if (start >= haystack_size) + return 0; + + if (needle_size > haystack_size - start) + return 0; + + for (int64_t i= start; i <= haystack_size - needle_size; i++) + { + if (memcmp(haystack_data + i, needle_data, needle_size) == 0) + return i + 1; + } + return 0; + }); +} + +/* ================================================================ + Registration + ================================================================ */ + +void register_mysql_compat_functions(duckdb::DatabaseInstance &db) +{ + auto &catalog= duckdb::Catalog::GetSystemCatalog(db); + auto transaction= duckdb::CatalogTransaction::GetSystemTransaction(db); + + /* octet_length(VARCHAR) -> BIGINT */ + { + duckdb::ScalarFunctionSet set("octet_length"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR}, duckdb::LogicalType::BIGINT, + octet_length_varchar_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* length(VARCHAR) -> BIGINT (byte count, replaces DuckDB char count) */ + /* length(BLOB) -> BIGINT */ + { + duckdb::ScalarFunctionSet set("length"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR}, duckdb::LogicalType::BIGINT, + length_varchar_byte_func)); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::BLOB}, duckdb::LogicalType::BIGINT, + length_blob_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* ascii(VARCHAR) -> INTEGER (first byte, replaces DuckDB codepoint) */ + { + duckdb::ScalarFunctionSet set("ascii"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR}, duckdb::LogicalType::INTEGER, + ascii_byte_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* ord(VARCHAR) -> INTEGER (multibyte byte-value, replaces DuckDB codepoint) */ + { + duckdb::ScalarFunctionSet set("ord"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR}, duckdb::LogicalType::INTEGER, + ord_byte_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* json_contains(VARCHAR, VARCHAR, VARCHAR) -> BOOLEAN -- 3-arg */ + { + duckdb::ScalarFunctionSet set("json_contains"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR, + duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::BOOLEAN, json_contains_3arg_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* hex() -- full AliSQL-compatible overloads */ + { + using namespace duckdb; + ScalarFunctionSet set("hex"); + set.AddFunction(ScalarFunction( + {LogicalType::VARCHAR}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::BLOB}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::BIGINT}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UBIGINT}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::HUGEINT}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UHUGEINT}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::DOUBLE}, LogicalType::VARCHAR, + ToHexFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::FLOAT}, LogicalType::VARCHAR, + ToHexFunction)); + CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* oct() -- full AliSQL-compatible overloads */ + { + using namespace duckdb; + ScalarFunctionSet set("oct"); + set.AddFunction(ScalarFunction( + {LogicalType::VARCHAR}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::BLOB}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::BIGINT}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UBIGINT}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::HUGEINT}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UHUGEINT}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::DOUBLE}, LogicalType::VARCHAR, + ToOctFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::FLOAT}, LogicalType::VARCHAR, + ToOctFunction)); + CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* bin() -- full AliSQL-compatible overloads */ + { + using namespace duckdb; + ScalarFunctionSet set("bin"); + set.AddFunction(ScalarFunction( + {LogicalType::VARCHAR}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::BIGINT}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UBIGINT}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::HUGEINT}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::UHUGEINT}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::DOUBLE}, LogicalType::VARCHAR, + ToBinaryFunction)); + set.AddFunction(ScalarFunction( + {LogicalType::FLOAT}, LogicalType::VARCHAR, + ToBinaryFunction)); + CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* locate(VARCHAR, VARCHAR) -> BIGINT (2-arg) */ + /* locate(VARCHAR, VARCHAR, BIGINT) -> BIGINT (3-arg) */ + { + duckdb::ScalarFunctionSet set("locate"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::BIGINT, locate_2arg_func)); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR, + duckdb::LogicalType::BIGINT}, + duckdb::LogicalType::BIGINT, locate_3arg_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* mid() — registered via SQL macro calling DuckDB's substr() which + handles multibyte UTF-8 correctly. We use a dedicated connection + for macro creation since macros support overloading by arg count + only when created with different names — so we use one 3-arg macro + that the 2-arg call will match via DuckDB's default parameter. + Actually DuckDB substr already works as 2 or 3 arg. */ + { + auto con= std::make_shared(db); + con->Query("CREATE OR REPLACE MACRO mid(s, p, n := NULL) AS " + "CASE WHEN n IS NULL THEN substr(s, p) " + "ELSE substr(s, p, n) END"); + } + + /* regexp_instr(VARCHAR, VARCHAR) → INTEGER + Returns 1-based position of first match, 0 if no match. */ + { + duckdb::ScalarFunctionSet set("regexp_instr"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::INTEGER, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [](duckdb::string_t expr, duckdb::string_t pat) -> int32_t { + duckdb_re2::RE2 re( + duckdb_re2::StringPiece(pat.GetData(), pat.GetSize())); + if (!re.ok()) + return 0; + duckdb_re2::StringPiece match; + duckdb_re2::StringPiece input(expr.GetData(), expr.GetSize()); + if (re.Match(input, 0, expr.GetSize(), + duckdb_re2::RE2::UNANCHORED, &match, 1)) + return (int32_t)(match.data() - expr.GetData()) + 1; + return 0; + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* regexp_replace(VARCHAR, VARCHAR, VARCHAR) → VARCHAR + Replaces all occurrences of pattern in expr with replacement. */ + { + duckdb::ScalarFunctionSet set("regexp_replace"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR, + duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::VARCHAR, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::TernaryExecutor::Execute( + args.data[0], args.data[1], args.data[2], result, args.size(), + [&](duckdb::string_t expr, duckdb::string_t pat, + duckdb::string_t repl) -> duckdb::string_t { + duckdb_re2::RE2 re( + duckdb_re2::StringPiece(pat.GetData(), pat.GetSize())); + if (!re.ok()) + return expr; + std::string s(expr.GetData(), expr.GetSize()); + duckdb_re2::RE2::GlobalReplace( + &s, re, + duckdb_re2::StringPiece(repl.GetData(), repl.GetSize())); + return duckdb::StringVector::AddString(result, s); + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* regexp_substr(VARCHAR, VARCHAR) → VARCHAR + Returns the substring matching pattern, or NULL if no match. */ + { + duckdb::ScalarFunctionSet set("regexp_substr"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::VARCHAR, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::BinaryExecutor::ExecuteWithNulls( + args.data[0], args.data[1], result, args.size(), + [&](duckdb::string_t expr, duckdb::string_t pat, + duckdb::ValidityMask &mask, + duckdb::idx_t idx) -> duckdb::string_t { + duckdb_re2::RE2 re( + duckdb_re2::StringPiece(pat.GetData(), pat.GetSize())); + if (!re.ok()) + { + mask.SetInvalid(idx); + return duckdb::string_t(); + } + duckdb_re2::StringPiece match; + duckdb_re2::StringPiece input(expr.GetData(), expr.GetSize()); + if (re.Match(input, 0, expr.GetSize(), + duckdb_re2::RE2::UNANCHORED, &match, 1)) + return duckdb::StringVector::AddString( + result, match.data(), match.size()); + mask.SetInvalid(idx); + return duckdb::string_t(); + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* json_unquote(VARCHAR) → VARCHAR + Removes JSON quotes and unescapes. Simple implementation. */ + { + duckdb::ScalarFunctionSet set("json_unquote"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR}, duckdb::LogicalType::VARCHAR, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::UnaryExecutor::Execute( + args.data[0], result, args.size(), + [&](duckdb::string_t input) -> duckdb::string_t { + auto data= input.GetData(); + auto size= input.GetSize(); + /* If not quoted, return as-is */ + if (size < 2 || data[0] != '"' || data[size - 1] != '"') + return input; + /* Strip quotes and unescape */ + std::string out; + out.reserve(size); + for (size_t i= 1; i < size - 1; i++) + { + if (data[i] == '\\' && i + 1 < size - 1) + { + i++; + switch (data[i]) + { + case '"': out+= '"'; break; + case '\\': out+= '\\'; break; + case '/': out+= '/'; break; + case 'b': out+= '\b'; break; + case 'f': out+= '\f'; break; + case 'n': out+= '\n'; break; + case 'r': out+= '\r'; break; + case 't': out+= '\t'; break; + default: out+= '\\'; out+= data[i]; break; + } + } + else + out+= data[i]; + } + return duckdb::StringVector::AddString(result, out); + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* addtime(TIMESTAMP/TIME, VARCHAR) → TIMESTAMP/TIME */ + { + duckdb::ScalarFunctionSet set("addtime"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::TIMESTAMP, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::TIMESTAMP, addtime_func)); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::TIME, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::TIME, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [](duckdb::dtime_t t, duckdb::string_t s) -> duckdb::dtime_t { + int64_t us= parse_mariadb_interval_us(s.GetData(), + s.GetSize()); + /* Wrap around 24h for DuckDB TIME range */ + int64_t r= t.micros + us; + const int64_t day_us= 86400LL * 1000000; + r= ((r % day_us) + day_us) % day_us; + return duckdb::dtime_t(r); + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* subtime(TIMESTAMP/TIME, VARCHAR) → TIMESTAMP/TIME */ + { + duckdb::ScalarFunctionSet set("subtime"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::TIMESTAMP, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::TIMESTAMP, subtime_func)); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::TIME, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::TIME, + [](duckdb::DataChunk &args, duckdb::ExpressionState &, + duckdb::Vector &result) { + duckdb::BinaryExecutor::Execute( + args.data[0], args.data[1], result, args.size(), + [](duckdb::dtime_t t, duckdb::string_t s) -> duckdb::dtime_t { + int64_t us= parse_mariadb_interval_us(s.GetData(), + s.GetSize()); + int64_t r= t.micros - us; + const int64_t day_us= 86400LL * 1000000; + r= ((r % day_us) + day_us) % day_us; + return duckdb::dtime_t(r); + }); + })); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* rtrim(VARCHAR, VARCHAR) — substring semantics (MariaDB TRIM) */ + { + duckdb::ScalarFunctionSet set("rtrim"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::VARCHAR, rtrim_substr_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + /* ltrim(VARCHAR, VARCHAR) — substring semantics (MariaDB TRIM) */ + { + duckdb::ScalarFunctionSet set("ltrim"); + set.AddFunction(duckdb::ScalarFunction( + {duckdb::LogicalType::VARCHAR, duckdb::LogicalType::VARCHAR}, + duckdb::LogicalType::VARCHAR, ltrim_substr_func)); + duckdb::CreateScalarFunctionInfo info(std::move(set)); + info.on_conflict= duckdb::OnCreateConflict::ALTER_ON_CONFLICT; + catalog.CreateFunction(transaction, info); + } + + sql_print_information( + "DuckDB: registered MySQL-compatible function overloads " + "(octet_length, length, ascii, ord, hex, oct, bin, locate, mid, " + "rtrim, ltrim, regexp_instr, regexp_replace, regexp_substr, " + "json_unquote, json_contains)"); +} + +} /* namespace myduck */ diff --git a/storage/duckdb/runtime/duckdb_mysql_compat.h b/storage/duckdb/runtime/duckdb_mysql_compat.h new file mode 100644 index 0000000000000..c771347cc225c --- /dev/null +++ b/storage/duckdb/runtime/duckdb_mysql_compat.h @@ -0,0 +1,25 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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. +*/ + +#pragma once + +namespace duckdb +{ +class DatabaseInstance; +} + +namespace myduck +{ + +/** + Register DuckDB scalar function overloads for MariaDB compatibility. + Called once during DuckdbManager::Initialize(). +*/ +void register_mysql_compat_functions(duckdb::DatabaseInstance &db); + +} /* namespace myduck */ diff --git a/storage/duckdb/runtime/duckdb_query.cc b/storage/duckdb/runtime/duckdb_query.cc new file mode 100644 index 0000000000000..77aad44848b36 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_query.cc @@ -0,0 +1,114 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "log.h" + +#undef UNKNOWN + +#include "duckdb_query.h" +#include "duckdb/common/exception.hpp" +#include "duckdb_context.h" +#include "duckdb_manager.h" +#include "duckdb_log.h" + +extern handlerton *duckdb_hton; + +namespace myduck +{ + +static std::string backticks_to_double_quotes(const std::string &sql) +{ + std::string out(sql); + for (auto &ch : out) + if (ch == '`') + ch= '"'; + return out; +} + +duckdb::unique_ptr +duckdb_query(duckdb::Connection &connection, const std::string &query) +{ + const std::string q= backticks_to_double_quotes(query); + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information("DuckDB query: %s", q.c_str()); + + try + { + auto res= connection.Query(q); + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY_RESULT) + { + if (res->HasError()) + sql_print_information("DuckDB error: %s", res->GetError().c_str()); + } + return res; + } + catch (duckdb::Exception &e) + { + auto result= duckdb::make_uniq( + duckdb::ErrorData(e.what())); + return result; + } + catch (std::exception &e) + { + auto result= duckdb::make_uniq( + duckdb::ErrorData(e.what())); + return result; + } +} + +static std::string get_thd_schema(THD *thd) +{ + if (thd->db.str && thd->db.length > 0) + return std::string(thd->db.str, thd->db.length); + return {}; +} + +duckdb::unique_ptr +duckdb_query(THD *thd, const std::string &query, bool need_config) +{ + auto *ctx= + static_cast(thd_get_ha_data(thd, duckdb_hton)); + if (!ctx) + { + ctx= new DuckdbThdContext(); + thd_set_ha_data(thd, duckdb_hton, ctx); + } + + if (need_config) + { + ctx->config_duckdb_env(get_thd_schema(thd)); + ctx->config_duckdb_session(thd); + } + + return duckdb_query(ctx->get_connection(), query); +} + +duckdb::unique_ptr +duckdb_query(const std::string &query) +{ + auto connection= DuckdbManager::CreateConnection(); + return duckdb_query(*connection, query); +} + +} // namespace myduck diff --git a/storage/duckdb/runtime/duckdb_query.h b/storage/duckdb/runtime/duckdb_query.h new file mode 100644 index 0000000000000..72a2da271d437 --- /dev/null +++ b/storage/duckdb/runtime/duckdb_query.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + Copyright (c) 2026, Roman Nozdrin + Copyright (c) 2026, Leonid Fedorov. + + 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-1335 USA +*/ + +#pragma once + +#include +#include +#include + +#include "duckdb.hpp" +#include "duckdb/main/connection.hpp" + +class THD; + +namespace myduck +{ + +duckdb::unique_ptr +duckdb_query(duckdb::Connection &connection, const std::string &query); + +duckdb::unique_ptr +duckdb_query(THD *thd, const std::string &query, bool need_config= true); + +duckdb::unique_ptr +duckdb_query(const std::string &query); + +} // namespace myduck diff --git a/storage/duckdb/runtime/fiber_context.c b/storage/duckdb/runtime/fiber_context.c new file mode 100644 index 0000000000000..25d66bcd85103 --- /dev/null +++ b/storage/duckdb/runtime/fiber_context.c @@ -0,0 +1,829 @@ +/* + Copyright 2011, 2012 Kristian Nielsen and Monty Program Ab + 2016 MariaDB Corporation AB + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see . +*/ + +/* + Fiber context implementation for DuckDB cross-engine scan. + + Copied from libmariadb/libmariadb/ma_context.c with libmariadb- + specific dependencies removed and functions renamed from + my_context_* to fiber_context_*. +*/ + +#include +#include +#include +#include +#include + +#include "fiber_context.h" + + +#ifdef FIBER_USE_UCONTEXT + +typedef void (*uc_func_t)(void); + +union pass_void_ptr_as_2_int { + int a[2]; + void *p; +}; + +static void +fiber_context_spawn_internal(int i0, int i1) +{ + int err; + struct fiber_context *c; + union pass_void_ptr_as_2_int u; + + u.a[0]= i0; + u.a[1]= i1; + c= (struct fiber_context *)u.p; + + (*c->user_func)(c->user_data); + c->active= 0; + err= setcontext(&c->base_context); + fprintf(stderr, "fiber_context: setcontext() failed: %d (errno=%d)\n", + err, errno); +} + + +int +fiber_context_continue(struct fiber_context *c) +{ + int err; + + if (!c->active) + return 0; + + err= swapcontext(&c->base_context, &c->spawned_context); + if (err) + { + fprintf(stderr, "fiber_context: swapcontext() failed: %d (errno=%d)\n", + err, errno); + return -1; + } + + return c->active; +} + + +int +fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) +{ + int err; + union pass_void_ptr_as_2_int u; + + err= getcontext(&c->spawned_context); + if (err) + return -1; + c->spawned_context.uc_stack.ss_sp= c->stack; + c->spawned_context.uc_stack.ss_size= c->stack_size; + c->spawned_context.uc_link= NULL; + c->user_func= f; + c->user_data= d; + c->active= 1; + u.a[1]= 0; + u.p= c; +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif + makecontext(&c->spawned_context, (uc_func_t)fiber_context_spawn_internal, 2, + u.a[0], u.a[1]); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + return fiber_context_continue(c); +} + + +int +fiber_context_yield(struct fiber_context *c) +{ + int err; + + if (!c->active) + return -1; + + err= swapcontext(&c->spawned_context, &c->base_context); + if (err) + return -1; + return 0; +} + +int +fiber_context_init(struct fiber_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + if (!(c->stack= malloc(stack_size))) + return -1; + c->stack_size= stack_size; + return 0; +} + +void +fiber_context_destroy(struct fiber_context *c) +{ + if (c->stack) + free(c->stack); +} + +#endif /* FIBER_USE_UCONTEXT */ + + +#ifdef FIBER_USE_X86_64_GCC_ASM + +int +fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) +{ + int ret; + + __asm__ __volatile__ + ( + "movq %%rsp, (%[save])\n\t" + "movq %[stack], %%rsp\n\t" +#if defined(__GCC_HAVE_DWARF2_CFI_ASM) || (defined(__clang__) && __clang_major__ < 13) + ".cfi_escape 0x07, 16\n\t" +#endif + "movq %%rbp, 8(%[save])\n\t" + "movq %%rbx, 16(%[save])\n\t" + "movq %%r12, 24(%[save])\n\t" + "movq %%r13, 32(%[save])\n\t" + "movq %%r14, 40(%[save])\n\t" + "movq %%r15, 48(%[save])\n\t" + "leaq 1f(%%rip), %%rax\n\t" + "leaq 2f(%%rip), %%rcx\n\t" + "movq %%rax, 56(%[save])\n\t" + "movq %%rcx, 64(%[save])\n\t" + "callq *%[f]\n\t" + "jmpq *56(%[save])\n" + "1:\n\t" + "movq (%[save]), %%rsp\n\t" + "xorl %[ret], %[ret]\n\t" + "jmp 3f\n" + "2:\n\t" + "movl $1, %[ret]\n" + "3:\n" + : [ret] "=a" (ret), + [f] "+S" (f), + [d] "+D" (d) + : [stack] "a" (c->stack_top), + [save] "b" (&c->save[0]) + : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc" + ); + + return ret; +} + +int +fiber_context_continue(struct fiber_context *c) +{ + int ret; + + __asm__ __volatile__ + ( + "movq (%[save]), %%rax\n\t" + "movq %%rsp, (%[save])\n\t" + "movq %%rax, %%rsp\n\t" + "movq 8(%[save]), %%rax\n\t" + "movq %%rbp, 8(%[save])\n\t" + "movq %%rax, %%rbp\n\t" + "movq 24(%[save]), %%rax\n\t" + "movq %%r12, 24(%[save])\n\t" + "movq %%rax, %%r12\n\t" + "movq 32(%[save]), %%rax\n\t" + "movq %%r13, 32(%[save])\n\t" + "movq %%rax, %%r13\n\t" + "movq 40(%[save]), %%rax\n\t" + "movq %%r14, 40(%[save])\n\t" + "movq %%rax, %%r14\n\t" + "movq 48(%[save]), %%rax\n\t" + "movq %%r15, 48(%[save])\n\t" + "movq %%rax, %%r15\n\t" + + "leaq 1f(%%rip), %%rax\n\t" + "leaq 2f(%%rip), %%rcx\n\t" + "movq %%rax, 56(%[save])\n\t" + "movq 64(%[save]), %%rax\n\t" + "movq %%rcx, 64(%[save])\n\t" + + "movq 16(%[save]), %%rcx\n\t" + "movq %%rbx, 16(%[save])\n\t" + "movq %%rcx, %%rbx\n\t" + + "jmpq *%%rax\n" + "1:\n\t" + "movq (%[save]), %%rsp\n\t" + "movq 8(%[save]), %%rbp\n\t" + "movq 24(%[save]), %%r12\n\t" + "movq 32(%[save]), %%r13\n\t" + "movq 40(%[save]), %%r14\n\t" + "movq 48(%[save]), %%r15\n\t" + "xorl %[ret], %[ret]\n\t" + "jmp 3f\n" + "2:\n\t" + "movl $1, %[ret]\n" + "3:\n" + : [ret] "=a" (ret) + : [save] "b" (&c->save[0]) + : "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "memory", "cc" + ); + + return ret; +} + +int +fiber_context_yield(struct fiber_context *c) +{ + uint64_t *save= &c->save[0]; + __asm__ __volatile__ + ( + "movq (%[save]), %%rax\n\t" + "movq %%rsp, (%[save])\n\t" + "movq %%rax, %%rsp\n\t" + "movq 8(%[save]), %%rax\n\t" + "movq %%rbp, 8(%[save])\n\t" + "movq %%rax, %%rbp\n\t" + "movq 16(%[save]), %%rax\n\t" + "movq %%rbx, 16(%[save])\n\t" + "movq %%rax, %%rbx\n\t" + "movq 24(%[save]), %%rax\n\t" + "movq %%r12, 24(%[save])\n\t" + "movq %%rax, %%r12\n\t" + "movq 32(%[save]), %%rax\n\t" + "movq %%r13, 32(%[save])\n\t" + "movq %%rax, %%r13\n\t" + "movq 40(%[save]), %%rax\n\t" + "movq %%r14, 40(%[save])\n\t" + "movq %%rax, %%r14\n\t" + "movq 48(%[save]), %%rax\n\t" + "movq %%r15, 48(%[save])\n\t" + "movq %%rax, %%r15\n\t" + "movq 64(%[save]), %%rax\n\t" + "leaq 1f(%%rip), %%rcx\n\t" + "movq %%rcx, 64(%[save])\n\t" + + "jmpq *%%rax\n" + + "1:\n" + : [save] "+D" (save) + : + : "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc" + ); + return 0; +} + +int +fiber_context_init(struct fiber_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + + if (!(c->stack_bot= malloc(stack_size))) + return -1; + /* + The x86_64 ABI specifies 16-byte stack alignment. + Also put two zero words at the top of the stack. + */ + c->stack_top= (void *) + (( ((intptr_t)c->stack_bot + stack_size) & ~(intptr_t)0xf) - 16); + memset(c->stack_top, 0, 16); + + return 0; +} + +void +fiber_context_destroy(struct fiber_context *c) +{ + if (c->stack_bot) + free(c->stack_bot); +} + +#endif /* FIBER_USE_X86_64_GCC_ASM */ + + +#ifdef FIBER_USE_I386_GCC_ASM + +int +fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) +{ + int ret; + + __asm__ __volatile__ + ( + "movl %%esp, (%[save])\n\t" + "movl %[stack], %%esp\n\t" +#if defined(__GCC_HAVE_DWARF2_CFI_ASM) || (defined(__clang__) && __clang_major__ < 13) + ".cfi_escape 0x07, 8\n\t" +#endif + "pushl %[d]\n\t" + "movl %%ebp, 4(%[save])\n\t" + "movl %%ebx, 8(%[save])\n\t" + "movl %%esi, 12(%[save])\n\t" + "movl %%edi, 16(%[save])\n\t" + "call 1f\n" + "1:\n\t" + "popl %%eax\n\t" + "addl $(2f-1b), %%eax\n\t" + "movl %%eax, 20(%[save])\n\t" + "addl $(3f-2f), %%eax\n\t" + "movl %%eax, 24(%[save])\n\t" + "call *%[f]\n\t" + "jmp *20(%[save])\n" + "2:\n\t" + "movl (%[save]), %%esp\n\t" + "xorl %[ret], %[ret]\n\t" + "jmp 4f\n" + "3:\n\t" + "movl $1, %[ret]\n" + "4:\n" + : [ret] "=a" (ret), + [f] "+c" (f), + [d] "+d" (d) + : [stack] "a" (c->stack_top), + [save] "D" (&c->save[0]) + : "memory", "cc" + ); + + return ret; +} + +int +fiber_context_continue(struct fiber_context *c) +{ + int ret; + + __asm__ __volatile__ + ( + "movl (%[save]), %%eax\n\t" + "movl %%esp, (%[save])\n\t" + "movl %%eax, %%esp\n\t" + "movl 4(%[save]), %%eax\n\t" + "movl %%ebp, 4(%[save])\n\t" + "movl %%eax, %%ebp\n\t" + "movl 8(%[save]), %%eax\n\t" + "movl %%ebx, 8(%[save])\n\t" + "movl %%eax, %%ebx\n\t" + "movl 12(%[save]), %%eax\n\t" + "movl %%esi, 12(%[save])\n\t" + "movl %%eax, %%esi\n\t" + + "movl 24(%[save]), %%eax\n\t" + "call 1f\n" + "1:\n\t" + "popl %%ecx\n\t" + "addl $(2f-1b), %%ecx\n\t" + "movl %%ecx, 20(%[save])\n\t" + "addl $(3f-2f), %%ecx\n\t" + "movl %%ecx, 24(%[save])\n\t" + + "movl 16(%[save]), %%ecx\n\t" + "movl %%edi, 16(%[save])\n\t" + "movl %%ecx, %%edi\n\t" + + "jmp *%%eax\n" + "2:\n\t" + "movl (%[save]), %%esp\n\t" + "movl 4(%[save]), %%ebp\n\t" + "movl 8(%[save]), %%ebx\n\t" + "movl 12(%[save]), %%esi\n\t" + "movl 16(%[save]), %%edi\n\t" + "xorl %[ret], %[ret]\n\t" + "jmp 4f\n" + "3:\n\t" + "movl $1, %[ret]\n" + "4:\n" + : [ret] "=a" (ret) + : [save] "D" (&c->save[0]) + : "ecx", "edx", "memory", "cc" + ); + + return ret; +} + +int +fiber_context_yield(struct fiber_context *c) +{ + uint64_t *save= &c->save[0]; + __asm__ __volatile__ + ( + "movl (%[save]), %%eax\n\t" + "movl %%esp, (%[save])\n\t" + "movl %%eax, %%esp\n\t" + "movl 4(%[save]), %%eax\n\t" + "movl %%ebp, 4(%[save])\n\t" + "movl %%eax, %%ebp\n\t" + "movl 8(%[save]), %%eax\n\t" + "movl %%ebx, 8(%[save])\n\t" + "movl %%eax, %%ebx\n\t" + "movl 12(%[save]), %%eax\n\t" + "movl %%esi, 12(%[save])\n\t" + "movl %%eax, %%esi\n\t" + "movl 16(%[save]), %%eax\n\t" + "movl %%edi, 16(%[save])\n\t" + "movl %%eax, %%edi\n\t" + + "movl 24(%[save]), %%eax\n\t" + "call 1f\n" + "1:\n\t" + "popl %%ecx\n\t" + "addl $(2f-1b), %%ecx\n\t" + "movl %%ecx, 24(%[save])\n\t" + + "jmp *%%eax\n" + + "2:\n" + : [save] "+d" (save) + : + : "eax", "ecx", "memory", "cc" + ); + return 0; +} + +int +fiber_context_init(struct fiber_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + if (!(c->stack_bot= malloc(stack_size))) + return -1; + c->stack_top= (void *) + (( ((intptr_t)c->stack_bot + stack_size) & ~(intptr_t)0xf) - 16); + memset(c->stack_top, 0, 16); + + return 0; +} + +void +fiber_context_destroy(struct fiber_context *c) +{ + if (c->stack_bot) + free(c->stack_bot); +} + +#endif /* FIBER_USE_I386_GCC_ASM */ + + +#ifdef FIBER_USE_AARCH64_GCC_ASM + +#if defined __clang_major__ && __clang_major__ >= 12 +# define BTI_J_STR "bti j" +#else +# define BTI_J_STR ".inst 0xd503249f" +#endif + +int +fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) +{ + register int ret asm("w0"); + register void (*f_reg)(void *) asm("x1") = f; + register void *d_reg asm("x2") = d; + register void *stack asm("x13") = c->stack_top; + register const uint64_t *save asm("x19") = &c->save[0]; + + __asm__ __volatile__ + ( + "mov x10, sp\n\t" + "mov sp, %[stack]\n\t" +#if defined(__GCC_HAVE_DWARF2_CFI_ASM) || (defined(__clang__) && __clang_major__ < 13) + ".cfi_escape 0x07, 30\n\t" +#endif + "stp x19, x20, [%[save], #0]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "stp x29, x10, [%[save], #80]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "adr x10, 1f\n\t" + "adr x11, 2f\n\t" + "stp x10, x11, [%[save], #160]\n\t" + + "mov x0, %[d]\n\t" + "blr %[f]\n\t" + "ldr x11, [%[save], #160]\n\t" + "br x11\n" + "1:\n\t" + BTI_J_STR "\n\t" + "ldr x10, [%[save], #88]\n\t" + "mov sp, x10\n\t" + "mov %w[ret], #0\n\t" + "b 3f\n" + "2:\n\t" + BTI_J_STR "\n\t" + "mov %w[ret], #1\n" + "3:\n" + : [ret] "=r" (ret), + [f] "+r" (f_reg), + [d] "+r" (d_reg), + [stack] "+r" (stack) + : [save] "r" (save) + : "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x14", "x15", +#if defined(__linux__) && !defined(__ANDROID__) + "x18", +#endif + "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + + return ret; +} + +int +fiber_context_continue(struct fiber_context *c) +{ + register int ret asm("w0"); + register const uint64_t *save asm("x19") = &c->save[0]; + + __asm__ __volatile__ + ( + "ldp x13, x11, [%[save], #0]\n\t" + "stp x19, x20, [%[save], #0]\n\t" + "mov x20, x11\n\t" + + "ldp x10, x11, [%[save], #16]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "mov x21, x10\n\t" + "mov x22, x11\n\t" + + "ldp x10, x11, [%[save], #32]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "mov x23, x10\n\t" + "mov x24, x11\n\t" + + "ldp x10, x11, [%[save], #48]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "mov x25, x10\n\t" + "mov x26, x11\n\t" + + "ldp x10, x11, [%[save], #64]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "mov x27, x10\n\t" + "mov x28, x11\n\t" + + "ldp x10, x11, [%[save], #80]\n\t" + "mov x14, sp\n\t" + "stp x29, x14, [%[save], #80]\n\t" + "mov x29, x10\n\t" + "mov sp, x11\n\t" + + "ldp d0, d1, [%[save], #96]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "fmov d8, d0\n\t" + "fmov d9, d1\n\t" + + "ldp d0, d1, [%[save], #112]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "fmov d10, d0\n\t" + "fmov d11, d1\n\t" + + "ldp d0, d1, [%[save], #128]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "fmov d12, d0\n\t" + "fmov d13, d1\n\t" + + "ldp d0, d1, [%[save], #144]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "fmov d14, d0\n\t" + "fmov d15, d1\n\t" + + "adr x10, 1f\n\t" + "adr x11, 2f\n\t" + "ldr x15, [%[save], #168]\n\t" + "stp x10, x11, [%[save], #160]\n\t" + "mov x19, x13\n\t" + "br x15\n" + "1:\n\t" + BTI_J_STR "\n\t" + "ldr x20, [%[save], #8]\n\t" + "ldp x21, x22, [%[save], #16]\n\t" + "ldp x23, x24, [%[save], #32]\n\t" + "ldp x25, x26, [%[save], #48]\n\t" + "ldp x27, x28, [%[save], #64]\n\t" + "ldp x29, x10, [%[save], #80]\n\t" + "mov sp, x10\n\t" + "ldp d8, d9, [%[save], #96]\n\t" + "ldp d10, d11, [%[save], #112]\n\t" + "ldp d12, d13, [%[save], #128]\n\t" + "ldp d14, d15, [%[save], #144]\n\t" + "mov %w[ret], #0\n\t" + "b 3f\n" + "2:\n\t" + BTI_J_STR "\n\t" + "mov %w[ret], #1\n" + "3:\n" + : [ret] "=r" (ret) + : [save] "r" (save) + : "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x12", "x13", "x14", "x15", +#if defined(__linux__) && !defined(__ANDROID__) + "x18", +#endif + "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + + return ret; +} + +int +fiber_context_yield(struct fiber_context *c) +{ + register const uint64_t *save asm("x19") = &c->save[0]; + __asm__ __volatile__ + ( + "ldp x13, x11, [%[save], #0]\n\t" + "stp x19, x20, [%[save], #0]\n\t" + "mov x20, x11\n\t" + + "ldp x10, x11, [%[save], #16]\n\t" + "stp x21, x22, [%[save], #16]\n\t" + "mov x21, x10\n\t" + "mov x22, x11\n\t" + + "ldp x10, x11, [%[save], #32]\n\t" + "stp x23, x24, [%[save], #32]\n\t" + "mov x23, x10\n\t" + "mov x24, x11\n\t" + + "ldp x10, x11, [%[save], #48]\n\t" + "stp x25, x26, [%[save], #48]\n\t" + "mov x25, x10\n\t" + "mov x26, x11\n\t" + + "ldp x10, x11, [%[save], #64]\n\t" + "stp x27, x28, [%[save], #64]\n\t" + "mov x27, x10\n\t" + "mov x28, x11\n\t" + + "ldp x10, x11, [%[save], #80]\n\t" + "mov x14, sp\n\t" + "stp x29, x14, [%[save], #80]\n\t" + "mov x29, x10\n\t" + "mov sp, x11\n\t" + + "ldp d0, d1, [%[save], #96]\n\t" + "stp d8, d9, [%[save], #96]\n\t" + "fmov d8, d0\n\t" + "fmov d9, d1\n\t" + + "ldp d0, d1, [%[save], #112]\n\t" + "stp d10, d11, [%[save], #112]\n\t" + "fmov d10, d0\n\t" + "fmov d11, d1\n\t" + + "ldp d0, d1, [%[save], #128]\n\t" + "stp d12, d13, [%[save], #128]\n\t" + "fmov d12, d0\n\t" + "fmov d13, d1\n\t" + + "ldp d0, d1, [%[save], #144]\n\t" + "stp d14, d15, [%[save], #144]\n\t" + "fmov d14, d0\n\t" + "fmov d15, d1\n\t" + + "ldr x11, [%[save], #168]\n\t" + "adr x10, 1f\n\t" + "str x10, [%[save], #168]\n\t" + "mov x19, x13\n\t" + "br x11\n" + + "1:\n" + BTI_J_STR "\n\t" + : + : [save] "r" (save) + : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x9", "x10", "x11", "x12", "x13", "x14", "x15", +#if defined(__linux__) && !defined(__ANDROID__) + "x18", +#endif + "x30", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", + "memory", "cc" + ); + return 0; +} + +int +fiber_context_init(struct fiber_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + + if (!(c->stack_bot= malloc(stack_size))) + return -1; + c->stack_top= (void *) + (( ((intptr_t)c->stack_bot + stack_size) & ~(intptr_t)0xf) - 16); + memset(c->stack_top, 0, 16); + + return 0; +} + +void +fiber_context_destroy(struct fiber_context *c) +{ + if (c->stack_bot) + free(c->stack_bot); +} + +#endif /* FIBER_USE_AARCH64_GCC_ASM */ + + +#ifdef FIBER_USE_WIN32_FIBERS + +#include + +int +fiber_context_yield(struct fiber_context *c) +{ + c->return_value= 1; + SwitchToFiber(c->app_fiber); + return 0; +} + +static void WINAPI +fiber_context_trampoline(void *p) +{ + struct fiber_context *c= (struct fiber_context *)p; + for(;;) + { + (*(c->user_func))(c->user_arg); + c->return_value= 0; + SwitchToFiber(c->app_fiber); + } +} + +int +fiber_context_init(struct fiber_context *c, size_t stack_size) +{ + memset(c, 0, sizeof(*c)); + c->lib_fiber= CreateFiber(stack_size, fiber_context_trampoline, c); + if (c->lib_fiber) + return 0; + return -1; +} + +void +fiber_context_destroy(struct fiber_context *c) +{ + if (c->lib_fiber) + { + DeleteFiber(c->lib_fiber); + c->lib_fiber= NULL; + } +} + +int +fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) +{ + c->user_func= f; + c->user_arg= d; + return fiber_context_continue(c); +} + +int +fiber_context_continue(struct fiber_context *c) +{ + void *current_fiber= IsThreadAFiber() ? GetCurrentFiber() + : ConvertThreadToFiber(c); + c->app_fiber= current_fiber; + SwitchToFiber(c->lib_fiber); + return c->return_value; +} + +#endif /* FIBER_USE_WIN32_FIBERS */ + + +#ifdef FIBER_CONTEXT_DISABLE + +int fiber_context_continue(struct fiber_context *c) { return -1; } +int fiber_context_spawn(struct fiber_context *c, void (*f)(void *), void *d) { return -1; } +int fiber_context_yield(struct fiber_context *c) { return -1; } +int fiber_context_init(struct fiber_context *c, size_t stack_size) { return -1; } +void fiber_context_destroy(struct fiber_context *c) { } + +#endif diff --git a/storage/duckdb/runtime/fiber_context.h b/storage/duckdb/runtime/fiber_context.h new file mode 100644 index 0000000000000..88c60852456ec --- /dev/null +++ b/storage/duckdb/runtime/fiber_context.h @@ -0,0 +1,171 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab + 2015, 2022 MariaDB Corporation AB + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see . +*/ + +/* + Fiber context for DuckDB cross-engine scan. + + Copied from libmariadb/include/ma_context.h and + libmariadb/libmariadb/ma_context.c with libmariadb-specific + dependencies removed (mysql_async_context, ma_global.h, DBUG, + boost::context). Only the core co-routine primitives are kept. +*/ + +#ifndef FIBER_CONTEXT_H +#define FIBER_CONTEXT_H + +#include +#include + +/* + Platform selection — same priority order as ma_context.h but without + boost::context and ASAN overrides (those need extra source files). +*/ +#ifdef _WIN32 +#define FIBER_USE_WIN32_FIBERS 1 +#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__x86_64__) && !defined(__ILP32__) +#define FIBER_USE_X86_64_GCC_ASM +#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__) +#define FIBER_USE_I386_GCC_ASM +#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__aarch64__) +#define FIBER_USE_AARCH64_GCC_ASM +#elif defined(__unix__) || defined(__APPLE__) +#include +#if defined(_POSIX_VERSION) +#define FIBER_USE_UCONTEXT +#endif +#endif + +#if !defined(FIBER_USE_WIN32_FIBERS) && \ + !defined(FIBER_USE_X86_64_GCC_ASM) && \ + !defined(FIBER_USE_I386_GCC_ASM) && \ + !defined(FIBER_USE_AARCH64_GCC_ASM) && \ + !defined(FIBER_USE_UCONTEXT) +#define FIBER_CONTEXT_DISABLE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FIBER_USE_WIN32_FIBERS +struct fiber_context { + void (*user_func)(void *); + void *user_arg; + void *app_fiber; + void *lib_fiber; + int return_value; +}; +#endif + +#ifdef FIBER_USE_UCONTEXT +#include + +struct fiber_context { + void (*user_func)(void *); + void *user_data; + void *stack; + size_t stack_size; + ucontext_t base_context; + ucontext_t spawned_context; + int active; +}; +#endif + +#ifdef FIBER_USE_X86_64_GCC_ASM +struct fiber_context { + uint64_t save[9]; + void *stack_top; + void *stack_bot; + int active; +}; +#endif + +#ifdef FIBER_USE_I386_GCC_ASM +struct fiber_context { + uint64_t save[7]; + void *stack_top; + void *stack_bot; + int active; +}; +#endif + +#ifdef FIBER_USE_AARCH64_GCC_ASM +struct fiber_context { + uint64_t save[22]; + void *stack_top; + void *stack_bot; + int active; +}; +#endif + +#ifdef FIBER_CONTEXT_DISABLE +struct fiber_context { + int dummy; +}; +#endif + +/* + Initialize a fiber context object. + Returns 0 on success, non-zero on failure. +*/ +extern int fiber_context_init(struct fiber_context *c, size_t stack_size); + +/* Free a fiber context object, deallocating any resources used. */ +extern void fiber_context_destroy(struct fiber_context *c); + +/* + Spawn a fiber context. The fiber will run the supplied user function, + passing the supplied user data pointer. + + The user function may call fiber_context_yield(), which will cause this + function to return 1. Then later fiber_context_continue() may be called, + which will resume the fiber by returning from the previous + fiber_context_yield() call. + + When the user function returns, this function returns 0. + In case of error, -1 is returned. +*/ +extern int fiber_context_spawn(struct fiber_context *c, + void (*f)(void *), void *d); + +/* + Suspend a fiber started with fiber_context_spawn. + + When fiber_context_yield() is called, execution immediately returns from + the last fiber_context_spawn() or fiber_context_continue() call. Then + when later fiber_context_continue() is called, execution resumes by + returning from this fiber_context_yield() call. + + Returns 0 if ok, -1 in case of error. +*/ +extern int fiber_context_yield(struct fiber_context *c); + +/* + Resume a suspended fiber. + + Each time it is suspended, this function returns 1. When the originally + spawned user function returns, this function returns 0. + In case of error, -1 is returned. +*/ +extern int fiber_context_continue(struct fiber_context *c); + +#ifdef __cplusplus +} +#endif + +#endif /* FIBER_CONTEXT_H */ diff --git a/storage/duckdb/runtime/fiber_context_test.cc b/storage/duckdb/runtime/fiber_context_test.cc new file mode 100644 index 0000000000000..70cd3d1427a39 --- /dev/null +++ b/storage/duckdb/runtime/fiber_context_test.cc @@ -0,0 +1,386 @@ +/* + Unit tests for fiber_context — the coroutine / fiber primitives + used by DuckDB cross-engine predicate pushdown. + + Build (standalone, from the runtime/ directory): + cc -c fiber_context.c -o fiber_context.o + c++ -std=c++17 fiber_context_test.cc fiber_context.o -o fiber_context_test + ./fiber_context_test + + Or via CMake: see the ADD_EXECUTABLE block in CMakeLists.txt +*/ + +#include "fiber_context.h" + +#include +#include +#include +#include + +#define FIBER_STACK_SIZE (64 * 1024) + +/* ---------------------------------------------------------------- + Helpers + ---------------------------------------------------------------- */ + +static int test_count = 0; +static int pass_count = 0; + +#define TEST(name) \ + static void test_##name(); \ + static struct Register_##name { \ + Register_##name() { tests.push_back({#name, test_##name}); } \ + } reg_##name; \ + static void test_##name() + +#define EXPECT(cond) do { \ + test_count++; \ + if (!(cond)) { \ + fprintf(stderr, " FAIL: %s:%d: %s\n", __FILE__, __LINE__, #cond); \ + } else { \ + pass_count++; \ + } \ + } while(0) + +struct TestEntry { + const char *name; + void (*func)(); +}; +static std::vector tests; + +/* ---------------------------------------------------------------- + Test 1: init / destroy — basic lifecycle + ---------------------------------------------------------------- */ +TEST(init_destroy) +{ + struct fiber_context ctx; + memset(&ctx, 0xAB, sizeof(ctx)); /* poison */ + + int rc = fiber_context_init(&ctx, FIBER_STACK_SIZE); + EXPECT(rc == 0); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 2: spawn a fiber that runs to completion without yielding + ---------------------------------------------------------------- */ +static int simple_run_flag = 0; + +static void simple_func(void *arg) +{ + simple_run_flag = *(int *)arg; +} + +TEST(spawn_no_yield) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + simple_run_flag = 0; + int val = 42; + + /* spawn should return 0 when the user function completes without yielding */ + int rc = fiber_context_spawn(&ctx, simple_func, &val); + EXPECT(rc == 0); + EXPECT(simple_run_flag == 42); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 3: spawn + single yield + continue → completion + ---------------------------------------------------------------- */ +struct single_yield_data { + struct fiber_context *ctx; + int phase; +}; + +static void single_yield_func(void *arg) +{ + auto *d = (single_yield_data *)arg; + d->phase = 1; + fiber_context_yield(d->ctx); + d->phase = 2; +} + +TEST(single_yield) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + single_yield_data d{&ctx, 0}; + + int rc = fiber_context_spawn(&ctx, single_yield_func, &d); + EXPECT(rc == 1); /* 1 = suspended */ + EXPECT(d.phase == 1); + + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0); /* 0 = completed */ + EXPECT(d.phase == 2); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 4: multiple yields — simulate chunk-based streaming + ---------------------------------------------------------------- */ +struct multi_yield_data { + struct fiber_context *ctx; + int chunks_produced; + static constexpr int TOTAL_CHUNKS = 5; +}; + +static void multi_yield_func(void *arg) +{ + auto *d = (multi_yield_data *)arg; + for (int i = 0; i < multi_yield_data::TOTAL_CHUNKS; i++) + { + d->chunks_produced++; + fiber_context_yield(d->ctx); + } +} + +TEST(multi_yield) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + multi_yield_data d{&ctx, 0}; + + int rc = fiber_context_spawn(&ctx, multi_yield_func, &d); + EXPECT(rc == 1); + EXPECT(d.chunks_produced == 1); + + for (int i = 2; i <= multi_yield_data::TOTAL_CHUNKS; i++) + { + rc = fiber_context_continue(&ctx); + EXPECT(rc == 1); /* still suspended */ + EXPECT(d.chunks_produced == i); + } + + /* One more continue — fiber function returns */ + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0); /* completed */ + EXPECT(d.chunks_produced == multi_yield_data::TOTAL_CHUNKS); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 5: continue on a completed context returns 0 + ---------------------------------------------------------------- */ +TEST(continue_after_done) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + int val = 1; + int rc = fiber_context_spawn(&ctx, simple_func, &val); + EXPECT(rc == 0); + + /* Calling continue on a finished fiber should return 0 (not crash). */ + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 6: large stack usage — verify the fiber stack is adequate + ---------------------------------------------------------------- */ +static void deep_stack_func(void *arg) +{ + auto *d = (single_yield_data *)arg; + /* Allocate ~16KB on the fiber stack. */ + volatile char buf[16384]; + memset((char *)buf, 0xCC, sizeof(buf)); + d->phase = (buf[0] == (char)0xCC && buf[16383] == (char)0xCC) ? 1 : -1; + fiber_context_yield(d->ctx); + d->phase = 2; +} + +TEST(large_stack) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + single_yield_data d{&ctx, 0}; + + int rc = fiber_context_spawn(&ctx, deep_stack_func, &d); + EXPECT(rc == 1); + EXPECT(d.phase == 1); + + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0); + EXPECT(d.phase == 2); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 7: two independent fibers — verify no cross-contamination + ---------------------------------------------------------------- */ +struct two_fiber_data { + struct fiber_context *ctx; + int id; + int phase; +}; + +static void two_fiber_func(void *arg) +{ + auto *d = (two_fiber_data *)arg; + d->phase = d->id * 10 + 1; + fiber_context_yield(d->ctx); + d->phase = d->id * 10 + 2; +} + +TEST(two_fibers) +{ + struct fiber_context ctx_a, ctx_b; + EXPECT(fiber_context_init(&ctx_a, FIBER_STACK_SIZE) == 0); + EXPECT(fiber_context_init(&ctx_b, FIBER_STACK_SIZE) == 0); + + two_fiber_data a{&ctx_a, 1, 0}; + two_fiber_data b{&ctx_b, 2, 0}; + + /* Spawn A, it yields at phase 11 */ + int rc = fiber_context_spawn(&ctx_a, two_fiber_func, &a); + EXPECT(rc == 1); + EXPECT(a.phase == 11); + + /* Spawn B, it yields at phase 21 */ + rc = fiber_context_spawn(&ctx_b, two_fiber_func, &b); + EXPECT(rc == 1); + EXPECT(b.phase == 21); + + /* A's state is not corrupted by B */ + EXPECT(a.phase == 11); + + /* Resume B first */ + rc = fiber_context_continue(&ctx_b); + EXPECT(rc == 0); + EXPECT(b.phase == 22); + + /* Resume A — should still work */ + rc = fiber_context_continue(&ctx_a); + EXPECT(rc == 0); + EXPECT(a.phase == 12); + + fiber_context_destroy(&ctx_a); + fiber_context_destroy(&ctx_b); +} + +/* ---------------------------------------------------------------- + Test 8: reuse context — spawn again after completion + ---------------------------------------------------------------- */ +TEST(reuse_after_completion) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + single_yield_data d1{&ctx, 0}; + int rc = fiber_context_spawn(&ctx, single_yield_func, &d1); + EXPECT(rc == 1 && d1.phase == 1); + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0 && d1.phase == 2); + + /* Spawn again on the same context */ + single_yield_data d2{&ctx, 0}; + rc = fiber_context_spawn(&ctx, single_yield_func, &d2); + EXPECT(rc == 1 && d2.phase == 1); + rc = fiber_context_continue(&ctx); + EXPECT(rc == 0 && d2.phase == 2); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Test 9: simulate DuckDB scan pattern — chunked row streaming + ---------------------------------------------------------------- */ +struct scan_sim_data { + struct fiber_context *ctx; + int total_rows; + int chunk_size; + int rows_produced; + int yields; +}; + +static void scan_sim_func(void *arg) +{ + auto *d = (scan_sim_data *)arg; + int count = 0; + for (int i = 0; i < d->total_rows; i++) + { + d->rows_produced++; + count++; + if (count >= d->chunk_size) + { + d->yields++; + fiber_context_yield(d->ctx); + count = 0; + } + } + /* Final partial chunk — no yield, just return */ +} + +TEST(scan_simulation) +{ + struct fiber_context ctx; + EXPECT(fiber_context_init(&ctx, FIBER_STACK_SIZE) == 0); + + scan_sim_data d{&ctx, 1000, 128, 0, 0}; + + int rc = fiber_context_spawn(&ctx, scan_sim_func, &d); + int continues = 0; + while (rc == 1) + { + continues++; + rc = fiber_context_continue(&ctx); + } + EXPECT(rc == 0); + EXPECT(d.rows_produced == 1000); + EXPECT(d.yields == 7); /* 1000/128 = 7 full chunks, remainder returns */ + EXPECT(continues == 7); + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + main + ---------------------------------------------------------------- */ +int main() +{ + printf("fiber_context unit tests\n"); + printf("========================\n"); + +#ifdef FIBER_CONTEXT_DISABLE + printf("SKIPPED: fiber context is disabled on this platform.\n"); + return 0; +#endif + + int failures = 0; + for (auto &t : tests) + { + int before = test_count; + int before_pass = pass_count; + printf(" %-30s ", t.name); + t.func(); + int ran = test_count - before; + int passed = pass_count - before_pass; + if (passed == ran) + printf("OK (%d checks)\n", ran); + else + { + printf("FAILED (%d/%d)\n", passed, ran); + failures++; + } + } + + printf("------------------------\n"); + printf("%d/%d tests passed, %d/%d checks passed\n", + (int)tests.size() - failures, (int)tests.size(), + pass_count, test_count); + + return failures ? 1 : 0; +} diff --git a/storage/duckdb/runtime/fiber_scan.cc b/storage/duckdb/runtime/fiber_scan.cc new file mode 100644 index 0000000000000..849845056699a --- /dev/null +++ b/storage/duckdb/runtime/fiber_scan.cc @@ -0,0 +1,378 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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-1335 USA +*/ + +#define MYSQL_SERVER 1 +#include +#include "sql_class.h" +#include "item.h" +#include "sql_parse.h" +#include "sql_lex.h" + +#undef UNKNOWN + +#include "fiber_scan.h" +#include "duckdb_log.h" + +MYSQL_THD create_background_thd(); +void destroy_background_thd(MYSQL_THD thd); + +extern "C" pthread_key(struct st_my_thread_var *, THR_KEY_mysys); + +namespace myduck +{ + +/* ---------------------------------------------------------------- + Item → duckdb::Value conversion + ---------------------------------------------------------------- */ + +duckdb::Value item_to_duckdb_value(Item *item, const duckdb::LogicalType &type) +{ + if (item->is_null()) + return duckdb::Value(); + + switch (type.id()) + { + case duckdb::LogicalTypeId::TINYINT: + return duckdb::Value::TINYINT(static_cast(item->val_int())); + case duckdb::LogicalTypeId::SMALLINT: + return duckdb::Value::SMALLINT(static_cast(item->val_int())); + case duckdb::LogicalTypeId::INTEGER: + return duckdb::Value::INTEGER(static_cast(item->val_int())); + case duckdb::LogicalTypeId::BIGINT: + return duckdb::Value::BIGINT(item->val_int()); + case duckdb::LogicalTypeId::UTINYINT: + return duckdb::Value::UTINYINT(static_cast(item->val_uint())); + case duckdb::LogicalTypeId::USMALLINT: + return duckdb::Value::USMALLINT(static_cast(item->val_uint())); + case duckdb::LogicalTypeId::UINTEGER: + return duckdb::Value::UINTEGER(static_cast(item->val_uint())); + case duckdb::LogicalTypeId::UBIGINT: + return duckdb::Value::UBIGINT(item->val_uint()); + case duckdb::LogicalTypeId::FLOAT: + return duckdb::Value::FLOAT(static_cast(item->val_real())); + case duckdb::LogicalTypeId::DOUBLE: + return duckdb::Value::DOUBLE(item->val_real()); + case duckdb::LogicalTypeId::BLOB: { + String buf; + String *s= item->val_str(&buf); + if (!s) + return duckdb::Value(); + return duckdb::Value::BLOB(std::string(s->ptr(), s->length())); + } + default: { + String buf; + String *s= item->val_str(&buf); + if (!s) + return duckdb::Value(); + return duckdb::Value(std::string(s->ptr(), s->length())); + } + } +} + +/* ---------------------------------------------------------------- + select_to_duckdb implementation + ---------------------------------------------------------------- */ + +select_to_duckdb::select_to_duckdb(THD *thd_arg, + struct fiber_context *ctx, + duckdb::DataChunk *buffer, + const duckdb::vector *types) + : select_result_interceptor(thd_arg), + ctx_(ctx), + buffer_(buffer), + types_(types), + row_count_(0), + finished_(false), + error_(false) +{} + +int select_to_duckdb::send_data(List &items) +{ + List_iterator_fast it(items); + Item *item; + duckdb::idx_t col= 0; + + while ((item= it++)) + { + if (col < types_->size()) + { + duckdb::Value val= item_to_duckdb_value(item, (*types_)[col]); + buffer_->data[col].SetValue(row_count_, val); + } + col++; + } + row_count_++; + + if (row_count_ >= STANDARD_VECTOR_SIZE) + { + buffer_->SetCardinality(row_count_); + row_count_= 0; + fiber_context_yield(ctx_); + } + + return thd->killed ? -1 : 0; +} + +bool select_to_duckdb::send_eof() +{ + if (row_count_ > 0) + { + buffer_->SetCardinality(row_count_); + row_count_= 0; + } + finished_= true; + return false; +} + +void select_to_duckdb::abort_result_set() +{ + error_= true; + finished_= true; +} + +/* ---------------------------------------------------------------- + FiberScanState + ---------------------------------------------------------------- */ + +int FiberScanState::init(TABLE *tbl, + const duckdb::vector &col_ids, + const duckdb::vector &col_types, + const std::string &where_sql) +{ + table= tbl; + column_ids= col_ids; + types= col_types; + where_clause= where_sql; + + if (tbl->s->db.str) + db_name.assign(tbl->s->db.str, tbl->s->db.length); + if (tbl->s->table_name.str) + table_name.assign(tbl->s->table_name.str, tbl->s->table_name.length); + + buffer.Initialize(duckdb::Allocator::DefaultAllocator(), types); + + if (fiber_context_init(&ctx, FIBER_STACK_SIZE)) + return 1; + + fiber_thd= create_background_thd(); + if (!fiber_thd) + { + fiber_context_destroy(&ctx); + return 1; + } + + /* Grant full privileges so the fiber can open any table */ + fiber_thd->security_ctx->master_access= ALL_KNOWN_ACL; + + /* Disable query cache — fiber queries are internal, not cacheable */ + fiber_thd->query_cache_is_applicable= 0; + + return 0; +} + +FiberScanState::~FiberScanState() +{ + if (fiber_started && !finished) + { + if (fiber_thd) + fiber_thd->set_killed_no_mutex(KILL_QUERY); + + /* + Swap TLS to fiber context before resuming so the fiber can + finish cleanly (same pattern as mdb_scan_function). + */ + THD *prev_thd= _current_thd(); + void *prev_mysys= pthread_getspecific(THR_KEY_mysys); + + set_current_thd(fiber_thd); + if (fiber_thd && fiber_thd->mysys_var) + pthread_setspecific(THR_KEY_mysys, fiber_thd->mysys_var); + + while (!finished) + { + buffer.Reset(); + int rc= fiber_context_continue(&ctx); + if (rc <= 0) /* 0 = fiber returned, -1 = error */ + break; + } + + set_current_thd(prev_thd); + pthread_setspecific(THR_KEY_mysys, prev_mysys); + } + + delete result; + result= nullptr; + + if (fiber_thd) + { + THD *saved= _current_thd(); + set_current_thd(nullptr); + destroy_background_thd(fiber_thd); + set_current_thd(saved); + fiber_thd= nullptr; + } + + fiber_context_destroy(&ctx); +} + +/* ---------------------------------------------------------------- + Build synthetic SELECT from column_ids + WHERE + ---------------------------------------------------------------- */ + +static std::string escape_backticks(const char *s) +{ + std::string out; + for (; *s; s++) + { + if (*s == '`') + out+= "``"; + else + out+= *s; + } + return out; +} + +static std::string build_synthetic_select(FiberScanState *state) +{ + std::string sql= "SELECT "; + + bool first= true; + uint nfields= state->table->s->fields; + for (auto col_idx : state->column_ids) + { + if (!first) + sql+= ", "; + first= false; + + if (col_idx >= nfields) + { + sql+= "NULL"; + continue; + } + Field *field= state->table->field[col_idx]; + sql+= '`'; + sql+= escape_backticks(field->field_name.str); + sql+= '`'; + } + + if (first) + sql+= "*"; + + sql+= " FROM `"; + sql+= escape_backticks(state->db_name.c_str()); + sql+= "`.`"; + sql+= escape_backticks(state->table_name.c_str()); + sql+= '`'; + + if (!state->where_clause.empty()) + { + sql+= " WHERE "; + sql+= state->where_clause; + } + + return sql; +} + +/* ---------------------------------------------------------------- + Fiber entry point + ---------------------------------------------------------------- */ + +void fiber_scan_func(void *arg) +{ + auto *state= static_cast(arg); + THD *thd= state->fiber_thd; + + /* + TLS (current_thd + THR_KEY_mysys) is already set by the caller + (mdb_scan_function) before fiber_context_spawn/continue. + Do NOT use thd_attach_thd/thd_detach_thd here — they assert + !current_thd which fails because fibers share the OS thread. + */ + + /* Set thread_stack for check_stack_overrun() — point to fiber's stack */ + char stack_top; + thd->thread_stack= &stack_top; + + state->result= new select_to_duckdb(thd, &state->ctx, + &state->buffer, + &state->types); + + std::string sql= build_synthetic_select(state); + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information("FIBER: synthetic SQL: %s", sql.c_str()); + + /* Allocate query buffer on THD mem_root */ + size_t len= sql.size(); + char *buf= static_cast(thd->alloc(len + 1)); + if (!buf) + { + state->error= true; + state->finished= true; + return; + } + memcpy(buf, sql.c_str(), len + 1); + thd->set_query_inner(buf, static_cast(len), + system_charset_info); + + /* Initialize parser */ + lex_start(thd); + thd->reset_for_next_command(); + + Parser_state parser_state; + if (parser_state.init(thd, buf, len) || + parse_sql(thd, &parser_state, NULL) || + thd->is_error()) + { + sql_print_error("FIBER: parse/init error for: %s", sql.c_str()); + if (thd->is_error()) + sql_print_error("FIBER: THD error: %s", thd->get_stmt_da()->message()); + state->error= true; + state->finished= true; + thd->end_statement(); + thd->cleanup_after_query(); + return; + } + + /* Install our result interceptor */ + thd->lex->result= state->result; + + /* Execute — send_data() will yield when DataChunk is full */ + mysql_execute_command(thd); + + /* Prevent end_statement() from deleting our result interceptor */ + thd->lex->result= NULL; + + if (thd->is_error()) + { + sql_print_error("FIBER: execution error: %s", + thd->get_stmt_da()->message()); + state->error= true; + } + + if (myduck::duckdb_log_options & LOG_DUCKDB_QUERY) + sql_print_information("FIBER: finished, buffer rows=%llu error=%d", + (ulonglong) state->buffer.size(), + (int) state->error); + + thd->end_statement(); + thd->cleanup_after_query(); + + state->finished= true; +} + +} /* namespace myduck */ diff --git a/storage/duckdb/runtime/fiber_scan.h b/storage/duckdb/runtime/fiber_scan.h new file mode 100644 index 0000000000000..63d717f05e278 --- /dev/null +++ b/storage/duckdb/runtime/fiber_scan.h @@ -0,0 +1,133 @@ +/* + Copyright (c) 2026, MariaDB Foundation. + + 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-1335 USA +*/ + +#pragma once + +#include "fiber_context.h" + +#include "duckdb/common/types/data_chunk.hpp" +#include "duckdb/common/types/value.hpp" + +#include + +class THD; +struct TABLE; +class Item; +template class List; + +namespace myduck +{ + +/** + Custom select_result_interceptor that writes result rows into + a DuckDB DataChunk buffer and yields to the caller fiber when + the chunk is full. + + Lifecycle: + 1. Created by the fiber function before mysql_execute_command(). + 2. MariaDB executor calls send_data() once per row. + 3. When the DataChunk is full (STANDARD_VECTOR_SIZE rows), + send_data() yields back to the DuckDB scan function. + 4. When the query finishes, executor calls send_eof(); + the finished flag is set. + 5. On error, abort_result_set() sets the error flag. +*/ +class select_to_duckdb : public select_result_interceptor +{ +public: + select_to_duckdb(THD *thd_arg, + struct fiber_context *ctx, + duckdb::DataChunk *buffer, + const duckdb::vector *types); + + int send_data(List &items) override; + bool send_eof() override; + void abort_result_set() override; + + bool is_finished() const { return finished_; } + bool has_error() const { return error_; } + duckdb::idx_t row_count() const { return row_count_; } + +private: + struct fiber_context *ctx_; + duckdb::DataChunk *buffer_; + const duckdb::vector *types_; + duckdb::idx_t row_count_; + bool finished_; + bool error_; +}; + +duckdb::Value item_to_duckdb_value(Item *item, + const duckdb::LogicalType &type); + +/** + Default fiber stack size (512 KB). + The fiber runs mysql_execute_command() which may use significant stack + for parsing, optimization and handler calls. 512 KB ensures that + check_stack_overrun() (which uses my_thread_stack_size, ~292 KB by + default) will fire before we exhaust the actual fiber stack. +*/ +static constexpr size_t FIBER_STACK_SIZE= 512 * 1024; + +/** + Holds all state needed to run a MariaDB SELECT inside a fiber + and stream result rows into a DuckDB DataChunk buffer. + + Owned by MdbScanGlobalState. Created lazily on the first call + to mdb_scan_function when a WHERE clause is available. + + Lifecycle: + 1. Created & initialized in mdb_scan_function (first call). + 2. fiber_context_spawn() starts fiber_scan_func(). + 3. Each fiber_context_continue() produces one DataChunk. + 4. Destructor performs graceful teardown (KILL_QUERY + resume). +*/ +struct FiberScanState +{ + struct fiber_context ctx; + THD *fiber_thd= nullptr; + select_to_duckdb *result= nullptr; + + duckdb::DataChunk buffer; + duckdb::vector types; + duckdb::vector column_ids; + + TABLE *table= nullptr; + std::string db_name; + std::string table_name; + std::string where_clause; + + bool fiber_started= false; + bool finished= false; + bool error= false; + + int init(TABLE *tbl, + const duckdb::vector &col_ids, + const duckdb::vector &col_types, + const std::string &where_sql); + + ~FiberScanState(); +}; + +/** + Fiber entry point. Receives FiberScanState* as argument. + Builds a synthetic SELECT, executes it via mysql_execute_command(), + rows are streamed through select_to_duckdb → DataChunk → yield. +*/ +void fiber_scan_func(void *arg); + +} /* namespace myduck */ diff --git a/storage/duckdb/third_parties/duckdb b/storage/duckdb/third_parties/duckdb new file mode 160000 index 0000000000000..8a5851971fae8 --- /dev/null +++ b/storage/duckdb/third_parties/duckdb @@ -0,0 +1 @@ +Subproject commit 8a5851971fae891f292c2714d86046ee018e9737 diff --git a/storage/duckdb/utils.sh b/storage/duckdb/utils.sh new file mode 100644 index 0000000000000..ed7cb74109189 --- /dev/null +++ b/storage/duckdb/utils.sh @@ -0,0 +1,237 @@ +#!/bin/bash + +# ─── Color setup ──────────────────────────────────────────────────────────────── + +if [[ -n "$TERM" && "$TERM" != "dumb" ]] && command -v tput &>/dev/null; then + _CLR_RESET=$(tput sgr0) + _CLR_BOLD=$(tput bold) + _CLR_RED="${_CLR_BOLD}$(tput setaf 1)" + _CLR_GREEN="${_CLR_BOLD}$(tput setaf 2)" + _CLR_YELLOW="${_CLR_BOLD}$(tput setaf 3)" + _CLR_BLUE="${_CLR_BOLD}$(tput setaf 4)" + _CLR_CYAN="${_CLR_BOLD}$(tput setaf 6)" + _CLR_GRAY=$(tput setaf 7) + _CLR_DARKGRAY="${_CLR_BOLD}$(tput setaf 0)" + + if [[ $(tput colors) -ge 256 ]]; then + _CLR_RED=$(tput setaf 196) + _CLR_GREEN=$(tput setaf 156) + _CLR_YELLOW=$(tput setaf 228) + _CLR_CYAN=$(tput setaf 87) + _CLR_DARKGRAY=$(tput setaf 59) + fi +else + _CLR_RESET="\e[0m" + _CLR_BOLD="\e[1m" + _CLR_RED="\e[1;31m" + _CLR_GREEN="\e[1;32m" + _CLR_YELLOW="\e[1;33m" + _CLR_BLUE="\e[1;34m" + _CLR_CYAN="\e[1;36m" + _CLR_GRAY="\e[37m" + _CLR_DARKGRAY="\e[1;30m" +fi + +# ─── Logging functions ────────────────────────────────────────────────────────── + +info() { + echo -e "${_CLR_CYAN} ● $*${_CLR_RESET}" +} + +warn() { + echo -e "${_CLR_YELLOW} ⚠ $*${_CLR_RESET}" +} + +error() { + echo -e "${_CLR_RED} ✖ $*${_CLR_RESET}" +} + +success() { + echo -e "${_CLR_GREEN} ✔ $*${_CLR_RESET}" +} + +fail() { + echo -e "${_CLR_RED} ✖ $*${_CLR_RESET}" + exit 1 +} + +separator() { + echo -e "${_CLR_DARKGRAY} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${_CLR_RESET}" +} + +header() { + separator + echo -e "${_CLR_GREEN} $*${_CLR_RESET}" + separator +} + +# ─── One-liner pipe ───────────────────────────────────────────────────────────── +# Usage: cmake --build . | one_liner +# Pipes stdout through a single updating line. Stderr goes straight to terminal. + +one_liner() { + local cols + cols=$(tput cols 2>/dev/null || echo 120) + while IFS= read -r line; do + printf "\r\e[K %s" "${line:0:$((cols - 4))}" + done + printf "\r\e[K" +} + +# ─── Arrow-key menu ───────────────────────────────────────────────────────────── +# Usage: +# options=("Debug" "RelWithDebInfo" "Release") +# menu_choice "Select build type:" options +# echo "Selected: $MENU_RESULT" +# +# Sets global MENU_RESULT to the chosen item string. + +menu_choice() { + local prompt="$1" + local -n _opts=$2 + local total=${#_opts[@]} + local selected=0 + local filter="" + local prev_lines=0 + + if [[ $total -eq 0 ]]; then + error "menu_choice: no options provided" + return 1 + fi + + # Build filtered list (indices into _opts that match the filter) + local -a _filtered=() + _menu_filter() { + _filtered=() + local lc_filter="${filter,,}" + for ((i = 0; i < total; i++)); do + if [[ -z "$filter" || "${_opts[$i],,}" == *"$lc_filter"* ]]; then + _filtered+=("$i") + fi + done + } + _menu_filter + + # Max visible rows (cap long lists) + local max_visible=20 + + # Hide cursor, restore on exit/ctrl-c + printf "\e[?25l" + trap 'printf "\e[?25h"' RETURN + trap 'printf "\e[?25h"; exit 130' INT + + _menu_render() { + local fcount=${#_filtered[@]} + local visible=$fcount + (( visible > max_visible )) && visible=$max_visible + local lines=$((visible + 2)) # prompt + filter line + items + + # Move up to overwrite previous render (skip on first draw) + if [[ $prev_lines -gt 0 ]]; then + printf "\e[%dA" "$prev_lines" + fi + # Clear old lines + for ((i = 0; i < prev_lines; i++)); do + printf "\e[K\n" + done + if [[ $prev_lines -gt 0 ]]; then + printf "\e[%dA" "$prev_lines" + fi + + echo -e " ${_CLR_CYAN}${prompt}${_CLR_RESET}" + if [[ -n "$filter" ]]; then + echo -e " ${_CLR_YELLOW}🔍 ${filter}${_CLR_DARKGRAY} (${fcount}/${total})${_CLR_RESET}" + else + echo -e " ${_CLR_DARKGRAY}type to filter... (${fcount}/${total})${_CLR_RESET}" + fi + + if [[ $fcount -eq 0 ]]; then + echo -e " ${_CLR_RED}no matches${_CLR_RESET}" + lines=3 + else + # Scroll window: keep selected in view + local scroll_start=0 + if (( selected >= scroll_start + visible )); then + scroll_start=$(( selected - visible + 1 )) + fi + if (( scroll_start > fcount - visible )); then + scroll_start=$(( fcount - visible )) + fi + (( scroll_start < 0 )) && scroll_start=0 + + for ((j = scroll_start; j < scroll_start + visible; j++)); do + local idx=${_filtered[$j]} + if [[ $j -eq $selected ]]; then + echo -e " ${_CLR_GREEN}▸ ${_opts[$idx]}${_CLR_RESET}" + else + echo -e " ${_CLR_GRAY} ${_opts[$idx]}${_CLR_RESET}" + fi + done + if (( fcount > visible )); then + echo -e " ${_CLR_DARKGRAY}… $((fcount - visible)) more${_CLR_RESET}" + lines=$((lines + 1)) + fi + fi + prev_lines=$lines + } + + _menu_render + + while true; do + read -rsn1 key || true + case "$key" in + $'\x1b') + read -rsn2 rest || true + case "$rest" in + '[A') # Up + if [[ ${#_filtered[@]} -gt 0 ]]; then + selected=$(( (selected - 1 + ${#_filtered[@]}) % ${#_filtered[@]} )) + _menu_render + fi + ;; + '[B') # Down + if [[ ${#_filtered[@]} -gt 0 ]]; then + selected=$(( (selected + 1) % ${#_filtered[@]} )) + _menu_render + fi + ;; + esac + ;; + '') # Enter + if [[ ${#_filtered[@]} -gt 0 ]]; then + break + fi + ;; + $'\x7f'|$'\b') # Backspace + if [[ -n "$filter" ]]; then + filter="${filter%?}" + _menu_filter + selected=0 + _menu_render + fi + ;; + [[:print:]]) # Printable character — add to filter + filter+="$key" + _menu_filter + selected=0 + _menu_render + ;; + esac + done + + local result_idx=${_filtered[$selected]} + + # Clear the menu from terminal + if [[ $prev_lines -gt 0 ]]; then + printf "\e[%dA" "$prev_lines" + for ((i = 0; i < prev_lines; i++)); do + printf "\e[K\n" + done + printf "\e[%dA" "$prev_lines" + fi + + printf "\e[?25h" + echo -e " ${_CLR_CYAN}${prompt}${_CLR_RESET} ${_CLR_GREEN}${_opts[$result_idx]}${_CLR_RESET}" + + MENU_RESULT="${_opts[$result_idx]}" +}