# Manage Reverse ETL Table Sources using Rudder CLI

<p>A table source is a <a href="https://www.rudderstack.com/docs/data-pipelines/reverse-etl/" >Reverse ETL source</a> that reads rows from a warehouse table, or from files under an S3 bucket prefix, without a SQL query. Rudder CLI manages table sources as YAML next to your SQL models, accounts, destinations, and connections.</p>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text"><p>Rudder CLI v0.27.0 or later can manage table source resources. The <code>rudder-cli retl-sources validate</code> and <code>rudder-cli retl-sources preview</code> commands support table sources from v0.28.0.</p>
<p>In v0.28.0 and earlier, table sources are experimental: set <code>RUDDERSTACK_CLI_EXPERIMENTAL=true</code> and <code>RUDDERSTACK_X_RETL_TABLE_SUPPORT=true</code> before you validate, apply, or import them. From v0.29.0, table sources are on by default and these two settings have no effect.</p>
</div>
  </div>
</blockquote>

</html>
<p>Use <code>kind: retl-source-table</code>. See the <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-tables/" >Table Source Resources YAML Reference</a> for every field.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="supported-source-types">Supported source types</h2><p>Table sources support PostgreSQL, Amazon Redshift, Snowflake, Google BigQuery, MySQL, Databricks, Trino, and S3.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="before-you-begin">Before you begin</h2><ul>
<li>Install and authenticate <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/installation/#install-rudder-cli" >Rudder CLI</a>.</li>
<li>Have a CLI project directory.</li>
<li>Create an account for the source to read through. Rudder CLI can <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-accounts/" >define BigQuery, PostgreSQL, and Snowflake accounts in the project</a>. For Redshift, MySQL, Databricks, Trino, and S3, create the account in your workspace first and set <code>account_id</code> in the table source spec. Run <code>rudder-cli workspace accounts list</code> to get an existing account&rsquo;s ID.</li>
<li>Use a <a href="https://www.rudderstack.com/docs/access-management/service-access-tokens/#workspace-sat" >workspace-level Service Access Token</a> that can create, edit, and delete Reverse ETL sources.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="define-a-table-source">Define a table source</h2><p>Create a YAML file in your project, for example <code>users-table.yaml</code>:</p>
<div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">version: rudder/v1
kind: retl-source-table
metadata:
  name: users-table
spec:
  id: users-table
  display_name: Users
  account: &#34;#account:prod-pg&#34;
  source_definition: postgres
  primary_key: id
  schema: public
  table: users</code></pre></div>
</div>
<p>To use an account that already exists in your workspace, replace <code>account</code> with <code>account_id</code>.</p>
<p>For an S3 source, remove <code>account</code> and set <code>account_id</code> to an existing S3 account&rsquo;s ID. Set <code>source_definition: s3</code> and replace <code>primary_key</code>, <code>schema</code>, and <code>table</code> with <code>bucket_name</code> and <code>object_prefix</code>. S3 sources have no primary key.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="validate-the-project">Validate the project</h2><div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli validate -l path/to/project</code></pre></div>
</div>
<p>Validation runs locally and doesn&rsquo;t read your warehouse. It catches missing or conflicting fields, a duplicate <code>display_name</code>, and an <code>account</code> reference whose type doesn&rsquo;t match <code>source_definition</code>. See <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-tables/#validation-rules" >Validation rules</a> for the messages.</p>
<p>From v0.28.0, you can check one table source:</p>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli retl-sources validate users-table -l path/to/project</code></pre></div>
</div>
<p>This command takes the source <code>id</code> and runs the same project checks. It doesn&rsquo;t run a query.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="apply-the-table-source">Apply the table source</h2><div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli apply -l path/to/project --dry-run
rudder-cli apply -l path/to/project</code></pre></div>
</div>
<p>Run the dry run first to see the plan. If your project defines the account that the table source references, Rudder CLI creates the account before the source.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="preview-the-rows">Preview the rows</h2><p>Previewing a table source requires Rudder CLI v0.28.0 or later and an account that already exists in the workspace. If your spec uses <code>account: &quot;#account:prod-pg&quot;</code> as in the example above, apply the project first if <code>prod-pg</code> hasn&rsquo;t been created in the workspace. To preview before applying, use <code>account_id</code> for an existing account instead.</p>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli retl-sources preview users-table -l path/to/project --limit 5</code></pre></div>
</div>
<p>For a warehouse table source, Rudder CLI runs a bounded query on your warehouse and prints the rows. For example, it runs <code>select * from &quot;public&quot;.&quot;users&quot; limit 5</code> for the PostgreSQL source above. It quotes schema and table names with double quotes for PostgreSQL, Redshift, Snowflake, and Trino, and backticks for MySQL and Databricks. For BigQuery, it quotes the <code>dataset.table</code> path with backticks. Quoting preserves the case you set in the spec. The result confirms the account can read the table. The command accepts the same <code>--limit</code>, <code>--json</code>, and <code>--interactive</code> options as it does for <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/sql-models/validate/#preview-command" >SQL models</a>.</p>
<p>S3 sources have no query, so preview isn&rsquo;t available for them.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="connect-the-source-to-a-destination">Connect the source to a destination</h2><p>Reference the source from a <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/retl-connections/" >Reverse ETL connection</a> with <code>#retl-source-table:&lt;id&gt;</code>:</p>
<div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">source: &#34;#retl-source-table:users-table&#34;</code></pre></div>
</div>
<p>See the <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-connections/" >Reverse ETL Connections YAML Reference</a> for the rest of the connection fields.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="import-existing-table-sources">Import existing table sources</h2><p>To bring table sources created in the dashboard under Git, run:</p>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">rudder-cli import workspace -l path/to/project</code></pre></div>
</div>
<p>Import writes one file per source under <code>imported/retl/tables/</code>, for example <code>imported/retl/tables/users-table.yaml</code>. Where an account is already managed by the CLI, the file references it with <code>account</code>, and otherwise it sets <code>account_id</code>. See <a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/import-resources/" >How to Import Workspace Resources</a>.</p>
<p><code>rudder-cli import retl-sources</code> imports SQL models only. Use <code>import workspace</code> for table sources.</p>
<p>To link a table source you already wrote locally to the existing remote source instead of creating a duplicate, use <code>import workspace --merge</code>. Enable experimental mode and the separate <code>importMerge</code> flag first:</p>
<div class="rs-code">
  <div class="rs-code__head">bash<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">export RUDDERSTACK_CLI_EXPERIMENTAL=true
export RUDDERSTACK_X_IMPORT_MERGE=true
rudder-cli import workspace -l path/to/project --merge</code></pre></div>
</div>
<p>In v0.28.0 and earlier, also set <code>RUDDERSTACK_X_RETL_TABLE_SUPPORT=true</code>. Keep <code>importMerge</code> enabled when you validate or apply the generated import manifest. Matching compares <code>display_name</code> and <code>account_id</code> exactly, so a spec that uses <code>account</code> isn&rsquo;t linked by <code>--merge</code>. Set <code>account_id</code> in the spec for the merge, then switch to <code>account</code>.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">Rudder CLI requires <code>object_prefix</code> for S3 sources. If an S3 source in your workspace has an empty prefix, the imported spec fails <code>validate</code> until you set one.</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="limits">Limits</h2><ul>
<li><code>source_definition</code> can&rsquo;t change after the source is created.</li>
<li><code>schema</code> and <code>table</code> can&rsquo;t change while a destination is connected to the source.</li>
<li>Table sources use <code>version: rudder/v1</code>.</li>
<li>A Reverse ETL source created from an <a href="https://www.rudderstack.com/docs/activate/audiences/" >Audience</a> isn&rsquo;t supported.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="see-also">See also</h2><ul>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-retl-tables/" >Table Source Resources YAML Reference</a></li>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/sql-models/" >Manage SQL Models using Rudder CLI</a></li>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/retl-connections/" >Manage Reverse ETL Connections using Rudder CLI</a></li>
<li><a href="https://www.rudderstack.com/docs/dev-tools/rudder-cli/yaml-accounts/" >Account Resources YAML Reference</a></li>
</ul>

