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

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
Last Week in AI
Last Week in AI
博客园 - 聂微东
Jina AI
Jina AI
月光博客
月光博客
爱范儿
爱范儿
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
T
Tailwind CSS Blog
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
有赞技术团队
有赞技术团队
罗磊的独立博客
小众软件
小众软件
雷峰网
雷峰网
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog

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