<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Databend</title>
    <description>The latest articles on DEV Community by Databend (@databend).</description>
    <link>https://dev.to/databend</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F868100%2F0d46af0d-ac0a-4dbe-a38c-30bbdb28acb4.png</url>
      <title>DEV Community: Databend</title>
      <link>https://dev.to/databend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/databend"/>
    <language>en</language>
    <item>
      <title>Feature Preview: Iceberg Integration with Databend</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 04 Aug 2023 00:43:16 +0000</pubDate>
      <link>https://dev.to/databend/feature-preview-iceberg-integration-with-databend-184c</link>
      <guid>https://dev.to/databend/feature-preview-iceberg-integration-with-databend-184c</guid>
      <description>&lt;p&gt;A few weeks ago, during the yearly conferences of Databricks and Snowflake, AI was getting a lot of attention, but the progress in data lakes and data warehouses was also significant because data is fundamental. Apache Iceberg emerged as a prominent solution for data lakes, and Databricks unveiled UniForm to better handle Apache Iceberg and Hudi table formats from Delta data. Meanwhile, Snowflake made timely updates to Iceberg Tables, aiming to eliminate data silos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One of the significant new features that &lt;a href="https://github.com/datafuselabs/databend"&gt;Databend&lt;/a&gt; has been working on in recent months is supporting the reading of data in Apache Iceberg table format.&lt;/strong&gt; Though it's still a work in progress, they have made good progress so far.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This article is intended to give you a preview of this new capability by demonstrating how to use Databend to mount and query an Iceberg Catalog.&lt;/strong&gt; We will cover the core concepts of Iceberg and table formats while also introducing Databend's solutions, including its ability to handle multiple data catalogs and the implementation of IceLake in Rust from scratch. As part of the demonstration, a comprehensive workshop will be provided, so you can try it out yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Apache Iceberg?
&lt;/h2&gt;

&lt;p&gt;An increasing amount of data is now moving to the cloud and being stored in object storage. However, this setup may not fully meet the demands of modern analytics. There are two key issues to address: First, how to organize data in a more structured manner, achieving a more organized data storage approach. Second, how to provide users with broader consistency guarantees, necessary schema information, and advanced features that cater to the requirements of modern analytics workloads.&lt;/p&gt;

&lt;p&gt;Data lakes often focus on addressing and resolving the first issue, while table formats are dedicated to providing solutions for the second one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://iceberg.apache.org/"&gt;Apache Iceberg&lt;/a&gt; is a high-performance open table format designed for large-scale analytics workloads. It is known for its simplicity and reliability. It supports various query engines like Spark, Trino, Flink, Presto, Hive, and Impala. One of its killer features includes full schema evolution, time travel, and rollback capabilities. Additionally, Apache Iceberg's data partitioning and well-defined data structures make concurrent access to data sources more secure, reliable, and convenient.&lt;/p&gt;

&lt;p&gt;If you're interested in Iceberg we recommend reading &lt;a href="https://tabular.io/blog/docker-spark-and-iceberg/"&gt;&lt;em&gt;Docker Spark And Iceberg: The Fastest Way To Try Iceberg!&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Format
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Table Format&lt;/strong&gt; is a specification for storing data using a collection of files. It consists of definitions for the following three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to store data in files&lt;/li&gt;
&lt;li&gt;How to store metadata for related files&lt;/li&gt;
&lt;li&gt;How to store metadata for the table itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Table format files are usually stored in underlying storage services such as HDFS, S3 or GCS, and are accessed by upper-level data warehouses such as Databend and Snowflake. Compared to CSV or Parquet, table format offers a standardized and structured data definition in tabular form, enabling its usage without the need to load it into a data warehouse.&lt;/p&gt;

&lt;p&gt;Although there are strong competitors like Delta Lake and Apache Hudi in the field of table formats, this article focuses on Apache Iceberg. Let's take a look at its underlying file organization structure together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zn5f01Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://iceberg.apache.org/img/iceberg-metadata.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zn5f01Tk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://iceberg.apache.org/img/iceberg-metadata.png" alt="stack of Apache Iceberg" width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The figures above illustrate "s0" and "s1," representing snapshots of the table. A snapshot captures the table's state at specific points in time. Each commit results in a snapshot, and each snapshot is associated with an inventory list (manifest list). The inventory list can hold multiple addresses of manifest files, along with statistical information, including path and partition range. The manifest file serves to record the addresses and statistical details of data files generated from current operations, such as maximum/minimum values and number of rows per column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Catalog
&lt;/h2&gt;

&lt;p&gt;To integrate Databend with Iceberg, the first step is to add the Multiple Catalog capability to Databend. The Multiple Catalog enables the data that was previously managed by other data analysis systems to be mounted onto Databend.&lt;/p&gt;

&lt;p&gt;From the very beginning, Databend's objective has been to function as a cloud-native OLAP data warehouse, with a focus on addressing the complexities of handling multiple data sources. In Databend, data is structured into three layers: catalog -&amp;gt; database -&amp;gt; table. The catalog represents the highest level and encompasses all databases and tables.&lt;/p&gt;

&lt;p&gt;Based on this foundation, our team designed and implemented support for Hive and Iceberg data catalogs, providing various mounting forms such as configuration files and &lt;code&gt;CREATE CATALOG&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;To mount Iceberg Catalog located in S3, simply execute the following SQL statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;CATALOG&lt;/span&gt; &lt;span class="n"&gt;iceberg_ctl&lt;/span&gt;
&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ICEBERG&lt;/span&gt;
&lt;span class="k"&gt;CONNECTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'s3://warehouse/path/to/db'&lt;/span&gt;
    &lt;span class="n"&gt;AWS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;
    &lt;span class="n"&gt;AWS_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;
    &lt;span class="n"&gt;ENDPOINT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your-endpoint-url'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IceLake - A Pure Rust Implementation of Apache Iceberg
&lt;/h2&gt;

&lt;p&gt;Although the Rust ecosystem has seen the rise of many new projects related to databases and big data analysis in recent years, there is still a notable absence of mature Apache Iceberg bindings in Rust. This has presented significant challenges for Databend when it comes to integrating with Iceberg.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/icelake-io/icelake"&gt;IceLake&lt;/a&gt;, supported and initiated by Databend Labs, aims to overcome the challenges  and establish an open ecosystem where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users can read/write iceberg tables from &lt;strong&gt;ANY&lt;/strong&gt; storage services like s3, gcs, azblob, hdfs and so on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ANY&lt;/strong&gt; databases can integrate with &lt;code&gt;icelake&lt;/code&gt; to facilitate reading and writing of iceberg tables.&lt;/li&gt;
&lt;li&gt;Provides &lt;strong&gt;NATIVE&lt;/strong&gt; support transmute between &lt;code&gt;arrow&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;Provides bindings so that other languages can work with iceberg tables powered by Rust core.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, IceLake only supports reading data (in Parquet format) from Apache Iceberg storage services. The design and implementation of Databend's Iceberg catalog is supported by Icelake and has been validated through integration with Databend.&lt;/p&gt;

&lt;p&gt;In addition, we have also collaborated with the Iceberg community to initiate and participate in the &lt;a href="https://github.com/apache/iceberg-rust"&gt;iceberg-rust&lt;/a&gt; project. The project aims to contribute Iceberg-related implementations from IceLake upstream, and the first version is currently under intense development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workshop: Experience Iceberg with Databend
&lt;/h2&gt;

&lt;p&gt;In this workshop, we will demonstrate how to prepare data in Iceberg table format and mount it onto Databend as a Catalog, and perform some basic queries. Relevant files and configurations can be found at &lt;a href="https://github.com/psiace/databend-workshop"&gt;PsiACE/databend-workshop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you already have data that conforms to the Iceberg table format stored in a storage service supported by OpenDAL, we recommend using Databend Cloud so that you can skip the tedious process of service deployment and data preparation, and easily get started with the Iceberg Catalog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Services
&lt;/h2&gt;

&lt;p&gt;To simplify the service deployment and data preparation issues of Iceberg, we will be using Docker and Docker Compose. You need to install these components first, and then write the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;spark-iceberg&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tabulario/spark-iceberg&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spark-iceberg&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spark/&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;iceberg_net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rest&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;minio&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./warehouse:/home/iceberg/warehouse&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./notebooks:/home/iceberg/notebooks/notebooks&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY=password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION=us-east-1&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8888:8888&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:8080&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;10000:10000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;10001:10001&lt;/span&gt;
  &lt;span class="na"&gt;rest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tabulario/iceberg-rest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;iceberg-rest&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;iceberg_net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8181:8181&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY=password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION=us-east-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CATALOG_WAREHOUSE=s3://warehouse/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CATALOG_IO__IMPL=org.apache.iceberg.aws.s3.S3FileIO&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CATALOG_S3_ENDPOINT=http://minio:9000&lt;/span&gt;
  &lt;span class="na"&gt;minio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio/minio&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MINIO_ROOT_USER=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MINIO_ROOT_PASSWORD=password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MINIO_DOMAIN=minio&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;iceberg_net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;aliases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;warehouse.minio&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9001:9001&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9000:9000&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/data"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--console-address"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:9001"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;mc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;minio&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio/mc&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mc&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;iceberg_net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID=admin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY=password&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION=us-east-1&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;/bin/sh -c "&lt;/span&gt;
      &lt;span class="s"&gt;until (/usr/bin/mc config host add minio http://minio:9000 admin password) do echo '...waiting...' &amp;amp;&amp;amp; sleep 1; done;&lt;/span&gt;
      &lt;span class="s"&gt;/usr/bin/mc rm -r --force minio/warehouse;&lt;/span&gt;
      &lt;span class="s"&gt;/usr/bin/mc mb minio/warehouse;&lt;/span&gt;
      &lt;span class="s"&gt;/usr/bin/mc policy set public minio/warehouse;&lt;/span&gt;
      &lt;span class="s"&gt;tail -f /dev/null&lt;/span&gt;
      &lt;span class="s"&gt;"      &lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;iceberg_net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above configuration file, we use MinIO as the underlying storage, Iceberg provides table formatting capabilities, and &lt;code&gt;spark-iceberg&lt;/code&gt; can help us prepare some pre-set data and perform conversion operations.&lt;/p&gt;

&lt;p&gt;Next, we start all services in the directory corresponding to the &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Preparation
&lt;/h2&gt;

&lt;p&gt;In this workshop, we plan to use the NYC Taxis dataset (data on taxi rides in New York City), which is already built into &lt;code&gt;spark-iceberg&lt;/code&gt; in Parquet format. We just need to convert it to Iceberg format.&lt;/p&gt;

&lt;p&gt;First, enable &lt;code&gt;pyspark-notebook&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; spark-iceberg pyspark-notebook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can start Jupyter Notebook at &lt;a href="http://localhost:8888:"&gt;http://localhost:8888:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eMnrFG3s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rsv6kz7od79nugsqgwas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eMnrFG3s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rsv6kz7od79nugsqgwas.png" alt="jupyter notebook with spark-iceberg" width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we need to run the following code to implement the data conversion operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parquet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/home/iceberg/data/yellow_tripdata_2021-04.parquet"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;saveAsTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nyc.taxis"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"iceberg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line will read the Parquet data and the second line will convert it into Iceberg format.&lt;/p&gt;

&lt;p&gt;To verify that the data has been successfully converted, we can access the MinIO instance located at &lt;a href="http://localhost:9001"&gt;http://localhost:9001&lt;/a&gt; and notice that the data is managed according to the Iceberg underlying file organization described earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LoFkLU-8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvk0nqv9vvtlqu5suazi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LoFkLU-8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvk0nqv9vvtlqu5suazi.png" alt="minio dashboard" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying Databend
&lt;/h2&gt;

&lt;p&gt;Here we will manually deploy a single-node Databend service. The overall deployment process can refer to &lt;a href="https://databend.rs/doc/deploy/deploying-databend"&gt;Docs | Deploying a Standalone Databend&lt;/a&gt;, and some details that need attention are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;First, prepare the relevant directories for logs and meta data.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/log/databend
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /var/lib/databend
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt; /var/log/databend
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt; /var/lib/databend
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Secondly, because the default &lt;code&gt;admin_api_address&lt;/code&gt; has been occupied by &lt;code&gt;spark-iceberg&lt;/code&gt;, it is necessary to edit &lt;code&gt;databend-query.toml&lt;/code&gt; to make some modifications to avoid conflicts:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;admin_api_address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0.0:8088"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In addition, according to &lt;a href="https://databend.rs/doc/sql-clients/admin-users"&gt;Docs | Configuring Admin Users&lt;/a&gt;, we also need to configure administrator users. Since this is just a workshop, we choose the simplest way by simply uncommenting &lt;code&gt;[[query.users]]&lt;/code&gt; field and root user:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[query.users]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"root"&lt;/span&gt;
&lt;span class="py"&gt;auth_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"no_password"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because we deployed MinIO locally without setting certificate encryption, we need to use insecure HTTP protocol to load data. Therefore, it is necessary to change the configuration file of &lt;code&gt;databend-query.toml&lt;/code&gt; in order to allow this behavior. Please try your best not enable it in production services:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;

&lt;span class="nn"&gt;[storage]&lt;/span&gt;

&lt;span class="err"&gt;...&lt;/span&gt;

&lt;span class="py"&gt;allow_insecure&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is to start up Databend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
./scripts/start.sh
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6zq0Ezwe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6e1rscdb5wu6ny0ubebp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6zq0Ezwe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6e1rscdb5wu6ny0ubebp.png" alt="start databend in terminal" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We strongly recommend using BendSQL as a client tool for accessing Databand. Of course, we also support various access methods such as MySQL Client and HTTP API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mounting Iceberg Catalog
&lt;/h2&gt;

&lt;p&gt;According to the previous configuration file, you only need to execute the following SQL statement to mount the Iceberg Catalog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;CATALOG&lt;/span&gt; &lt;span class="n"&gt;iceberg_ctl&lt;/span&gt;
&lt;span class="k"&gt;TYPE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ICEBERG&lt;/span&gt;
&lt;span class="k"&gt;CONNECTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'s3://warehouse/'&lt;/span&gt;
    &lt;span class="n"&gt;AWS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;
    &lt;span class="n"&gt;AWS_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;
    &lt;span class="n"&gt;ENDPOINT_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:9000'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To verify the mounting, we can execute &lt;code&gt;SHOW CATALOGS&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Ozo6HKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8d0i7lwnloe7jinymse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Ozo6HKB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8d0i7lwnloe7jinymse.png" alt="result of show catalogs" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, Databend also supports &lt;code&gt;SHOW DATABASES&lt;/code&gt; and &lt;code&gt;SHOW TABLES&lt;/code&gt; statements. The &lt;code&gt;nyc.taxis&lt;/code&gt; in the data conversion corresponds to a second-level directory in MinIO and will be mapped to databases and tables in Databend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DspQnzoS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqinf1ohoqjyhjb4a4fi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DspQnzoS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqinf1ohoqjyhjb4a4fi.png" alt="result of show databases" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xPSBPtYo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e5ajxev741tz2xq4dowk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xPSBPtYo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e5ajxev741tz2xq4dowk.png" alt="result of show tables" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Queries
&lt;/h2&gt;

&lt;p&gt;Now that the data has been mounted, let's try some simple queries:&lt;/p&gt;

&lt;p&gt;Firstly, let's count the number of rows in the data. We can see that a total of 2 million rows have been mounted to Databend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;iceberg_ctl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;taxis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aNxTZqh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tdq4yerz807eemk6hp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aNxTZqh5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tdq4yerz807eemk6hp4.png" alt="count data size" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's try to retrieve some data from a few columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tpep_pickup_datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tpep_dropoff_datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passenger_count&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;iceberg_ctl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;taxis&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6-PyhbFR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flh7m63wugzjlyf4azpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-PyhbFR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flh7m63wugzjlyf4azpz.png" alt="get some data from table" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following query can help us explore the correlation between passenger count and travel distance. Here we only take 10 results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;passenger_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;to_year&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tpep_pickup_datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trip_distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;iceberg_ctl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nyc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;taxis&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="n"&gt;passenger_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
  &lt;span class="nb"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt;
  &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rafqcPVO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q02psuf5ezajbd65b00n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rafqcPVO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q02psuf5ezajbd65b00n.png" alt="explore the correlation" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this article, we introduced Apache Iceberg table format and Databend solution for it, and provided a workshop for everyone to gain some hands-on experience.&lt;/p&gt;

&lt;p&gt;Currently, Databend only provide catalog mounting capability for Iceberg Integration, but it can handle some basic query processing tasks. We also welcome everyone to try it out on their own interested data and provide us with feedback.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>bigdata</category>
    </item>
    <item>
      <title>Designing and Querying JSON in Databend</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Thu, 29 Sep 2022 07:37:20 +0000</pubDate>
      <link>https://dev.to/databend/designing-and-querying-json-in-databend-1j07</link>
      <guid>https://dev.to/databend/designing-and-querying-json-in-databend-1j07</guid>
      <description>&lt;p&gt;JSON (JavaScript Object Notation) is a commonly used semi-structured data type. With the self-describing schema structure, JSON can hold all data types, including multi-level nested data types, such as Array, Object, etc. JSON takes advantage of high flexibility and easy dynamic expansion compared with the structured data types that must strictly follow the fields in a tabular data structure.   &lt;/p&gt;

&lt;p&gt;As data volume increases rapidly in recent years, many platforms have started to use and get the most out of semi-structured data types (such as JSON). For example, the JSON data shared by various platforms through open interfaces, and the public datasets and application logs stored in JSON format.   &lt;/p&gt;

&lt;p&gt;Databend supports structured data types, as well as JSON. This post dives deeply into the JSON data type in Databend.   &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G9PjKsWz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/882z405kvnph8d5ty325.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G9PjKsWz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/882z405kvnph8d5ty325.png" alt="Image description" width="880" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Working with JSON in Databend​
&lt;/h1&gt;

&lt;p&gt;Databend stores semi-structured data as the VARIANT (also called JSON) data type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE test 
  ( 
     id INT32, 
     v1 VARIANT, 
     v2 JSON 
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON data needs to be generated by calling the” parse_json”or “try_parse_json”function. The input string must be in the standard JSON format, including Null, Boolean, Number, String, Array, and Object. In case of parsing failure due to invalid string, the “parse_json”function will return an error while the “try_parse_json”function will return a NULL value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO test VALUES
  (1, parse_json('{"a":{"b":1,"c":[1,2]}}'), parse_json('[["a","b"],{"k":"a"}]')),
  (2, parse_json('{"a":{"b":2,"c":[3,4]}}'), parse_json('[["c","d"],{"k":"b"}]'));
SELECT * FROM test;
+----+-------------------------+-----------------------+
| id | v1                      | v2                    |
+----+-------------------------+-----------------------+
| 1  | {"a":{"b":1,"c":[1,2]}} | [["a","b"],{"k":"a"}] |
| 2  | {"a":{"b":2,"c":[3,4]}} | [["c","d"],{"k":"b"}] |
+----+-------------------------+-----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JSON usually holds data of Array or Object type. Due to the nested hierarchical structure, the internal elements can be accessed through JSON PATH. The syntax supports the following delimiters:&lt;/p&gt;

&lt;p&gt;“:”: Colon can be used to obtain the elements in an object by the key.&lt;br&gt;
“.”: Dot can be used to obtain the elements in an object by the key. Do NOT use a dot as the first delimiter in a statement, or Databend would consider the dot as the delimiter to separate the table name from the column name.&lt;br&gt;
“[]”: Brackets can be used to obtain the elements in an object by the key or the elements in an array by the index.&lt;br&gt;
You can mix the three types of delimiters above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT v1:a.c, v1:a['b'], v1['a']:c, v2[0][1], v2[1].k FROM test;
+--------+-----------+-----------+----------+---------+
| v1:a.c | v1:a['b'] | v1['a']:c | v2[0][1] | v2[1].k |
+--------+-----------+-----------+----------+---------+
| [1,2]  | 1         | [1,2]     | "b"      | "a"     |
| [3,4]  | 2         | [3,4]     | "d"      | "b"     |
+--------+-----------+-----------+----------+---------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The internal elements extracted through JSON PATH are also of JSON type, and they can be converted to basic types through the cast function or using the conversion operator “::”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT cast(v1:a.c[0], int64), v1:a.b::int32, v2[0][1]::string FROM test;
+--------------------------+---------------+------------------+
| cast(v1:a.c[0] as int64) | v1:a.b::int32 | v2[0][1]::string |
+--------------------------+---------------+------------------+
| 1                        | 1             | b                |
| 3                        | 2             | d                |
+--------------------------+---------------+------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Parsing JSON from GitHub​
&lt;/h1&gt;

&lt;p&gt;Many public datasets are stored in JSON format. We can import these data into Databend for parsing. The following introduction uses the GitHub events dataset as an example.&lt;/p&gt;

&lt;p&gt;The GitHub events dataset (downloaded from GH Archive) uses the following JSON format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "id":"23929425917",
  "type":"PushEvent",
  "actor":{
    "id":109853386,
    "login":"teeckyar-bot",
    "display_login":"teeckyar-bot",
    "gravatar_id":"",
    "url":"https://api.github.com/users/teeckyar-bot",
    "avatar_url":"https://avatars.githubusercontent.com/u/109853386?"
  },
  "repo":{
    "id":531248561,
    "name":"teeckyar/Times",
    "url":"https://api.github.com/repos/teeckyar/Times"
  },
  "payload":{
    "push_id":10982315959,
    "size":1,
    "distinct_size":1,
    "ref":"refs/heads/main",
    "head":"670e7ca4085e5faa75c8856ece0f362e56f55f09",
    "before":"0a2871cb7e61ce47a6790adaf09facb6e1ef56ba",
    "commits":[
      {
        "sha":"670e7ca4085e5faa75c8856ece0f362e56f55f09",
        "author":{
          "email":"support@teeckyar.ir",
          "name":"teeckyar-bot"
        },
        "message":"1662804002 Timehash!",
        "distinct":true,
        "url":"https://api.github.com/repos/teeckyar/Times/commits/670e7ca4085e5faa75c8856ece0f362e56f55f09"
      }
    ]
  },
  "public":true,
  "created_at":"2022-09-10T10:00:00Z",
  "org":{
    "id":106163581,
    "login":"teeckyar",
    "gravatar_id":"",
    "url":"https://api.github.com/orgs/teeckyar",
    "avatar_url":"https://avatars.githubusercontent.com/u/106163581?"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the data above, we can see that the “actor”, “repo”, “payload”, and “org”fields have a nested structure and can be stored as JSON. Others can be stored as basic data types. So we can create a table like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE `github_data` 
             ( 
                          `id`   VARCHAR, 
                          `type` VARCHAR, 
                          `actor` JSON, 
                          `repo` JSON, 
                          `payload` JSON, 
                          `public` BOOLEAN, 
                          `created_at` timestamp(0), 
                          `org` json 
             );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the COPY INTO command to load the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY INTO github_data
FROM 'https://data.gharchive.org/2022-09-10-10.json.gz'
FILE_FORMAT = (
  compression = auto
  type = NDJSON
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following code returns the top 10 projects with the most commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT   repo:name, 
         count(id) 
FROM     github_data 
WHERE    type = 'PushEvent' 
GROUP BY repo:name 
ORDER BY count(id) DESC 
LIMIT    10;
+----------------------------------------------------------+-----------+
| repo:name                                                | count(id) |
+----------------------------------------------------------+-----------+
| "Lombiq/Orchard"                                         | 1384      |
| "maique/microdotblog"                                    | 970       |
| "Vladikasik/statistic"                                   | 738       |
| "brokjad/got_config"                                     | 592       |
| "yanonono/booth-update"                                  | 537       |
| "networkoperator/demo-cluster-manifests"                 | 433       |
| "kn469/web-clipper-bed"                                  | 312       |
| "ufapg/jojo"                                             | 306       |
| "bj5nj7oh/bj5nj7oh"                                      | 291       |
| "appseed-projects2/500f32d3-8019-43ee-8f2a-a273163233fb" | 247       |
+----------------------------------------------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following code returns the top 10 users with the most forks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT   actor:login, 
         count(id) 
FROM     github_data 
WHERE    type='ForkEvent' 
GROUP BY actor:login 
ORDER BY count(id) DESC 
LIMIT    10;
+-----------------------------------+-----------+
| actor:login                       | count(id) |
+-----------------------------------+-----------+
| "actions-marketplace-validations" | 191       |
| "alveraboquet"                    | 59        |
| "ajunlonglive"                    | 50        |
| "Shutch420"                       | 13        |
| "JusticeNX"                       | 13        |
| "RyK-eR"                          | 12        |
| "DroneMad"                        | 10        |
| "UnqulifiedEngineer"              | 9         |
| "PeterZs"                         | 8         |
| "lgq2015"                         | 8         |
+-----------------------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Performance Optimization​
&lt;/h1&gt;

&lt;p&gt;The JSON data generally is saved in plaintext format and needs to be parsed to generate the enumeration value of serde_json::Value every time the data is read. Compared to other basic data types, handling JSON data takes more parsing time and needs more memory space.   &lt;/p&gt;

&lt;p&gt;Databend has improved the read performance of JSON data using the following methods:   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To speed up the parsing and reduce memory usage, Databend stores the JSON data as JSONB in binary format and uses the built-in j_entry structure to hold data type and offset position of each element.&lt;/li&gt;
&lt;li&gt;Adding virtual columns to speed up the queries. Databend extracts the frequently queried fields and the fields of the same data type and stores them as separate virtual columns. Data will be directly read from the virtual columns when querying, which makes Databend achieve the same performance as querying other basic data types.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Learn Databend's New SQL Type System in Five Minutes</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 23 Sep 2022 08:27:48 +0000</pubDate>
      <link>https://dev.to/databend/learn-databends-new-sql-type-system-in-five-minutes-194m</link>
      <guid>https://dev.to/databend/learn-databends-new-sql-type-system-in-five-minutes-194m</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Type system is an important part of database, which provides a consistent way to determine the data type in SQL. The design of the type system greatly affects the usability and robustness of the database, as a well-designed and consistent type system makes it easy for users to check SQL behaviors. A poorly designed type system, on the contrary, may raise various errors and inconsistent behaviors, bringing a series of potential inconvenience to users. Let's take programming languages as an example, the type system of JavaScript has always been criticized.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9phlxESG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t06hydmgpvnayl985s6j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9phlxESG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t06hydmgpvnayl985s6j.png" alt="Image description" width="602" height="915"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, we hope to implement a type inference system that is powerful yet easy to understand in Databend. To achieve this goal, we learned from the compiler designs of many excellent programming languages, and selected a subset that is suitable for SQL. The design principles of this system is described below.&lt;/p&gt;

&lt;h1&gt;
  
  
  Interface Design
&lt;/h1&gt;

&lt;p&gt;The "low coupling and high cohesion" principle, which we often refer to, is to combine the code that does the same thing, and then define a simple interface for external use. Since the type inference system has relatively complex functions, its interfaces need to be defined from the very beginning, that is, what functions can be provided and how to call them.&lt;/p&gt;

&lt;p&gt;In short, there are three functions in the type inference system we designed:&lt;/p&gt;

&lt;p&gt;1.Check whether the input SQL statements (RawExpr) conform to the type rules and select appropriate overloads for the functions called, and return executable expressions (Expr)&lt;/p&gt;

&lt;p&gt;2.Return the expression value corresponding to the input data&lt;/p&gt;

&lt;p&gt;3.Return the expression value range corresponding to the input data range (stored in meta data)&lt;/p&gt;

&lt;p&gt;Callers only need to finish these operations:&lt;/p&gt;

&lt;p&gt;4.Define type signatures, mappings of definition domains to function domains, and the bodies of all available functions&lt;/p&gt;

&lt;p&gt;5.Call the executor when executing SQL or constant folding&lt;/p&gt;

&lt;p&gt;Use the “and”function as an example. The function definition is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;registry.register_2_arg::&amp;lt;BooleanType, BooleanType, BooleanType, _, _&amp;gt;(
    "and",
    FunctionProperty::default(),
    |lhs, rhs| {
        Some(BooleanDomain {
            has_false: lhs.has_false || rhs.has_false,
            has_true: lhs.has_true &amp;amp;&amp;amp; rhs.has_true,
        })
    },
    |lhs, rhs| lhs &amp;amp;&amp;amp; rhs,
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A complete example of execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Parse the SQL expressions into structured AST
let raw_expr = parse_raw_expr("and(true, false)");

// Get built-in functions, like the 'and' function mentioned before
let fn_registry = builtin_functions();

// Check type validity
let expr = type_check::check(&amp;amp;raw_expr, &amp;amp;fn_registry).unwrap();

// Execute
let evaluator = Evaluator {
    input_columns: Chunk::new(vec![]),
    context: FunctionContext::default(),
};
let result: Value&amp;lt;AnyType&amp;gt; = evaluator.run(&amp;amp;raw_expr).unwrap();

assert_eq!(result, Value::Scalar(Scalar::Boolean(false)));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Principles of Type inference
&lt;/h1&gt;

&lt;p&gt;The new type system supports the following data types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Null&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Boolean&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;String&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UInt8&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UInt16&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UInt32&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UInt64&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Int8&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Int16&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Int32&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Int64&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Float32&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Float64&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Date&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Interval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Timestamp&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Array&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nullalbe&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Variant&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's us learn about how the type inference system works through an example. Suppose this expression is input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 + 'foo'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type inferencer first converts the expression input to a function call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plus(1, 'foo')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the type checker can simply infer the type of the constant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 :: Int8
'foo' :: String
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type checker knows that there are six overloads for the function plus after querying FunctionRegistry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plus(Null, Null) :: Null
plus(Int8, Int8) :: Int8
plus(Int16, Int16) :: Int16
plus(Int32, Int32) :: Int32
plus(Float32, Float32) :: Float32
plus(Timestamp, Timestamp) :: Timestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the argument types Int8 and String don't match any of the overloads, an error is raised by the type checker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 + 'foo'
  ^ function `plus` has no overload for parameters `(Int8, String)`

  available overloads:
    plus(Int8, Int8) :: Int8
    plus(Int16, Int16) :: Int16
    plus(Int32, Int32) :: Int32
    plus(Float32, Float32) :: Float32
    plus(Timestamp, Timestamp) :: Timestamp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One exception in type checking is that a subtype can be converted to its super type (CAST), so that functions can takes parameters of the subtypes. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plus(1, 2.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type inferencer infers the constants' types according to rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 1 :: Int8
 2.0 :: Float32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By querying FunctionRegistry, we can find that there are two overloads for function plus that seems to fit but neither one matches perfectly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(Int8, Int8) :: Int8
plus(Float32, Float32) :: Float32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type checker will try to select an overload according to the CAST rule. Since values of type Int8 can be lossless converted to type Float 32, the type checker then overwrites the expression and recheck the types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plus(CAST(1 AS Float32), 2.0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The type check is passed in this way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Genericity
&lt;/h1&gt;

&lt;p&gt;The new type checker supports the use of genericity in function signature definitions to reduce the workload of manually defining overloaded functions. For example, we can define a function by stating “array_ Get(Array, UInt64):: T0”, which accepts an array and a subscript, and returns the element corresponding to the subscript in the array.&lt;/p&gt;

&lt;p&gt;Compared with the type check process mentioned in the previous section, checking functions with generic signatures requires one additional step: select an appropriate specific type to replace the generic type. The replaced types should conform to the type check rules, and explanations (such as conflicting constraints) are required when there is no available type. This step is generally called Unification, and we also have an example to illustrate it:&lt;br&gt;
Suppose there are two expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ty1 :: (X, Boolean) 
ty2 :: (Int32, Y)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want “ty1”and“ty2” to have the same type (for example, when “ty1”is used as the input parameter type and “ty2”is used as the input parameter signature), “unify”will try to replace “X”and “Y”with specific types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let subst: Subsitution = unify(ty1, ty2).unwrap();

assert_eq!(subst['X'], DataType::Int32]);
assert_eq!(subst['Y'], DataType::Boolean]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For readers interested in “unify”, the source code of “type_check.rs”is highly recommended. We also recommend a book: Types and Programming Languages, which introduces the development of type inferences for programming languages and discusses in details the principles and trade-offs of various inference theories. A supporting toy implementation is provided for every major concept. You'll obtain great pleasure from this book especially on sleepless nights.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article, we introduce the design background and operation principles of our new type system, and explain how to use the executor. We look forward to introduce you the detailed methods of defining SQL functions and some related Rust tricks in another article, for this topic is as exciting as type inference.&lt;/p&gt;

&lt;h1&gt;
  
  
  About Databend
&lt;/h1&gt;

&lt;p&gt;Databend is an open source modern data warehouse with elasticity and low cost. It can do real-time data analysis on object-based storage.We look forward to your attention and hope to explore the cloud native data warehouse solution, and create a new generation of open source data cloud together.&lt;/p&gt;

&lt;p&gt;Databend documentation：&lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twitter：&lt;a href="https://twitter.com/Datafuse_Labs"&gt;https://twitter.com/Datafuse_Labs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slack：&lt;a href="https://datafusecloud.slack.com/"&gt;https://datafusecloud.slack.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wechat：Databend&lt;/p&gt;

&lt;p&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sqllogictest Illustrated</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 16 Sep 2022 09:13:01 +0000</pubDate>
      <link>https://dev.to/databend/sqllogictest-illustrated-4c4g</link>
      <guid>https://dev.to/databend/sqllogictest-illustrated-4c4g</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;You might want to know that the Databend team is rolling out a sqllogictest to replace the stateless tests. In this post, we share our experience of designing, developing, and using the sqllogictest.&lt;/p&gt;

&lt;h2&gt;
  
  
  About sqllogictest
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Database quality assurance
&lt;/h3&gt;

&lt;p&gt;Test dimension and test coverage are the keys to ensure database quality. Test dimension includes unit test, fuzzy test, functional test (sqllogitest belongs to this category), end-to-end (e2e) test, performance test, etc.&lt;br&gt;&lt;br&gt;
The basic idea of functional tests for database is to compare the execution returns with expectations. Generally, the following issues need to be considered in advance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; How to prescribe the test-script format?&lt;/li&gt;
&lt;li&gt; How to compare the results? In most solutions, the returns are saved as files, making detailed execution results invisible . We had to add additional output between cases to determine approximately where the cases were making errors.&lt;/li&gt;
&lt;li&gt; How to bridge the differences between clients and databases? In most cases, different clients have different return formats, similarly different databases have different outputs for certain types.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  What is sqllogictest?
&lt;/h4&gt;

&lt;p&gt;Developed by SQLite's author D. Richard Hipp, sqllogictest was originally designed to test SQLite. See more about its design concepts in &lt;a href="https://www.sqlite.org/sqllogictest/doc/trunk/about.wiki"&gt;https://www.sqlite.org/sqllogictest/doc/trunk/about.wiki&lt;/a&gt;.The sqllogictest program seeks to answer just one question: Does the database engine compute the correct answer? No attention is paid to performance, optimal use of index, disk and memory, transactional behavior, or concurrency and locking issues.&lt;br&gt;&lt;br&gt;
At present, mainstream databases have all implemented their own sqllogictest test tools and test cases, yet the syntaxes of test cases are slightly different and not compatible with each other. The implementation methods of test tools are also different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  [&lt;a href="https://github.com/ydb-platform/ydb/tree/main/ydb/tests/functional/suite_tests"&gt;YDB&lt;/a&gt;] implemented by Python&lt;/li&gt;
&lt;li&gt;  [&lt;a href="https://github.com/cockroachdb/cockroach/tree/master/pkg/sql/logictest"&gt;CockroachDB&lt;/a&gt;] implemented by Go&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Why Databend needs sqllogictest?
&lt;/h3&gt;

&lt;p&gt;There is already a set of functional test tools implemented in Databend, which divides the functional test cases into stateless tests and stateful tests, using Clickhouse for reference. The use cases are written as scripts (or SQL files), and the expected results are saved as files with the same name but different suffixes. We can run Databend-test(written in Python) to execute tests, and then use diff to compare the results.&lt;br&gt;&lt;br&gt;
This method, however, is not friendly to the compilation and modification of error cases. Moreover, Databend supports multiple sets of handlers (such as MySQL, HTTP, and Clickhouse), yet this method cannot conduct tests for every handler (kind of like testing different databases). Therefore, we looked for methods and tools that can solve these problems.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to implement sqllogictest in Databend?
&lt;/h3&gt;

&lt;p&gt;The implementation versions of sqllogictest differ greatly, which is not only reflected in the supported use case syntaxes, but also in the technical stacks used and the functions implemented. As a result, it is difficult to use the existing test cases or the tools directly.&lt;br&gt;&lt;br&gt;
After analyzing and comparing different implementation schemes, we found that the core function requirements of sqllogictest are in fact not too much. The existing versions in the community differed greatly from each other and can't be used directly. Moreover, with the advancement of testing practice, many new demands would be proposed, therefore the workload of customized development would certainly be quite arduous. Considering all these, we chose to start from scratch with Python.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://preview.redd.it/q1wgrwz296o91.png?width=596&amp;amp;format=png&amp;amp;auto=webp&amp;amp;s=8702811907715221d61891bb0a875f6bbb622ff5"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fs9-sCsT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c9de619ceff94522b6853ad3cf89e282%257Etplv-k3u1fbpfcp-zoom-1.image" alt="r/DatafuseLabs - Sqllogictest Illustrated" width="596" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In sqllogictest, there are multiple Runners interacting with different databases or handlers. Each runner should implement all the methods in the base class SuiteRunner, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  execute_ok&lt;/li&gt;
&lt;li&gt;  execute_error&lt;/li&gt;
&lt;li&gt;  execute_query&lt;/li&gt;
&lt;li&gt;  batch_execute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods are the core of sqllogictest execution. Besides, the SuiteRunner class also stores some of the state and control variables during execution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://preview.redd.it/ule03kw496o91.png?width=408&amp;amp;format=png&amp;amp;auto=webp&amp;amp;s=6031bfb4c1fb51231ed30a08c515434f8834a77c"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zZRD_eO2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6eaca01a349d4c6f813ddc1e19b8d6c0%257Etplv-k3u1fbpfcp-zoom-1.image" alt="r/DatafuseLabs - Sqllogictest Illustrated" width="408" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take the typo of Http Runner as an example . In addition to the necessary interfaces like“execute_ok”，“execute_error”，“execute_query”，“batch_execute”, two functions are also realized for resetting connections and sessions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://preview.redd.it/sgcsjtgf96o91.png?width=440&amp;amp;format=png&amp;amp;auto=webp&amp;amp;s=68184b03a108dc8f50e93b87e079e8fa79e08a54"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bs5KYNfY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4845ba71487f4cf0ac4e5da288e4afe0%257Etplv-k3u1fbpfcp-zoom-1.image" alt="r/DatafuseLabs - Sqllogictest Illustrated" width="440" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The use case file is parsed by the Statement class. At present, a simple method is used for syntax parsing, that is, reading the file line by line and using regular expressions for matching. Compared with implementing an interpreter, the advantage of this approach is that it can be implemented quickly, yet the disadvantage is that it would be cumbersome to extend the syntax support.&lt;/p&gt;

&lt;p&gt;Error information is output through logicError, including the name of the runner where the error occurred, the error message (including the details of the error statement) and the error type.&lt;/p&gt;

&lt;p&gt;A LogicTestStatistics class is also implemented to record the time cost of each SQL execution. The final statistical output is relatively simple now but can be supplemented in the future.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to write test scripts for sqllogictest ?
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Basic functions
&lt;/h4&gt;

&lt;p&gt;Here is a quick guide&lt;a href="https://github.com/datafuselabs/databend/blob/main/tests/logictest/suites/select_0"&gt;https://github.com/datafuselabs/databend/blob/main/tests/logictest/suites/select_0&lt;/a&gt;.The supported handlers: MySQL handler, HTTP handler, Clickhouse handlerAnnotation: The use of '--' to annotate specific lines is supported.Statement types：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ok&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The statement is executed correctly and no error is returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;error &lt;code&gt;&amp;lt;error regex&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  An error occurs when the statement is executed, and the returned error info contains expected content. Usually return codes are used, text messages are also feasible (but not intuitive).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;query &lt;code&gt;&amp;lt;options&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;labels&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  The execution return includes a result set, and the comparison method of the result sets be specified by options and labels parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;options: Composed of characters, and each character represents a column in the result set.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The supported characters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;B&lt;/code&gt; Boolean&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;T&lt;/code&gt; text&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;F&lt;/code&gt; floating point&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;I&lt;/code&gt; integer&lt;/li&gt;
&lt;li&gt;  labels: The differences in the processing of results by different databases (handlers) are distinguished by labels. Commas are used when there are multiple differences.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared to ok and error statements, query statements are more complicated. Here is a use case of query type (for reference only and may be inconsistent with the actual results):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;statement query III label(mysql) 
select number, number + 1, number + 999 from numbers(10); 
----0     1   9991     2  10002     3  10013     4  10024     5  10035     6  10046     7  10057     8  10068     9  10079    10  1008.0 
----  mysql0     1   9991     2  10002     3  10013     4  10024     5  10035     6  10046     7  10057     8  10068     9  10079    10  1008
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Control syntax in the testing process:&lt;/p&gt;

&lt;p&gt;1.Use“ skipif” to skip specified runner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skipif clickhouse 
statement query I 
select 1; 
---- 
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Use “onlyif” to execute the specified runner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;onlyif mysql 
statement query I 
select 1; 
---- 
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.When encountering occasional test failures that cannot be solved easily, we can use skipped to skip the use case, or annotate it for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;statement query skipped I 
select 1; 
---- 
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Execution output
&lt;/h4&gt;

&lt;p&gt;A &lt;strong&gt;SUCCESS&lt;/strong&gt; example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://preview.redd.it/8w1ibxa4a6o91.png?width=1280&amp;amp;format=png&amp;amp;auto=webp&amp;amp;s=eedca9c0673501a8a41cc04cdae098df73b8c0e6"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6lgbz2Jr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4b1d6d2605a442318acc1763c55c51f8%257Etplv-k3u1fbpfcp-zoom-1.image" alt="r/DatafuseLabs - Sqllogictest Illustrated" width="880" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current summary contains simple statistics on the test execution process, including the number of case files executed, the number of statements contained in each case file, the average time of each statement execution and the average time of case execution.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;FAILURE&lt;/strong&gt; example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CCPFlRex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/08fcbe6cf154417f8c56042d757d9f26%257Etplv-k3u1fbpfcp-zoom-1.image" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CCPFlRex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/08fcbe6cf154417f8c56042d757d9f26%257Etplv-k3u1fbpfcp-zoom-1.image" alt="" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;he failed case is in the 4th line of “base/15_query/alias/having_with_alias.test”. The expected return is 1 but get null instead.&lt;/p&gt;

&lt;p&gt;Another &lt;strong&gt;FAILURE&lt;/strong&gt; example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x0nsrWnH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4e678c9bb83a45c48e1601affd17ee8b%257Etplv-k3u1fbpfcp-zoom-1.image" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x0nsrWnH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4e678c9bb83a45c48e1601affd17ee8b%257Etplv-k3u1fbpfcp-zoom-1.image" alt="" width="880" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The failed case is in the 1st line of “base/02_function/02_0017_function_strings_oct”. The reported error is table already exists.   &lt;/p&gt;

&lt;p&gt;As can be seen from the above examples, it is easy to locate the specific use case file or even the index of line or SQL with the returned output. For those that need to compare the results, the expected and actual return values will also be printed out so users can easily find out the error. This greatly improves the use experience of developers and improves the efficiency of debugging.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using sqllogictest in a pipeline
&lt;/h4&gt;

&lt;p&gt;When a PR(Pull Request) is proposed to the Databend repository, a pipeline will be constructed. Then enter the testing process, the constructed product will be executed in a new environment, and various tests will be conducted at the same time. Sqllogictest is an important part of the process.&lt;br&gt;&lt;br&gt;
As shown in the figure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--urH_pNEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/162df5f41bd44e78b60d1f2db86ec7a2%257Etplv-k3u1fbpfcp-zoom-1.image" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--urH_pNEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/162df5f41bd44e78b60d1f2db86ec7a2%257Etplv-k3u1fbpfcp-zoom-1.image" alt="" width="710" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only after all the tests pass successfully can the submission be merged into the branch, ensuring that none revision will interfere with the expected functions. All we need to do is to extend the use cases and prompt the test coverage.&lt;/p&gt;
&lt;h4&gt;
  
  
  Running sqllogictest
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;For contributors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Directly execute “make sqllogic-test”in the Databend directory after cloning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.Deploy and run Databend, refer to &lt;a href="https://databend.rs/doc/deploy/deploying-databend"&gt;https://databend.rs/doc/deploy/deploying-databend&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;2.Copy Databend code of corresponding version, then change the directory to tests/logictest.&lt;/p&gt;

&lt;p&gt;3.Install python3(&amp;gt;=3.8).&lt;/p&gt;

&lt;p&gt;4.Install dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.Run “python3” &lt;a href="http://main.py/"&gt;main.py&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run parameters
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Command-line parameters&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; “--suites other_dir” will run all the case files in ."/other_dir"&lt;/li&gt;
&lt;li&gt; “--run-dir ydb” will run all the case files from directories in."/suites/"with "ydb" contained in their names&lt;/li&gt;
&lt;li&gt; "--skip-dir ydb" will skip all the case files from directories in."/suites/"with "ydb" contained in their names&lt;/li&gt;
&lt;li&gt; "python &lt;a href="http://main.py/"&gt;main.py&lt;/a&gt; "03_0001""will run the specific case file with "03_0001" contained in its name&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Environment variables parameters&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;B&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SKIP_TEST_FILES&lt;/td&gt;
&lt;td&gt;Cases containing the specified filename are skipped, separated by commas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISABLE_MYSQL_LOGIC_TEST&lt;/td&gt;
&lt;td&gt;Close the test of mysql handler, any value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISABLE_HTTP_LOGIC_TEST&lt;/td&gt;
&lt;td&gt;Close the test of http handler, any value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DISABLE_CLICKHOUSE_LOGIC_TEST&lt;/td&gt;
&lt;td&gt;Close the test of clickhouse handler, any value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_MYSQL_HANDLER_HOST&lt;/td&gt;
&lt;td&gt;mysql handler address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_MYSQL_HANDLER_PORT&lt;/td&gt;
&lt;td&gt;mysql handler port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_HTTP_HANDLER_HOST&lt;/td&gt;
&lt;td&gt;http handler address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_HTTP_HANDLER_PORT&lt;/td&gt;
&lt;td&gt;http handler port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_CLICKHOUSE_HANDLER_HOST&lt;/td&gt;
&lt;td&gt;clickhouse handler address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUERY_CLICKHOUSE_HANDLER_PORT&lt;/td&gt;
&lt;td&gt;clickhouse handler port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MYSQL_DATABASE&lt;/td&gt;
&lt;td&gt;Default database,usually default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MYSQL_USER&lt;/td&gt;
&lt;td&gt;Default user, usually root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ADDITIONAL_HEADERS&lt;/td&gt;
&lt;td&gt;Usually used for the extension requirements of HTTP protocol, such as identity authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These parameters can meet personalized running conditions, such as Databend not deployed locally or testing MySQL and Clickhouse (when only HTTP is supported, and Clickhouse native protocol is not supported)Note: due to the SQL dialect problem, our use cases may have statements that are not supported by other databases, and the use cases of other databases may also have similar situations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tips
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Use ok statements for use cases where the results are less important.&lt;/li&gt;
&lt;li&gt;  Prefer to use error codes in error statements, as messages are unstable.&lt;/li&gt;
&lt;li&gt;  The spaces in the result sets of query statements are only used to distinguish different columns. Additional spaces will not affect the test results.&lt;/li&gt;
&lt;li&gt;  When using query statements, a /t tab key is needed if there is a blank line in the returned result.&lt;/li&gt;
&lt;li&gt;  Since we dropped the support for sorting and retry syntaxes in use cases (now implemented in the test tool), it is necessary to add an order by statement to ensure that the result order is always consistent&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to organize test cases?
&lt;/h3&gt;

&lt;p&gt;Test modules are under the first level directory. For example, we currently have two modules: &lt;strong&gt;base&lt;/strong&gt; and &lt;strong&gt;ydb&lt;/strong&gt;, where &lt;strong&gt;base&lt;/strong&gt; stores our own use cases, and &lt;strong&gt;ydb&lt;/strong&gt; represents cases imported from YDB.As for organizations in the respective modules, there is no clear specification yet. These methods are generally followed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Organize cases by statements like cockroachdb&lt;/li&gt;
&lt;li&gt;  Organize cases by statement types or related modules, such as DML, DDL and planner_v2, according to function development process&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Extension
&lt;/h3&gt;

&lt;p&gt;Regular matching of returned columns is required, for currently only precise matching is supported in query statements, and this can't meet the needs of some fuzzy matching.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Time formats matching, thus use cases without a fixed time in its return would be supported.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future plans
&lt;/h2&gt;

&lt;p&gt;Improve the use experience and toolchain of sqllogictest&lt;/p&gt;

&lt;p&gt;The use experience of sqllogictest includes the improvement of functional requirements, more friendly log output method, use case migration tools (from SQL files or third-party sqllogictest use case files), etc.&lt;/p&gt;

&lt;p&gt;Extend test cases and raise test coverage&lt;/p&gt;

&lt;p&gt;The test case sets are valuable assets, which often take a lot of time to design and perfect. It is of great significance to improve the test coverage by using migration cases. We also need to improve our own test scenarios and functional test coverage at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open-SQL-Logictest?
&lt;/h2&gt;

&lt;p&gt;Many database projects have included a sqllogictest. A sqllogictest is usually implemented based on a specified project and cannot work in another project. We need a set of standardized practical methods for the sqllogictest implementation that includes all the considerations of the requirements from sqllogictest.&lt;br&gt;&lt;br&gt;
If one day we can sort out all the requirements of sqllogictest and define certain standards, this can then be an option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reference&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sqlite.org/sqllogictest/doc/trunk/about.wiki"&gt;https://www.sqlite.org/sqllogictest/doc/trunk/about.wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/datafuselabs/databend/tree/main/tests/logictest"&gt;https://github.com/datafuselabs/databend/tree/main/tests/logictest&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Reading Source Code of Databend (2) ：Query Server Startup, Session Management &amp; Request Processing</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 09 Sep 2022 06:54:21 +0000</pubDate>
      <link>https://dev.to/databend/reading-source-code-of-databend-2-query-server-startup-session-management-request-processing-4ki</link>
      <guid>https://dev.to/databend/reading-source-code-of-databend-2-query-server-startup-session-management-request-processing-4ki</guid>
      <description>&lt;h1&gt;
  
  
  Entrypoint of query server
&lt;/h1&gt;

&lt;p&gt;The entrypoint of query server is in the directory of databend/src/binaries/query/main.rs. After initial configurations are completed, a “ GlobalServices” and a “shutdown_handle” are created. The latter handles shutdown logic when the server is closed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GlobalServices::init(conf.clone()).await?;
let mut shutdown_handle = ShutdownHandle::create()?;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GlobalServices
&lt;/h2&gt;

&lt;p&gt;“GlobalServices” are responsible for starting all the global services for databend-query, and all of them follow the single responsibility principle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct GlobalServices {
    global_runtime: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;Runtime&amp;gt;&amp;gt;&amp;gt;,
    // Process query logs
    query_logger: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;QueryLogger&amp;gt;&amp;gt;&amp;gt;,
    // Implement cluster discovery mechanism for databend-query
    cluster_discovery: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;ClusterDiscovery&amp;gt;&amp;gt;&amp;gt;,
    // Interact with the storage layer to read/write data
    storage_operator: UnsafeCell&amp;lt;Option&amp;lt;Operator&amp;gt;&amp;gt;,
    async_insert_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;AsyncInsertManager&amp;gt;&amp;gt;&amp;gt;,
    cache_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;CacheManager&amp;gt;&amp;gt;&amp;gt;,
    catalog_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;CatalogManager&amp;gt;&amp;gt;&amp;gt;,
    http_query_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;HttpQueryManager&amp;gt;&amp;gt;&amp;gt;,
    data_exchange_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;DataExchangeManager&amp;gt;&amp;gt;&amp;gt;,
    session_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;SessionManager&amp;gt;&amp;gt;&amp;gt;,
    users_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;UserApiProvider&amp;gt;&amp;gt;&amp;gt;,
    users_role_manager: UnsafeCell&amp;lt;Option&amp;lt;Arc&amp;lt;RoleCacheManager&amp;gt;&amp;gt;&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the global services in “GlobalServices” implement a trait singleton. This article focuses on the logics of session processing, and the global managers will be introduced in subsequent articles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub trait SingletonImpl&amp;lt;T&amp;gt;: Send + Sync {
    fn get(&amp;amp;self) -&amp;gt; T;

    fn init(&amp;amp;self, value: T) -&amp;gt; Result&amp;lt;()&amp;gt;;
}

pub type Singleton&amp;lt;T&amp;gt; = Arc&amp;lt;dyn SingletonImpl&amp;lt;T&amp;gt;&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ShutdownHandler
&lt;/h2&gt;

&lt;p&gt;Next, the handlers are initialized according to the network protocol and registered in “shutdown_handler” service, types that implements server trait can all be added to the services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H3Orqd-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ig4xxb1fnhmtanlvs0cr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H3Orqd-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ig4xxb1fnhmtanlvs0cr.png" alt="Image description" width="880" height="564"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[async_trait::async_trait]
pub trait Server: Send {
    async fn shutdown(&amp;amp;mut self, graceful: bool);
    async fn start(&amp;amp;mut self, listening: SocketAddr) -&amp;gt; Result&amp;lt;SocketAddr&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, Databend supports three request protocols (MySQL, Clickhouse HTTP, and raw HTTP).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// MySQL handler.
{
    let hostname = conf.query.mysql_handler_host.clone();
    let listening = format!("{}:{}", hostname, conf.query.mysql_handler_port);
    let mut handler = MySQLHandler::create(session_manager.clone());
    let listening = handler.start(listening.parse()?).await?;
    // register the service in shutdown_handle to process server shutdown，same as below
    shutdown_handle.add_service(handler);
}

// ClickHouse HTTP handler.
{
    let hostname = conf.query.clickhouse_http_handler_host.clone();
    let listening = format!("{}:{}", hostname, conf.query.clickhouse_http_handler_port);

    let mut srv = HttpHandler::create(session_manager.clone(), HttpHandlerKind::Clickhouse);
    let listening = srv.start(listening.parse()?).await?;
    shutdown_handle.add_service(srv);
}

// Databend HTTP handler.
{
    let hostname = conf.query.http_handler_host.clone();
    let listening = format!("{}:{}", hostname, conf.query.http_handler_port);

    let mut srv = HttpHandler::create(session_manager.clone(), HttpHandlerKind::Query);
    let listening = srv.start(listening.parse()?).await?;
    shutdown_handle.add_service(srv);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then some other services are created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Metric service: Metrics related services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Admin service: Administration related services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RPC service: RPC service for query nodes, which handles the communications between query nodes using arrow flight protocol&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Metric API service.
{
    let address = conf.query.metric_api_address.clone();
    let mut srv = MetricService::create(session_manager.clone());
    let listening = srv.start(address.parse()?).await?;
    shutdown_handle.add_service(srv);
    info!("Listening for Metric API: {}/metrics", listening);
}

// Admin HTTP API service.
{
    let address = conf.query.admin_api_address.clone();
    let mut srv = HttpService::create(session_manager.clone());
    let listening = srv.start(address.parse()?).await?;
    shutdown_handle.add_service(srv);
    info!("Listening for Admin HTTP API: {}", listening);
}

// RPC API service.
{
    let address = conf.query.flight_api_address.clone();
    let mut srv = RpcService::create(session_manager.clone());
    let listening = srv.start(address.parse()?).await?;
    shutdown_handle.add_service(srv);
    info!("Listening for RPC API (interserver): {}", listening);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this query node is registered in meta server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Cluster register.
{
    let cluster_discovery = session_manager.get_cluster_discovery();
    let register_to_metastore = cluster_discovery.register_to_metastore(&amp;amp;conf);
    register_to_metastore.await?;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  About sessions
&lt;/h1&gt;

&lt;p&gt;There are four parts in session management：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;session_manager: Globally unique, manages client sessions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;session: Every time a new client connects to the server, a session is created and registered to the session_ manager&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;query_ctx: Each query creates a query_ctx, which is used to store the context information&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;query_ctx_shared: Context information shared by subqueries&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5cjfWk8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhl8extssh0z1gu98p84.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5cjfWk8j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhl8extssh0z1gu98p84.png" alt="Image description" width="880" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at them one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  SessionManager (query/src/sessions/session_mgr.rs)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct SessionManager {
    pub(in crate::sessions) conf: Config,
    pub(in crate::sessions) max_sessions: usize,
    pub(in crate::sessions) active_sessions: Arc&amp;lt;RwLock&amp;lt;HashMap&amp;lt;String, Arc&amp;lt;Session&amp;gt;&amp;gt;&amp;gt;&amp;gt;,
    pub status: Arc&amp;lt;RwLock&amp;lt;SessionManagerStatus&amp;gt;&amp;gt;,

    // When session type is MySQL, insert into this map, key is id, val is MySQL connection id.
    pub(crate) mysql_conn_map: Arc&amp;lt;RwLock&amp;lt;HashMap&amp;lt;Option&amp;lt;u32&amp;gt;, String&amp;gt;&amp;gt;&amp;gt;,
    pub(in crate::sessions) mysql_basic_conn_id: AtomicU32,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“SessionManager” is mainly used to create and destroy sessions, the specific methods are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create a session according to the client protocol
pub async fn create_session(self: &amp;amp;Arc&amp;lt;Self&amp;gt;, typ: SessionType) -&amp;gt; Result&amp;lt;SessionRef&amp;gt;

// Destroy a session by session_ids
pub fn destroy_session(self: &amp;amp;Arc&amp;lt;Self&amp;gt;, session_id: &amp;amp;String)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Session (query/src/sessions/session.rs)
&lt;/h2&gt;

&lt;p&gt;Context information of the client server stored in the session. No more detail will be described as the code logic is already clear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct Session {
    pub(in crate::sessions) id: String,
    pub(in crate::sessions) typ: RwLock&amp;lt;SessionType&amp;gt;,
    pub(in crate::sessions) session_ctx: Arc&amp;lt;SessionContext&amp;gt;,
    status: Arc&amp;lt;RwLock&amp;lt;SessionStatus&amp;gt;&amp;gt;,
    pub(in crate::sessions) mysql_connection_id: Option&amp;lt;u32&amp;gt;,
}
pub struct SessionContext {
    conf: Config,
    abort: AtomicBool,
    current_catalog: RwLock&amp;lt;String&amp;gt;,
    current_database: RwLock&amp;lt;String&amp;gt;,
    current_tenant: RwLock&amp;lt;String&amp;gt;,
    current_user: RwLock&amp;lt;Option&amp;lt;UserInfo&amp;gt;&amp;gt;,
    auth_role: RwLock&amp;lt;Option&amp;lt;String&amp;gt;&amp;gt;,
    client_host: RwLock&amp;lt;Option&amp;lt;SocketAddr&amp;gt;&amp;gt;,
    io_shutdown_tx: RwLock&amp;lt;Option&amp;lt;Sender&amp;lt;Sender&amp;lt;()&amp;gt;&amp;gt;&amp;gt;&amp;gt;,
    query_context_shared: RwLock&amp;lt;Option&amp;lt;Arc&amp;lt;QueryContextShared&amp;gt;&amp;gt;&amp;gt;,
}

pub struct SessionStatus {
    pub session_started_at: Instant,
    pub last_query_finished_at: Option&amp;lt;Instant&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another major function of “Session” is to create and obtain “QueryContext” s. Each time a query request is received, a “QueryContext” is created and bound to the corresponding query statement.&lt;/p&gt;

&lt;h2&gt;
  
  
  QueryContext (query/src/sessions/query_ctx.rs)
&lt;/h2&gt;

&lt;p&gt;QueryContexts are used to maintain context information of certain queries . They're created by “QueryContext::create_from_shared(query_ctx_shared)”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[derive(Clone)]
pub struct QueryContext {
    version: String,
    statistics: Arc&amp;lt;RwLock&amp;lt;Statistics&amp;gt;&amp;gt;,
    partition_queue: Arc&amp;lt;RwLock&amp;lt;VecDeque&amp;lt;PartInfoPtr&amp;gt;&amp;gt;&amp;gt;,
    shared: Arc&amp;lt;QueryContextShared&amp;gt;,
    precommit_blocks: Arc&amp;lt;RwLock&amp;lt;Vec&amp;lt;DataBlock&amp;gt;&amp;gt;&amp;gt;,
    fragment_id: Arc&amp;lt;AtomicUsize&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Among the members, “partition_queue” stores the corresponding PartInfo, including the address and version of the part, rows of data involved, the compression algorithm used, and the meta information related to column. Partition is set when building the pipeline. There will be subsequent articles on pipeline. Metadata that has been written to the storage by the temporary insert operation but has not been submitted is stored in “precommit_blocks” . “DataBlock” contains the meta information reference of column and information of arrow schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  QueryContextShared (query/src/sessions/query_ctx_shared.rs)
&lt;/h2&gt;

&lt;p&gt;For queries containing subqueries, much context information needs to be shared. This is why we need “QueryContextShared”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// It is important that data is shared among query context, for example:
///     USE database_1;
///     SELECT
///         (SELECT scalar FROM table_name_1) AS scalar_1,
///         (SELECT scalar FROM table_name_2) AS scalar_2,
///         (SELECT scalar FROM table_name_3) AS scalar_3
///     FROM table_name_4;
/// runtime, session, progress, init_query_id are shared among the subqueries
pub struct QueryContextShared {
    /// scan_progress for scan metrics of datablocks (uncompressed)
    pub(in crate::sessions) scan_progress: Arc&amp;lt;Progress&amp;gt;,
    /// write_progress for write/commit metrics of datablocks (uncompressed)
    pub(in crate::sessions) write_progress: Arc&amp;lt;Progress&amp;gt;,
    /// result_progress for metrics of result datablocks (uncompressed)
    pub(in crate::sessions) result_progress: Arc&amp;lt;Progress&amp;gt;,
    pub(in crate::sessions) error: Arc&amp;lt;Mutex&amp;lt;Option&amp;lt;ErrorCode&amp;gt;&amp;gt;&amp;gt;,
    pub(in crate::sessions) session: Arc&amp;lt;Session&amp;gt;,
    pub(in crate::sessions) runtime: Arc&amp;lt;RwLock&amp;lt;Option&amp;lt;Arc&amp;lt;Runtime&amp;gt;&amp;gt;&amp;gt;&amp;gt;,
    pub(in crate::sessions) init_query_id: Arc&amp;lt;RwLock&amp;lt;String&amp;gt;&amp;gt;,
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It provides all the basic information required by the query context.&lt;/p&gt;

&lt;h1&gt;
  
  
  Handler
&lt;/h1&gt;

&lt;p&gt;As mentioned earlier, Databend supports multiple handlers. Let's take MySQL handlers as an example to see the processing procedure of handlers and how they interact with sessions. First, a reference to “SessionManager” is contained in the “MySQLHandler” .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub struct MySQLHandler {
    abort_handle: AbortHandle,
    abort_registration: Option&amp;lt;AbortRegistration&amp;gt;,
    join_handle: Option&amp;lt;JoinHandle&amp;lt;()&amp;gt;&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After it's started, the “MySQLHandler” spawns a tokio task to continuously listen to TCP stream, create a session, and then start a task to execute the following query requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn accept_socket(session_mgr: Arc&amp;lt;SessionManager&amp;gt;, executor: Arc&amp;lt;Runtime&amp;gt;, socket: TcpStream) {
    executor.spawn(async move {
        // create a session
        match session_mgr.create_session(SessionType::MySQL).await {
            Err(error) =&amp;gt; Self::reject_session(socket, error).await,
            Ok(session) =&amp;gt; {
                info!("MySQL connection coming: {:?}", socket.peer_addr());
                // execut queries
                if let Err(error) = MySQLConnection::run_on_stream(session, socket) {
                    error!("Unexpected error occurred during query: {:?}", error);
                };
            }
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the function of “MySQLConnection::run_on_stream”, the session first attaches to the corresponding client host and registers a shutdown closure to handle related cleanups when the connection is closed.&lt;/p&gt;

&lt;p&gt;Related code is as follows：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// mysql_session.rs
pub fn run_on_stream(session: SessionRef, stream: TcpStream) -&amp;gt; Result&amp;lt;()&amp;gt; {
    let blocking_stream = Self::convert_stream(stream)?;
    MySQLConnection::attach_session(&amp;amp;session, &amp;amp;blocking_stream)?;

    ...
}

fn attach_session(session: &amp;amp;SessionRef, blocking_stream: &amp;amp;std::net::TcpStream) -&amp;gt; Result&amp;lt;()&amp;gt; {
    let host = blocking_stream.peer_addr().ok();
    let blocking_stream_ref = blocking_stream.try_clone()?;
    session.attach(host, move || {
        // register shutdown 
        if let Err(error) = blocking_stream_ref.shutdown(Shutdown::Both) {
            error!("Cannot shutdown MySQL session io {}", error);
        }
    });

    Ok(())
}

// session.rs
pub fn attach&amp;lt;F&amp;gt;(self: &amp;amp;Arc&amp;lt;Self&amp;gt;, host: Option&amp;lt;SocketAddr&amp;gt;, io_shutdown: F)
where F: FnOnce() + Send + 'static {
    let (tx, rx) = oneshot::channel();
    self.session_ctx.set_client_host(host);
    self.session_ctx.set_io_shutdown_tx(Some(tx));

    common_base::base::tokio::spawn(async move {
        // trigger cleanups when the session quits 
        if let Ok(tx) = rx.await {
            (io_shutdown)();
            tx.send(()).ok();
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then a MySQL InteractiveWorker is started to handle subsequent queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let join_handle = query_executor.spawn(async move {
    let client_addr = non_blocking_stream.peer_addr().unwrap().to_string();
    let interactive_worker = InteractiveWorker::create(session, client_addr);
    let opts = IntermediaryOptions {
        process_use_statement_on_query: true,
    };
    let (r, w) = non_blocking_stream.into_split();
    let w = BufWriter::with_capacity(DEFAULT_RESULT_SET_WRITE_BUFFER_SIZE, w);
    AsyncMysqlIntermediary::run_with_options(interactive_worker, r, w, &amp;amp;opts).await
});
let _ = futures::executor::block_on(join_handle);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“InteractiveWorker” implements AsyncMysqlShim trait methods, such as “on_execute”, “on_query” and so on. When a query arrives, these methods are called to make executions.Take “on_query”&lt;/p&gt;

&lt;p&gt;for example, the core code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async fn on_query&amp;lt;'a&amp;gt;(
    &amp;amp;'a mut self,
    query: &amp;amp;'a str,
    writer: QueryResultWriter&amp;lt;'a, W&amp;gt;,
) -&amp;gt; Result&amp;lt;()&amp;gt; {
    ...

    // response writer
    let mut writer = DFQueryResultWriter::create(writer);

    let instant = Instant::now();
    // execute queries
    let blocks = self.base.do_query(query).await;

    // write results
    let format = self.base.session.get_format_settings()?;
    let mut write_result = writer.write(blocks, &amp;amp;format);

    ...

    // metrics info
    histogram!(
        super::mysql_metrics::METRIC_MYSQL_PROCESSOR_REQUEST_DURATION,
        instant.elapsed()
    );

    write_result
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In “do_query”, a “QueryContext ” is created and the subsequent SQL queries are executed with the parsed SQL process. Related code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// create a QueryContext
let context = self.session.create_query_context().await?;
// attach it to the query statement
context.attach_query_str(query);

let settings = context.get_settings();

// parse sql
let stmts_hints = DfParser::parse_sql(query, context.get_current_session().get_type());
...

// Define and generate a query plan
let mut planner = Planner::new(context.clone());
let interpreter = planner.plan_sql(query).await.and_then(|v| {
    has_result_set = has_result_set_by_plan(&amp;amp;v.0);
    InterpreterFactoryV2::get(context.clone(), &amp;amp;v.0)
})

// Execute queries and return the results
Self::exec_query(interpreter.clone(), &amp;amp;context).await?;
let schema = interpreter.schema();
Ok(QueryResult::create(
    blocks,
    extra_info,
    has_result_set,
    schema,
))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Epilogue
&lt;/h1&gt;

&lt;p&gt;The whole process from starting Databend to accepting SQL requests and starting processing is described in this article. Recently, we removed the Clickhouse native TCP client for some reasons (The Clickhouse TCP protocol is biased towards the underlying protocol of Clickhouse. With heavy historical burdens and no public documentation, the debugging process became too exhausting. See more in: &lt;a href="https://github.com/datafuselabs/databend/pull/7012"&gt;https://github.com/datafuselabs/databend/pull/7012&lt;/a&gt;) .&lt;/p&gt;

&lt;p&gt;Please feel free to discuss your good ideas here with us. In addition, if any relevant problem is found, you can always submit issues to help improve Databend's stability. Databend community welcomes all well-intentioned comments and suggestions :)&lt;/p&gt;

&lt;h1&gt;
  
  
  About Databend
&lt;/h1&gt;

&lt;p&gt;Databend is an open source modern data warehouse with elasticity and low cost. It can do real-time data analysis on object-based storage.We look forward to your attention and hope to explore the cloud native data warehouse solution, and create a new generation of open source data cloud together.&lt;/p&gt;

&lt;p&gt;Databend documentation：&lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twitter：&lt;a href="https://twitter.com/Datafuse_Labs"&gt;https://twitter.com/Datafuse_Labs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slack：&lt;a href="https://datafusecloud.slack.com/"&gt;https://datafusecloud.slack.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wechat：Databend&lt;/p&gt;

&lt;p&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Read Code</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 02 Sep 2022 09:04:19 +0000</pubDate>
      <link>https://dev.to/databend/how-to-read-code-32pc</link>
      <guid>https://dev.to/databend/how-to-read-code-32pc</guid>
      <description>&lt;p&gt;The ability to read source code is considered to be one of the underlying fundamental programmer skills, and the reason why this ability is important is that   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;inevitably need to read or take over other people’s projects. For example, researching an open source project, such as taking over a project from someone else.   &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reading good project source code is one of the important ways to learn from other people’s good experience, which I know myself.  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reading code and writing code are two not quite the same skills, the reason is that “&lt;a href="https://www.zhihu.com/question/21820752/answer/19427754"&gt;writing code is expressing yourself, reading code is understanding others&lt;/a&gt;”. Because of the many projects, the authors of the project have their own style, it takes a lot of energy to understand.   &lt;/p&gt;

&lt;p&gt;I’ve been in the business for years reading the project source code in general and in detail, and have written some code analysis articles one after another, so I’ll briefly summarize my approach in this article.   &lt;/p&gt;

&lt;h2&gt;
  
  
  The first thing:run it!
&lt;/h2&gt;

&lt;p&gt;The first step to start reading a project source code, is to let the project through your own compilation and run smoothly. This is especially important.&lt;br&gt;&lt;br&gt;
Some projects are complex and depend on many components, so it is not easy to set up a debugging environment, so it is not always possible for all projects to run smoothly. If you can compile and run it yourself, then the scenario analysis, plus debugging code, debugging, and so on will have the basis to unfold.&lt;br&gt;&lt;br&gt;
In my experience, a project code, whether the debugging environment can be built smoothly, the efficiency is very different.&lt;br&gt;&lt;br&gt;
After running, and to try to streamline their environment to reduce the debugging process of interference information. For example, Nginx uses multiple processes to process requests. In order to debug the behavior of Nginx, I often set the number of workers to 1, so that when debugging you know which process is to be tracked.&lt;br&gt;&lt;br&gt;
For example, many projects are compiled with optimization options or without debugging information by default, which can be a problem when debugging, so I modify the makefile to compile to “-O0 -g”, which is compiled to generate a version with debugging information and no optimization.&lt;br&gt;&lt;br&gt;
All in all, the debugging efficiency can be improved a lot after running, but under the premise of running and trying to streamline the environment to exclude the disturbing factors.&lt;/p&gt;
&lt;h2&gt;
  
  
  Clearly define your purpose
&lt;/h2&gt;

&lt;p&gt;Although it is important to read the project source code, not all projects need to be read from start to finish. Before you start reading, you need to be clear about your purpose: whether you need to understand the implementation of one of the modules, the general structure of the framework, the implementation of one of the algorithms, and so on.&lt;br&gt;&lt;br&gt;
For example, many people look at the code of Nginx, and the project has many modules, including the basic core modules (epoll, network sending and receiving, memory pooling, etc.) and modules that extend a specific function, and not all of these modules need to be understood very clearly.   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding the underlying processes at the core of Nginx and the data structures.&lt;/li&gt;
&lt;li&gt;Understanding how Nginx implements a module.
With this general understanding of the project, all that remains is to look at the specific code implementation when you encounter a specific problem.
All in all, it is not recommended to start reading the code of a project without a purpose, as looking at it headlessly will only consume your time and enthusiasm.
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Distinguish between main and branch storylines
&lt;/h2&gt;

&lt;p&gt;With a clear purpose in mind, you can distinguish between main and subplots in the reading process. For example.   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For example, if you want to understand the implementation of a business logic that uses a dictionary to store data in a function, here, “how the dictionary data structure is implemented” is a side plot, and you don’t need to look deeper into its implementation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guided by this principle, the reader only needs to understand the external interfaces to the code of the stems, such as a class that does not need to understand its implementation, and to understand the entry and exit parameters and the role of these interfaces, treating this part as a “black box”.&lt;br&gt;&lt;br&gt;
By the way, in the early years, I saw a way of writing C++ in which the header file contains only the external interface declaration of a class, and the implementation is transferred to the C++ file through an internal impl class, for example.&lt;br&gt;&lt;br&gt;
Header file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// test.h
class Test {
public:
  void fun();
private:
  class Impl;
  Impl *impl_;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C++ file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void Test::fun() {
  impl_-&amp;gt;fun()
}
class Test::Impl {
public:
  void fun() {
    // Concrete implementation
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way of writing makes the header file a lot cleaner: there are no private members or functions associated with the implementation, only the exposed interface, so the user can know at a glance what the class offers to the public.    &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zT0_iQfD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8im06b34d0xvuzwk9z8u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zT0_iQfD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8im06b34d0xvuzwk9z8u.jpg" alt="Image description" width="880" height="587"&gt;&lt;/a&gt;&lt;br&gt;
The “main” and “branch” storylines switch frequently throughout the code reading process, requiring the reader to have some experience in knowing which part of the code they are reading is the main storyline.&lt;/p&gt;
&lt;h2&gt;
  
  
  Vertical and horizontal
&lt;/h2&gt;

&lt;p&gt;The code reading process is divided into two different directions.   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vertical: Read in the order of the code. Vertical reading is often required when a specific understanding of a process or algorithm is needed.
&lt;/li&gt;
&lt;li&gt;Horizontal: Read different modules, and often when you need to first figure out the overall framework, you need to read horizontally.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two directions of reading, should alternate, which requires the code reader to have some experience and be able to grasp the current direction of code reading. My advice is: the process still puts the whole first, and do not go too deep into a particular detail without understanding the overall premise. Consider a function or data structure as a black box, know their input and output, as long as it does not affect the understanding of the whole, then put it aside and move on.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scenario analysis
&lt;/h2&gt;

&lt;p&gt;If you have the previous foundation, you have been able to make the project run smoothly in your own debugging environment, and you have clarified the functions you want to understand, then you can do a scenario analysis of the project code.&lt;br&gt;&lt;br&gt;
The so-called “scenario analysis” is to construct some scenarios by yourself, and then analyze the behavior in these scenarios by adding breakpoints and debugging statements.&lt;br&gt;&lt;br&gt;
For example, when I wrote &lt;a href="https://book.douban.com/subject/27108476/"&gt;Lua Design and Implementation&lt;/a&gt;, I explained the process of interpreting and executing Lua virtual machine instructions, and needed to analyze each instruction, so I used scenario analysis. I would simulate the Lua script code that uses the instruction, and then breakpoint in the program to debug the behavior in these scenarios.&lt;br&gt;&lt;br&gt;
My usual approach is to add a breakpoint to an important entry function, then construct debugging code that triggers the scenario, and when the code stops at the breakpoint, observe the behavior of the code by looking at the stack, variable values, and so on.&lt;br&gt;&lt;br&gt;
For example, in Lua interpreter code, generating Opcode will eventually call the function luaK_code, so I’ll add a breakpoint above this function and construct the scene I want to debug, and as soon as I break at the breakpoint, I’ll see the complete call flow through the function stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(lldb) bt
* thread #1: tid = 0xb1dd2, 0x00000001000071b0 lua`luaK_code, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001000071b0 lua`luaK_code
frame #1: 0x000000010000753e lua`discharge2reg + 238
frame #2: 0x000000010000588f lua`exp2reg + 31
frame #3: 0x000000010000f15b lua`statement + 3131
frame #4: 0x000000010000e0b6 lua`luaY_parser + 182
frame #5: 0x0000000100009de9 lua`f_parser + 89
frame #6: 0x0000000100008ba5 lua`luaD_rawrunprotected + 85
frame #7: 0x0000000100009bf4 lua`luaD_pcall + 68
frame #8: 0x0000000100009d65 lua`luaD_protectedparser + 69
frame #9: 0x00000001000047e1 lua`lua_load + 65
frame #10: 0x0000000100018071 lua`luaL_loadfile + 433
frame #11: 0x0000000100000eb9 lua`pmain + 1545
frame #12: 0x00000001000090cd lua`luaD_precall + 589
frame #13: 0x00000001000098c1 lua`luaD_call + 81
frame #14: 0x0000000100008ba5 lua`luaD_rawrunprotected + 85
frame #15: 0x0000000100009bf4 lua`luaD_pcall + 68
frame #16: 0x00000001000046fb lua`lua_cpcall + 43
frame #17: 0x00000001000007af lua`main + 63
frame #18: 0x00007fff6468708d libdyld.dylib`start + 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage of scenario analysis is that instead of looking for a needle in a haystack, you can narrow down the problem to a scope and understand it.&lt;br&gt;&lt;br&gt;
The concept of “scenario analysis” is not a term I came up with. For example, there are several books that analyze code, such as &lt;a href="https://book.douban.com/subject/1231584/"&gt;Linux Kernel Source Code Scenario Analysis&lt;/a&gt;, Windows Kernel Scenario Analysis, and &lt;a href="https://book.douban.com/subject/1231584/"&gt;Windows Kernel Scenario Analysis&lt;/a&gt;. Windows Kernel Scenario Analysis"](&lt;a href="https://book.douban.com/subject/3715700/"&gt;https://book.douban.com/subject/3715700/&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Make use of good test cases
&lt;/h2&gt;

&lt;p&gt;Good projects come with a lot of use cases, examples of this type are: etcd, a few open source projects produced by google.&lt;/p&gt;

&lt;p&gt;If the test cases are written very carefully, then it is worthwhile to study them. The reason is: test cases are often for a single scenario, alone to construct some data to verify the process of the program. So, like the previous “scenario analysis”, it’s a way to move you from a big project to a specific scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clarify the relationship between core data structures
&lt;/h2&gt;

&lt;p&gt;Although it is said that “programming = algorithm + data structure”, my experience in practice is that data structure is more important.&lt;br&gt;&lt;br&gt;
Because the structure defines the architecture of a program, there is no concrete implementation until the structure is set. Like building a house, the data structure is the framework structure of the house, if a house is very large, and you do not know the structure of the house, will be lost in it. As for the algorithm, if it is a part of the details that you do not need to delve into for the time being, you can refer to the previous section “Distinguishing the main and branch storylines” to understand the entrance and exit parameters and their roles first.&lt;br&gt;&lt;br&gt;
Linus says: “Bad programmers care about code. Good programmers&lt;br&gt;&lt;br&gt;
Therefore, when reading a piece of code, it is especially important to clarify the relationships between the core data structures. At this time, it is necessary to use some tools to draw the relationship between these structures. There are many such examples in my source code analysis class blogs, such as &lt;a href="https://www.codedump.info/post/20190215-leveldb/"&gt;Notes on Reading Leveldb Code&lt;/a&gt;, &lt;a href="https://www.codedump.info/post/20181125-etcd-server/"&gt;Implementation of Etcd Storage&lt;/a&gt;, and so on.&lt;br&gt;&lt;br&gt;
It should be noted that there is no strict sequential relationship between the two steps of scenario analysis and clarifying the core data structure; it does not have to be something first and then something else, but rather interactively.&lt;br&gt;&lt;br&gt;
For example, if you have just taken over a project and need to understand the project briefly, you can first read the code to understand what core data structures are available. Once you understand it, if you are not sure about the process in certain scenarios, you can use scenario analysis. In short, alternate until your questions are answered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask yourself more questions
&lt;/h2&gt;

&lt;p&gt;The learning process cannot be separated from the interaction.&lt;br&gt;&lt;br&gt;
If reading code is just an input, then there needs to be an output. Only simple input is like feeding something to you, and only better digestion can become your own nutrition, and output is an important means to better digest knowledge.&lt;br&gt;&lt;br&gt;
In fact, this idea is very common, for example, students need to do practice assignments (Output) in class (Input), such as learning algorithms (Input) need to practice their own coding (Output), and so on. In short, output is a kind of timely feedback in the learning process, and the higher the quality, the more efficient the learning.&lt;br&gt;&lt;br&gt;
There are many means of output, and when reading the code, it is more recommended to be able to ask yourself more questions, such as.   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did you choose this data structure to describe this problem? How are other projects designed in similar scenarios? What data structures are there to do such a thing?
&lt;/li&gt;
&lt;li&gt;If I were to design such a project, what would I do?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And so on and so forth. The more active and positive thinking you do, the better output you will have, and the quality of output is directly proportional to the quality of learning&lt;/p&gt;

&lt;h2&gt;
  
  
  Write your own code reading note
&lt;/h2&gt;

&lt;p&gt;nce I started blogging, I have been writing code reading articles for various projects, and my screen name “codedump” also comes from the idea of trying to “dump the code internal implementation principle”.&lt;br&gt;&lt;br&gt;
As mentioned earlier, the quality of learning is directly proportional to the quality of output, which is my own deep experience. Because of this, I insist on writing my own analysis notes after reading the source code.&lt;br&gt;&lt;br&gt;
Here are a few things to keep in mind when writing these kinds of notes.&lt;br&gt;&lt;br&gt;
Although they are notes, imagine that you are explaining the principles to someone who is less familiar with the project, or imagine that you are looking back at the article months or even years later. In this case, try to organize the language as well as possible and explain it in a step-by-step manner.&lt;br&gt;&lt;br&gt;
Try to avoid posting large paragraphs of code. I think it’s a bit self-defeating to post large paragraphs of code in such articles: it just looks like you understand it, but you don’t. If you really want to explain a piece of code, you can use pseudo-code or reduced code. Remember: don’t kid yourself, really get it. If you really want to add your own comments to the code, one suggestion I have is to fork a copy of some version of the project and commit it to your own github, where you can always add your own comments and save the commit. For example, my own commented code for etcd 3.1.10: etcd-3.1.10-codedump, similarly other projects I read will fork a project on github with codedump suffix of the project.&lt;br&gt;&lt;br&gt;
Draw more diagrams, a picture is worth a thousand words, use graphics to show the relationship between the code flow, data structures. I just recently discovered that the ability to draw diagrams is also an important ability, and I’m learning how to use images to express my ideas from scratch.&lt;br&gt;&lt;br&gt;
Writing is an important foundational ability, and a friend of mine recently educated me to the effect that if you are strong in something, if you add good writing and good English, then it will greatly amplify your ability in that area. And similar to writing, English such as the underlying basic ability, not a handful, need to keep practicing for a long time to be able to. And writing a blog, for technical staff, is a good means to exercise writing.&lt;br&gt;&lt;br&gt;
PS: If many things, you then do when you can think of the future to face the output of the person is your own, such as their own written code to maintain the back of their own articles written for their own eyes, and so on, the world will be much better. For example, writing a technical blog about these things, because I am writing when I consider that the person who will look at this document may be myself, so when writing will try to be clear and easy to understand, trying to see myself after a period of time when I see their own document, I can immediately recall the details of the time, but also because of this, I rarely post large sections of code in the blog, as far as possible to supplement the legend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The above is my brief summary of some of the means and methods of attention when reading source code, in general there are so many points, right?   &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only better output can better digest the knowledge, the so-called build debugging environment, scenario analysis, ask yourself more questions, write code reading notes, etc. are all around the output to start. In short, you can’t be like a dead fish and expect to fully understand its principles just by reading the code, you need to find ways to interact with it.
&lt;/li&gt;
&lt;li&gt;Writing is one of the basic hard skills of a person, not only to exercise their ability to express themselves, but also to help organize their thoughts. One means of exercising writing skills for programmers is to write a blog, and the sooner you start exercising, the better.
Finally, as with any skill that can be acquired, the ability to read code requires long hours and lots of repetition, so next time start working on a project that interests you.
## About Databend
Databend is an open source modern data warehouse with elasticity and low cost. It can do real-time data analysis on object-based storage.
We look forward to your attention and hope to explore the cloud native data warehouse solution, and create a new generation of open source data cloud together.
&lt;/li&gt;
&lt;li&gt;Databend documentation：&lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter：&lt;a href="https://twitter.com/Datafuse_Labs"&gt;https://twitter.com/Datafuse_Labs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Slack：&lt;a href="https://datafusecloud.slack.com/"&gt;https://datafusecloud.slack.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wechat：Databend&lt;/li&gt;
&lt;li&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Reading Source Code of Databend (1) ：Introduction</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 26 Aug 2022 07:51:31 +0000</pubDate>
      <link>https://dev.to/databend/reading-source-code-of-databend-1-introduction-2bpe</link>
      <guid>https://dev.to/databend/reading-source-code-of-databend-1-introduction-2bpe</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;Databend has gained the attention of many community members since it was open sourced in 2021. Databend is developed with Rust, therefore we designed Rust related courses and established several Rust interest groups in order to attract more developers, especially those with zero Rust development experience.&lt;/p&gt;

&lt;p&gt;Databend also introduced Good First issue” labels to encourage newcomers of the community to make their first contributions. So far, there are more than 100 contributors, which is quite something.&lt;/p&gt;

&lt;p&gt;However, after several iterations in the past year, the code of Databend has become increasingly complex. With 260, 000 lines of Rust code and 46 crates in the master branch at present, even developers familiar with Rust would be confused after cloning the repository, not to mention the increasing difficulty for newcomers. An article column on reading source code of Databend has been appealed for multiple times in many community groups to help make Databend code more accessible.&lt;/p&gt;

&lt;p&gt;In response, we will launch a series of articles on Reading source code of Databend, we hope that these articles can help strengthen the communication between developers and the community, and create a source of inspiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Story of Databend
&lt;/h2&gt;

&lt;p&gt;A question that many developers have asked me is that: Why do you use Rust to build a database from scratch? In fact, this question can be divided into two sub-questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why choose rust？&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answer: Most of our early members were contributors to well-known databases such as Clickhouse, tidb and tokudb. From the perspective of technology stacks, we are more familiar with C++ and Go. Tiger brother u/bohutang also used Go to implement a small database prototype vectorsql during the epidemic. Some developers said that the vectorsql architecture is very elegant and worth learning from.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G3p4ReS5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebtwpbv3qqk60v6uusrg.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G3p4ReS5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebtwpbv3qqk60v6uusrg.PNG" alt="Image description" width="880" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All languages have advantages and disadvantages, and should be selected according to the specific scenarios. At present, most DMBS use C++ or Java, and the rather new NewSQL uses more of Go. According to experience, C and C++ stand for high performance because it is easy for developers to write C/C++ code with high operating efficiency. However, the development efficiency of C++ is unbearably low, and it is difficult for developers to write memory-safe and concurrent-safe code at one time with the deficient tool chain. On the contrary, Go fits the standard of elegance and simplicity better with its sufficient tool chain and high development efficiency. Yet the generics procedure is too slow, and the memory of DB cannot be flexibly controlled. The running performance cannot be compared with C/C + +, especially when using SIMD instructions where interaction with assembly code is required. What we needed is a language with both development efficiency (memory security, concurrency security, and sufficient tool chain) and operation efficiency. At that time rust seemed to be our only choice, and we never regretted our choice afterwards. Rust can meet our needs perfectly, and is also cool!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why build a database system from scratch？&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In general, there are only two routes：&lt;/p&gt;

&lt;p&gt;Secondary Development and Optimization based on well-known Open-Source Database&lt;/p&gt;

&lt;p&gt;Most people may choose this route, because with a good database base there is no need to do more repetitive work and thus can focus on optimization, improvement and reconstruction. In this way, the release of versions can be promoted and commercialized earlier. The disadvantage is that versions after forking is equivalent to another independent system, which cannot be fed back to the community, such as various sub genres under PG.&lt;/p&gt;

&lt;p&gt;Building a new database system from scratch&lt;/p&gt;

&lt;p&gt;This route is rather hard to follow, because the whole concept of database system is too large, and each sub direction is worthy of ten years of study or more. On the other hand, since there is no existing foundation, the designer can adjust and design more flexibly without paying too much attention to the historical problems. At the very beginning, Databend was designed for the scenario of cloud native data warehouse, which is very different from the traditional database system. The cost of code transformation may be the same as the cost of doing it from scratch. Therefore, we chose the second route to create a new cloud data warehouse from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture determines the superstructure, so let's start with the architecture of Databend.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qc6Eq4_L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a51ibxtzkilsg3090o14.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qc6Eq4_L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a51ibxtzkilsg3090o14.PNG" alt="Image description" width="880" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;r/DatafuseLabs - Reading Source Code of Databend (1) ：Introduction&lt;br&gt;
Although we started from scratch with Rust, we have also integrated some excellent open-source components or ecology to avoid duplication of work, for example, Databend is compatible with the ANSI SQL standard, provides support for mainstream protocols such as MySQL / Clickhouse, embraces the arrow ecology of the Internet of things and the storage format is based on the parquet format of big data, etc. We not only actively make contributions to the upstream communities, such as arrow2/Tokio and other open- source libraries, but also open sourced some common components as independent projects in GitHub (openraft, opendal, opencache, opensrv, etc.).&lt;/p&gt;

&lt;p&gt;Databend is a cloud native elastic database. We not only separated computing and storage, but also carefully designed each layer to obtain extreme elasticity. Databend can be divided into three layers: meta-service layer, query layer and storage layer. These three layers can be flexibly expanded, which means that users can choose the most suitable cluster size for their business and scale the cluster according to the development of business.&lt;/p&gt;

&lt;p&gt;Next, we will introduce the main code modules of Databend from the perspective of these three layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MetaService Layer
&lt;/h3&gt;

&lt;p&gt;MetaService layer is mainly used to store and read persistent meta-data information, such as Catalogs / Users.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;meta&lt;/td&gt;
&lt;td&gt;The MetaService service is deployed as an independent process and can be deployed in multiple clusters. The bottom layer uses Raft for distributed consensus, and queries are sent and received as Grpc and MetaService.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;meta/types&lt;/td&gt;
&lt;td&gt;Definition of various structures that are stored in MetaService layer. Since these structures need to be persisted eventually, data serialization methods also need to be considered. Currently, Protobuf format is used for serialization and deserialization, the mutual serialization rule code of related Rust structures and Protobuf is defined in the common/ proto-conv subdirectory.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;meta/sled-store&lt;/td&gt;
&lt;td&gt;Currently, sled is used in MetaService layer to save persistent data. The interfaces related to sled are encapsulated in this subdirectory.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;meta/raft-store&lt;/td&gt;
&lt;td&gt;The openraft user layer needs to implement a storage interface to save data in the raft store. This subdirectory is the storage layer of openraft implemented by MetaService layer, which depends on sled storage, besides a state machine is implemented here which needs be customized by the openraft user layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;meta/api&lt;/td&gt;
&lt;td&gt;The user layer API interface exposed to query implemented based on kvapi.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/meta/grpc&lt;/td&gt;
&lt;td&gt;A client module encapsulated by grpc, the MetaService client uses this to communicate with the MetaService.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;raft&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/datafuselabs/openraft"&gt;https://github.com/datafuselabs/openraft&lt;/a&gt;, a full asynchronous Raft library derived from the async-raft project.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Query Layer
&lt;/h3&gt;

&lt;p&gt;Query nodes are mainly used for calculation. Multiple query nodes can form MPP clusters, and the performance will expand horizontally with the number of query nodes theoretically. The SQL instructions will undergo the following conversion processes in query:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YwjX-4b0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/co3klwer1vgclzqib7v6.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YwjX-4b0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/co3klwer1vgclzqib7v6.PNG" alt="Image description" width="880" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A SQL string is first parsed into AST syntax tree by parser, then bound with other information like catalog and turned into logical plan by Binder. Next, the logical plan is converted into physical plan by a series of optimizer processing. Finally, the physical plan is traversed to build the corresponding execution logic. The modules involved in the query layer are as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;query&lt;/td&gt;
&lt;td&gt;Query service, the entry of the entire function is in bin/databend-query.rs, and contains some sub modules. Here are some important ones: (1) api - exposed to external http/RPC interfaces, (2) catalogs - catalogs management. The default catalog (stored in MetaService) and hive catalog (stored in hive meta store) are supported at present, (3) Clusters - query clustering, (4) Config - query configuration, (5) databases - database engines supported by query, (6) evaluator - expression evaluation tools, (7) Interpreters - SQL executor, which performs physical execution after the plan is built by SQL, (8) pipelines - the implemented scheduling framework of physical operators, (9) Servers - exposed services, including Clickhouse/MySQL/http, etc, (10) Sessions - session management, (11) SQL - including new planner design, new binder logic, new optimizers design, (12) Storages - table engines, the most common one is the fuse engine, (13) table_functions - table functions, such as numbers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/ast&lt;/td&gt;
&lt;td&gt;New SQL parser implemented base on nom_rule.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/datavalues&lt;/td&gt;
&lt;td&gt;The definition of various columns, representing the layout of data in memory. This part will be gradually migrated to common/expressions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/datablocks&lt;/td&gt;
&lt;td&gt;Datablock represents Vec Set, which encapsulates some common methods. This part will be gradually migrated to common/expressions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/functions&lt;/td&gt;
&lt;td&gt;Declarations of scalar functions and aggregate functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/hashtable&lt;/td&gt;
&lt;td&gt;The implementation of a linear detection, which is mainly used in group by aggregation functions and join scenarios.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;common/formats&lt;/td&gt;
&lt;td&gt;Serialization and deserialization of external data in various formats, such as CSV/TSV/Json formats.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opensrv&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/datafuselabs/opensrv"&gt;https://github.com/datafuselabs/opensrv&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Storage Layer
&lt;/h3&gt;

&lt;p&gt;Storage layer mainly involves the management of Snapshots, Segments and index information of tables, and the interaction with the underlying IO. One of the highlights of the storage layer is the implementation of increment view like that of Iceberge based on Snapshot isolation. We can now obtain time travel access to tables in any historical state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future work
&lt;/h2&gt;

&lt;p&gt;This source code reading series has just started, the following tutorials will explain the source code of each module step by step according to the introduction sequence. Most of the tutorials will be in the form of articles, and when it comes to some important and interesting module designs, we may use live video to encourage communication. It is only a preliminary plan at present. We will accept your suggestions to make adjustment on time or content during the process. In any case, we hope that this activity may appeal to more like-minded people to participate in the development of Databend, and to learn, communicate and grow together.&lt;/p&gt;

&lt;p&gt;About Databend&lt;br&gt;
Databend is an open source modern data warehouse with elasticity and low cost. It can do real-time data analysis on object-based storage.We look forward to your attention and hope to explore the cloud native data warehouse solution, and create a new generation of open source data cloud together.&lt;/p&gt;

&lt;p&gt;Databend documentation：&lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twitter：&lt;a href="https://twitter.com/Datafuse_Labs"&gt;https://twitter.com/Datafuse_Labs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slack：&lt;a href="https://datafusecloud.slack.com/"&gt;https://datafusecloud.slack.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wechat：Databend&lt;/p&gt;

&lt;p&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Databend v0.8</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Thu, 25 Aug 2022 03:13:55 +0000</pubDate>
      <link>https://dev.to/databend/databend-v08-3986</link>
      <guid>https://dev.to/databend/databend-v08-3986</guid>
      <description>&lt;p&gt;Hello, everyone! I’m Xuanwo. Today, on behalf of the Databend community, I would like to announce the official release of v0.8.&lt;br&gt;
Development of Databend v0.8 started on March 28th, with 5000+ commits and 4600+ file changes. In the last 5 months, the community of 120+ contributors added 420K lines of code and removed 160K lines, equivalent to rewriting Databend once. In this release, the community made significant improvements to the SQL Planner framework and migrated all SQL statements to the new Planner, providing full JOIN and subquery support.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/datafuselabs/databend/releases/tag/v0.8.0-nightly"&gt;Download Latest Version Now&lt;/a&gt; (link to release here)&lt;/p&gt;

&lt;p&gt;Let’s see what has been done in v0.8.&lt;/p&gt;
&lt;h1&gt;
  
  
  What’s Databend?
&lt;/h1&gt;

&lt;p&gt;Databend is a modern cloud data warehouse based on Rust that enables high-performance, elastic and scalable real-time data analysis and activates the data potential of users.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j8pfoBF1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8xskefmbec0h8exfdar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j8pfoBF1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8xskefmbec0h8exfdar.png" alt="Image description" width="880" height="661"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Significant improvements
&lt;/h1&gt;
&lt;h2&gt;
  
  
  New Planner: JOIN! JOIN! JOIN!
&lt;/h2&gt;

&lt;p&gt;To better support complex SQL queries and improve user experience, Databend v0.8 is designed with a new Planner framework.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sXD_onUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohn7c9229ho1xi0a0fq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sXD_onUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohn7c9229ho1xi0a0fq4.png" alt="Image description" width="880" height="231"&gt;&lt;/a&gt;&lt;br&gt;
Databend has added JOIN and proper subquery support, driven by New Planner.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_gw_28Yv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/419qtojrkry2q1poz6pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_gw_28Yv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/419qtojrkry2q1poz6pl.png" alt="Image description" width="880" height="142"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select vip_info.Client_ID, vip_info.Region
      from vip_info right
      join purchase_records
      on vip_info.Client_ID = purchase_records.Client_ID;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  New Parser: The Best Parser！
&lt;/h2&gt;

&lt;p&gt;While refactoring Planner, the databend community has implemented a new nom-based Parser that balances development efficiency with user experience.New Parser makes it easy for developers to design/develop/test complex SQL syntax in an intuitive way&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xmIW-ph3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/behjaj7oufni8nwbd8gk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xmIW-ph3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/behjaj7oufni8nwbd8gk.png" alt="Image description" width="880" height="223"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY
     ~ INTO ~ #copy_unit
     ~ FROM ~ #copy_unit
     ~ ( FILES ~ "=" ~ "(" ~ #comma_separated_list0(literal_string) ~ ")")?
     ~ ( PATTERN ~ "=" ~ #literal_string)?
     ~ ( FILE_FORMAT ~ "=" ~ #options)?
     ~ ( VALIDATION_MODE ~ "=" ~ #literal_string)?
     ~ ( SIZE_LIMIT ~ "=" ~ #literal_u64)?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also gives the user specific and precise information about the error.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nSnwUH9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et8iayq720ts4obyqrt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nSnwUH9i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/et8iayq720ts4obyqrt2.png" alt="Image description" width="880" height="203"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MySQL [(none)]&amp;gt; select number from numbers(10) as t inner join numbers(30) as t1 using(number); 
ERROR 1105 (HY000): Code: 1065, displayText = error:
   --&amp;gt; SQL:1:8
   |
 1 | select number from numbers(10) as t inner join numbers(30) as t1 using(number)
   |        ^^^^^^ column reference is ambiguous
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more worrying about not knowing what’s wrong with SQL.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://databend.rs/blog/new-planner"&gt;The New Databend SQL Planner&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h1&gt;
  
  
  New Features
&lt;/h1&gt;

&lt;p&gt;In addition to the newly designed Planner, the Databend community has implemented a number of new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  COPY Enhancement
&lt;/h2&gt;

&lt;p&gt;COPY capabilities have been greatly enhanced, and Databend can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy data from any supported storage service (even https!)
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rEcg5MlG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwty26bve4cdrhkyzddd.png" alt="Image description" width="880" height="142"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY
      INTO ontime200
      FROM 'https://repo.databend.rs/dataset/stateful/ontime_2006_[200-300].csv'      
  FILE_FORMAT = (TYPE = 'CSV')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Support for copying compressed files
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vCFJDD0K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dgdns1ohyaftbngjqw9.png" alt="Image description" width="880" height="142"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY
      INTO ontime200
      FROM 's3://bucket/dataset/stateful/ontime.csv.gz'
      FILE_FORMAT = (TYPE = 'CSV' COMPRESSION=AUTO)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UNLOAD data to any supported storage service&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NBupD92n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jeexkccadvy2xl33zhbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NBupD92n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jeexkccadvy2xl33zhbp.png" alt="Image description" width="880" height="142"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY
      INTO 'azblob://bucket/'
       FROM ontime200
       FILE_FORMAT = (TYPE = 'PARQUET‘)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hive Support
&lt;/h2&gt;

&lt;p&gt;Databend v0.8 designed and developed the Multi Catalog and implemented Hive Metastore support on top of it!&lt;br&gt;
Databend can now interface directly to Hive and read data from HDFS.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gv2CGe-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sim6ajs5oek4sc5pj965.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gv2CGe-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sim6ajs5oek4sc5pj965.png" alt="Image description" width="880" height="80"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select * from hive.default.customer_p2 order by c_nation;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Time Travel
&lt;/h2&gt;

&lt;p&gt;A long time ago, the Databend community shared an implementation of the underlying FUSE Engine, &lt;a href="https://databend.rs/blog/databend-engine"&gt;From Git to Fuse Engine&lt;/a&gt;, where one of the most important features was the support for time travel, allowing us to query data tables at any point in time.&lt;br&gt;
Starting from v0.8, this feature is now officially installed and we can now&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query the data table for a specified time
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--alQbjcg9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yddbu6kmrzggtr5kh4bo.png" alt="Image description" width="880" height="243"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Travel to the time when the last row was inserted 
select * from demo at (TIMESTAMP =&amp;gt; '2022-06-22 08:58:54.509008'::TIMESTAMP);  
+----------+ 
| c        | 
+----------+ 
| batch1.1 | 
| batch1.2 | 
| batch2.1 | 
+----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Recover mistakenly deleted data tables
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--inwtAth2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avjjre9rlzypfccotzyz.png" alt="Image description" width="880" height="366"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DROP TABLE test;

SELECT * FROM test; 
ERROR 1105 (HY000): Code: 1025, displayText = Unknown table 'test'.  

-- un-drop table 
UNDROP TABLE test; 

-- check 
SELECT * FROM test; 
+------+------+ 
| a    | b    | 
+------+------+ 
|    1 | a    | 
+------+------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Make business data have more security!&lt;/p&gt;
&lt;h2&gt;
  
  
  CTE Support
&lt;/h2&gt;

&lt;p&gt;CTE (Common Table Expression) is a frequently used feature in OLAP business to define a temporary result set within the execution of a single statement, which is valid only during the query period, enabling the reuse of code segments, improving readability and better implementation of complex queries.&lt;br&gt;
Databend v0.8 re-implements the CTE based on New Planner and now users can happily use WITH to declare the CTE.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cyoDMSMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/subge7rfg89fmx8l0mo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cyoDMSMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/subge7rfg89fmx8l0mo2.png" alt="Image description" width="880" height="243"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH customers_in_quebec
       AS (SELECT customername,
                  city
           FROM   customers
           WHERE  province = 'Québec')  
SELECT customername  
FROM   customers_in_quebec 
WHERE  city = 'Montréal'  
ORDER  BY customername;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to these features mentioned above, Databend v0.8 also supports UDFs, adds DELETE statements, further enhances support for semi-structured data types, not to mention the numerous SQL statement improvements and new methods added. Thanks to all the contributors to the Databend community, without you all the new features mentioned here would not have been possible!&lt;/p&gt;

&lt;h1&gt;
  
  
  Quality Enhancement
&lt;/h1&gt;

&lt;p&gt;Feature implementation is just the first part of product delivery. In Databend v0.8, the community introduced the concept of engineering quality, which evaluates the quality of Databend development in three dimensions: users, contributors, and community.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reassuring users
&lt;/h2&gt;

&lt;p&gt;In order for users to use Databend with confidence, the community has added a lot of tests over the last three months, fetching stateless test sets from YDB and others, adding stateful tests for ontime, hits and other datasets, putting SQL Logic Test online to cover all interfaces, and enabling SQL Fuzz testing to cover boundary cases.Furthermore, the community has also gone live with Databend Perf to do continuous performance testing of Databend in production environments to catch unexpected performance regressions in time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make contributors comfortable
&lt;/h2&gt;

&lt;p&gt;Databend is a large Rust project that has been criticized by the community for its build time.To improve this issue and make contributors feel comfortable, the community went live with a highly configurable, specially tuned Self-hosted Runner to perform integration tests for PR and enabled several services or tools such as Mergify, mold, dev-tools, etc. to optimize the CI process.We also initiated a new plan to restructure the Databend project, splitting the original huge query crate into multiple sub-crates to avoid, as much as possible, the situation of changing one line of code and check execution for five minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the community happy
&lt;/h2&gt;

&lt;p&gt;Databend is a contributor and participant in the open source community. During the development of v0.8, the Databend community established the principle of Upstream First, actively following and adopting the latest upstream releases, giving feedback on known bugs, contributing their own patches, and starting Tracking issues of upstream first violation to keep up with the latest developments.&lt;/p&gt;

&lt;p&gt;The Databend community is actively exploring integration with other open source projects and has already implemented integration and support for third-party drivers such as Vector, sqlalchemy, clickhouse-driver, etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Steps
&lt;/h1&gt;

&lt;p&gt;Databend v0.8 is a solid foundation release with a new Planner that makes it easier to implement features and make optimizations. In version 0.9, we expect improvements in the following areas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query Result Cache&lt;/li&gt;
&lt;li&gt;JSON Optimization&lt;/li&gt;
&lt;li&gt;Table Share&lt;/li&gt;
&lt;li&gt;Processor Profiling&lt;/li&gt;
&lt;li&gt;Resource Quota&lt;/li&gt;
&lt;li&gt;Data Caching
Please check the Release proposal: Nightly v0.9 for the latest news~&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Get going now!
&lt;/h1&gt;

&lt;p&gt;Visit the release log (link) and download the latest version (link) to learn more, and feel free to submit feedback using Github Issues if you encounter problems!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to write RFCs for open source projects</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 05 Aug 2022 08:29:49 +0000</pubDate>
      <link>https://dev.to/databend/how-to-write-rfcs-for-open-source-projects-lnd</link>
      <guid>https://dev.to/databend/how-to-write-rfcs-for-open-source-projects-lnd</guid>
      <description>&lt;h2&gt;
  
  
  About RFCs
&lt;/h2&gt;

&lt;p&gt;The importance of RFCs has been emphasized by many people. As &lt;a href="https://github.com/tisonkun/_"&gt;@tison&lt;/a&gt; said in &lt;a href="https://zhuanlan.zhihu.com/p/93334196_"&gt;How to Participate in the Apache project community&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;A description is certainly needed for any non-trivial change to explain the motivation. For major changes, design documentation becomes even more necessary since no one has a permanent memory and people always forget why they did something in the first place. The precipitation of design documentation plays a vital role in freeing the community from uncertainty evolution of human activities.&lt;/p&gt;

&lt;p&gt;I myself also elaborated my understanding of RFC in &lt;a href="https://xuanwo.io/2022/01-refactor-in-open-source-project/"&gt;How to Refactor in Open Source Projects？&lt;/a&gt;：&lt;/p&gt;

&lt;p&gt;It takes more than qualified code to make a good open source project, talking only about abstract techs and code with the open source community aside would be meaningless. Therefore, we must clarify our ideas and explain our motives before submitting large-scale changes to open source projects, this way the community will understand what contributions we want to make and how we plan to do that.&lt;/p&gt;

&lt;p&gt;These archived documents can help supplement information, improve ideas, and build better designs during discussion. From a long-term perspective, these documents can also help latecomers understand why such a design was proposed at that time, and help promote the community more efficiently. Moreover, good design documentation can often influence and inspire the design of other open source projects, thus promoting the progress of the entire industry.&lt;/p&gt;

&lt;p&gt;The following examples can prove that open source projects that work well often have sound RFC processes, and the relationship between them is complementary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rust-lang/rfcs"&gt;Rust RFCs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://forum.cockroachlabs.com/c/open-source-contributors/rfc/6"&gt;CockroachDB RFCs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ethereum/EIPs"&gt;thereum EIPs&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So here comes the question:&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write an RFC
&lt;/h2&gt;

&lt;p&gt;In my opinion, writing RFCs is a very natural thing to do. The essential reason why some people find it hard is often the lack of sufficient preparation, since it’s easy to put forward a new idea, but transforming the idea into a feasible solution takes hard work. RFCs are the embodiment of this transforming process.Usually I would go through the following steps when writing an RFC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collect background information&lt;/li&gt;
&lt;li&gt;Analyze feasible schemes&lt;/li&gt;
&lt;li&gt;Write an RFC&lt;/li&gt;
&lt;li&gt;Discuss among the community&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Collect background information
&lt;/h2&gt;

&lt;p&gt;The most laborious and often overlooked step is to collect background information.&lt;/p&gt;

&lt;p&gt;After coming up with a good idea, we need to refer to the historical RFCs and relevant issues/PR to make sure whether this idea is feasible or not. We need to collect enough information to answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do the existing related modules work? What is the problem to be solved now? Is it really necessary to make further changes?&lt;/li&gt;
&lt;li&gt;Is there any similar work in related fields, and how’s that going? Is there any similar experience for reference in other projects?&lt;/li&gt;
&lt;li&gt;Has this idea been considered before, and why was it shelved？What has changed the situation now?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This process of collecting background information allows us to get more context information and have a better understanding about relevant modules, and thus avoid proposing improvements which are practically infeasible. Besides, collecting background information can help prevent us from carrying out repeated work, after all there is nothing new under the sun.&lt;/p&gt;

&lt;p&gt;I made the following preparations before proposing RFC: Config Backward Compatibility：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conduct one-vs-one communication with users who raised relevant questions to understand their demands and needs&lt;/li&gt;
&lt;li&gt;Understand the core logic of how Databend implements config processing&lt;/li&gt;
&lt;li&gt;Find out how similar projects implement config compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analyze feasible schemes
&lt;/h2&gt;

&lt;p&gt;After fully understanding the background information, what to do next is to analyze feasible schemes.&lt;/p&gt;

&lt;p&gt;There are often many possible solutions for a technical problem, so rather than trying to find a definite answer, we need to analyze and investigate among various schemes and select a relatively better one. Sometimes a simple demo is necessary to help verify your ideas.&lt;/p&gt;

&lt;p&gt;Here are several mistakes I often make in this process:：&lt;/p&gt;

&lt;p&gt;• Laziness: Knowing that there are other possible solutions and not taking the initiative to investigate, this usually led to being pointed out by the community when entering the discussion stage and forced to make additional explanations. It takes much more time to go back and forth than thinking things straight at the beginning.&lt;/p&gt;

&lt;p&gt;• Path dependence: Focusing too much on how to fix the appeared problems, and fail to jump out of the box with better solutions.&lt;/p&gt;

&lt;p&gt;• Self-defense: The moment we come up with an idea, a general design is also formed in our minds, and we are inclined to this existing design when conducting research work. Even if the existing design has serious defects, we often are unwilling to give it up and choose to add unreasonable settings for it.&lt;/p&gt;

&lt;p&gt;• Implementation before design: The implementation has been completed before the investigating process, and therefore the analysis of the schemes becomes the discussion and replication of the specific implementation.&lt;/p&gt;

&lt;p&gt;These mistakes often lead to obvious bias in the subsequent writing of RFCs, and thus the conclusions are biased. Either the RFCs will be greatly adjusted, or in more serious situations there will be major differences and even verbal conflicts with the defenders. To avoid such problems as much as possible, we should strive to make a fair and objective evaluation of different implementation schemes. Surely everyone has his/her own technological preferences, so there is no need to be too strict about fairness, as long as the advantages and disadvantages are clearly analyzed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write an RFC
&lt;/h2&gt;

&lt;p&gt;Now that the preparatory work has been completed, we enter the stage of writing RFCs.&lt;/p&gt;

&lt;p&gt;Usually every project has its own format and requirements, and thus the way to write RFCs also vary according to local conditions.&lt;/p&gt;

&lt;p&gt;Most RFCs include the following chapters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background/ Motivation&lt;/li&gt;
&lt;li&gt;Detailed description&lt;/li&gt;
&lt;li&gt;Basic principles&lt;/li&gt;
&lt;li&gt;Unsolved problems&lt;/li&gt;
&lt;li&gt;Future possibilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Background/ Motivation In this chapter we need to explain the background and motivation of this change, and explain why this change should be made. According to different situations, the specific description should be slightly different according to specific situations. For example, when adding new functions we should explain that why the existing function cannot meet the requirements, what usage scenarios it would support and what the expected output is. In other situations when function reconfiguration and modification is needed, we should focus on explaining what problems exist in the existing implementation and what attempts have been made before.&lt;/p&gt;

&lt;p&gt;Detailed description This chapter needs to specify how the project needs to be changed, and also needs to be adjusted according to the specific requirements. Take Rust as an example, there are two main parts in this chapter: guide-level explanation and reference-level explanation.&lt;/p&gt;

&lt;p&gt;Guide-level explanation: This part explains what changes this change will bring from the perspective of users, what new concepts have been introduced and what new syntax has been added.&lt;/p&gt;

&lt;p&gt;Reference-level explanation: This part explains how to implement the change from a technical point of view, how to interact with other features, and which edge cases need to be considered. Please remember not to paste too much detailed implementations in this chapter, brief explanation of the ideas and some core code is enough. This way reviewer will have a better grasp of the macro design rather than get caught up in the implementation details.&lt;/p&gt;

&lt;p&gt;Basic principles This chapter should explain why the change is implemented in this particular way.&lt;/p&gt;

&lt;p&gt;Is there any other implementation schemes? What are the advantages and disadvantages of each, and what reasons drove us to adopt scheme A instead of scheme B? What is the existing technology? How are other projects realized, and what are their respective starting points?&lt;/p&gt;

&lt;p&gt;Unsolved problems This chapter needs to explain the limitations of this change. For example, problems raised in the review but cannot be solved at present can be recorded in this chapter.Future possibilities This chapter is used to record the future possibilities of this change, which has been considered but has not been implemented this time.&lt;/p&gt;

&lt;p&gt;Such as how a newly introduced feature can be combined with other features in the future. Or when someone puts forward ideas beyond the scope of the current RFC during reviews, these ideas can be recorded in this chapter for future reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discuss among the community
&lt;/h2&gt;

&lt;p&gt;After completing the RFC, we can propose it to the community for discussion.&lt;/p&gt;

&lt;p&gt;Of course we don’t want our own RFCs be rejected after all this work, so we need to participate in community discussions and respond to the concerns of community members actively. It’s necessary to adjust our implementation plan according to the feedback we receive, common adjustments are like supplementing what is not considered, adjusting the idea of implementation, splitting RFCs when necessary, implementing part of the original plan, and so on. Keep in mind that the interests of community members tend to be consistent, and every member’s ultimate goal is to help develop the project. Negative behaviors like taking others’ objections as an attack or passively confronting community members are not conducive to the further promotion of RFCs.&lt;/p&gt;

&lt;p&gt;The RFC I proposed for DataBend — &lt;a href="https://github.com/datafuselabs/databend/pull/5324"&gt;RFC: Config Backward Compatibility&lt;/a&gt; was originally a larger one — Scope: Versioned Config. In the original RFC, I planned to introduce the concept of version into DataBend config so that developers can make destructive changes with more confidence. However, the community members were concerned about the complexity of the design, and they generally believed that the design was too complex and would introduce a lot of redundant code. Moreover, they believed that DataBend had not released&lt;/p&gt;

&lt;p&gt;a stable version yet, and there was no need to introduce versioned config then. Based on the feedback, I made my own &lt;a href="https://github.com/datafuselabs/databend/pull/5324#issuecomment-1125794431"&gt;response&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Comment: Our community members have great concern about the complexity of introducing a versioned config at this stage(no stable release, no production users). It seems better to only split outer and inner configs and leave the decision of versioned config till the future.Response: I have updated the RFC and moved content about the versioned config to Future possibilities part. This change only introduces a reconfiguration, which splits outer and inner configs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After this adjustment, the community gave a LGTM and merged this RFC to the specific implementation stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Writing RFCs is an effective way to communicate and exchange ideas with the community. By completing the steps of collecting background information, analyzing feasible schemes, writing RFCs and community discussion, combined adjustments according to the particular project, we can all write a reliable proposal.&lt;/p&gt;

&lt;p&gt;Go submit a proposal for your favorite open source project!&lt;/p&gt;

&lt;h2&gt;
  
  
  About Databned
&lt;/h2&gt;

&lt;p&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;File： &lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Excellent Performance of Databend in Data Archiving Analysis</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 29 Jul 2022 08:51:54 +0000</pubDate>
      <link>https://dev.to/databend/excellent-performance-of-databend-in-data-archiving-analysis-2clh</link>
      <guid>https://dev.to/databend/excellent-performance-of-databend-in-data-archiving-analysis-2clh</guid>
      <description>&lt;h1&gt;
  
  
  What's Databend?
&lt;/h1&gt;

&lt;p&gt;Databend, developed with Rust, is a new open-source and cloud-native data warehouse. It offers high-speed elastic expansion capabilities and is committed to building an on-demand and volume-based data cloud product experience:&lt;/p&gt;

&lt;p&gt;Famous open-source cloud data warehouse project.&lt;/p&gt;

&lt;p&gt;Vectorized Execution and Pull &amp;amp; Push-Based Processor Model.&lt;/p&gt;

&lt;p&gt;Separation of compute and storage: Available on-demand with high performance and low cost.&lt;/p&gt;

&lt;p&gt;Support for various databases: Compatible with MySQL, Clickhouse protocol, SQL Over HTTP, etc.&lt;/p&gt;

&lt;p&gt;Perfect capabilities for handling transactions: Support for Time Travel, Database Clone, Data Share, etc.&lt;/p&gt;

&lt;p&gt;Support for reading, writing, and sharing the same data by multiple tenants.&lt;/p&gt;

&lt;p&gt;Databend tutorials: &lt;a href="https://databend.rs/doc/deploy"&gt;https://databend.rs/doc/deploy&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploying Databend to Work with Ceph
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TaXJv57N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/na2mkspvs9uj4u1ukjze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TaXJv57N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/na2mkspvs9uj4u1ukjze.png" alt="Image description" width="880" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databend Architecture:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Query node: Processes protocol analysis and SQL push-down.   &lt;/p&gt;

&lt;p&gt;Meta node: Stores metadata on the local disk.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supported storage solutions:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Public: OSS products from AWS, Alibaba, Tencent, etc.Self-hosted: S3-compatible products such as MinlO and Ceph.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment steps:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1.Download the latest binary package.&lt;/p&gt;

&lt;p&gt;2.Unzip the package and create folders.&lt;/p&gt;

&lt;p&gt;3.Modify the configuration file.&lt;/p&gt;

&lt;p&gt;4.Start up Databend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment environment:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Operating system: Centos7&lt;br&gt;
Cpeh version: 12.2.13&lt;br&gt;
Databend version: v0.7.65Overall, &lt;br&gt;
Databend deployment is fairly straightforward.&lt;/p&gt;

&lt;p&gt;Step 1: Download Databend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]#
wget https://github.com/datafuselabs/databend/releases/download/v0.7.65-nightly/databend-v0.7.65-nightly-x86_64-unknown-linux-musl.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Unzip the package and create folders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]#
tar -zxvf databend-v0.7.65-nightly-x86_64-unknown-linux-musl.tar.gz
mkdir /usr/local/databend/{bin,data,etc,logs} -p
mv databend-meta /usr/local/databend/bin/ 
mv databend-query /usr/local/databend/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Modify the configuration files for startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]#  Modify config file for meta node
cd /usr/local/databend/etc/
cat databend-meta.toml
log_dir            = "/usr/local/databend/logs/_logs1"
metric_api_address = "0.0.0.0:28100"
admin_api_address  = "0.0.0.0:28101"
grpc_api_address   = "0.0.0.0:9191"
[raft_config]
id            = 1
raft_dir ="/usr/local/databend/data/_meta1"
raft_api_port = 28103#172.16.16.12#
raft_listen_host = "172.16.16.12"#
raft_advertise_host = "172.16.16.12"
# Start up mode: singlenode cluster
single        = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]# Modify config file for query node
cat databend-query-node-1.toml
[query]
max_active_sessions = 256
wait_timeout_mills = 5000
# For flight rpc. Use the IP and ports of the current machine
flight_api_address = "本机IP:9091"
# Databend Query http address.
# For admin RESET 
API.http_api_address = "0.0.0.0:8081"
# Databend Query metrics RESET API.
metric_api_address = "0.0.0.0:7071"
# Databend Query MySQL Handler.
mysql_handler_host = "0.0.0.0"
mysql_handler_port = 3307
# Databend Query ClickHouse Handler.
clickhouse_handler_host = "0.0.0.0"
clickhouse_handler_port = 9001
# Databend Query HTTP Handler.
http_handler_host = "0.0.0.0"
http_handler_port = 8000
tenant_id = "test_tenant"
cluster_id = "test_cluster"
table_engine_memory_enabled = true
table_engine_csv_enabled = true
table_engine_parquet_enabled = true
database_engine_github_enabled = true
table_cache_enabled = true
table_memory_cache_mb_size = 1024
table_disk_cache_root = "/usr/local/databend/data/_cache"
table_disk_cache_mb_size = 10240
[log]
log_level = "DEBUG"
log_dir = "/usr/local/databend/logs/_logs"
[meta]
# To enable embedded meta-store, set meta_address to ""
meta_embedded_dir = "/usr/local/databend/data/_meta_embedded_1"
meta_address = "0.0.0.0:9191"
meta_username = "root"
meta_password = "root"
meta_client_timeout_in_second = 60
# Storage config.
[storage]storage_type = "s3"
# DISK storage.
[storage.disk]
data_path = "/usr/local/databend/data/stateless_test_data"
# S3 storage. If you want you s3 ,please storage type : s3
[storage.s3]
bucket="databend"
region="region"
endpoint_url="&amp;lt;Your Ceph S3 address&amp;gt;"
access_key_id="&amp;lt;Your Ceph S3 key id&amp;gt;"
secret_access_key="&amp;lt;Your Ceph S3 access key&amp;gt;"
# Azure storage
[storage.azure_storage_blob]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]# #Modify start.sh
/usr/local/databend/bin
[root@testsrv ~]#cat start.sh
ulimit  -n 65535cd /usr/local/databend/nohup /usr/local/databend/bin/databend-meta --config-file=/usr/local/databend/etc/databend-meta.toml 2&amp;gt;&amp;amp;1 &amp;gt;meta.log &amp;amp;
sleep 3
nohup /usr/local/databend/bin/databend-query --config-file=/usr/local/databend/etc/databend-query-node-1.toml 2&amp;gt;&amp;amp;1 &amp;gt;query.log &amp;amp;
cd -
echo "Please usage: mysql -h127.0.0.1 -P3307 -uroot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Start Databend&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]#
 bash start.sh&amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Verify that Databend was deployed successfully to work with Ceph&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[root@testsrv ~]# mysql -h127.0.0.1 -P3307 -uroot  -- No passwords by default  
-- Execute SQL Statements 'root'@127.0.0.1 18:59:  [(none)]&amp;gt; 
select * from system.configs; 
-- Successful if ceph address and key are displayed
s3.region              
s3.endpoint_url        
s3.access_key_id       
s3.secret_access_key   
s3.bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Excellent Performance of Databend in Data Archiving Analysis
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Archiving options
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A large amount of historical data (such as log and transaction flow) persisting in MySQL will occupy a lot of storage space and affect your business performance (for example, jitters might be caused). However, you cannot permanently delete the historical data because you might want to read it for some analysis purposes at a later time. &lt;br&gt;
You might want, for example, to calculate a total number on a certain condition in a certain month of the year 2000, so it is necessary to consider regularly archiving the data. Now there are many options to do it. You can use the pt-archiver tool, or an archiving applet developed by DBA.The data you choose to archive must meet the following conditions:&lt;/p&gt;

&lt;p&gt;Compatible with MySQL protocol for minimizing business logic changes. Other protocols might require a big change.&lt;/p&gt;

&lt;p&gt;High-performance compression ratio to save storage costs.&lt;/p&gt;

&lt;p&gt;Support for data calculation and analysis.&lt;/p&gt;

&lt;p&gt;Here are some options for you: MySQL (separate archive cluster), Databend, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comparing data compressions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We generated 200 million rows of data and imported them into MySQL and Databend respectively to compare the physical sizes after compression. The table below shows that you can have a better compression ratio when using Databend.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Physical Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;88 G&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSV&lt;/td&gt;
&lt;td&gt;84 G&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databend&lt;/td&gt;
&lt;td&gt;8 G&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;47 G&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Data query tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This section compares the response time of MySQL and Databend for SQL queries.   &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test environments:&lt;/strong&gt;&lt;br&gt;
Server hardware: 40 Cores, 256 G, SSD hard disk.MySQL: Innodb buffer pool set to 100 G. SSD hard disk.Databend: Default configuration. The S3 service must be running on a server with HDD.&lt;/p&gt;

&lt;p&gt;select count(*) from ontime;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Execution Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Databend-hdd&lt;/td&gt;
&lt;td&gt;0.02 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databend-ssd&lt;/td&gt;
&lt;td&gt;0.04sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;4 min 9.05 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;select count(*),Year from ontime group by Year; (No Indexes)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Execution Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Databend&lt;/td&gt;
&lt;td&gt;1.89 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;5 min 19.20 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;select count(*),Year from ontime group by Year;（ Indexed by year）&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Execution Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Databend-hdd&lt;/td&gt;
&lt;td&gt;0.56 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Databend-ssd&lt;/td&gt;
&lt;td&gt;1.89 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;2 min 46.72 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Conclusion: Compared with MySQL, Databend has a slight advantage in the response time of SQL queries for data analysis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compatible with MySQL protocol
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Databend supports MySQL protocol, Clickhouse protocol, and HTTP protocol. Programs that use MySQL are basically compatible with Databend.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Archiving&lt;br&gt;
Databend has advantages over MySQL in data archiving, such as data compression and SQL queries for data analysis. Databend works with mechanical hard drives, so it doesn't need very good hardware to get good results. I would recommend Databend for data archiving analysis. If you only need data archiving now, Databend can help lower your cost. If you need data analysis at a later time, remember that Databend is easy to scale up and down.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment&lt;br&gt;
Deploying Databend to work with Ceph is easy. Databend has more advantages over traditional databases in the cloud-native computing scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High availability&lt;br&gt;
The Query node is a stateless node. Metadata must be kept properly, and you can create copies of metadata to keep it from being lost. In my opinion, metadata can be saved to the storage layer for real-time backup, which is only used for emergency recovery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Storage layer&lt;br&gt;
Cloud scenarios will ensure high availability of the OSS layer. For private cloud environments, you need to ensure the high availability of Ceph or MinIo. We're improving Databend to support k8s out of the box and provide a real pay-as-you-go experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Author information: shuaimeng Tian, senior DBA&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The New Databend SQL Planner</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 15 Jul 2022 10:39:12 +0000</pubDate>
      <link>https://dev.to/databend/the-new-databend-sql-planner-1kcj</link>
      <guid>https://dev.to/databend/the-new-databend-sql-planner-1kcj</guid>
      <description>&lt;p&gt;To support complex SQL queries and improve user experience, a large-scale refactoring work for Databend's SQL planner was started several months ago. At present, the refactoring is coming to an end. You can now modify the Session settings of Databend as follows to enable the new planner for early access:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ar9zHb_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ogkpix8ro9w6f7jed53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ar9zHb_I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ogkpix8ro9w6f7jed53.png" alt="Image description" width="880" height="77"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A more friendly query experience
&lt;/h3&gt;

&lt;p&gt;Data analysts and developers usually get various errors when coding SQL queries, and troubleshooting can be a nightmare when the queries are complex. I hate MySQL's error prompts because I have coded a query with dozens of JOIN clauses.The new planner now includes a step for strict semantic checking so that most errors can be intercepted during the compilation. A new error prompt algorithm was also introduced to help users locate the errors. When there is invalid syntax in your SQL query (for example, misspelled keywords or missing clauses), you will receive an error message that is more instructive.&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iMRe2LEn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7lhafrzqrtgl8coyvaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iMRe2LEn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q7lhafrzqrtgl8coyvaz.png" alt="Image description" width="880" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your SQL query has a semantic error (for example, you reference a column that is ambiguous, or a column does not exist at all), Databend can help you locate it.&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1dD4-XjQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nidzewiab779csb7lcw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1dD4-XjQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nidzewiab779csb7lcw3.png" alt="Image description" width="880" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also get a better experience when coding complex queries:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QDtbhqj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tc93x27ft4x5ld5k1di.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QDtbhqj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7tc93x27ft4x5ld5k1di.png" alt="Image description" width="880" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for JOIN queries and correlated subqueries
&lt;/h2&gt;

&lt;p&gt;The new SQL planner supports JOIN queries (INNER JOIN, OUTER JOIN, CROSS JOIN) and correlated subqueries, and provides a Hash Join algorithm to execute JOIN queries. For more information about how to use JOIN in Databend, go to &lt;a href="https://databend.rs/doc/reference/sql/query-syntax/dml-joinJOIN"&gt;https://databend.rs/doc/reference/sql/query-syntax/dml-joinJOIN&lt;/a&gt; is a very important part of the OLAP query. In traditional star and snowflake schemas, we join dimensional tables with fact tables through the JOIN query to generate the resulting report.TPC-H Benchmark is a set of OLAP query benchmarks developed by the TPC committee to evaluate the OLAP capabilities of database systems. It contains the following eight tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lineitem: Holds product information. &lt;/li&gt;
&lt;li&gt;Orders: Holds order information. &lt;/li&gt;
&lt;li&gt;Customer: Holds customer information. &lt;/li&gt;
&lt;li&gt;Part: Holds parts information.&lt;/li&gt;
&lt;li&gt;Supplier: Holds supplier information.&lt;/li&gt;
&lt;li&gt;Partsupp: Parts-Supplier Relationship Table&lt;/li&gt;
&lt;li&gt;Nation: Holds nation information.&lt;/li&gt;
&lt;li&gt;Region: Holds region information. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TPC-H includes 22 complex queries, corresponding to different business needs. The new SQL planner now supports the Q9 query that calculates the profit amount for a specified year and region using a large number of JOIN calculations:&lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z6b8KdKo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m966qcnoibbdwg17ampi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z6b8KdKo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m966qcnoibbdwg17ampi.png" alt="Image description" width="880" height="1177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Correlated subqueries are also an essential part of SQL for coding complex queries. The Q4 query of TPC-H shows the order delivery status of various priority levels over a period of time and uses a correlated subquery with the EXISTS clause to filter overdue orders:&lt;br&gt;
 &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X6S7vvbb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ve5b94qewf1sdlw8zfgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X6S7vvbb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ve5b94qewf1sdlw8zfgj.png" alt="Image description" width="880" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Brand new architecture
&lt;/h2&gt;

&lt;p&gt;We redesigned the process of SQL parsing for the new SQL planner to support more complex semantic analysis and SQL optimization.After the client sends a SQL statement to the databend-query server, the components in the new SQL planner process the SQL statement in the order shown in the flowchart below before returning the query result to the client:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gYIdgdF4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47xwo2u1rh5r8ugbe2h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gYIdgdF4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47xwo2u1rh5r8ugbe2h8.png" alt="Image description" width="880" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Parse starts to parse a SQL query after receiving it. If a syntax error is found during the parsing, the error information will be directly returned to the client; If the parsing is successful, an AST (Abstract Syntax Tree) for the query will be constructed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parser
&lt;/h3&gt;

&lt;p&gt;To provide more powerful syntax analysis functions and a better development experience, we have developed a DSL (Domain Specific Language) nom-rule based on the nom Parser combinator and rewritten SQL Parser based on this framework.With this framework, we can easily define the syntax for a statement. Taking the CREATE TABLE statement as an example, we can use DSL to describe it as follows:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M09Na1fz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5njpdllc4otyapswf625.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M09Na1fz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5njpdllc4otyapswf625.png" alt="Image description" width="880" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The elegant syntax brings more fun to the work of coding a parser. Try it out if you’re interested.&lt;/p&gt;

&lt;h3&gt;
  
  
  Binder
&lt;/h3&gt;

&lt;p&gt;After the AST is successfully parsed by the Parser, we will semantically analyze it through Binder and generate an initial logical plan. During this process, we perform different types of semantic analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name resolution: Check the validity of the variables referenced in the SQL query by querying the relevant table and column object information in the Databend Catalog and bind the valid variables to their corresponding objects for subsequent analysis.&lt;/li&gt;
&lt;li&gt;Type check: Check the validity of the expression according to the information obtained in the name resolution, and find a proper return type for the expression.&lt;/li&gt;
&lt;li&gt;Subquery unnesting: Extract the subquery from the expression and translate it into relational algebra.&lt;/li&gt;
&lt;li&gt;Grouping check: For queries with aggregate calculations, check whether non-aggregate columns are referenced.
With semantic analysis, we can eliminate most semantic errors and return them to the user during the compilation to provide the best troubleshooting experience.
### Optimizer
After getting the initial logical plan, the optimizer will rewrite and optimize it and, finally, generate an executable physical plan.The new planner introduced a set of Transformer Rule-based optimizer frameworks (Volcano/Cascades). An independent rule can be implemented by defining a relational algebra subtree structure pattern with related transform logic.Take Predicate Push Down as a simple example:&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n5RNmjSr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n347gvsobne36horz74l.png" alt="Image description" width="786" height="606"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We only need to define the pattern of the input plan:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7h24czfz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16l6547c5tojnetr0gaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7h24czfz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16l6547c5tojnetr0gaq.png" alt="Image description" width="880" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then implement a conversion function:&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AhV8ShL8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mbvdwdlwbhks3mrj15z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AhV8ShL8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mbvdwdlwbhks3mrj15z.png" alt="Image description" width="880" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Interpreter
&lt;/h3&gt;

&lt;p&gt;After the physical plan is generated by the Optimizer, we will translate it into an executable pipeline and hand it over to Databend's processor execution framework for calculation. &lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Building a SQL planner from the ground up is a very challenging job, but the redesign and development let us find the most suitable architecture and functionalities for the system. In the future, we will continue to improve and consolidate the new SQL planner on these functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cost-based Optimization (CBO)&lt;/li&gt;
&lt;li&gt;Distributed query optimization&lt;/li&gt;
&lt;li&gt;More optimization rules
Currently, we’re in the middle of migrating to the new SQL planner. We will release an announcement when the migration is complete (around July 2022). Stay tuned. &lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Archiving and Analyzing MySQL Data Using Databend — a Cloud Native Data Warehouse</title>
      <dc:creator>Databend</dc:creator>
      <pubDate>Fri, 08 Jul 2022 10:23:53 +0000</pubDate>
      <link>https://dev.to/databend/archiving-and-analyzing-mysql-data-using-databend-a-cloud-native-data-warehouse-1p3d</link>
      <guid>https://dev.to/databend/archiving-and-analyzing-mysql-data-using-databend-a-cloud-native-data-warehouse-1p3d</guid>
      <description>&lt;h2&gt;
  
  
  Requirement analysis on archiving MySQL data
&lt;/h2&gt;

&lt;p&gt;MySQL is commonly used in OLTP to provide external services with qualified hardware resources, the amount of data provided can often reach TB level. In many scenarios, the storage data has a relatively long TTL. However, data records may lose business significance after being online for a period (examples below).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A certain service is deactivated&lt;/li&gt;
&lt;li&gt;The life cycle of data exceeds service requirements. For example, a service only requires data within nearly 3 months&lt;/li&gt;
&lt;li&gt;Archiving log data&lt;/li&gt;
&lt;li&gt;Merging databases and tables to provide statistical query and analysis services&lt;/li&gt;
&lt;li&gt;Regular backup, archiving, and audit services&lt;/li&gt;
&lt;li&gt;......&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually, archiving schemes are proposed by DBAs, and developers then analyze which data can be archived. The archiving process can be completed with the help of standardization and automated execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common solutions for archiving MySQL data
&lt;/h3&gt;

&lt;p&gt;Current archiving methods are generally divided into two categories: MySQL and MariaDB. The key tool is pt-archiver or obtaining archived data by parsing binlog.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using MySQL for storage and archiving
&lt;/h4&gt;

&lt;p&gt;This is the most common solution: the archiving, or even synchronous backup, of the online production repository is processed by a PC (usually with a large capacity -- about 50T, and large memory, to run instances). It's also possible to build a master/slave server configuration offline to archive PolarDB data and provide offline intranet queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--krH5JFzN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bl0grqnrszgsj9ag8zo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--krH5JFzN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bl0grqnrszgsj9ag8zo2.png" alt="Image description" width="880" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Based on the familiar MySQL environment, it's easy to manage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Highly compatible with the online environment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Archiving environments can be built with large, inexpensive disks&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;In this kind of architecture, binlog is usually turned off in in the archive nodes for cost reduction. There will be a backup in the object storage, and no slave database. Therefore, once the data or the hard disk is damaged, it will take a long time to recover&lt;/li&gt;
&lt;li&gt;There's not enough computing power, and not much ability to extend the computing node either. The data needs to be extracted and put into a big data environment when computing is required&lt;/li&gt;
&lt;li&gt;Large amount of idle CPU and RAM resources&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Using MariaDB for archiving
&lt;/h4&gt;

&lt;p&gt;S3 engine is an experimental feature introduced in MariaDB, it has a better compression capability while retaining the usage habits of MySQL users. The complete archiving process includes writing to InnoDB first, then &lt;code&gt;alter table tb_ name engine=s3;&lt;/code&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Advantages
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;Maintaining MySQL compatibility&lt;/li&gt;
&lt;li&gt; Supporting S3 class object storage&lt;/li&gt;
&lt;li&gt;Supporting highly compressed storage&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  Disadvantages
&lt;/h5&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;S3 engine is write only&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Append writing is not supported,  so conversion to an InnoDB table is required&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The conversion from InnoDB to S3 engine takes a very long time, adding complexity to the process&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Archiving method provided by Databend
&lt;/h3&gt;

&lt;p&gt;So, is there a more elegant solution? Here we recommend Databend, a cloud native data warehouse.&lt;/p&gt;

&lt;h4&gt;
  
  
  Introduction &amp;amp; Architecture
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iDFueCB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmbiz.qpic.cn/mmbiz_png/8H8TlwVsFtr0hDibViaXuLHme1EcN7xhiaVgLV1Cr5MhoHIuIsAuDorcNwkQGrYnyWEhRBiaWYicibsO4mO8Vek2dQJQ/640%3Fwx_fmt%3Dpng%26wxfrom%3D5%26wx_lazy%3D1%26wx_co%3D1" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iDFueCB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://mmbiz.qpic.cn/mmbiz_png/8H8TlwVsFtr0hDibViaXuLHme1EcN7xhiaVgLV1Cr5MhoHIuIsAuDorcNwkQGrYnyWEhRBiaWYicibsO4mO8Vek2dQJQ/640%3Fwx_fmt%3Dpng%26wxfrom%3D5%26wx_lazy%3D1%26wx_co%3D1" alt="图片" width="880" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Databend is a modern, open-source data warehouse developed using Rust. It's completely cloud-oriented, provides extremely fast elastic expansion capabilities, and is committed to creating an on-demand and volume-based Data Cloud product experience.&lt;/p&gt;

&lt;p&gt;Features are as follows：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A star project among open-source, cloud-based data warehouses &lt;/li&gt;
&lt;li&gt;Vectorized execution, pull&amp;amp;push-based processor model&lt;/li&gt;
&lt;li&gt;Real storage/computing separation architecture, high performance, low cost, on-demand use&lt;/li&gt;
&lt;li&gt;Complete database support, compatible with MySQL, Clickhouse protocol, SQL over HTTP, etc.&lt;/li&gt;
&lt;li&gt;Complete transaction, &lt;/li&gt;
&lt;li&gt;Ensured transaction integrity, support time travel, database clone, data share and other functions&lt;/li&gt;
&lt;li&gt;Support multi-tenant read/write and sharing operations on the same data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Databend's design principles：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No Partition&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No index（Auto Index）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support Transaction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Time travel/Data Zero copy clone/Data Share&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enough Performance/Low Cost&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Deployment
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aEJBTYy3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sk09r9maf8cib34qsl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aEJBTYy3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sk09r9maf8cib34qsl8.png" alt="Image description" width="880" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three processing methods are supported，including MySQL, Clickhouse and SQL Over Http.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://databend.rs/doc/deploy"&gt;https://databend.rs/doc/deploy&lt;/a&gt; for installation instructions.&lt;/p&gt;

&lt;p&gt;For more support during installation or usage, please contact us via wechat (wechat number: 82565387).&lt;/p&gt;

&lt;h4&gt;
  
  
  Writing methods
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Insert into
&lt;/h5&gt;

&lt;p&gt;Insert writing operations using JDBC, python and golang are supported. Here is a recommended guidance: &lt;a href="https://databend.rs/doc/develop"&gt;https://databend.rs/doc/develop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's suggested to use Bulk insert to achieve batch writing operations, which has similar usage with MySQL.&lt;/p&gt;

&lt;h5&gt;
  
  
  Streaming load
&lt;/h5&gt;

&lt;p&gt;Please refer to &lt;a href="https://databend.rs/doc/load-data/local"&gt;https://databend.rs/doc/load-data/local&lt;/a&gt; to see more of streaming load.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--83bqAyLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6naqtdidsnkilmritob7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--83bqAyLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6naqtdidsnkilmritob7.png" alt="Image description" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It only takes about 3 minutes to load an 81G file with 200 million rows of data into Databend (As seen in the picture above).&lt;/p&gt;

&lt;p&gt;Besides, Databend now supports reading directly from compressed files. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls ./dataset/*.csv.gz|xargs -P 8 -I{} curl -H "insert_sql:insert into ontime format CSV" -H "skip_header:1"   -H "compression:gzip" -F "upload=@{}" -XPUT http://root:@localhost:8000/v1/streaming_load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It should be noted that the scheme for reading compressed files has not been optimized, it takes about 13 minutes to load the same data using this method. There is much room for performance improvement in the future.&lt;/strong&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Using Stage
&lt;/h5&gt;

&lt;p&gt;Stage can be considered as an online storage manager of Databend, please refer to &lt;a href="https://databend.rs/doc/load-data/stage"&gt;https://databend.rs/doc/load-data/stage&lt;/a&gt; for detailed syntax。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QC58RNzE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uviejqebsq3tapo5skp6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QC58RNzE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uviejqebsq3tapo5skp6.png" alt="Image description" width="880" height="490"&gt;&lt;/a&gt;&lt;br&gt;
The above PPT page shows the process of creating Stage, uploading files, and viewing files online. Files in Stage can be loaded into Databend by "copy into" command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages of archiving MySQL data with Databend
&lt;/h3&gt;

&lt;p&gt;We recommend to use Databend combined with object storage for MySQL data archiving.&lt;/p&gt;

&lt;p&gt;The advantages are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Object storage breaks the limit of storage capacity&lt;/li&gt;
&lt;li&gt;Databend has a relatively high data compression ratio of 10:1, which saves a lot of storage resource&lt;/li&gt;
&lt;li&gt;Databend manages data based on MySQL protocols, so users don't need to change their usage habits&lt;/li&gt;
&lt;li&gt;The storage/computing separated architecture makes it easier to scale up, when facing computing resources insufficiency, and there is no need to worry about the high availability of storage&lt;/li&gt;
&lt;li&gt; Most original MySQL tools can be reused &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Databend now supports object storage: AWS S3, Azure, Alicloud, Tencent Cloud, QingCloud, Kingsoft Cloud and equipments like minio and ceph. Meanwhile the surprising computing ability of Databend supports data computation services if needed.&lt;/p&gt;

&lt;p&gt;People can make better use of cloud resources and obtain qualified performance with a relatively low cost using Databend. &lt;/p&gt;

&lt;p&gt;Please contact us via wechat (wechat number: 82565387) to get more information!&lt;/p&gt;

&lt;h3&gt;
  
  
  About Databend
&lt;/h3&gt;

&lt;p&gt;Databend is an open-source modern data warehouse with elasticity and low cost. It can do real-time data analysis on object-based storage. &lt;/p&gt;

&lt;p&gt;We look forward to your attention and hope to explore the cloud native data warehouse solution,  and create a new generation of open-source data cloud together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databend docs：&lt;a href="https://databend.rs/"&gt;https://databend.rs/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Twitter：&lt;a href="https://twitter.com/Datafuse_Labs"&gt;https://twitter.com/Datafuse_Labs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Slack：&lt;a href="https://datafusecloud.slack.com/"&gt;https://datafusecloud.slack.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;WeChat：Databend&lt;/li&gt;
&lt;li&gt;GitHub ：&lt;a href="https://github.com/datafuselabs/databend"&gt;https://github.com/datafuselabs/databend&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
