惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
量子位
阮一峰的网络日志
阮一峰的网络日志
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hackread – Cybersecurity News, Data Breaches, AI and More
云风的 BLOG
云风的 BLOG
D
DataBreaches.Net
B
Blog
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
B
Blog RSS Feed
H
Help Net Security
The Cloudflare Blog
U
Unit 42

alexwlchan’s notes

What is WS11 1DB? Blocking referrers with Caddy How to type a Spanish question mark (¿) on a Mac Non-overlapping type comparisons and Python type checkers Why does t.Setenv panic after t.Parallel? Use Path.glob() and Path.rglob() for typed versions of glob.glob() Curious clocks and colourful eyes Track which templates are used by Jinja2 Archeologists distinguish between “sherds” and “shards” A single command to test all my changed Go packages Disable the new message animations in WhatsApp Finding high-churn folders that bother Backblaze Always-on SSH agent forwarding with my Git pushes Managing the caption of a photo with AppleScript (but not PhotoKit) Goodhart’s and Campbell’s Law are different Notes from The Cornishman No. 176 (Spring 2026) Notes from The Cornishman No. 176 (Spring 2026) GitUp can’t diff text files larger than 8MB Home Testing the width of a page on a mobile device using Playwright Disable AirPods charging notifications Start a Caddy server in a subprocess during a Python session Filter a list of JSON object based on a list of tags HOME_GET_ME_HOME is a Citymapper Shortcuts action The FileExistsError exception exposes a filename attribute The red-lined bubble snail Why can’t Python connect to example.com? Useful type hints for Python How to truncate the middle of long command output AirPlay Receiver can interfere with Flask apps
What’s the main prefix in SQLite queries?
2026-03-18 · via alexwlchan’s notes

SQLite uses schema prefixes like main and temp to disambiguate between attached databases and connection-specific temporary tables.

I was reading the SQLite database queries in the Tailscale source code today, and tables are referred to inconsistently: the schema creates tables like CREATE TABLE main.TKAChonk, but then queries may use TKAChonk or main.TKAChonk. What’s the difference?

I asked about it in Slack, and Michael and Brad explained what’s going on: it’s possible to attach multiple databases in SQLite, and the main prefix tells SQLite to look in the main database. We only attach one database so the two references are equivalent, but in the past there used to be separate databases and it was useful to disambiguate.

Here’s the relevant part of the SQLite docs:

In SQLite, a database object (a table, index, trigger or view) is identified by the name of the object and the name of the database that it resides in. […]

If no database is specified as part of the object reference, then SQLite searches the main, temp and all attached databases for an object with a matching name. The temp database is searched first, followed by the main database, followed by all attached databases in the order that they were attached. […]

If a schema name is specified as part of an object reference, it must be either “main”, or “temp” or the schema-name of an attached database.

The temp database referred to here is a set of tables created using CREATE TEMP TABLE which are only visible to the database connection that created them.

Example

  1. Create two databases which both have an IntID table, and store a different value in each:

    $ sqlite3 db1.sqlite 'CREATE TABLE IntID (id INTEGER);
                          INSERT INTO IntID (id) VALUES (100);'
    
    $ sqlite3 db2.sqlite 'CREATE TABLE IntID (id INTEGER);
                          INSERT INTO IntID (id) VALUES (200);'
  2. Open one of the databases, attach the other, and then look up the identically-named tables:

    sqlite> ATTACH DATABASE 'db2.sqlite' as db2;
    sqlite> SELECT id FROM IntID;
    100
    sqlite> SELECT id FROM main.IntID;
    100
    sqlite> SELECT id FROM db2.IntID;
    200

    Observe that when I query a bare IntID, SQLite chooses the table from the main database.

  3. Create a temporary table, insert a value, and query IntID again:

    sqlite> CREATE TEMP TABLE IntID (id INTEGER);
    sqlite> INSERT INTO temp.IntID VALUES (300);
    sqlite> SELECT id FROM IntID;
    300
    sqlite> SELECT id FROM main.IntID;
    100
    sqlite> SELECT id FROM temp.IntID;
    300
  4. Try to read the temporary table from a different database connection, and observe that it fails:

    $ sqlite3 db1.sqlite 'SELECT id FROM temp.IntID;'
    Error: in prepare, no such table: temp.IntID
  5. Create a table in the attached database, and observe it can be queried by the bare name, but doesn’t exist in the main database:

    sqlite> CREATE TABLE db2.NewID (id INTEGER);
    sqlite> INSERT INTO db2.NewID (id) VALUES (500);
    sqlite> SELECT id FROM NewID;
    500
    sqlite> SELECT id FROM main.NewID;
    Parse error: no such table: main.NewID
    sqlite> SELECT id FROM db2.NewID;
    500