# Ghost completions > Gray inline text that finishes the part of a query the editor can already predict (a join condition, a table alias, a condition you wrote before) so you press Tab instead of typing it. Where the dropdown shows every match, ghost text shows one suggestion, and it disappears the moment you keep typing. Source: https://docs.querylab.io/writing-sql/ghost-completions/ **Query 1 · Playground** ```sql SELECT * FROM users u JOIN orders o ON u.user_id = o.user_id ``` Tab accepts Ghost text comes from three places, in this order: 1. Rules that know the answer from the query and your tables: closing brackets, the column list after `SELECT`, aliases, join conditions. 2. Your own past queries, once the editor has learned from them. See [Learning from your queries](#learning-from-your-queries). 3. A model, when you choose one. See [Choosing a model](#choosing-a-model). ## Accepting and dismissing - `Tab` accepts. - `Enter` does **not** accept; it inserts a newline and dismisses. - `Esc` or any other key dismisses. Ghost text is not shown while the dropdown has an item selected. ## Where ghost text appears ### Alias after AS After a table in `FROM` or `JOIN`, the alias is the first letter of each word of the table name, as long as no other table or alias in the query already has that name. - `user_accounts AS` → `ua` - `fact_defect AS` → `fd` - `orders AS` → `o` A word that BigQuery reserves (`account_status` would give `as`) is skipped. After `AS` in a SELECT list, the alias is built from the expression: - `COUNT(*) AS` → `row_count` - `COUNT(user_id) AS` → `user_id_count` - `MAX(amount) AS` → `max_amount` - `DATE_TRUNC(created_at, MONTH) AS` → `month` A name that is a reserved word gets `_value` added. A plain column such as `name` gets no alias, and neither does an expression the editor cannot name. The alias you gave the same expression before comes first, once the editor has learned from your queries, and a model, when you choose one, names it instead. ### Column list after SELECT When the cursor is in an empty SELECT list that has a `FROM` after it, the ghost text is the columns of that table, one per line. This needs every column of the source to be known, and it stops at 30 columns. With joined tables, a name that both tables have is written with its alias (`o.country`, `u.country`). ### JOIN condition After `JOIN table ON`, the condition, when the two tables are linked by a foreign key or by key-like columns such as `orders.user_id` and `users.user_id`. Two tables that only share a plain column such as `status` get no suggestion, and neither do two tables with several equally good candidates. ```sql SELECT * FROM users u JOIN orders o ON u.user_id = o.user_id ``` ### Right side of a join comparison After the left side of a comparison in an ON clause, the matching key column on the other table. ```sql SELECT * FROM users u JOIN orders o ON u.user_id = o.user_id ``` ### IS NULL After `IS NOT`, the ghost text is `NULL`. If you have typed the start of a word, it finishes it: `IS NU` gives `LL`. This also works in the middle of a condition, and it stays quiet when a value already follows the cursor. After a bare `IS`, the ghost text is the form you use most, as learned from your queries: `NOT NULL` or `NULL`, and `TRUE` or `FALSE` only on a BOOL column. Without a clear habit there is nothing to suggest. ### GROUP BY column list At the start of a `GROUP BY`, the ghost text lists the non-aggregate columns of your SELECT in the form you usually write them: positions (`1, 2`), names, or `ALL`. The form is learned from your queries, so a new setup shows nothing here. If the clause already has items, the editor leaves it to the dropdown. ### FROM for a CTE When you start a new line after a SELECT list that has no `FROM` yet, and its plain qualified columns (`cte_name.column`, not inside a function) all come from one CTE, the ghost text is `FROM cte_name`. After a comma there is nothing, since the list goes on. ```sql WITH recent AS (SELECT user_id FROM users) SELECT recent.user_id FROM recent -- ghost text ``` ### Closing syntax When the closing position is deterministic (a parenthesis, a quote), ghost text closes it. Nested parentheses close in the right order. Nothing is suggested when the closing character is already after the cursor. ## Learning from your queries The editor learns from the queries you ran. It looks at three things: - WHERE conditions, for the same tables. - JOIN conditions, for the same pair of tables. - Aliases after `AS`: of a table, and of an expression over columns, for the same tables. It also learns how you write `IS` and `GROUP BY` lists, which is what the rules above use. A suggestion needs a past that agrees. The condition must have been used on at least two different days. Running the same query ten times in one day counts once. When your past queries differ at some point (two different dates), the ghost text stops before it. The suggestion is written for the query you have now. It uses your current aliases, your keyword case, and the way the query qualifies its columns. What it will not suggest: - Dates and timestamps, ever. - Numbers other than `0` and `1` unless you used them on at least three days, and then only small ones. - Text that looks personal or random: an email address, anything with a long run of digits, a mix of letters and digits, or a long string. Other text needs at least three days of use. What it learns from: - Successful SELECT queries you ran from a query tab or a notebook. The newest 2000 count, and newer ones weigh more. - Not the queries that Prism AI, MCP clients or other app features ran for you. - Not queries with Jinja templates, and not tables the editor cannot identify without guessing. If you press `Esc` on a learned suggestion, or type something else over it, the editor shows that kind of suggestion less often, until you accept some again. This fades over about a week. Everything is learned on this device, from the query history stored in your browser or the desktop app. What it learns stays on this device. To turn it off, clear **Learn from my queries** under [Settings → Editor → SQL Editor → Completions](https://docs.querylab.io/settings/#sql-editor). It needs **Enable ghost completions** to be on. ## Choosing a model When the rules and your history have nothing to suggest, a model can write the ghost text. You choose it under **Settings → Prism AI → AI ghost completions → Model**. It needs **Enable ghost completions** to be on. | Model | Where it runs | Your SQL | | -------------------------------------------- | ---------------------------------- | -------------------------------- | | Off | No model is asked | | | Querylab.io cloud model | Our server | The query you are typing is sent | | Chrome's on-device model | Chrome, in the web app only | Stays on your device | | Model on this computer (Ollama or LM Studio) | A server on your computer | Stays on your device | | Apple Intelligence model (Mac) | macOS, in the Mac desktop app only | Stays on your device | The model is asked after you pause typing. For an alias, the model's name is used in place of the rule's name when it answers. With a model on and ready on this device, a space does not open the suggestion list by itself, so the model can answer as ghost text. The list still opens when you type a letter or press `Ctrl Space`. To get the list after every space as before, turn on **Show suggestions after a space** in [Settings → Editor → SQL Editor → Completions](https://docs.querylab.io/settings/#sql-editor). With no model chosen, or one this device cannot run yet (Chrome's model before its download, a local server that does not answer), nothing changes. ### Querylab.io cloud model It needs **Enable Prism AI Features** to be on. ### Chrome's on-device model - Chrome 148 or later, in the web app. It is not available in the desktop app. - About 22 GB of free disk, and a GPU with more than 4 GB of memory or 16 GB of RAM. - The model is a one-time download. Press **Download Chrome's on-device model**. A progress bar shows the download. ### Model on this computer 1. Choose **Server**: Ollama or LM Studio. 2. Set **Address**. Leave it empty for the usual one (`http://localhost:11434` for Ollama, `http://localhost:1234` for LM Studio). 3. Set **Model name**, for example `qwen2.5-coder:1.5b` for Ollama. 4. Press **Test connection**. The address must be on this computer: `http://localhost`, `http://127.0.0.1` or `http://[::1]`, with a port and no path. Anything else is refused. In the web app the browser calls the server directly, so the server must accept calls from the page. For Ollama, start it with `OLLAMA_ORIGINS` set to the address of the app. For LM Studio, turn on **Enable CORS** in its server settings. The settings page shows the exact text. The desktop app needs none of this. ### Apple Intelligence model (Mac) This option is listed in the Mac desktop app only. It needs macOS 26 or later with Apple Intelligence turned on in System Settings. If it is not ready, the settings page says why. **Check again** asks again. ### Checks on model suggestions A suggestion from Chrome's model, a model on your computer or Apple's model is checked before it shows. It must not repeat text that is already after the cursor or a condition the same `AND` or `OR` list already has, must not add a syntax error, and every column it names must exist in the tables of the query. It must not invent a text or date value that is not in your query (a format string such as `'%Y-%m'` in `FORMAT_DATE` is allowed), and it must not repeat the same piece three times in a row. A long answer is cut at the end of a line, at most four lines, and an answer that adds the next clause is cut before it. The check uses table schemas the editor has already loaded and never fetches new ones. A suggestion that fails is dropped. In a query with Jinja templates, these models are not asked. ## Turning it off Clear **Enable ghost completions** under [Settings → Editor → SQL Editor → Completions](https://docs.querylab.io/settings/#sql-editor). That turns off all ghost text, including the model and the learning.