<?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: shiyuhang0</title>
    <description>The latest articles on DEV Community by shiyuhang0 (@shiyuhang0).</description>
    <link>https://dev.to/shiyuhang0</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%2F917866%2F97732e98-3e2a-407f-ade9-52ae55a58e03.jpeg</url>
      <title>DEV Community: shiyuhang0</title>
      <link>https://dev.to/shiyuhang0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shiyuhang0"/>
    <language>en</language>
    <item>
      <title>Best PlanetScale alternative: TiDB Serverless</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Sat, 09 Mar 2024 04:20:41 +0000</pubDate>
      <link>https://dev.to/shiyuhang0/the-best-planetscale-alternative-tidb-serverless-3o79</link>
      <guid>https://dev.to/shiyuhang0/the-best-planetscale-alternative-tidb-serverless-3o79</guid>
      <description>&lt;p&gt;PlanetScale is &lt;a href="https://planetscale.com/blog/planetscale-forever"&gt;sunsetting their hobby plan&lt;/a&gt;, it is still a good product if you can afford at least $39 monthly. Otherwise, you may need an alternative as they will sleep all the databases in the hobby plan on April 8th, 2024.&lt;/p&gt;

&lt;p&gt;Neon or supabase sounds good, but you should be very careful because you need to deal with the difference between MySQL and PostgreSQL. Here, I want to introduce &lt;a href="https://tidb.cloud/"&gt;&lt;strong&gt;TiDB Serverless&lt;/strong&gt;&lt;/a&gt; for you, it may be the best choice for you at present.&lt;/p&gt;

&lt;h2&gt;
  
  
  MySQL compatible
&lt;/h2&gt;

&lt;p&gt;PlanetScale is built on top of open-source Vitess, a database clustering system for horizontal scaling of MySQL.&lt;/p&gt;

&lt;p&gt;TiDB Serverless is also fully &lt;strong&gt;MySQL compatible&lt;/strong&gt;. Which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can import the data from PlanetScale to TiDB Serverless smoothly without considering data types and other details.&lt;/li&gt;
&lt;li&gt;You can still use your preferred driver or ORM. TiDB Serverless works with almost all popular MySQL drivers and ORMs.&lt;/li&gt;
&lt;li&gt;You need not change any of your codes except the connection string. The connection to TiDB Serverless enforces TLS, but you need not worry about the TLS cert as TiDB Serverless's cert is signed from &lt;a href="https://letsencrypt.org/"&gt;Let's encrypt&lt;/a&gt;, the same as PlanetScale. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  True Serverless
&lt;/h2&gt;

&lt;p&gt;PlanetScale advertises that they are serverless in the beginning. But it is strange that you need to choose CPU and memory when you start your PlanetScale journey.&lt;/p&gt;

&lt;p&gt;TiDB Serverless provides true serverless for you. It will auto-scale based on your traffic, you only need to pay as you used. It also scales to zero when you don't use it, and It will be ready to serve in seconds when traffic income.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;TiDB Serverless provides the following for you, you can continue to use the branching that you are already familiar with in PlanetScale.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Branching&lt;/li&gt;
&lt;li&gt;Point-in-time Restore&lt;/li&gt;
&lt;li&gt;Vector Search&lt;/li&gt;
&lt;li&gt;Serverless Driver&lt;/li&gt;
&lt;li&gt;Data Service&lt;/li&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more waiting for your exploration. &lt;a href="https://tidb.cloud/"&gt;get started&lt;/a&gt; with TiDB Serverles now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ecosystem
&lt;/h2&gt;

&lt;p&gt;There are lots of integrations in TiDB Serverless. Here is a simple list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;li&gt;Cloudflare&lt;/li&gt;
&lt;li&gt;Dbt&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;Zapier&lt;/li&gt;
&lt;li&gt;Prisma&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find more &lt;a href="https://docs.pingcap.com/tidbcloud/integrate-tidbcloud-with-vercel"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;TiDB Serverless provides a more generous free quota for you. You can enjoy 25G storage and 50M Request Units(worth around $5) for every TiDB Serverless cluster and create up to 5 free clusters.&lt;/p&gt;

&lt;p&gt;I believe you can also enjoy a free journey in TiDB Serverless If you are going to migrate from PlanetScale hobby plan. One thing you need to pay attention to is importing into TiDB Serverless may burn up your free quota if your data is too large. &lt;/p&gt;

&lt;p&gt;According to my test, you may cost around $0.5 for importing one GB of data, which means you may need to pay if your data is larger than 10GB in a database. This may not be accurate, you can import your data to have a try. Don't worry about the fee, TiDB Serverless only limits your traffic instead of charging an extra fee when you exceed the free quota unless you add your credit card and set the spending limit.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As far as I know, TiDB Serverless is optimizing import costs and may charge less for importing in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;p&gt;Want to have a try with TiDB Serverless? I find a &lt;a href="https://medium.com/@minianter/migrating-your-mysql-application-from-planetscale-to-tidb-serverless-a13d90753f37"&gt;migration guide&lt;/a&gt; for you.&lt;/p&gt;

</description>
      <category>planetscale</category>
      <category>serverless</category>
      <category>mysql</category>
      <category>database</category>
    </item>
    <item>
      <title>Connect database from the edge</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Mon, 28 Aug 2023 05:17:56 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/connect-database-at-the-edge-31fj</link>
      <guid>https://dev.to/cloud-ecosystem/connect-database-at-the-edge-31fj</guid>
      <description>&lt;h1&gt;
  
  
  What is edge
&lt;/h1&gt;

&lt;p&gt;Cloud is not a new thing, you needn't worry about the infrastructure on the cloud. For example, if you want to deploy your code in us-west-2. You don't need to buy a machine and configure the network for it. Just pay for an EC2 in us-west-2 and deploy your code on that is all what you need to do. But wait, is this a real &lt;code&gt;Cloud&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Why do I need to buy a EC2 first? Image that you can write and deploy your code directly without a server. Here comes the serverless.&lt;/p&gt;

&lt;p&gt;Based on the serverless, why do I need to pay attention to where the codes are running? Image the code can not only run in us-west-2, but it also runs everywhere - closer to the code triggers. Here comes the edge.&lt;/p&gt;

&lt;p&gt;Cloudflare Workers, Vercel Edge Function, and Netlify Edge Function all provide the edge, which allows you to implement low-latency HTTP API without any other infrastructure. And your code can run closer to your customer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Edge is actually edge computing, which is a networking philosophy focused on bringing computing as close to the source of data as possible in order to reduce latency and bandwidth use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Why it is hard to connect the database at the edge
&lt;/h1&gt;

&lt;p&gt;Before talking about &lt;code&gt;why it is hard&lt;/code&gt;. We'd better learn how the edge works.&lt;/p&gt;

&lt;p&gt;There are two important parts of edge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edge network: Your code runs everywhere, which means your code will be copied into a bunch of nodes of a a global network (edge network).&lt;/li&gt;
&lt;li&gt;Edge runtime: The runtime of your code. For example, Java runs on JVM, javascript runs on Node.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Whether you can connect to the database depends on the edge runtime. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;provider&lt;/th&gt;
&lt;th&gt;edge runtime&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netlify&lt;/td&gt;
&lt;td&gt;Deno&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supbase&lt;/td&gt;
&lt;td&gt;Deno&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cloudflare Workers is written in JavaScript, and executed using the V8 JavaScript engine (from Google Chrome). Deno also provides edge runtime beyond V8 JavaScript engine. The main reason why they don't choose to build on top of node.js lies in the security —— node is not designed to be a sandbox.&lt;/p&gt;

&lt;p&gt;Thus, Cloudflare Workers and Deno don't support all the node.js APIs that are used by the database JavaScript drivers. Instead, their APIs are based on the Web Platform APIs (the APIs generally follow the specifications and should match the implementation in Chrome and Firefox). There is also &lt;a href="https://wintercg.org/"&gt;WinterCG&lt;/a&gt;, which aims to use Web Platform APIs outside of browsers, namely on the server (Deno / Node.js) or edge runtimes (Cloudflare Workers / Deno).&lt;/p&gt;

&lt;p&gt;In other words, we can not use database drivers directly at the edge with Cloudflare Workers or Deno.&lt;/p&gt;

&lt;h1&gt;
  
  
  Efforts from the database providers
&lt;/h1&gt;

&lt;p&gt;Despite insufficient TCP connect support, edge runtimes usually have better support for WebSocket and HTTP. Thus, many cloud database providers use serverless driver or data service to connect to the database.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;provider&lt;/th&gt;
&lt;th&gt;connection method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;planetscale&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/planetscale/database-js"&gt;serverless driver&lt;/a&gt; (HTTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;neon&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/neondatabase/serverless"&gt;serverless driver&lt;/a&gt; (HTTP and Websocket )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tidb serverless&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/tidbcloud/serverless-js"&gt;serverless driver&lt;/a&gt; (HTTP) and data service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supbase&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/supabase/supabase-js"&gt;serverless driver&lt;/a&gt; (HTTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mongo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.mongodb.com/docs/realm/web/"&gt;Realm Web SDK&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Efforts from edge provider
&lt;/h1&gt;

&lt;p&gt;The limitations of Data service or serverless driver is obvious. As a stateless protocol, HTTP performs differently from the traditional TCP way when connected to the database. For example, it is harder to provide &lt;code&gt;transactions&lt;/code&gt; beyond HTTP. &lt;/p&gt;

&lt;p&gt;Thus, I think the better way is to optimize in edge runtime. Let's see what edge providers have done for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deno
&lt;/h2&gt;

&lt;p&gt;Apart from the Web Platform APIs, Deno also provides global APIs to read from files, open TCP sockets, server HTTP, and execute subprocesses, etc. Based on these APIs, there are &lt;a href="https://github.com/denodrivers"&gt;deno drivers&lt;/a&gt;, make it easy to connect to different databases from deno edge runtime.&lt;/p&gt;

&lt;p&gt;What's more, Deno paid a lot to the node compatibility. Deno supports import node and npm modules, you can import and use &lt;a href="https://github.com/sidorares/node-mysql2"&gt;node-mysql2&lt;/a&gt; in deno, but node compatibility is not an easy thing. For example, errors occur when using TLS with node-mysql2. There is still a long way to go for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Workers
&lt;/h2&gt;

&lt;p&gt;Cloudflare Workers also provide &lt;a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/"&gt;node compatibility&lt;/a&gt; with the &lt;code&gt;compatibility_flags = [ "nodejs_compat" ]&lt;/code&gt; configuration. But it doesn't mean you can use all the node APIs. Database drivers still can not be used in Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;Recently, Cloudflare Workers provided socket API for creating TCP sockets. Meanwhile, They add the socket API in &lt;a href="https://github.com/brianc/node-postgres"&gt;node-postgres&lt;/a&gt;, which means you can use this driver in Cloudflare Workers edge runtime directly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article introduces what's edge and why it is hard to connect to the database at the edge. The article also shows you the efforts of database providers and edge providers. Believe that connecting to the database at the edge will no longer be a hassle in the near future.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reference
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/introducing-cloudflare-workers/"&gt;Introducing Cloudflare Workers: Run JavaScript Service Workers at the Edge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.cloudflare.com/cloudflare-workers-unleashed/"&gt;Everyone can now run JavaScript on Cloudflare with Workers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>deno</category>
      <category>database</category>
      <category>cloudflareworkers</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Access TiDB Cloud by Hashicorp Vault</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Tue, 17 Jan 2023 12:27:27 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/using-hashicorp-vault-with-tidb-cloud-noc</link>
      <guid>https://dev.to/cloud-ecosystem/using-hashicorp-vault-with-tidb-cloud-noc</guid>
      <description>&lt;p&gt;Hashicorp Vault can be used to manage database access.&lt;/p&gt;

&lt;p&gt;As a MySQL-compatible Database. TiDB Cloud Serverless Tier can use the MySQL database Secrets Engine as a plugin to generate database credentials dynamically.&lt;/p&gt;

&lt;p&gt;The following figure shows the workflow of using Vault's Dynamic Secrets. This article will demonstrate how to implement the workflow with TiDB Cloud.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Prerequest
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a serverless tier on &lt;a href="https://tidbcloud.com/"&gt;TiDB Cloud console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://developer.hashicorp.com/vault/tutorials/getting-started/getting-started-install#install-vault"&gt;Vault&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Starting the Server
&lt;/h2&gt;

&lt;p&gt;Start a Vault server in development mode (dev server) with root as the root token&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; vault server -dev -dev-root-token-id root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launch a new terminal session and set environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export VAULT_ADDR='http://127.0.0.1:8200'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the Server is Running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.12.2
Build Date      2022-11-23T12:53:46Z
Storage Type    inmem
Cluster Name    vault-cluster-7368793c
Cluster ID      d892d1b1-f975-8bbb-cc73-369d1f48f3ad
HA Enabled      false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure secrets engine (admin role)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Launch a new terminal session and perform the admin role.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Export an environment variable to authenticate with the root token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN=root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the database secrets engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault secrets enable database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure Vault with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write database/config/my-tidb-database \
    plugin_name=mysql-database-plugin \
    connection_url="{{username}}:{{password}}@tcp(gateway01.us-east-1.prod.aws.tidbcloud.com:4000)/" \
    allowed_roles="readonly" \
    username="8kwxfAybggzi***.root" \
    password="***"\
    username_template="8kwxfAybggzi***.{{.RoleName | truncate 8}}_{{random 7}}" \
    tls_ca=@/etc/ssl/cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;A templated &lt;code&gt;connection_url&lt;/code&gt; is required when using root credential rotation.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;username_template&lt;/code&gt; is required because Serverless Tier's username must be started with 8kwxfAybggzi*** and no longer than 32. You can adjust it according to &lt;a href="https://developer.hashicorp.com/vault/docs/concepts/username-templating"&gt;Vault official doc&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;tls_ca&lt;/code&gt; is required because Serverless Tier must be connected with TLS. You need to change the path according to your OS, here is a &lt;a href="https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters#tls-connection-to-serverless-tier"&gt;reference&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configure a role that maps a name in Vault to an SQL statement to execute to create the database credential. I create a user and grant SELECT privileges for read-only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write database/roles/readonly \
    db_name=my-tidb-database \
    creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';" \
    revocation_statements="DROP USER '{{name}}'@'%'; " \
    default_ttl="1h" \
    max_ttl="24h"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a readonly token (admin role)
&lt;/h2&gt;

&lt;p&gt;Now, we need to create a token with a suitable policy for our customers.&lt;/p&gt;

&lt;p&gt;create a readonly policy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ vim readonly.hcl

# Get credentials from the database secrets engine 'readonly' role.
path "database/creds/readonly" {
  capabilities = [ "read" ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;write the policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault policy write readonly-policy readonly.hcl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;create a token with the policy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ ~ vault token create -policy=readonly-policy
Key                  Value
---                  -----
token                hvs.CAESIOMvEUz3UuoUyp_nv5YV5KdyzeSnowZp_UoBAzDh64VdGh4KHGh2cy5FNllaUWFRN1I4VnBNdDAyZFNjTkI1U0Q
token_accessor       4CZMNFZ8PNKUPsLZFQJ2dXvQ
token_duration       768h
token_renewable      true
token_policies       ["default" "readonly-policy"]
identity_policies    []
policies             ["default" "readonly-policy"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the admin can control the permission by giving the readonly token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use dynamic secrets (app role)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Launch a new terminal session and perform the app role.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Export an environment variable to authenticate with the readonly token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN=hvs.CAESIOMvEUz3UuoUyp_nv5YV5KdyzeSnowZp_UoBAzDh64VdGh4KHGh2cy5FNllaUWFRN1I4VnBNdDAyZFNjTkI1U0Q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate a new credential by reading from the /creds endpoint with the readonly role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ vault read database/creds/readonly
Key                Value
---                -----
lease_id           database/creds/readonly/3PNLHrgWyoRLg5QVWblDFiPP
lease_duration     1h
lease_renewable    true
password           DTuVZ-rvWlbMVG8ST***
username           8kwxfAybggzi***.readonly_MVmoAos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can use the password and username to connect to TiDB Cloud. And it will only have read permission.&lt;/p&gt;

&lt;p&gt;Other operators such as creating a role or reading with another role will be denied.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ vault write database/roles/readonly2 \
    db_name=my-tidb-database \
    creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT SELECT ON *.* TO '{{name}}'@'%';" \
    default_ttl="1h" \
    max_ttl="24h"
Error writing data to database/roles/readonly2: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/database/roles/readonly2
Code: 403. Errors:

* 1 error occurred:
    * permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;using root token to create a writeonly role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault write database/roles/writeonly \
    db_name=my-tidb-database \
    creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}';GRANT INSERT ON *.* TO '{{name}}'@'%';" \
    revocation_statements="DROP USER '{{name}}'@'%'; " \
    default_ttl="2m" \
    max_ttl="24h"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;using readonly token to read the writeonly role:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜  ~ vault read database/creds/writeonly
Error reading database/creds/writeonly: Error making API request.

URL: GET http://127.0.0.1:8200/v1/database/creds/writeonly
Code: 403. Errors:

* 1 error occurred:
    * permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  About the leases
&lt;/h2&gt;

&lt;p&gt;The credential generated with the readonly role will expire after the 1h because readonly role's &lt;code&gt;default_ttl&lt;/code&gt; is 1h.&lt;/p&gt;

&lt;p&gt;For admin role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One way to avoid the expiration of credential is to delete the &lt;code&gt;revocation_statements&lt;/code&gt; when admin creates the readonly role.&lt;/li&gt;
&lt;li&gt;admin can revoke the credential at any time.&lt;/li&gt;
&lt;li&gt;admin can renew the credential before it expires. But can't renew more than readonly role's &lt;code&gt;max_ttl&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For app role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app role can ask for the renew permission from admin. Then app role can renew the credential.&lt;/li&gt;
&lt;li&gt;Once the credential expires, app role needs to generate a new credential with the readonly token by &lt;code&gt;vault read database/creds/readonly&lt;/code&gt; command. For example, use a cron job to reload the credential for each ttl.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prospect
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You can also try out TiDB Cloud Dedicated tier with Vault on the similar workflow.&lt;/li&gt;
&lt;li&gt;TiDB Cloud Serverless tier may modify the mechanism of user management in the future. We may also provide a TiDB Cloud plugin in that time. Thus, this doc may not always work.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>vault</category>
      <category>hashicorp</category>
      <category>tidbcloud</category>
      <category>security</category>
    </item>
    <item>
      <title>Use OpenAI and Zapier to perform blog moderation automatically - no code required</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Thu, 22 Dec 2022 09:48:52 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/use-openai-and-zapier-to-perform-blog-moderation-automatically-no-code-required-26hp</link>
      <guid>https://dev.to/cloud-ecosystem/use-openai-and-zapier-to-perform-blog-moderation-automatically-no-code-required-26hp</guid>
      <description>&lt;p&gt;Nowadays, there has been an increasing number of people building their own blogs. However, moderate blogs can be time-consuming and tedious.&lt;/p&gt;

&lt;p&gt;Fortunately, Zapier integrates OpenAI 2 weeks ago. With it, you can automate the moderation process for your blog and ensure that only appropriate content is published.&lt;/p&gt;

&lt;p&gt;Just imagine when you write your blog. Zapier and OpenAI will moderate it automatically. Then the blog will be published automatically if it passes the moderation. Otherwise, you will receive a notification email.&lt;/p&gt;

&lt;p&gt;Let's see how I achieve this - no code required.&lt;/p&gt;

&lt;h1&gt;
  
  
  Set up your blog
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Ignore this step if you have built your own blog site.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't worry if you don't have your own blog site. Here is a simple &lt;a href="https://github.com/tidbcloud/nextjs-prisma-example" rel="noopener noreferrer"&gt;demo&lt;/a&gt; that you can build a blog site in several minutes.&lt;/p&gt;

&lt;p&gt;You can deploy it with Vercel or Netlify to publish your blog site to the internet. I just build it locally for convenience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl05wagw4g8j69es9zb37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl05wagw4g8j69es9zb37.png" alt="Image description" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This demo enables you to write a draft blog and then publish it. All blogs are stored in &lt;a href="https://tidbcloud.com/" rel="noopener noreferrer"&gt;TiDB Cloud Serverless Tier&lt;/a&gt;, a free and MySQL-Compatible HTAP database service.&lt;/p&gt;

&lt;h1&gt;
  
  
  Use OpenAI and Zapier
&lt;/h1&gt;

&lt;p&gt;Here is the Zap named &lt;code&gt;Blog moderation&lt;/code&gt; I build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvajnpsfwkdx5tatvsbjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvajnpsfwkdx5tatvsbjw.png" alt="Image description" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;New Row in TiDB Cloud&lt;/code&gt; will trigger the whole workflow once you create a new draft (a new row will be added in Serverless Tier's &lt;code&gt;post&lt;/code&gt; table)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Check Moderation in OpenAI&lt;/code&gt; will check for hate, hate/threatening, self-harm, sexual, sexual/minors, violence, or violence/graphic content in your post.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Paths&lt;/code&gt; will perform different actions according to the result of moderation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click view you can see the details of the &lt;code&gt;Paths&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmya2dgi4qfegngwqvno9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmya2dgi4qfegngwqvno9.png" alt="Image description" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if your post passed the moderation of OpenAI(Flagged is false), Zapier will perform &lt;code&gt;Update Row in TiDB Cloud&lt;/code&gt;. It will update the &lt;code&gt;published&lt;/code&gt; field in the Serverless tier's &lt;code&gt;post&lt;/code&gt; table so that the draft will be published.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfi79tu3uh3i01bc1vij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfi79tu3uh3i01bc1vij.png" alt="Image description" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if your post doesn't pass the moderation of OpenAI(Flagged is true), Zapier will perform &lt;code&gt;Send Email in Gmail&lt;/code&gt;. It will notify you by email with the moderation result.&lt;/p&gt;

&lt;h1&gt;
  
  
  Try the workflow
&lt;/h1&gt;

&lt;p&gt;Now, let's try it. Click &lt;code&gt;Create draft&lt;/code&gt; on your blog site and create two drafts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faepg4st450r8hkiz05yx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faepg4st450r8hkiz05yx.png" alt="Image description" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsd6stn771qzqs2e7f0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpsd6stn771qzqs2e7f0h.png" alt="Image description" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait for a moment and you will find the first blog has been published, while the second blog is still a draft.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwypae6aeucfc53zu2kj0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwypae6aeucfc53zu2kj0.png" alt="Image description" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl56w55cfiolsg2t2dv6v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl56w55cfiolsg2t2dv6v.png" alt="Image description" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check your email, you will find the notification.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xwnw99g1p22rw1mriyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7xwnw99g1p22rw1mriyf.png" alt="Image description" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;As you can see. OpenAI and Zapier can help you be freed from the repetitive moderation work. And a &lt;a href="https://zapier.com/apps/tidb-cloud/integrations" rel="noopener noreferrer"&gt;Zap template&lt;/a&gt; is on the way to help you build such a workflow. You can also build by yourself, it isn't too difficult.&lt;/p&gt;

&lt;p&gt;Hope the post will help you!&lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>Make Deno MySQL driver works better</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Thu, 01 Dec 2022 11:07:09 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/make-deno-mysql-driver-works-better-31k6</link>
      <guid>https://dev.to/cloud-ecosystem/make-deno-mysql-driver-works-better-31k6</guid>
      <description>&lt;p&gt;&lt;a href="http://deno.land/" rel="noopener noreferrer"&gt;&lt;strong&gt;Deno&lt;/strong&gt;&lt;/a&gt; is a JavaScript and TypeScript runtime similar to Node.js, built on Rust and the V8 JavaScript engine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/denodrivers/mysql" rel="noopener noreferrer"&gt;&lt;strong&gt;Deno MySQL driver&lt;/strong&gt;&lt;/a&gt; (also be called as &lt;code&gt;deno_mysql&lt;/code&gt;) is a library that allows us to connect our deno application to SQL servers similar to MySQL or MariaDB. &lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;deno_mysql&lt;/code&gt; is not fully compatible with mysql protocol, causing it not works with all MySQL versions and some MySQL-compatible database. such as &lt;a href="https://www.pingcap.com/tidb/" rel="noopener noreferrer"&gt;TiDB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I will introduce two compatible issues of the &lt;code&gt;deno_mysql&lt;/code&gt; v2.10.3 and how to solve them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Method Mismatch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html#sect_protocol_connection_phase_auth_method_mismatch" rel="noopener noreferrer"&gt;Authentication Method Mismatch&lt;/a&gt; is a new feature of MySQL 8.0. It is a phase of connection which is used to prevent the downgrade attack of the authentication method. When the client and server use different authentication methods, the server will send an AuthSwitchRequest packet to the client. The client can then choose to use the authentication method that the server supports.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf5sarmn6zuj7iugc3yq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf5sarmn6zuj7iugc3yq.png" alt="Authentication Method Mismatch" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deno_mysql&lt;/code&gt; does not support the authentication method mismatch, so it may fail to connect to MySQL 8.0.x and other MySQL-compatible databases which trigger the mismatch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;switch (authResult) {
    case AuthResult.AuthMoreRequired:
          const adaptedPlugin = (authPlugin as any)[handshakePacket.authPluginName];
          handler = adaptedPlugin;
          break;
    case AuthResult.MethodMismatch:
          // TODO: Negotiate
          throw new Error("Currently cannot support auth method mismatch!");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example code to support it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse AuthSwitchRequest&lt;/li&gt;
&lt;li&gt;Send AuthSwitchResponse with authData&lt;/li&gt;
&lt;li&gt;Do not allow to change the auth plugin more than once
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;case AuthResult.MethodMismatch:
    // 1. parse AuthSwitchRequest
    const authSwitch = parseAuthSwitch(receive.body); 
    // If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is sent and we have to keep using the cipher sent in the init packet. 
    if ( authSwitch.authPluginData === undefined || authSwitch.authPluginData.length === 0 ) { 
        authSwitch.authPluginData = handshakePacket.seed; 
    } 
    // 2. build authData
    let authData; 
    if (password) { 
        authData = auth(authSwitch.authPluginName, password, authSwitch.authPluginData); 
    } else { 
        authData = Uint8Array.from([]); 
    } 
    // 3. send AuthSwitchResponse with authData
    await new SendPacket(authData, receive.header.no + 1).send(this.conn); 
    // 4. do not allow to change the auth plugin more than once
    receive = await this.nextPacket(); 
    const authSwitch2 = parseAuthSwitch(receive.body); 
    if (authSwitch2.authPluginName !== "") { 
        throw new Error( "Do not allow to change the auth plugin more than once!"); 
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Parse AuthSwitchRequest
&lt;/h3&gt;

&lt;p&gt;Once Auth Method Mismatch, server will send a AuthSwitchRequest to client. We need to parse the AuthSwitchRequest follow the payload:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;int&amp;lt;1&amp;gt;&lt;/td&gt;
&lt;td&gt;0xFE (254)&lt;/td&gt;
&lt;td&gt;status tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;string[NUL]&lt;/td&gt;
&lt;td&gt;plugin name&lt;/td&gt;
&lt;td&gt;name of the client authentication plugin to switch to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;string[EOF]&lt;/td&gt;
&lt;td&gt;plugin provided data&lt;/td&gt;
&lt;td&gt;Initial authentication data for that client plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Send AuthSwitchResponse with authData
&lt;/h3&gt;

&lt;p&gt;Client will select the authentication method that the server supports first. deno_mysql supports &lt;code&gt;mysql_native_password&lt;/code&gt; and &lt;code&gt;caching_sha2_password&lt;/code&gt; now. So, it will throw error if server does not support both.&lt;/p&gt;

&lt;p&gt;Then client will encode password and plugin provided data with the authentication method. The result is authData.&lt;/p&gt;

&lt;p&gt;At last, client need to send AuthSwitchResponse to server with the authData.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Change the auth plugin only once
&lt;/h3&gt;

&lt;p&gt;Authentication method mismatch is not allowed to occur more than once. It is not a part of MySQL protocol. &lt;a href="https://github.com/go-sql-driver/mysql" rel="noopener noreferrer"&gt;go-sql-driver&lt;/a&gt; also has the same rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  CLIENT_DEPRECATE_EOF
&lt;/h2&gt;

&lt;p&gt;MySQL deprecated EOF packet since MySQL v5.7.5 and &lt;a href="https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html#gaad8e6e886899e90e820d6c2e0248469d" rel="noopener noreferrer"&gt;CLIENT_DEPRECATE_EOF&lt;/a&gt; was introduced since then.&lt;/p&gt;

&lt;p&gt;Once Client and Server both support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;, server will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send OK packet rather than EOF packet in some cases.&lt;/li&gt;
&lt;li&gt;Not send EOF packet in some cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;deno_mysql&lt;/code&gt; does support the &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; flag. However, there are some problems of the implementation. it causes the &lt;code&gt;deno_mysql&lt;/code&gt; not works with the latest mariadb and other MySQL-compatible databases which support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Deprecated EOF packet without &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; flag
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;deno_mysql&lt;/code&gt; judge the EOF packet by the version of the server. For example, it will expect an EOF packet for the version less than 5.7.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (this.lessThan5_7() || this.isMariaDBAndVersion10_0Or10_1()) {
    // EOF(less than 5.7 or mariadb version is 10.0 or 10.1)
    receive = await this.nextPacket();
    if (receive.type !== PacketType.EOF_Packet) {
        throw new ProtocolError();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is too hacker and not reliable. It may block some MySQL-compatible databases which do not follow the version rule. The best practice is to judge the EOF packet by the &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; flag. Here is 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;if (!(this.capabilities &amp;amp; ServerCapabilities.CLIENT_DEPRECATE_EOF)) {
    receive = await this.nextPacket();
    if (receive.type !== PacketType.EOF_Packet) {
          throw new ProtocolError();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. EOF packet need to be replaced by OK packet
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;deno_mysql&lt;/code&gt; always expect an EOF packet after the result set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!iterator) {
        while (true) {
          receive = await this.nextPacket();        
          if (receive.type === PacketType.EOF_Packet) {
            break;
          } else {
            const row = parseRow(receive.body, fields);
            rows.push(row);
          }
        }
        return { rows, fields };
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EOF packet's header is &lt;code&gt;0xFE&lt;/code&gt;, OK packet's header can be either &lt;code&gt;0x00&lt;/code&gt; or &lt;code&gt;0xFE&lt;/code&gt;. When the client and server both support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;, serve will send OK packet. It will not work once the server send OK packet with &lt;code&gt;0x00&lt;/code&gt; header.&lt;/p&gt;

&lt;p&gt;We need to support OK packet too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        while (true) {
          receive = await this.nextPacket();
          // OK_Packet when CLIENT_DEPRECATE_EOF is set. OK_Packet can be 0xfe or 0x00
          if ( receive.type === PacketType.EOF_Packet ||receive.type === PacketType.OK_Packet ) {
            break;
          } else {
            const row = parseRow(receive.body, fields);
            rows.push(row);
          }
        }
        return { rows, fields };
      }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. More things to do
&lt;/h3&gt;

&lt;p&gt;There are more things to do if &lt;code&gt;deno_mysql&lt;/code&gt; want to support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;. For example, the &lt;a href="https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_command_phase_ps.html" rel="noopener noreferrer"&gt;prepared statement&lt;/a&gt; protocol was introduced in MySQL 4.1. &lt;code&gt;deno_mysql&lt;/code&gt; need to handle the &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; once it support this ability. &lt;/p&gt;

&lt;p&gt;In fact, there is no clear solution to support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;. MariaDB and MySQL also behavior different with &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; flag. It is the main reason that some driver does not support &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt;, for example, the &lt;a href="https://github.com/go-sql-driver/mysql/pull/1153" rel="noopener noreferrer"&gt;go-sql-driver&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;deno_mysql&lt;/code&gt;, fully supporting &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; may be too far ahead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test with TiDB
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.pingcap.com/tidb/" rel="noopener noreferrer"&gt;TiDB&lt;/a&gt; is fully compatible with MySQL protocol. There is also a &lt;a href="https://tidbcloud.com/" rel="noopener noreferrer"&gt;TiDB Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You may find &lt;code&gt;deno_mysql&lt;/code&gt; &amp;lt;= v2.10.3 can't work with TiDB &amp;lt;= v6.3 and TiDB Cloud. The former because of the problem of &lt;code&gt;CLIENT_DEPRECATE_EOF&lt;/code&gt; and the latter because of the unsupported of &lt;code&gt;Authentication Method Mismatch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, &lt;code&gt;deno_mysql&lt;/code&gt; is able to work with all the versions of TiDB and the TiDB Cloud. &lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Manage TiDB Cloud with Terraform</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Tue, 20 Sep 2022 09:46:40 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/manage-tidb-cloud-with-terraform-15id</link>
      <guid>https://dev.to/cloud-ecosystem/manage-tidb-cloud-with-terraform-15id</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Introduce&lt;/li&gt;
&lt;li&gt;Set up&lt;/li&gt;
&lt;li&gt;Create an API key&lt;/li&gt;
&lt;li&gt;Get TiDB Cloud provider&lt;/li&gt;
&lt;li&gt;Config the provider&lt;/li&gt;
&lt;li&gt;Get projectId with project Data Source&lt;/li&gt;
&lt;li&gt;Get cluster spec info with cluster spec Data Source&lt;/li&gt;
&lt;li&gt;Create a dedicated cluster with cluster resource&lt;/li&gt;
&lt;li&gt;Change the dedicated cluster&lt;/li&gt;
&lt;li&gt;Create a backup with backup resource&lt;/li&gt;
&lt;li&gt;Create a restore task with restore resource&lt;/li&gt;
&lt;li&gt;Importing the restore cluster&lt;/li&gt;
&lt;li&gt;Destroy the dedicated cluster&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introduce
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.pingcap.com/tidb-cloud/#"&gt;TiDB Cloud&lt;/a&gt; is a fully-managed Database-as-a-Service (DBaaS) that brings TiDB, an open-source Hybrid Transactional and Analytical Processing (HTAP) database, to your cloud. TiDB Cloud offers an easy way to deploy and manage databases to let you focus on your applications, not the complexities of the databases. You can create TiDB Cloud clusters to quickly build mission-critical applications on Google Cloud Platform (GCP) and Amazon Web Services (AWS).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; is an infrastructure as code tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share. You can then use a consistent workflow to provision and manage all of your infrastructure throughout its lifecycle.&lt;/p&gt;

&lt;p&gt;You can use terraform to manage the TiDB Cloud with the provider &lt;a href="https://github.com/tidbcloud/terraform-provider-tidbcloud"&gt;terraform-provider-tidbcloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article will give an example to show how to use the terraform-provider-tidbcloud&lt;/p&gt;

&lt;h1&gt;
  
  
  Set up
&lt;/h1&gt;

&lt;p&gt;TiDB Cloud provider has released to terraform registry. All you need to do is install terraform (&amp;gt;=1.0).&lt;/p&gt;

&lt;p&gt;For Mac user, you can install it with Homebrew.&lt;/p&gt;

&lt;p&gt;First, install the HashiCorp tap, a repository of all our Homebrew packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew tap hashicorp/tap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, install Terraform with hashicorp/tap/terraform.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;hashicorp/tap/terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started"&gt;official doc&lt;/a&gt; for other installation methods.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create an API key
&lt;/h1&gt;

&lt;p&gt;The TiDB Cloud API uses HTTP Digest Authentication. It protects your private key from being sent over the network. &lt;/p&gt;

&lt;p&gt;However, terraform-provider-tidbcloud does not support managing API key now. So you need to create the API key in the &lt;a href="https://tidbcloud.com/console/clusters"&gt;console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Turn to &lt;a href="https://docs.pingcap.com/tidbcloud/api/v1beta#section/Authentication/API-Key-Management"&gt;TiDB Cloud API doc&lt;/a&gt; for help if you meet any problems.&lt;/p&gt;

&lt;h1&gt;
  
  
  Get TiDB Cloud provider
&lt;/h1&gt;

&lt;p&gt;Create a main.tf file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    tidbcloud = {
      source = "tidbcloud/tidbcloud"
      version = "~&amp;gt; 0.0.1"
    }
  }
  required_version = "&amp;gt;= 1.0.0"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;source&lt;/code&gt; attribute defines the provider which will be downloaded from &lt;a href="https://registry.terraform.io/"&gt;Terraform Registry&lt;/a&gt; by default&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;version&lt;/code&gt; attribute is optional which defines the version of the provider, it will use the latest version by default&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;required_version&lt;/code&gt; is optional which defines the version of the terraform, it will use the latest version by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get the TiDB Cloud provider, execute &lt;code&gt;terraform init&lt;/code&gt;. It will download the provider from terraform registry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of tidbcloud/tidbcloud from the dependency lock file
- Using previously-installed tidbcloud/tidbcloud v0.0.1

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Config the provider
&lt;/h1&gt;

&lt;p&gt;You need to config the provider like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform {
  required_providers {
    tidbcloud = {
      source = "tidbcloud/tidbcloud"
      version = "~&amp;gt; 0.0.1"
    }
  }
  required_version = "&amp;gt;= 1.0.0"
}

provider "tidbcloud" {
  username = "fake_username"
  password = "fake_password"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;username and password are the API key's public key and private key, you can also pass them with the environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export TIDBCLOUD_USERNAME = ${public_key}
export TIDBCLOUD_PASSWORD = ${private_key}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can use the tidbcloud provider!&lt;/p&gt;

&lt;h1&gt;
  
  
  Get projectId with project Data Source
&lt;/h1&gt;

&lt;p&gt;Let us get all the projects by project data source first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;data&lt;/code&gt; block to define the data source of tidbcloud, it consists of the data source type and the data source name. In this example, data source type is &lt;code&gt;tidbcloud_project&lt;/code&gt; and the name is &lt;code&gt;example_project&lt;/code&gt;. The prefix of the type maps to the name of the provider.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;output&lt;/code&gt; block to get the information, and expose information for other Terraform configurations to use. It is similar to return values in programming languages. See &lt;a href="https://www.terraform.io/language/values/outputs"&gt;official doc&lt;/a&gt; for more detail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Besides, you can find all the supported configs for the data source and resource &lt;a href="//./docs"&gt;here&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;terraform {
  required_providers {
    tidbcloud = {
      source = "tidbcloud/tidbcloud"
      version = "~&amp;gt; 0.0.1"
    }
  }
  required_version = "&amp;gt;= 1.0.0"
}

provider "tidbcloud" {
  username = "fake_username"
  password = "fake_password"
}

data "tidbcloud_project" "example_project" {
  page      = 1
  page_size = 10
}

output "projects" {
  value = data.tidbcloud_project.example_project.items
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can apply the configuration with the &lt;code&gt;terraform apply&lt;/code&gt;, you need to type &lt;code&gt;yes&lt;/code&gt; at the confirmation prompt to proceed. Use &lt;code&gt;terraform apply --auto-approve&lt;/code&gt; to skip the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply &lt;span class="nt"&gt;--auto-approve&lt;/span&gt;
data.tidbcloud_project.example_project: Reading...
data.tidbcloud_project.example_project: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;just &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

Changes to Outputs:
  + projects &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
      + &lt;span class="o"&gt;{&lt;/span&gt;
          + cluster_count    &lt;span class="o"&gt;=&lt;/span&gt; 0
          + create_timestamp &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1649154426"&lt;/span&gt;
          + &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089191121286"&lt;/span&gt;
          + name             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test1"&lt;/span&gt;
          + org_id           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189921287"&lt;/span&gt;
          + user_count       &lt;span class="o"&gt;=&lt;/span&gt; 1
        &lt;span class="o"&gt;}&lt;/span&gt;,
      + &lt;span class="o"&gt;{&lt;/span&gt;
          + cluster_count    &lt;span class="o"&gt;=&lt;/span&gt; 1
          + create_timestamp &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1640602740"&lt;/span&gt;
          + &lt;span class="nb"&gt;id&lt;/span&gt;               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189561287"&lt;/span&gt;
          + name             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default project"&lt;/span&gt;
          + org_id           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189921287"&lt;/span&gt;
          + user_count       &lt;span class="o"&gt;=&lt;/span&gt; 1
        &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="o"&gt;]&lt;/span&gt;

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

projects &lt;span class="o"&gt;=&lt;/span&gt; tolist&lt;span class="o"&gt;([&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"cluster_count"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 0
    &lt;span class="s2"&gt;"create_timestamp"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1649154426"&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089191121286"&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test1"&lt;/span&gt;
    &lt;span class="s2"&gt;"org_id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189921287"&lt;/span&gt;
    &lt;span class="s2"&gt;"user_count"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"cluster_count"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1
    &lt;span class="s2"&gt;"create_timestamp"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1640602740"&lt;/span&gt;
    &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189561287"&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default project"&lt;/span&gt;
    &lt;span class="s2"&gt;"org_id"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189921287"&lt;/span&gt;
    &lt;span class="s2"&gt;"user_count"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1
  &lt;span class="o"&gt;}&lt;/span&gt;,
&lt;span class="o"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you get all the available projects, copy one of the id you need. Here we use the default project's ID.&lt;/p&gt;

&lt;h1&gt;
  
  
  Get cluster spec info with cluster spec Data Source
&lt;/h1&gt;

&lt;p&gt;Before creating a TiDB cluster, you may need to get the available config values (providers, regions, etc.) by cluster-spec Data Source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data "tidbcloud_cluster_spec" "example_cluster_spec" {
}

output "cluster_spec" {
  value = data.tidbcloud_cluster_spec.example_cluster_spec.items
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the &lt;code&gt;terraform apply --auto-approve&lt;/code&gt;, we will get all the specifications. Here we show a part of the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "cloud_provider" = "AWS"
    "cluster_type" = "DEDICATED"
    "region" = "eu-central-1"
    "tidb" = tolist([
      {
        "node_quantity_range" = {
          "min" = 1
          "step" = 1
        }
        "node_size" = "2C8G"
      },
      {
        "node_quantity_range" = {
          "min" = 1
          "step" = 1
        }
        "node_size" = "4C16G"
      },
      {
        "node_quantity_range" = {
          "min" = 1
          "step" = 1
        }
        "node_size" = "8C16G"
      },
      {
        "node_quantity_range" = {
          "min" = 1
          "step" = 1
        }
        "node_size" = "16C32G"
      },
    ])
    "tiflash" = tolist([
      {
        "node_quantity_range" = {
          "min" = 0
          "step" = 1
        }
        "node_size" = "8C64G"
        "storage_size_gib_range" = {
          "max" = 2048
          "min" = 500
        }
      },
      {
        "node_quantity_range" = {
          "min" = 0
          "step" = 1
        }
        "node_size" = "16C128G"
        "storage_size_gib_range" = {
          "max" = 2048
          "min" = 500
        }
      },
    ])
    "tikv" = tolist([
      {
        "node_quantity_range" = {
          "min" = 3
          "step" = 3
        }
        "node_size" = "2C8G"
        "storage_size_gib_range" = {
          "max" = 500
          "min" = 200
        }
      },
      {
        "node_quantity_range" = {
          "min" = 3
          "step" = 3
        }
        "node_size" = "4C16G"
        "storage_size_gib_range" = {
          "max" = 2048
          "min" = 200
        }
      },
      {
        "node_quantity_range" = {
          "min" = 3
          "step" = 3
        }
        "node_size" = "8C32G"
        "storage_size_gib_range" = {
          "max" = 4096
          "min" = 500
        }
      },
      {
        "node_quantity_range" = {
          "min" = 3
          "step" = 3
        }
        "node_size" = "8C64G"
        "storage_size_gib_range" = {
          "max" = 4096
          "min" = 500
        }
      },
      {
        "node_quantity_range" = {
          "min" = 3
          "step" = 3
        }
        "node_size" = "16C64G"
        "storage_size_gib_range" = {
          "max" = 4096
          "min" = 500
        }
      },
    ])
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cloud_provider&lt;/code&gt; is the cloud provider on which your TiDB cluster is hosted&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt; is the region of cloud_provider&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node_quantity_range&lt;/code&gt; shows the min quantity of the node and the step if you want to scale the node&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node_size&lt;/code&gt; is the size of the node&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;storage_size_gib_range&lt;/code&gt; shows the min and max storage you can set to the node&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Create a dedicated cluster with cluster resource
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure you have set a Project CIDR on TiDB Cloud console first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, you can create a dedicated cluster with the projectId and the spec info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;resource&lt;/code&gt; block to define the resource of tidbcloud, it consists of the resource type and the resource name. In this example, resource type is &lt;code&gt;tidbcloud_cluster&lt;/code&gt; and the name is &lt;code&gt;example_cluster&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once again, you can find all the supported configs for the data source and resource &lt;a href="//./docs"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here I give an example for tidbcloud_cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "tidbcloud_cluster" "example_cluster" {
  project_id     = "1372813089189561287"
  name           = "firstCluster"
  cluster_type   = "DEDICATED"
  cloud_provider = "AWS"
  region         = "eu-central-1"
  config = {
    root_password = "Your_root_password1."
    port = 4000
    components = {
      tidb = {
        node_size : "8C16G"
        node_quantity : 1
      }
      tikv = {
        node_size : "8C32G"
        storage_size_gib : 500,
        node_quantity : 3
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform apply&lt;/code&gt;, it is not recommended to use &lt;code&gt;terraform apply --auto-approve&lt;/code&gt; when you apply a resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply
data.tidbcloud_project.example_project: Reading...
data.tidbcloud_project.example_project: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;just &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  &lt;span class="c"&gt;# tidbcloud_cluster.example_cluster will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"tidbcloud_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"example_cluster"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + cloud_provider &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;
      + cluster_type   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DEDICATED"&lt;/span&gt;
      + config         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          + components     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
              + tidb &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                  + node_quantity &lt;span class="o"&gt;=&lt;/span&gt; 1
                  + node_size     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8C16G"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
              + tikv &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                  + node_quantity    &lt;span class="o"&gt;=&lt;/span&gt; 3
                  + node_size        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8C32G"&lt;/span&gt;
                  + storage_size_gib &lt;span class="o"&gt;=&lt;/span&gt; 500
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
          + ip_access_list &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="o"&gt;{&lt;/span&gt;
                  + cidr        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
                  + description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"all"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + port           &lt;span class="o"&gt;=&lt;/span&gt; 4000
          + root_password  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your_root_password1."&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"firstCluster"&lt;/span&gt;
      + project_id     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189561287"&lt;/span&gt;
      + region         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;
      + status         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to approve.

  Enter a value: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform will generate an execution plan for you: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can check the diff between the configuration and the state&lt;/li&gt;
&lt;li&gt;You can also see the results of this &lt;code&gt;apply&lt;/code&gt;: it will add a new resource, and no resource will be changed or destroyed&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;known after apply&lt;/code&gt; shows that you will get the value after &lt;code&gt;apply&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything is in your plan, type the &lt;code&gt;yes&lt;/code&gt; to continue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

tidbcloud_cluster.example_cluster: Creating...
tidbcloud_cluster.example_cluster: Creation complete after 1s [id=1379661944630234067]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;terraform show&lt;/code&gt; or &lt;code&gt;terraform state show tidbcloud_cluster.example_cluster&lt;/code&gt; to inspect the state of your resource. The former will show all the states (all the resources and the data source).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform state show tidbcloud_cluster.example_cluster

&lt;span class="c"&gt;# tidbcloud_cluster.example_cluster:&lt;/span&gt;
resource &lt;span class="s2"&gt;"tidbcloud_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"example_cluster"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    cloud_provider &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS"&lt;/span&gt;
    cluster_type   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DEDICATED"&lt;/span&gt;
    config         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        components     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            tidb &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                node_quantity &lt;span class="o"&gt;=&lt;/span&gt; 1
                node_size     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8C16G"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            tikv &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                node_quantity    &lt;span class="o"&gt;=&lt;/span&gt; 3
                node_size        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"8C32G"&lt;/span&gt;
                storage_size_gib &lt;span class="o"&gt;=&lt;/span&gt; 500
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        ip_access_list &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
            &lt;span class="c"&gt;# (1 unchanged element hidden)&lt;/span&gt;
        &lt;span class="o"&gt;]&lt;/span&gt;
        port           &lt;span class="o"&gt;=&lt;/span&gt; 4000
        root_password  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your_root_password1."&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1379661944630234067"&lt;/span&gt;
    name           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"firstCluster"&lt;/span&gt;
    project_id     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1372813089189561287"&lt;/span&gt;
    region         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;
    status         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CREATING"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status of the cluster is CREATING, we need to wait until it changes to &lt;code&gt;AVAILABLE&lt;/code&gt;, it usually takes 10+ minutes.&lt;/p&gt;

&lt;p&gt;Once you want to check the status, execute &lt;code&gt;terraform refresh&lt;/code&gt; to update the state, then use &lt;code&gt;terraform state show tidbcloud_cluster.example_cluster&lt;/code&gt; to check the status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform refresh

tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]

$ terraform state show tidbcloud_cluster.example_cluste

# tidbcloud_cluster.example_cluster:
resource "tidbcloud_cluster" "example_cluster" {
    cloud_provider = "AWS"
    cluster_type   = "DEDICATED"
    config         = {
        components     = {
            tidb = {
                node_quantity = 1
                node_size     = "8C16G"
            }
            tikv = {
                node_quantity    = 3
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        ip_access_list = [
            # (1 unchanged element hidden)
        ]
        port           = 4000
        root_password  = "Your_root_password1."
    }
    id             = "1379661944630234067"
    name           = "firstCluster"
    project_id     = "1372813089189561287"
    region         = "eu-central-1"
    status         = "AVAILABLE"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! Your cluster is available now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Change the dedicated cluster
&lt;/h1&gt;

&lt;p&gt;We can also use terraform to manage the resource. As for cluster resource, we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increase TiFlash component for the dedicated cluster&lt;/li&gt;
&lt;li&gt;Scale the TiDB cluster&lt;/li&gt;
&lt;li&gt;Pause or resume the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Increase TiFlash component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, add tiflash config in components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    components = {
      tidb = {
        node_size : "8C16G"
        node_quantity : 1
      }
      tikv = {
        node_size : "8C32G"
        storage_size_gib : 500
        node_quantity : 3
      }
      tiflash = {
        node_size : "8C64G"
        storage_size_gib : 500
        node_quantity : 1
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, execute &lt;code&gt;terraform apply&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # tidbcloud_cluster.example_cluster will be updated in-place
  ~ resource "tidbcloud_cluster" "example_cluster" {
      ~ config         = {
          ~ components     = {
              + tiflash = {
                  + node_quantity    = 1
                  + node_size        = "8C64G"
                  + storage_size_gib = 500
                }
                # (2 unchanged attributes hidden)
            }
            # (3 unchanged attributes hidden)
        }
        id             = "1379661944630234067"
        name           = "firstCluster"
      ~ status         = "AVAILABLE" -&amp;gt; (known after apply)
        # (4 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the plan, you will find tiflash is added. And one resource will be changed after apply. Type &lt;code&gt;yes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Enter a value: yes

tidbcloud_cluster.example_cluster: Modifying... [id=1379661944630234067]
tidbcloud_cluster.example_cluster: Modifications complete after 2s [id=1379661944630234067]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;terraform state show tidbcloud_cluster.example_cluster&lt;/code&gt; to see the status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform state show tidbcloud_cluster.example_cluster

# tidbcloud_cluster.example_cluster:
resource "tidbcloud_cluster" "example_cluster" {
    cloud_provider = "AWS"
    cluster_type   = "DEDICATED"
    config         = {
        components     = {
            tidb    = {
                node_quantity = 1
                node_size     = "8C16G"
            }
            tiflash = {
                node_quantity    = 1
                node_size        = "8C64G"
                storage_size_gib = 500
            }
            tikv    = {
                node_quantity    = 3
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        ip_access_list = [
            # (1 unchanged element hidden)
        ]
        port           = 4000
        root_password  = "Your_root_password1."
    }
    id             = "1379661944630234067"
    name           = "firstCluster"
    project_id     = "1372813089189561287"
    region         = "eu-central-1"
    status         = "MODIFYING"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;MODIFYING&lt;/code&gt; status shows the cluster is changing now. Wait for a moment, the status will be changed to &lt;code&gt;AVAILABLE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale the TiDB cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the status is &lt;code&gt;AVAILABLE&lt;/code&gt;, let us try to scale the TiDB cluster. &lt;/p&gt;

&lt;p&gt;Add one node for TiDB and TiFlash, TiKV needs to add at least 3 nodes for its step is 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    components = {
      tidb = {
        node_size : "8C16G"
        node_quantity : 2
      }
      tikv = {
        node_size : "8C32G"
        storage_size_gib : 500
        node_quantity : 6
      }
      tiflash = {
        node_size : "8C64G"
        storage_size_gib : 500
        node_quantity : 2
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform apply&lt;/code&gt; and type &lt;code&gt;yes&lt;/code&gt; after check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # tidbcloud_cluster.example_cluster will be updated in-place
  ~ resource "tidbcloud_cluster" "example_cluster" {
      ~ config         = {
          ~ components     = {
              ~ tidb    = {
                  ~ node_quantity = 1 -&amp;gt; 2
                    # (1 unchanged attribute hidden)
                }
              ~ tiflash = {
                  ~ node_quantity    = 1 -&amp;gt; 2
                    # (2 unchanged attributes hidden)
                }
              ~ tikv    = {
                  ~ node_quantity    = 3 -&amp;gt; 6
                    # (2 unchanged attributes hidden)
                }
            }
            # (3 unchanged attributes hidden)
        }
        id             = "1379661944630234067"
        name           = "firstCluster"
      ~ status         = "AVAILABLE" -&amp;gt; (known after apply)
        # (4 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

tidbcloud_cluster.example_cluster: Modifying... [id=1379661944630234067]
tidbcloud_cluster.example_cluster: Modifications complete after 2s [id=1379661944630234067]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the status from &lt;code&gt;MODIFYING&lt;/code&gt; to &lt;code&gt;AVAILABLE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pause or resume the cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cluster can also be paused when the status is &lt;code&gt;AVAILABLE&lt;/code&gt; or be resumed when the status is &lt;code&gt;PAUSED&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set &lt;code&gt;paused = true&lt;/code&gt; to pause the cluster&lt;/li&gt;
&lt;li&gt;set &lt;code&gt;paused = false&lt;/code&gt; to resume the cluster
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config = {
    paused = true
    root_password = "Your_root_password1."
    port          = 4000
    ...
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;execute &lt;code&gt;terraform apply&lt;/code&gt; and type &lt;code&gt;yes&lt;/code&gt; after check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # tidbcloud_cluster.example_cluster will be updated in-place
  ~ resource "tidbcloud_cluster" "example_cluster" {
      ~ config         = {
          + paused         = true
            # (4 unchanged attributes hidden)
        }
        id             = "1379661944630234067"
        name           = "firstCluster"
      ~ status         = "AVAILABLE" -&amp;gt; (known after apply)
        # (4 unchanged attributes hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

tidbcloud_cluster.example_cluster: Modifying... [id=1379661944630234067]
tidbcloud_cluster.example_cluster: Modifications complete after 2s [id=1379661944630234067]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the &lt;code&gt;terraform state show tidbcloud_cluster.example_cluster&lt;/code&gt; to check the status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform state show tidbcloud_cluster.example_cluster

# tidbcloud_cluster.example_cluster:
resource "tidbcloud_cluster" "example_cluster" {
    cloud_provider = "AWS"
    cluster_type   = "DEDICATED"
    config         = {
        components     = {
            tidb    = {
                node_quantity = 2
                node_size     = "8C16G"
            }
            tiflash = {
                node_quantity    = 2
                node_size        = "8C64G"
                storage_size_gib = 500
            }
            tikv    = {
                node_quantity    = 6
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        ip_access_list = [
            # (1 unchanged element hidden)
        ]
        paused         = true
        port           = 4000
        root_password  = "Your_root_password1."
    }
    id             = "1379661944630234067"
    name           = "firstCluster"
    project_id     = "1372813089189561287"
    region         = "eu-central-1"
    status         = "PAUSED"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, resume the cluster by set &lt;code&gt;paused = false&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config = {
    paused = false
    root_password = "Your_root_password1."
    port          = 4000
    ...
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After apply you will find the status turns to &lt;code&gt;RESUMING&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# tidbcloud_cluster.example_cluster:
resource "tidbcloud_cluster" "example_cluster" {
    cloud_provider = "AWS"
    cluster_type   = "DEDICATED"
    config         = {
        components     = {
            tidb    = {
                node_quantity = 2
                node_size     = "8C16G"
            }
            tiflash = {
                node_quantity    = 2
                node_size        = "8C64G"
                storage_size_gib = 500
            }
            tikv    = {
                node_quantity    = 6
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        ip_access_list = [
            # (1 unchanged element hidden)
        ]
        paused         = false
        port           = 4000
        root_password  = "Your_root_password1."
    }
    id             = "1379661944630234067"
    name           = "firstCluster"
    project_id     = "1372813089189561287"
    region         = "eu-central-1"
    status         = "RESUMING"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for a moment, the status will be changed to &lt;code&gt;AVAILABLE&lt;/code&gt; again.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a backup with backup resource
&lt;/h1&gt;

&lt;p&gt;You have created and managed a dedicated cluster with terraform now.&lt;/p&gt;

&lt;p&gt;Next, you will create a backup for the cluster by the backup resource.&lt;/p&gt;

&lt;p&gt;First, copy the following config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "tidbcloud_backup" "example_backup" {
  project_id  = "1372813089189561287"
  cluster_id  = "1379661944630234067"
  name        = "firstBackup"
  description = "create by terraform"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get project_id and cluster_id from the cluster resource like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "tidbcloud_backup" "example_backup" {
  project_id  = tidbcloud_cluster.example_cluster.project_id
  cluster_id  = tidbcloud_cluster.example_cluster.id
  name        = "firstBackup"
  description = "create by terraform"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use the second config and execute &lt;code&gt;terraform apply&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # tidbcloud_backup.example_backup will be created
  + resource "tidbcloud_backup" "example_backup" {
      + cluster_id       = "1379661944630234067"
      + create_timestamp = (known after apply)
      + description      = "create by terraform"
      + id               = (known after apply)
      + name             = "firstBackup"
      + project_id       = "1372813089189561287"
      + size             = (known after apply)
      + status           = (known after apply)
      + type             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; to create a backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Enter a value: yes

tidbcloud_backup.example_backup: Creating...
tidbcloud_backup.example_backup: Creation complete after 2s [id=1350048]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;terraform state show tidbcloud_backup.example_backup&lt;/code&gt; to check the state of the backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform state show tidbcloud_backup.example_backup

# tidbcloud_backup.example_backup:
resource "tidbcloud_backup" "example_backup" {
    cluster_id       = "1379661944630234067"
    create_timestamp = "2022-08-26T07:56:10Z"
    description      = "create by terraform"
    id               = "1350048"
    name             = "firstBackup"
    project_id       = "1372813089189561287"
    size             = "0"
    status           = "PENDING"
    type             = "MANUAL"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for some minutes and use &lt;code&gt;terraform refersh&lt;/code&gt;  to update the states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform refresh  
tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]
tidbcloud_backup.example_backup: Refreshing state... [id=1350048]
$ terraform state show tidbcloud_backup.example_backup
# tidbcloud_backup.example_backup:
resource "tidbcloud_backup" "example_backup" {
    cluster_id       = "1379661944630234067"
    create_timestamp = "2022-08-26T07:56:10Z"
    description      = "create by terraform"
    id               = "1350048"
    name             = "firstBackup"
    project_id       = "1372813089189561287"
    size             = "198775"
    status           = "SUCCESS"
    type             = "MANUAL"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You have create a backup for your cluster. Pay attention that the backup can not be updated.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a restore task with restore resource
&lt;/h1&gt;

&lt;p&gt;You have created a dedicated cluster and have a backup of the cluster.&lt;/p&gt;

&lt;p&gt;Now, it is time to create a restore task by restore resource. With it, you can restore a cluster according to a backup.&lt;/p&gt;

&lt;p&gt;Here is the config for restore resource. Note that you can only restore data from a smaller node size to a larger node size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
resource "tidbcloud_restore" "example_restore" {
  project_id = tidbcloud_cluster.example_cluster.project_id
  backup_id  = tidbcloud_backup.example_backup.id
  name       = "restoreCluster"
  config = {
    root_password = "Your_root_password1."
    port          = 4000
    components = {
      tidb = {
        node_size : "8C16G"
        node_quantity : 2
      }
      tikv = {
        node_size : "8C32G"
        storage_size_gib : 500
        node_quantity : 6
      }
      tiflash = {
        node_size : "8C64G"
        storage_size_gib : 500
        node_quantity : 2
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute &lt;code&gt;terraform apply&lt;/code&gt; and type &lt;code&gt;yes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply
tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]
tidbcloud_backup.example_backup: Refreshing state... [id=1350048]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # tidbcloud_restore.example_restore will be created
  + resource "tidbcloud_restore" "example_restore" {
      + backup_id        = "1350048"
      + cluster          = {
          + id     = (known after apply)
          + name   = (known after apply)
          + status = (known after apply)
        }
      + cluster_id       = (known after apply)
      + config           = {
          + components    = {
              + tidb    = {
                  + node_quantity = 2
                  + node_size     = "8C16G"
                }
              + tiflash = {
                  + node_quantity    = 2
                  + node_size        = "8C64G"
                  + storage_size_gib = 500
                }
              + tikv    = {
                  + node_quantity    = 6
                  + node_size        = "8C32G"
                  + storage_size_gib = 500
                }
            }
          + port          = 4000
          + root_password = "Your_root_password1."
        }
      + create_timestamp = (known after apply)
      + error_message    = (known after apply)
      + id               = (known after apply)
      + name             = "restoreCluster"
      + project_id       = "1372813089189561287"
      + status           = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

tidbcloud_restore.example_restore: Creating...
tidbcloud_restore.example_restore: Creation complete after 1s [id=780114]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the state of the restore task with &lt;code&gt;terraform state show tidbcloud_restore.example_restore&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform state show tidbcloud_restore.example_restore

# tidbcloud_restore.example_restore:
resource "tidbcloud_restore" "example_restore" {
    backup_id        = "1350048"
    cluster          = {
        id     = "1379661944630264072"
        name   = "restoreCluster"
        status = "INITIALIZING"
    }
    cluster_id       = "1379661944630234067"
    config           = {
        components    = {
            tidb    = {
                node_quantity = 2
                node_size     = "8C16G"
            }
            tiflash = {
                node_quantity    = 2
                node_size        = "8C64G"
                storage_size_gib = 500
            }
            tikv    = {
                node_quantity    = 6
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        port          = 4000
        root_password = "Your_root_password1."
    }
    create_timestamp = "2022-08-26T08:16:33Z"
    id               = "780114"
    name             = "restoreCluster"
    project_id       = "1372813089189561287"
    status           = "PENDING"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the restore task's status is &lt;code&gt;PENDING&lt;/code&gt; and the cluster's status is &lt;code&gt;INITIALIZING&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After the cluster is &lt;code&gt;AVAILABLE&lt;/code&gt;, the restore task will be &lt;code&gt;RUNNING&lt;/code&gt; and turn to &lt;code&gt;SUCCESS&lt;/code&gt; at last.&lt;/p&gt;

&lt;p&gt;It is everything ok? No, the bad news is the restored cluster is not managed by terraform. &lt;/p&gt;

&lt;p&gt;Don't worry, we can solve it in the next section.&lt;/p&gt;

&lt;h1&gt;
  
  
  Importing the restore cluster
&lt;/h1&gt;

&lt;p&gt;We can manage a cluster with terraform by import even if it is not created by terraform.&lt;/p&gt;

&lt;p&gt;Let us import the cluster which is created by the restore task in the last section.&lt;/p&gt;

&lt;p&gt;First add a cluster resource like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "tidbcloud_cluster" "restore_cluster1" {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then import the cluster by &lt;code&gt;terraform import tidbcloud_cluster.restore_cluster1 projectId,clusterId&lt;/code&gt;, you can get the projectId and clusterId by restore resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform import tidbcloud_cluster.restore_cluster1 1372813089189561287,1379661944630264072

tidbcloud_cluster.restore_cluster1: Importing from ID "1372813089189561287,1379661944630264072"...
tidbcloud_cluster.restore_cluster1: Import prepared!
  Prepared tidbcloud_cluster for import
tidbcloud_cluster.restore_cluster1: Refreshing state... [id=1379661944630264072]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;terraform state show tidbcloud_cluster.restore_cluster1&lt;/code&gt; to get the state of the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform state show tidbcloud_cluster.restore_cluster1

# tidbcloud_cluster.restore_cluster1:
resource "tidbcloud_cluster" "restore_cluster1" {
    cloud_provider = "AWS"
    cluster_type   = "DEDICATED"
    config         = {
        components = {
            tidb    = {
                node_quantity = 2
                node_size     = "8C16G"
            }
            tiflash = {
                node_quantity    = 2
                node_size        = "8C64G"
                storage_size_gib = 500
            }
            tikv    = {
                node_quantity    = 6
                node_size        = "8C32G"
                storage_size_gib = 500
            }
        }
        port       = 4000
    }
    id             = "1379661944630264072"
    name           = "restoreCluster"
    project_id     = "1372813089189561287"
    region         = "eu-central-1"
    status         = "AVAILABLE"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to manage it, you can copy it to your config file. Remember to delete the status and id, for they are computed by terraform and can not be set in the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "tidbcloud_cluster" "restore_cluster1" {
      cloud_provider = "AWS"
      cluster_type   = "DEDICATED"
      config         = {
          components = {
              tidb    = {
                  node_quantity = 2
                  node_size     = "8C16G"
              }
              tiflash = {
                  node_quantity    = 2
                  node_size        = "8C64G"
                  storage_size_gib = 500
              }
              tikv    = {
                  node_quantity    = 6
                  node_size        = "8C32G"
                  storage_size_gib = 500
              }
          }
          port       = 4000
      }
      name           = "restoreCluster"
      project_id     = "1372813089189561287"
      region         = "eu-central-1"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use &lt;code&gt;terraform fmt&lt;/code&gt; to format your config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform fmt 

main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure the consistency of the config and state, you can execute &lt;code&gt;terraform plan&lt;/code&gt; or &lt;code&gt;terraform apply&lt;/code&gt;. If you see &lt;code&gt;No changes&lt;/code&gt;, the import is successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform apply

tidbcloud_cluster.restore_cluster1: Refreshing state... [id=1379661944630264072]
tidbcloud_cluster.example_cluster: Refreshing state... [id=1379661944630234067]
tidbcloud_backup.example_backup: Refreshing state... [id=1350048]
tidbcloud_restore.example_restore: Refreshing state... [id=780114]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can manage the cluster created by the restore task.&lt;/p&gt;

&lt;h1&gt;
  
  
  Destroy the dedicated cluster
&lt;/h1&gt;

&lt;p&gt;To destroy the resource, you can simply use &lt;code&gt;terraform destroy&lt;/code&gt; and type &lt;code&gt;yes&lt;/code&gt;. Don't worry about the order of deletion, terraform will generate a DAG based on the dependencies automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform destroy

Plan: 0 to add, 0 to change, 4 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

tidbcloud_cluster.restore_cluster1: Destroying... [id=1379661944630264072]
tidbcloud_cluster.restore_cluster1: Destruction complete after 2s
tidbcloud_restore.example_restore: Destroying... [id=780114]
tidbcloud_restore.example_restore: Destruction complete after 0s
tidbcloud_backup.example_backup: Destroying... [id=1350048]
tidbcloud_backup.example_backup: Destruction complete after 2s
tidbcloud_cluster.example_cluster: Destroying... [id=1379661944630234067]
tidbcloud_cluster.example_cluster: Destruction complete after 0s
╷
│ Warning: Unsupported
│ 
│ restore can't be delete
╵

Destroy complete! Resources: 4 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that a warning is appeared for restore can't be deleted.&lt;/p&gt;

&lt;p&gt;If you execute &lt;code&gt;terraform show&lt;/code&gt;, you will find nothing for all the states is cleared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ terraform show

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article use &lt;a href="https://github.com/tidbcloud/terraform-provider-tidbcloud"&gt;terraform-provider-tidbcloud&lt;/a&gt; to manage TiDB Cloud's cluster backup and restore task. The provider's capabilities are limited by the &lt;a href="https://docs.pingcap.com/tidbcloud/api-overview"&gt;TiDB Cloud API (beta)&lt;/a&gt;. Believe that the ability of the provider will be stronger when the API GA.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understand TiSpark pushdown</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Tue, 06 Sep 2022 09:34:13 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/understand-tispark-pushdown-14fm</link>
      <guid>https://dev.to/cloud-ecosystem/understand-tispark-pushdown-14fm</guid>
      <description>&lt;p&gt;Author: shiyuhang&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/pingcap/tispark"&gt;TiSpark&lt;/a&gt; is a thin layer built for running Apache Spark on top of TiKV/TiFlash to answer complex OLAP queries. It supports reading, writing, and deleting from TiKV/TiFlash with the guarantee of transactions.&lt;/p&gt;

&lt;p&gt;In order to speed up reading, TiSpark will push some operators to TiKV or TiFlash. In this article, you will learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is pushdown in Spark&lt;/li&gt;
&lt;li&gt;How Spark implements pushdown&lt;/li&gt;
&lt;li&gt;TiSpark pushdown strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What is pushdown in Spark?
&lt;/h1&gt;

&lt;p&gt;Pushdown is a classic SQL optimization that can speed up SQL queries. It pushes some operators closer to the data source as much as possible to reduce the data that the upper layer needs to process. For example, predicate pushdown will push the where condition, and aggregation pushdown will push the aggregate function.&lt;/p&gt;

&lt;p&gt;So, what is pushdown in Spark? Does it have the same meaning as above?&lt;/p&gt;

&lt;p&gt;First of all, let us see the internal of Spark SQL. The core of Spark SQL is the catalyst, it will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Parse Spark SQL to the unresolved logical plan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply analysis rules and catalog to get the resolved logical plan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply optimization rules to get the optimized logical plan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use planning strategies to get the physical plan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select one of the physical plans based on the cost model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate the executable RDDs and assign them to Spark Core&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In the process of catalyst, Spark SQL will be parsed as a tree. The node of the tree is called TreeNode. Spark has several classes that inherit TreeNode to represent different types of nodes in the logical plan and physical plan. For example, where condition will be parsed as &lt;code&gt;filter&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7A9YHP4N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o5yscdpoj2c9zyxhxdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7A9YHP4N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o5yscdpoj2c9zyxhxdb.png" alt="Image description" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let 's talk about the pushdown in Spark. There are two steps of pushdown in Spark:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pushdown Optimization: first, Spark will push the operators closer to the data source when it optimizes the logical plan.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pushdown to data source: then Spark will push the operators to the data source when it builds the physical plan.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an example, consider the following SQL:&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This SQL will be parsed as a tree in Spark. The filter is where condition, join is the join operator and scan is the data source（here it represents the table A and table B）&lt;/p&gt;

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

&lt;p&gt;After the first step of pushdown, the filter will be closer to the data source to reduce the data which will be processed by join.&lt;/p&gt;

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

&lt;p&gt;Then, the filter may be pushed to data source when building the physical plan. That is to say, Spark need not filter the data anymore.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  How Spark implements pushdown
&lt;/h1&gt;

&lt;p&gt;In the last section, you have learned that there are two steps pushdown in Spark. The first will optimize Spark SQL and the second will push to the data source.&lt;/p&gt;

&lt;p&gt;In this section, you will learn how Spark implements the two pushdowns. The code in this section is based on Spark 3.2.&lt;/p&gt;

&lt;h3&gt;
  
  
  pushdown optimization
&lt;/h3&gt;

&lt;p&gt;pushdown optimization will be applied to the logical plan in the optimizer phase of the catalyst. Take the predicate pushdown as an example.&lt;/p&gt;

&lt;p&gt;The rule of predicate pushdown is called &lt;code&gt;PushDownPredicates&lt;/code&gt; in Spark.&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;object&lt;/span&gt; &lt;span class="n"&gt;PushDownPredicates&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="k"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;PredicateHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="k"&gt;transform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;CombineFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyLocally&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PushPredicateThroughNonJoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyLocally&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PushPredicateThroughJoin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;applyLocally&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule will be recursively applied to the tree with the &lt;code&gt;transform&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For every TreeNode in the plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;CombineFilters will be used to combine where condition. For example, a &amp;gt; 1 and a &amp;gt;2 will be combined to a &amp;gt;2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PushPredicateThroughNonJoin will handle the predicate without join&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PushPredicateThroughJoin will handle the predicate with join&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can refer to the &lt;a href="https://github.com/apache/spark/blob/4f25b3f71238a00508a356591553f2dfa89f8290/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala#L1409"&gt;Spark source code&lt;/a&gt; to see the details of PushPredicateThroughNonJoin and PushPredicateThroughJoin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushdown to data source
&lt;/h3&gt;

&lt;p&gt;Spark may pushdown to the data source when building the physical plan based on the results of the pushdown optimization.&lt;/p&gt;

&lt;p&gt;Whether to pushdown depends on the ability and implementation of the data source. If your data source does not support pushdowns, then you need to tell Spark does not do that.&lt;/p&gt;

&lt;p&gt;Thus, Spark provides some interface for data sources to communicate with Spark. We also take predicate pushdown as an example. Spark provides the following interface for it:&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="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;Evolving&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;interface&lt;/span&gt; &lt;span class="n"&gt;SupportsPushDownFilters&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;ScanBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pushFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pushedFilters&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Filter[] pushFilters(Filter[] filters)：the input is the result of the pushdown optimization and the output is the filters that can't be pushed to data source, which are called &lt;code&gt;postScanFilters&lt;/code&gt; in Spark.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filter[] pushedFilters()：the input is empty and the output is the filters that can be pushed to data source which are called &lt;code&gt;pushedFilters&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A filter can be both the &lt;code&gt;postScanFilters&lt;/code&gt; and the &lt;code&gt;pushedFilters&lt;/code&gt;. In this case, the data source and Spark will perform the filter together.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a maintainer of Spark Data source connector, we can easily control the pushdown with the interface. But, how Spark applies the pushdown rules? There are two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Keep the &lt;code&gt;postScanFilters&lt;/code&gt; by the implements of the interface. Spark will handle them later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle the &lt;code&gt;pushedFilters&lt;/code&gt; with the scan operator. How it is handled depends on the implementation of the data source.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Keep the&lt;/strong&gt; &lt;code&gt;postScanFilters&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The first step occurs in the optimization phase of the catalyst by &lt;code&gt;V2ScanRelationPushDown&lt;/code&gt;. The core code is as follows (simplified):&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;object&lt;/span&gt; &lt;span class="n"&gt;V2ScanRelationPushDown&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="k"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;PredicateHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DataSourceV2Implicits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;

  &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;applyColumnPruning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pushDownAggregates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pushDownFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createScanBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;private&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;pushDownFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;transform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sHolder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ScanBuilderHolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pushedFilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;postScanFiltersWithoutSubquery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PushDownUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pushFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;sHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;normalizedFiltersWithoutSubquery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;filterCondition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;postScanFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduceLeftOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;And&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;filterCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sHolder&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;getOrElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sHolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the pushdown rules are applied in the apply method, including &lt;code&gt;pushDownFilters&lt;/code&gt; which is responsible for predicate pushdown.&lt;/p&gt;

&lt;p&gt;pushDownFilters will get &lt;code&gt;postScanFilters&lt;/code&gt; and &lt;code&gt;pushedFilters&lt;/code&gt; by PushDownUtils and only return postScanFilters for the final logical plan. Spark will do the filters later.&lt;/p&gt;

&lt;p&gt;The PushDownUtils code is as follows:&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;object&lt;/span&gt; &lt;span class="n"&gt;PushDownUtils&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;PredicateHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;pushFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;scanBuilder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ScanBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&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="n"&gt;scanBuilder&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SupportsPushDownFilters&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;postScanFilters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pushFilters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translatedFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="n"&gt;DataSourceStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rebuildExpressionFromFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;translatedFilterToExpr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pushedFilters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;untranslatableExprs&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;postScanFilters&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toSeq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PushDownUtils will match the SupportsPushDownFilters and get pushedFilters and postScanFilters by the implementation of the data source. pushedFilters will be empty once the implementation is empty, which means no predicate will be pushdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle the&lt;/strong&gt; &lt;code&gt;pushedFilters&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let us take JDBC data source as an example:&lt;/p&gt;

&lt;p&gt;The build method in &lt;code&gt;JDBCScanBuilder&lt;/code&gt; will return JDBCScan with the input &lt;code&gt;pushedFilter&lt;/code&gt;.&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="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;JDBCScan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JDBCRelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jdbcOptions&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;finalSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pushedFilter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pushedAggregateList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pushedGroupByCols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;JDBCScan&lt;/code&gt; will call &lt;code&gt;relation.buildScan&lt;/code&gt; in the &lt;code&gt;toV1TableScan&lt;/code&gt; method and return &lt;code&gt;JDBCRDD&lt;/code&gt;&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;case&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;JDBCScan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JDBCRelation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;prunedSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StructType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pushedFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;pushedAggregateColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;groupByColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;V1Scan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;toV1TableScan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseRelation&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SQLContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BaseRelation&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;buildScan&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;RDD&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;Row&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="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;columnList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupByColumns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;prunedSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;toArray&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;pushedAggregateColumn&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildScan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prunedSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pushedFilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupByColumns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="n"&gt;asInstanceOf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filter will be parsed as where condition and saved into filterWhereClause in &lt;code&gt;JDBCRDD&lt;/code&gt;. Then a complete SQL with where condition will request the data sources which are compatible with the MySQL protocol.&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="n"&gt;private&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;jdbc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;JDBCRDD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StructType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;partitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;Partition&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JDBCOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;groupByColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
  &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;RDD&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;InternalRow&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="n"&gt;private&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;filterWhereClause&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;filters&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JDBCRDD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compileFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JdbcDialects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nv"&gt;"($p)"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;mkString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;" AND "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the implementation of JDBC data source will be applied to Spark in the planner phase by DataSourceV2Strategy. The simplified core code is as follows:&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;class&lt;/span&gt; &lt;span class="n"&gt;DataSourceV2Strategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Strategy&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;PredicateHelper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SparkPlan&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;DataSourceV2ScanRelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;V1ScanWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pushed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;output&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;v1Relation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toV1TableScan&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BaseRelation&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sqlContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;rdd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v1Relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildScan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;unsafeRowRDD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DataSourceStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toCatalystRDD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1Relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rdd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;dsScan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RowDataSourceScanExec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toStructType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pushed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;unsafeRowRDD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;v1Relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tableIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;withProjectAndFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dsScan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needsUnsafeConversion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataSourceV2ScanRelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LocalScan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;output&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DataSourceV2ScanRelation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StreamingDataSourceV2Relation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StreamingDataSourceV2Relation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spark will match PhysicalOperation in the strategy. As for the JDBC data source, it will go into the first case because it is a V1Scan. &lt;br&gt;
In the first case, &lt;code&gt;scan.toV1TableScan&lt;/code&gt; is called to get the JDBCRDD which is introduced above. Then &lt;code&gt;RowDataSourceScanExec&lt;/code&gt; will perform the data fetch with the JDBCRDD. Finally, &lt;code&gt;withProjectAndFilter&lt;/code&gt; will put &lt;code&gt;RowDataSourceScanExec&lt;/code&gt; into the entire physical plan. Spark will first execute the scan with filter and then execute the other operators.&lt;/p&gt;
&lt;h1&gt;
  
  
  TiSpark pushdown strategy
&lt;/h1&gt;
&lt;h3&gt;
  
  
  The strategy in TiSpark
&lt;/h3&gt;

&lt;p&gt;TiSpark is a connector of Spark that provides TiKV data source. So, we can push some operators from Spark to TiKV according to the discussion above.&lt;/p&gt;

&lt;p&gt;The question is: what is the strategy of pushdown in TiSpark?&lt;/p&gt;

&lt;p&gt;We need to support pushdown in Spark &amp;gt;= 2.4 and meet the following strategies.&lt;/p&gt;

&lt;p&gt;First, an operator can not be pushed down when the data source can not handle the operator. For predicate, TiKV can not support every expression and data type. So, TiSpark needs to exclude them automatically.&lt;/p&gt;

&lt;p&gt;Second, we may not want to pushdown to reduce the pressure of TiKV. It will help when your Spark resources are abundant and TiKV resources are scarce. TiSpark provides some configs for this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;spark.tispark.plan.allow_agg_pushdown: you can refuse the aggregation pushdown by this config.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;spark.tispark.plan.unsupported_pushdown_exprs: you can specify unsupported expressions to refuse them to pushdown. It will also help you work with the old version of TiKV which may not support some of the expressions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  How TiSpark implements the strategy
&lt;/h3&gt;

&lt;p&gt;Next, we will learn how TiSpark implements the strategy.&lt;/p&gt;

&lt;p&gt;We have introduced the pushdown interface provided by Spark. However, it can not meet the strategy of TiSpark. Here are some questions about the pushdown interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Poor expansion ability: The design of the pushdown interface is not good enough to expand in Data Source API V1 (DSV1). This means it is difficult to support various pushdowns in DSV1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The limited ability of pushdown: DSV2 improves the pushdown interface and solves the expansion issues. But the ability of pushdown is limited. Spark 3.0 only supports predicate pushdown and column prune pushdown. Spark 3.1 add the aggregation pushdown and Spark 3.2 add the limit pushdown.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inflexible pushdown strategy: For example, aggregation pushdown does not support push avg which can be converted as sum/count in DSV2&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TiSpark needs support common pushdowns in every supported spark version. Thus, the pushdown interface is not suitable. What to do next? The answer lies in catalyst extension.&lt;/p&gt;

&lt;p&gt;The catalyst extension is supported after Spark 2.2. We can inject custom rules and strategies into most of the phases of the catalyst. In other words, we can inject TiSpark pushdown strategies to control the pushdown precisely.&lt;/p&gt;

&lt;p&gt;Spark will pushdown to the data source in the planner phase. The corresponding extension interface is injectPlannerStrategy（based on Spark 3.2.1 and TiSpark 3.0.1）&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="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;injectPlannerStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StrategyBuilder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;plannerStrategyBuilders&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TiSpark needs to implement the interface:&lt;/p&gt;

&lt;p&gt;ReflectionUtil will return the &lt;code&gt;TiStrategy&lt;/code&gt; according to the different spark versions by reflection in scala. This can avoid compatibility issues caused by different spark versions.&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="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;injectPlannerStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TiStrategyFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getOrCreateTiContext&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TiStrategyFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getOrCreateTiContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TiContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SparkSession&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sparkSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TiExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateCatalog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sparkSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ReflectionUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newTiStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getOrCreateTiContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sparkSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TiStrategy is the core of pushdowns. It will match the TiDBtable which represents the TiDB data source and then execute the&lt;code&gt;doPlan&lt;/code&gt;. If the match fails, TiSpark will do nothing to avoid affecting other data sources.&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="n"&gt;ase&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TiStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getOrCreateTiContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TiContext&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;sparkSession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SparkSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Strategy&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Logging&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="n"&gt;override&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SparkPlan&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="n"&gt;plan&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;collectFirst&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;DataSourceV2ScanRelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataSourceV2Relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TiDBTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="n"&gt;doPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toSeq&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flatten&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strategies in doPlan imitate the strategies in DataSourceV2Strategy. TiSpark will identify the operators which can be pushed to the data source with pattern match. Then request TiKV based on these operators. Let us take predicate pushdown as an example. TiSpark will match the PhysicalOperation and execute the pruneFilterProject method.&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="n"&gt;private&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;doPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TiDBTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LogicalPlan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SparkPlan&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PhysicalOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;projectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;DataSourceV2ScanRelation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;DataSourceV2Relation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TiDBTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;pruneFilterProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newTiDAGRequest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt; &lt;span class="n"&gt;Nil&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;TiAggregation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;groupingExpressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;aggregateExpressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;resultExpressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;TiAggregationProjectionV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`source`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isValidAggregates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groupingExpressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aggregateExpressions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Nil&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pruneFilterProject method will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Convert Spark Filter expression to TiKV Filter expression with TiExprUtils.isSupportedFilter. TiSpark will also judge if the expression can be pushed in the method. The Filters that can be pushed will be put into &lt;code&gt;pushdownFilters&lt;/code&gt;, and those that cannot be pushed will be put into the &lt;code&gt;residualFilter&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The DAGRequest, which is the parameter in the request to TiKV will be built by filterToDAGRequest. The &lt;code&gt;pushdownFilters&lt;/code&gt; will be put into the DAGRequest. Then, A scan that can get TiKV data will be generated by &lt;code&gt;toCoprocessorRDD&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The scan will be wrapped and executed by FilterExec. Meanwhile, we need to apply the &lt;code&gt;residualFilter&lt;/code&gt; back to the Spark.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;private&lt;/span&gt; &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;pruneFilterProject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;projectList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;NamedExpression&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;filterPredicates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TiDBTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dagRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TiDAGRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;SparkPlan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pushdownFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;residualFilters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;filterPredicates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;partition&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;TiExprUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSupportedFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blocklist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;residualFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;residualFilters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduceLeftOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;catalyst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expressions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;And&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;filterToDAGRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiColumns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pushdownFilters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dagRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="n"&gt;scan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toCoprocessorRDD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projectSeq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dagRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;residualFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;FilterExec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, TiSpark can support most of the pushdown in every spark version (&amp;gt;=2.4).&lt;/p&gt;

&lt;p&gt;So far, TiSpark supports predicate pushdown, aggregation pushdown, limit pushdown, order by pushdown, and projection pushdown. And TiSpark can control whether a specific expression or data type can be pushed down.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;TiSpark support pushdown by catalyst extension which brings several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The increase in code complexity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unstable for we may touch the evolving interface or method in Spark&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We need to be very careful to avoid affecting the original Spark logical&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spark is focusing on the development of DataSource API as well as the pushdown interface. Hope that in the near future DataSource API will be strong enough to meet the needs of TiSpark. At that time, we will be happy to transfer pushdown to DataSource API.&lt;/p&gt;

&lt;h1&gt;
  
  
  Appendix
&lt;/h1&gt;

&lt;p&gt;The support pushdown in TiSpark is as follows&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;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Type&lt;/td&gt;
&lt;td&gt;sum&lt;/td&gt;
&lt;td&gt;count&lt;/td&gt;
&lt;td&gt;avg&lt;/td&gt;
&lt;td&gt;min&lt;/td&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;td&gt;predicate &amp;amp; order by &amp;amp; group by&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BIT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOOLEAN&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TINYINT&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMALLINT&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEDIUMINT&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INTEGER&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BIGINT&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FLOAT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOUBLE&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DECIMAL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DATE&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DATETIME&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TIME&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;？&lt;/td&gt;
&lt;td&gt;？&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YEAR&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CHAR&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VARCHAR&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TINYTEXT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEDIUMTEXT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LONGTEXT&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BINARY&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VARBINARY&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TINYBLOB&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BLOB&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MEDIUMBLOB&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LONGBLOB&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENUM&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SET&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;？&lt;/td&gt;
&lt;td&gt;？&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Pushdown min/max(time) leads to a wrong result&lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;
&lt;li&gt;Pushdown min/max(set) may cause TiKV panic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can judge if an operator is pushdown by &lt;code&gt;explain&lt;/code&gt; in TiSpark. Here is an example:&lt;/p&gt;

&lt;p&gt;1.Create a table in TiDB&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;TABLE&lt;/span&gt; &lt;span class="nv"&gt;`test`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;`t`&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;`id`&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;`id`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Execute Spark SQL with explan&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="n"&gt;spark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"select avg(id) from test.t where id &amp;gt; 10"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;explain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.You will get the execute plan&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="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HashAggregate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;specialsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;specialsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DecimalType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;258&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DecimalType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;specialsum&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;259&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LongType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="o"&gt;+-&lt;/span&gt; &lt;span class="n"&gt;Exchange&lt;/span&gt; &lt;span class="n"&gt;SinglePartition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="o"&gt;+-&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;HashAggregate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;functions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;partial_specialsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;specialsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DecimalType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;258&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DecimalType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;partial_specialsum&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;252&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;259&lt;/span&gt;&lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LongType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
      &lt;span class="o"&gt;+-&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ColumnarToRow&lt;/span&gt;
         &lt;span class="o"&gt;+-&lt;/span&gt; &lt;span class="n"&gt;TiKV&lt;/span&gt; &lt;span class="n"&gt;CoprocessorRDD&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;IndexReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;LONG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;IndexRangeScan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;RangeFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;LONG&lt;/span&gt; &lt;span class="n"&gt;GREATER_THAN&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="k"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[([&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="n"&gt;o_i&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;003&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="n"&gt;o_i&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="mi"&gt;372&lt;/span&gt;&lt;span class="p"&gt;])]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;Aggregates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;LONG&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;LONG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;startTs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;434873744501506049&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Focus on TiKV CoprocessorRDD&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RangeFilter: [[id@LONG GREATER_THAN 10]]&lt;/code&gt;: indicates that id&amp;gt;10 is pushed down&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Aggregates: Sum(id@LONG), Count(id@LONG)&lt;/code&gt;: indicates that Sum and Count are pushed down, they will be rewritten into avg in Spark.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tispark</category>
      <category>spark</category>
      <category>tikv</category>
      <category>pushdown</category>
    </item>
    <item>
      <title>Data Transformation on TiDB Made Easier</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Thu, 01 Sep 2022 04:36:01 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/data-transformation-on-tidb-made-easier-29a0</link>
      <guid>https://dev.to/cloud-ecosystem/data-transformation-on-tidb-made-easier-29a0</guid>
      <description>&lt;p&gt;Author: Wu Qiang&lt;/p&gt;

&lt;p&gt;Data build tool (&lt;a href="https://www.getdbt.com/"&gt;dbt&lt;/a&gt;) is a popular open-source data transformation tool that enables analytics engineers to transform data in their warehouses through SQL statements. The TiDB community recently released the &lt;a href="https://github.com/pingcap/dbt-tidb"&gt;dbt-tidb&lt;/a&gt; adapter to make TiDB, a distributed SQL database to work with dbt. Through the dbt-tidb plug-in, analytics engineers working with TiDB can directly create forms and match data through SQL without having to think about the process of creating tables or views. They can also use Jinja, a dbt template language for writing SQL, test, package management, and other functions, which greatly improves efficiency.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will show you how to use dbt with TiDB. Before you try any of the steps below, make sure the following items are installed: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TiDB 5.3 or later&lt;/li&gt;
&lt;li&gt;dbt 1.01 or later&lt;/li&gt;
&lt;li&gt;dbt-tidb 1.0.0&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;There are several ways you can install dbt and dbt-tidb, In this tutorial, we will use pypi. When you install dbt-tidb, dbt is installed as a dependency. So you only need one command to install both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip install dbt-tidb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also install dbt separately. Please refer to &lt;a href="https://docs.getdbt.com/dbt-cli/install/overview"&gt;How to install dbt&lt;/a&gt; in the dbt documentation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating the project: jaffle shop
&lt;/h1&gt;

&lt;p&gt;dbt-lab provides a project, jaffle_shop, to demonstrate dbt’s functionality. You can get the project directly from GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone https://github.com/dbt-labs/jaffle_shop
$ cd jaffle_shop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All files in the jaffle_shop project directory are structured 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;ubuntu@ubuntu:~/jaffle_shop$ tree
.
├── dbt_project.yml
├── etc
│ ├── dbdiagram_definition.txt
│ └── jaffle_shop_erd.png
├── LICENSE
├── models
│ ├── customers.sql
│ ├── docs.md
│ ├── orders.sql
│ ├── overview.md
│ ├── schema.yml
│ └── staging
│ ├── schema.yml
│ ├── stg_customers.sql
│ ├── stg_orders.sql
│ └── stg_payments.sql
├── README.md
└── seeds
├── raw_customers.csv
├── raw_orders.csv
└── raw_payments.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dbt_project.yml&lt;/strong&gt; is the dbt project configuration file, which holds the project name and database configuration file information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The models directory&lt;/strong&gt; contains the project’s SQL models and table schemas. Note that the data analyst at your company writes this section. To learn more about models, see &lt;a href="https://docs.getdbt.com/docs/building-a-dbt-project/building-models"&gt;dbt Docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The seed directory&lt;/strong&gt; stores CSV files that are dumped from database export tools. For example, TiDB can export the table data into CSV files through &lt;a href="https://docs.pingcap.com/tidb/v4.0/dumpling-overview?_ga=2.200702438.915825749.1661836776-1312519492.1639060796"&gt;Dumpling&lt;/a&gt;. In the jaffle shop project, these CSV files are used as raw data to be processed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Configuring the project
&lt;/h1&gt;

&lt;p&gt;To configure the project:&lt;/p&gt;

&lt;p&gt;1.Complete the global configuration. In the user directory, edit the default global profile, ~/.dbt/profiles.yml to configure the connection with TiDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vi ~/.dbt/profiles.yml
jaffle_shop_tidb:           # project name
target: dev                # target
outputs:
   dev:
      type: tidb           # adapter type
      server: 127.0.0.1
      port: 4000
      schema: analytics    # database name
      username: root
      password: ""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Complete the project configuration.&lt;br&gt;
In the jaffle_shop project directory, enter the project configuration file dbt_project.yml and change the profile field to jaffle_shop_tidb. This configuration allows the project to query from the database as specified in the ~/.dbt/profiles.yml file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat dbt_project.yml
name: 'jaffle_shop'

config-version: 2
version: '0.1'

profile: 'jaffle_shop_tidb'      # note the modification here

model-paths: ["models"]          # model path
seed-paths: ["seeds"]            # seed path
test-paths: ["tests"] 
analysis-paths: ["analysis"]
macro-paths: ["macros"]

target-path: "target"
clean-targets:
- "target"
- "dbt_modules"
- "logs"

require-dbt-version: ["&amp;gt;=1.0.0", "&amp;lt;2.0.0"]

models:
jaffle_shop:
materialized: table # *.sql which in models/ would be materialized to table
staging: 
materialized: view # *.sql which in models/staging/ would bt materialized to view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Verify the configuration.&lt;br&gt;
Run the following command to check whether the database and project configuration are correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dbt debug​​
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Loading CSV files
&lt;/h1&gt;

&lt;p&gt;Now that you have successfully created and configured the project, it’s time to load the CSV data and materialize the CSV as a table in the target database. Note that this step is not generally required for a dbt project because the data items for processing are already in the database.&lt;/p&gt;

&lt;p&gt;1.Load the CSV files by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dbt seed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This displays the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running with dbt=1.0.1
Partial parse save file not found. Starting full parse.
Found 5 models, 20 tests, 0 snapshots, 0 analyses, 172 macros, 0 operations, 3 seed files, 0 sources, 0 exposures, 0 metrics

Concurrency: 1 threads (target='dev')

1 of 3 START seed file analytics.raw_customers.................................. [RUN]
1 of 3 OK loaded seed file analytics.raw_customers.............................. [INSERT 100 in 0.19s]
2 of 3 START seed file analytics.raw_orders..................................... [RUN]
2 of 3 OK loaded seed file analytics.raw_orders................................. [INSERT 99 in 0.14s]
3 of 3 START seed file analytics.raw_payments................................... [RUN]
3 of 3 OK loaded seed file analytics.raw_payments............................... [INSERT 113 in 0.24s]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see in the results, the seed file was started and loaded into three tables: analytics.raw_customers, analytics.raw_orders, and analytics.raw_payments.&lt;/p&gt;

&lt;p&gt;2.Verify the results in TiDB. The show databases command lists the new analytics database that dbt created. The show tables command indicates that there are three tables in the analytics database, corresponding to the ones we created above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql&amp;gt; show databases;
+--------------------+
| Database |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA |
| PERFORMANCE_SCHEMA |
| analytics |
| mysql |
| test |
+--------------------+
6 rows in set (0.00 sec)

mysql&amp;gt; show tables;
+---------------------+
| Tables_in_analytics |
+---------------------+
| raw_customers |
| raw_orders |
| raw_payments |
+---------------------+
3 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Running the dbt project
&lt;/h1&gt;

&lt;p&gt;Now you are ready to run the configured projects and finish the data transformation.&lt;/p&gt;

&lt;p&gt;1.Run the dbt project to finish the data transformation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dbt run
Running with dbt=1.0.1
Unable to do partial parsing because profile has changed
Unable to do partial parsing because a project dependency has been added
Found 5 models, 20 tests, 0 snapshots, 0 analyses, 172 macros, 0 operations, 3 seed files, 0 sources, 0 exposures, 0 metrics
Concurrency: 1 threads (target='dev')
1 of 5 START view model analytics.stg_customers................................. [RUN]
1 of 5 OK created view model analytics.stg_customers............................ [SUCCESS 0 in 0.12s]
2 of 5 START view model analytics.stg_orders.................................... [RUN]
2 of 5 OK created view model analytics.stg_orders............................... [SUCCESS 0 in 0.08s]
3 of 5 START view model analytics.stg_payments.................................. [RUN]
3 of 5 OK created view model analytics.stg_payments............................. [SUCCESS 0 in 0.07s]
4 of 5 START table model analytics.customers.................................... [RUN]
4 of 5 OK created table model analytics.customers............................... [SUCCESS 0 in 0.16s]
5 of 5 START table model analytics.orders....................................... [RUN]
5 of 5 OK created table model analytics.orders.................................. [SUCCESS 0 in 0.12s]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result shows three views (analytics.stg_customers, analytics.stg_orders, and analytics.stg_payments) and two tables (analytics.customers and analytics.orders) were created successfully.&lt;/p&gt;

&lt;p&gt;2.Go to the TiDB database to verify that the operation is successful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql&amp;gt; show tables;
+---------------------+
| Tables_in_analytics |
+---------------------+
| customers     |
| orders        |
| raw_customers |
| raw_orders    |
| raw_payments  |
| stg_customers |
| stg_orders.   |
| stg_payments  |
+---------------------+
8 rows in set (0.00 sec)

mysql&amp;gt; select * from customers;
+-------------+------------+-----------+-------------+-------------------+------------------+-------------------------+
| customer_id | first_name | last_name | first_order | most_recent_order | number_of_orders | customer_lifetime_value |
+-------------+------------+-----------+-------------+-------------------+------------------+-------------------------+
| 1 | Michael   | P. | 2018-01-01 | 2018-02-10 | 2 | 33.0000 |
| 2 | Shawn     | M. | 2018-01-11 | 2018-01-11 | 1 | 23.0000 |
| 3 | Kathleen  | P. | 2018-01-02 | 2018-03-11 | 3 | 65.0000 |
| 4 | Jimmy     | C. | NULL | NULL | NULL | NULL |
| 5 | Katherine | R. | NULL | NULL | NULL | NULL |
| 6 | Sarah     | R. | 2018-02-19 | 2018-02-19 | 1 | 8.0000 |
| 7 | Martin    | M. | 2018-01-14 | 2018-01-14 | 1 | 26.0000 |
| 8 | Frank     | R. | 2018-01-29 | 2018-03-12 | 2 | 45.0000 |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output shows that five more tables or views have been added, and the data in the tables or views has been transformed. Note that only part of the data from the customer table is shown here.&lt;/p&gt;

&lt;h1&gt;
  
  
  Generating visual documents
&lt;/h1&gt;

&lt;p&gt;dbt lets you generate visual documents that display the overall structure of the project and describe all the tables and views. To generate visual documents:&lt;/p&gt;

&lt;p&gt;1.Generate the document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dbt docs generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dbt docs serve 
Running with dbt=1.0.1
Serving docs at 0.0.0.0:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.To access the document view from your browser, navigate to &lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Currently, TiDB supports dbt in TiDB 4.0 and later versions. Earlier versions of TiDB may run into issues when working with dbt. For details, visit the tidb-dbt project on GitHub. To get the most out of dbt, we recommend that you run TiDB 5.3 or later. These versions support all of dbt’s functions. &lt;/p&gt;

&lt;p&gt;If you run into issues, feel free to join our &lt;a href="https://slack.tidb.io/invite?team=tidb-community&amp;amp;channel=everyone&amp;amp;ref=pingcap-blog"&gt;community on Slack&lt;/a&gt; or file an &lt;a href="https://github.com/pingcap/dbt-tidb/issues"&gt;issue&lt;/a&gt; on our repository.&lt;/p&gt;

</description>
      <category>tidb</category>
      <category>dbt</category>
      <category>elt</category>
    </item>
    <item>
      <title>How to upgrade your dbt adapter</title>
      <dc:creator>shiyuhang0</dc:creator>
      <pubDate>Wed, 31 Aug 2022 09:47:03 +0000</pubDate>
      <link>https://dev.to/cloud-ecosystem/upgrade-dbt-adapter-to-support-new-dbt-core-5fln</link>
      <guid>https://dev.to/cloud-ecosystem/upgrade-dbt-adapter-to-support-new-dbt-core-5fln</guid>
      <description>&lt;p&gt;Author: shiyuhang&lt;/p&gt;

&lt;p&gt;Recently, we published a new release of &lt;a href="https://github.com/pingcap/dbt-tidb/issues/2"&gt;dbt-tidb&lt;/a&gt;, an adapter that enables dbt to work with TiDB.&lt;/p&gt;

&lt;p&gt;Here, I will introduce some experience of how to upgrade a dbt adapter to support new dbt-core. For how to build a new adapter, please refer to the &lt;a href="https://docs.getdbt.com/docs/contributing/building-a-new-adapter"&gt;official doc&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Version Rules
&lt;/h1&gt;

&lt;p&gt;dbt-core follows the &lt;a href="https://semver.org/"&gt;Semantic Versioning&lt;/a&gt;. So I suggest you also use &lt;a href="https://semver.org/"&gt;Semantic Versioning&lt;/a&gt; when you publish your own adapter.&lt;/p&gt;

&lt;p&gt;Although dbt-core will try to avoid break changes, compatibility issues still occur. For example, dbt-core v1.2.0 has added &lt;code&gt;retry_connection&lt;/code&gt; method in &lt;code&gt;BaseConnectionManager&lt;/code&gt;to support the retry connection feature. If the adapter implements this method, it can't run on dbt-core v1.1.0.&lt;/p&gt;

&lt;p&gt;In order to avoid compatibility issues, dbt-tidb will follow the version number of dbt-core. For example, dbt-tidb v1.2.0 will only support dbt-core v1.2.0. I suggest you do the same for your adapter.&lt;/p&gt;

&lt;h1&gt;
  
  
  Investigation
&lt;/h1&gt;

&lt;p&gt;When we support the new dbt-core, the first step is to investigate which features need to be supported.&lt;/p&gt;

&lt;p&gt;Here are a few investigation methods you can use in combination:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Visit the &lt;a href="https://docs.getdbt.com/guides/migration/versions"&gt;official Version migration guides&lt;/a&gt;, which may include suggestions for adapter maintainers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check out the dbt-core release note, focusing on new features for adapters. Sort out the new features that need to be implemented.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes, the dbt official will list TODO in the &lt;a href="https://github.com/dbt-labs/dbt-core/discussions/5468"&gt;Github Discussions&lt;/a&gt;. You can upgrade your adapter according to the discussion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refer to other adapters, you can find all the adapters apps in &lt;a href="https://docs.getdbt.com/docs/available-adapters"&gt;Available adapters&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not recommended: You can also do nothing but only upgrade the version of dbt-core at the setup.py. If you are lucky, it will still work for the new dbt-core, but you can't enjoy the new features brought by the new dbt-core.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;According to the investigation methods, dbt-tidb sorts out the features that need to be implemented as follows:&lt;/p&gt;

&lt;p&gt;For dbt-tidb 1.1.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deprecate Python 3.7 and support Python 3.10&lt;/li&gt;
&lt;li&gt;Implement the new adapter testing framework&lt;/li&gt;
&lt;li&gt;Support multiple unique keys in incremental&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For dbt-tidb 1.2.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support Connection retry feature&lt;/li&gt;
&lt;li&gt;Support grants feature&lt;/li&gt;
&lt;li&gt;Support Cross-database macros (some macros under the dbt-util package have been migrated to dbt-core&lt;/li&gt;
&lt;li&gt;Added BaseDocsGenerate and BaseValidateConnection tests&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Test
&lt;/h1&gt;

&lt;p&gt;I would like to introduce how to test before code because I strongly recommend using Test Driven Development(TTD) to develop the dbt adapter. That is: write the test first, and then implement the corresponding feature. If the test passes, the feature is considered to be supported.&lt;/p&gt;

&lt;p&gt;Since dbt-core v1.1.0, dbt has provided a new testing framework for adapter maintainers. The new testing framework is included in dbt-core. With it, you can write your own tests easier. For more detail, you can refer to &lt;a href="https://docs.getdbt.com/docs/contributing/testing-a-new-adapter"&gt;Testing a new adapter.&lt;/a&gt; dbt-tidb v1.1.0 started to use the new testing framework, and introduced the basic package to test the basic features.&lt;/p&gt;

&lt;p&gt;dbt-tidb v1.2.0 adds the following tests based on the new features&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;basic package: add BaseValidateConnection and BaseDocsGenerate for testing connection and document generation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;grant package: add a new grant package for testing grant features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;util: add a new util package for testing Cross-database macros which are migrated from dbt-util&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take &lt;code&gt;grants&lt;/code&gt; as an example, according to the newly added tests in dbt-core v1.2.0. We need to add the following tests for grants:&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;class&lt;/span&gt; &lt;span class="n"&gt;TestModelGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModelGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TestIncrementalGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseIncrementalGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TestSeedGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseSeedGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TestSnapshotGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseSnapshotGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;TestInvalidGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseInvalidGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we use &lt;code&gt;pass&lt;/code&gt; without any implementation, the tests will inherit the default implementation of the test framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Code
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Implement the feature
&lt;/h3&gt;

&lt;p&gt;Now, it is time to implement the features.&lt;/p&gt;

&lt;p&gt;Generally, dbt can be expanded by overriding the default macros or some methods. About which one needed to implement, here is a reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dbt officials may explain how to implement features in the GitHub discussions.&lt;/li&gt;
&lt;li&gt;Refer to dbt-core's implementation of this feature.&lt;/li&gt;
&lt;li&gt;Refer to the implementation of the other adapters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;According to the &lt;a href="https://github.com/dbt-labs/dbt-core/discussions/5468"&gt;Upgrading to dbt-core 1.2.0&lt;/a&gt; in the dbt-core's discussions. We find that grant is mainly implemented by overriding the macros of dbt-core, and the following macros need to be implemented:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;get_show_grant_sql&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Returns auth information, and the return format needs to be grantee (username in some adapter) + privilege_type.&lt;/p&gt;

&lt;p&gt;For TiDB, we query the &lt;code&gt;mysql.tables_priv&lt;/code&gt; table to obtain the auth information. Then filter the given DB and table. Next poll Select, Insert, Update, and Delete. Finally, output in the format of &lt;code&gt;grantee,privilege_type&lt;/code&gt;The corresponding SQL is as follows:&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;tidb__get_show_grant_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relation&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;select&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table_priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'select'&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;privilege_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`User`&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;grantee&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables_priv&lt;/span&gt;  &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nv"&gt;`DB`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.schema}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;`Table_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.identifier}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Table_priv&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="s1"&gt;'%Select%'&lt;/span&gt;
    &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table_priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'insert'&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;privilege_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`User`&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;grantee&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables_priv&lt;/span&gt;  &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nv"&gt;`DB`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.schema}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;`Table_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.identifier}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Table_priv&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="s1"&gt;'%Insert%'&lt;/span&gt;
    &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table_priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'update'&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;privilege_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`User`&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;grantee&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables_priv&lt;/span&gt;  &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nv"&gt;`DB`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.schema}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;`Table_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.identifier}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Table_priv&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="s1"&gt;'%Update%'&lt;/span&gt;
    &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table_priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'delete'&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;privilege_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;`User`&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;grantee&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables_priv&lt;/span&gt;  &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nv"&gt;`DB`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.schema}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;`Table_name`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{{relation.identifier}}'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;Table_priv&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="s1"&gt;'%Delete%'&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endmacro&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;get_grant_sql&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use standard grant SQL to give privileges to multiple users. Note that users need to use double quotes in TiDB. The corresponding SQL is as follows:&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%-&lt;/span&gt; &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;tidb__get_grant_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;privilege&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grantees&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;grant&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;privilege&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;grantees&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%-&lt;/span&gt; &lt;span class="n"&gt;endmacro&lt;/span&gt; &lt;span class="o"&gt;-%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;get_revoke_sql&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use standard revoke SQL to revoke privileges. Double quotes are also needed in TiDB. The corresponding SQL is as follows:&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%-&lt;/span&gt; &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;tidb__get_revoke_sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;privilege&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grantees&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;revoke&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;privilege&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;relation&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;grantees&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="k"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%-&lt;/span&gt; &lt;span class="n"&gt;endmacro&lt;/span&gt; &lt;span class="o"&gt;-%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Find the bug by tests
&lt;/h3&gt;

&lt;p&gt;After implementing the feature, we need to test it with the testing framework. In most cases, you will find the tests fail. Here are some suggestions for you to fix the bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Find the root cause according to the errors shown by dbt and fix it. For example, most of the SQL syntax errors can be found in this way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Read the pr for the feature in dbt-core.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if any macros/methods that have been overridden by your adapter changed. If so, we may need to change the adapter too.&lt;/li&gt;
&lt;li&gt;See if there are other macros/methods added.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refer to the implementation of the other adapters to see if you miss anything.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When we support grants in dbt-tidb. We find that the incremental materialization and snapshot materialization are changed in the dbt-core's pr of grant feature. Unfortunately, dbt-tidb has overridden them before. Thus, we need to update them in dbt-tidb:&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;materialization&lt;/span&gt; &lt;span class="n"&gt;incremental&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tidb'&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c1"&gt;-- other code&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;grant_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'grants'&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="c1"&gt;-- other code&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;should_revoke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;should_revoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing_relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full_refresh_mode&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="n"&gt;apply_grants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target_relation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grant_config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should_revoke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;should_revoke&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="c1"&gt;-- other code&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%-&lt;/span&gt; &lt;span class="n"&gt;endmaterialization&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code first gets the grant configuration and then calls &lt;code&gt;apply_grants&lt;/code&gt; to apply the &lt;code&gt;get_grant_sql&lt;/code&gt; method which is implemented above.&lt;/p&gt;

&lt;p&gt;We also found that the newly added &lt;code&gt;call_dcl_statements&lt;/code&gt; macro needs to be overwritten to convert multiple SQL into some single SQL. Because dbt-tidb does not support multiple SQL yet&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;macro&lt;/span&gt; &lt;span class="n"&gt;tidb__call_dcl_statements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dcl_statement_list&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dcl_statement&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dcl_statement_list&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="k"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'grant_or_revoke'&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="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;dcl_statement&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endcall&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endfor&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endmacro&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix the test
&lt;/h3&gt;

&lt;p&gt;After you fix all the bugs, there may also occur some errors in the test. These errors are not because of your implementation, but for some compatibility issues of the tests. Some tests need to be changed to be compatible with your adapter. You can also find out how to modify the test by &lt;a href="https://docs.getdbt.com/docs/contributing/testing-a-new-adapter#modifying-test-cases"&gt;Testing a new adapter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We also modify the test when implementing grants in dbt-tidb.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BaseInvalidGrants&lt;/code&gt; is the test for the invalid grantee and privilege_type. Obviously, different adapters may throw different errors for the invalid case. So, it is necessary to modify the test. For TiDB:&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;class&lt;/span&gt; &lt;span class="n"&gt;TestInvalidGrantsTiDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseInvalidGrants&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;grantee_does_not_exist_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;"You are not allowed to create a user with GRANT"&lt;/span&gt;

    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;privilege_does_not_exist_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;"Illegal privilege level specified for"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! I think your adapter is ready for release now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using dbt-tidb v1.2.0
&lt;/h1&gt;

&lt;p&gt;Now, let's experience the new adapter. Here we will use dbt-tidb v1.2.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up
&lt;/h3&gt;

&lt;p&gt;1.install dbt-tidb v1.2.0&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;pip3&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;dbt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tidb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Build TiDB. We choose to build TiDB with &lt;a href="https://en.pingcap.com/tidb-cloud/"&gt;TiDB Cloud&lt;/a&gt;'s free trial — developer tier&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign in with your account, and the page will jump to the TiDB Cloud console.&lt;/li&gt;
&lt;li&gt;Click Create Cluster button and you will jump to the creation page.&lt;/li&gt;
&lt;li&gt;Click Create on the creation page without any change to get a developer tier and you will go back to the TiDB Cloud console.&lt;/li&gt;
&lt;li&gt;Wait for the cluster to be ready. Then you can click Connect button to get the user and host.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AJX9kU_q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://tidb-blog.oss-cn-beijing.aliyuncs.com/media/unnamed-1661938484638.png" alt="" width="800" height="206"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;3.Download &lt;a href="https://github.com/dbt-labs/jaffle_shop"&gt;jaffle_shop&lt;/a&gt; on Github. It is a dbt project for testing maintained by the official&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="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dbt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;labs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jaffle_shop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.Add&lt;code&gt;profiles.yml&lt;/code&gt; in &lt;code&gt;~/.dbt&lt;/code&gt;. It configures the connection information&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="n"&gt;jaffle_shop_tidb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                        &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;                             
  &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt;                         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateway01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;southeast&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tidbcloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="k"&gt;host&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;                         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
      &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;                       &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;     &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fake_password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.Change the dbt_project.yml in jaffle_shop, you only need to change the &lt;code&gt;profile&lt;/code&gt;&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="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'jaffle_shop_tidb'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6.Execute &lt;code&gt;dbt debug&lt;/code&gt; in the jaffle_shop to check your configs&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="n"&gt;dbt&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connection Retry Feature
&lt;/h3&gt;

&lt;p&gt;Run, build and test in dbt may generate hundreds of independent connections. A single connection timeout due to networking may fail the entire project. Therefore, dbt-tidb adds a retry feature to solve the temporary connection timeout problem.&lt;/p&gt;

&lt;p&gt;Connection Retry example&lt;/p&gt;

&lt;p&gt;1.Add retries config in profile.yml, it determines the retry times. We use an invalid user to simulate connection failure.&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="n"&gt;jaffle_shop_tidb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                        &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt;                             
  &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt;                         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateway01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;southeast&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tidbcloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="k"&gt;host&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;                         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;
      &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;                       &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invaild_user&lt;/span&gt;     &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;TiDB&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
      &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fake_password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;         &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Execute dbt debug, you will find the Database Error&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dbt&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;
&lt;span class="k"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gateway01&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;southeast&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tidbcloud&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
  &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;
  &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;None&lt;/span&gt;
  &lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;
  &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invaild_user&lt;/span&gt;
  &lt;span class="k"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;dbt&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;unable&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;connect&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;specified&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="k"&gt;database&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;following&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;
  &lt;span class="mi"&gt;1045&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;Access&lt;/span&gt; &lt;span class="n"&gt;denied&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.invaild_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.123.88'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Check the dbt log and you will find three times of retry before throw error.&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="n"&gt;dbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;875482&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Got&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;attempting&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Retrying&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;1045&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;Access&lt;/span&gt; &lt;span class="n"&gt;denied&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.invaild_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.123.88'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;321733&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Got&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;attempting&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Retrying&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;1045&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;Access&lt;/span&gt; &lt;span class="n"&gt;denied&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.invaild_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.123.88'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;703960&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Got&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;retryable&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="n"&gt;attempting&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="k"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;attempts&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Retrying&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="mi"&gt;1045&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;Access&lt;/span&gt; &lt;span class="n"&gt;denied&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.invaild_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.123.88'&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;YES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;069883&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;MainThread&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;tidb&lt;/span&gt; &lt;span class="n"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Grants Feature
&lt;/h3&gt;

&lt;p&gt;In dbt-tidb, you can grant table privileges to the given user with grants.&lt;/p&gt;

&lt;p&gt;Now, you can add grants config in the model, seed, and snapshots. If you configure it under dbt_project.yml, all resources in the project will take effect. Of course, you can also configure it with SQL or YAML for a specific resource, which will override the config in dbt_project.yml. Pay attention that grants do not support creating users, you need to create the users in TiDB first.&lt;/p&gt;

&lt;p&gt;Grants Example&lt;/p&gt;

&lt;p&gt;1.Create users in TiDB, note that the user name in the dev tier contains a prefix&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;USER&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user1'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user2'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user3'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'%'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Add grants config in dbt_project.yml&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="n"&gt;seeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;grants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user2'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user3'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Execute dbt seed&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;dbt&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;  &lt;span class="n"&gt;Concurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'dev'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_customers&lt;/span&gt; &lt;span class="p"&gt;......................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;OK&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_customers&lt;/span&gt; &lt;span class="p"&gt;..................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_orders&lt;/span&gt; &lt;span class="p"&gt;.........................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;  &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;OK&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_orders&lt;/span&gt; &lt;span class="p"&gt;.....................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_payments&lt;/span&gt; &lt;span class="p"&gt;.......................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;OK&lt;/span&gt; &lt;span class="n"&gt;loaded&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;raw_payments&lt;/span&gt; &lt;span class="p"&gt;...................................&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;  &lt;span class="n"&gt;Finished&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;seeds&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;hours&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;  &lt;span class="n"&gt;Completed&lt;/span&gt; &lt;span class="n"&gt;successfully&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;  &lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;WARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;SKIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;TOTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.Query the result in TiDB&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="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables_priv&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;User&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'41y7Jq2g5sBr2ia.user3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;------+------+-----------------------+---------------+---------+---------------------+---------------+---------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Host&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;User&lt;/span&gt;                  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Table_name&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Grantor&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Timestamp&lt;/span&gt;           &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Table_priv&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Column_priv&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;------+------+-----------------------+---------------+---------+---------------------+---------------+---------------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_customers&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_customers&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_customers&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_orders&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_orders&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_orders&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_payments&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Insert&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_payments&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Select&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="n"&gt;y7Jq2g5sBr2ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;raw_payments&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;Insert&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;------+------+-----------------------+---------------+---------+---------------------+---------------+---------------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The grants are just as what we have set:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;41y7Jq2g5sBr2ia.user1 has Select and Insert priv&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;41y7Jq2g5sBr2ia.user2 has Select priv&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;41y7Jq2g5sBr2ia.user3 has Insert pri&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cross-database macros
&lt;/h3&gt;

&lt;p&gt;One of the strengths of dbt is that it can reuse macros (like functions). dbt-util is an official tool repository. We can reuse its macros by importing dbt-util.&lt;/p&gt;

&lt;p&gt;Cross-database macros have been migrated from dbt-util to dbt-core in v1.2.0, which means you can use them directly without importing dbt-util.&lt;/p&gt;

&lt;p&gt;dbt-tidb also supports using Cross-database macros directly. You can use the following macros in dbt-tidb.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;bool_or&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cast_bool_to_text&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dateadd&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;datediff&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;date_trunc&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;hash&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;safe_cast&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;split_part&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;last_day&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cast_bool_to_text&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;concat&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;escape_single_quotes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;except&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;intersect&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;length&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;position&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;replace&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;right&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read our &lt;a href="https://github.com/pingcap/dbt-tidb"&gt;doc&lt;/a&gt; to see how to use them. Here we will take &lt;code&gt;datediff&lt;/code&gt; as an example&lt;/p&gt;

&lt;p&gt;1.Execute dbt seed to generate raw_orders table&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="n"&gt;dbt&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Create datediff.sql in the models directory. It will calculate the days between order_date and 2018-01-01 in raw_orders&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;with&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;

    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="k"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'raw_orders'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;datediff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;"'2018-01-01'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"order_date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'day'&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;datediff&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Execute dbt run -s datediff&lt;/p&gt;

&lt;p&gt;4.Query the result in TiDB, you will get the 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="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datediff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;------+---------+------------+----------------+----------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;datediff&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;------+---------+------------+----------------+----------+&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;94&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;54&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;88&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;    &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;53&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;99&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;59&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;84&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;39&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;71&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;returned&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;54&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt;   &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;      &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This article takes dbt-tidb as an example to share the process and skills of upgrading dbt adapter. Then introduce how to use the new features in dbt-tidb v1.2.0. Hope it will help you with the upgrade of your adapter.&lt;/p&gt;

&lt;p&gt;Welcome to contribute &lt;a href="https://github.com/pingcap/dbt-tidb"&gt;dbt-tidb&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dbt</category>
      <category>tidb</category>
      <category>tidbcloud</category>
    </item>
  </channel>
</rss>
