From fc2596e8fc8726d93560f789ac56781cba3a3d2a Mon Sep 17 00:00:00 2001
From: canyrtcn <200778076+canyrtcn@users.noreply.github.com>
Date: Tue, 27 Oct 2026 21:50:01 +0300
Subject: [PATCH] Complete SELECT output aliases in ORDER BY and HAVING
---
README.md | 4 +-
README.tr.md | 4 +-
app/renderer/sql-completion.js | 19 ++++++-
docs/architecture/architecture.json | 6 +--
docs/architecture/index.html | 2 +-
docs/architecture/request-flow.en.svg | 2 +-
docs/architecture/request-flow.tr.svg | 2 +-
docs/releases/v3.4.270.md | 11 +++++
package-lock.json | 4 +-
package.json | 4 +-
scripts/test-sql-completion-ui.cjs | 6 +++
scripts/test-sql-output-aliases.cjs | 71 +++++++++++++++++++++++++++
12 files changed, 120 insertions(+), 15 deletions(-)
create mode 100644 docs/releases/v3.4.270.md
create mode 100644 scripts/test-sql-output-aliases.cjs
diff --git a/README.md b/README.md
index 7bf5d6b..5166af2 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ A desktop SQL learning workspace that brings local SQLite, an AI tutor, visual s
-
Windows x64 · v3.4.269 · macOS is in preparation.
+
Windows x64 · v3.4.270 · macOS is in preparation.
The button opens Releases, where the Windows x64 installer is available. No GitHub account is required to download it.
@@ -184,7 +184,7 @@ Choose an available model entry in your account. Model names and access can vary
3. Load a supported database or file, or open a built-in learning example. Start writing and running SQL immediately.
4. Connect AI when you want guided explanations, generated queries or AI-backed learning workflows. You can leave this step for later.
-[Release notes](docs/releases/v3.4.269.md) · [Build from source](#development-and-repository-layout)
+[Release notes](docs/releases/v3.4.270.md) · [Build from source](#development-and-repository-layout)
## More screens and documentation
diff --git a/README.tr.md b/README.tr.md
index bbe9d20..2f2a6fd 100644
--- a/README.tr.md
+++ b/README.tr.md
@@ -10,7 +10,7 @@ Yerel SQLite, AI öğretmen, görsel şema ve uygulamalı öğrenmeyi, bu akış
-
Windows x64 · v3.4.269 · macOS hazırlanıyor.
+
Windows x64 · v3.4.270 · macOS hazırlanıyor.
Düğme, Windows x64 kurucusunun bulunduğu Releases sayfasını açar. İndirmek için GitHub hesabı gerekmez.
@@ -182,7 +182,7 @@ Hesabında kullanılabilen model seçeneğini kullan. Model adları ve erişim d
3. Desteklenen veritabanını veya dosyanı yükle ya da hazır bir öğrenme örneği aç. SQL yazmaya ve çalıştırmaya hemen başlayabilirsin.
4. Açıklama, sorgu üretimi veya AI destekli öğrenme akışları istediğinde AI bağlantısı ekle. Bu adımı sonraya bırakabilirsin.
-[Sürüm notları](docs/releases/v3.4.269.md) · [Kaynaktan çalıştır](#geliştirme-ve-repo-düzeni)
+[Sürüm notları](docs/releases/v3.4.270.md) · [Kaynaktan çalıştır](#geliştirme-ve-repo-düzeni)
## Daha fazla ekran ve belge
diff --git a/app/renderer/sql-completion.js b/app/renderer/sql-completion.js
index 5f81c3e..3641ddb 100644
--- a/app/renderer/sql-completion.js
+++ b/app/renderer/sql-completion.js
@@ -12,6 +12,22 @@
const joins=['JOIN','LEFT JOIN','INNER JOIN','CROSS JOIN','RIGHT JOIN','FULL JOIN'];
const followingSource=['WHERE',...joins,'GROUP BY','ORDER BY','LIMIT'];
const followingPredicate=['AND','OR','GROUP BY','ORDER BY','LIMIT'];
+ function outputAliases(tokens,position){
+ // Projection names belong to one SELECT, never a CTE, sibling or UNION arm.
+ if(tokens.some(token=>is(token,'UNION','INTERSECT','EXCEPT')))return [];
+ const begin=tokens.findLastIndex(token=>token.startindex>begin&&is(token,'FROM','WHERE','GROUP','HAVING','ORDER','LIMIT','WINDOW'));
+ const projection=tokens.slice(begin+1,finish<0?undefined:finish),groups=[[]];
+ for(const token of projection){if(token.value===','&&!token.quoted)groups.push([]);else groups.at(-1).push(token);}
+ const names=[];
+ for(const group of groups){
+ const alias=group.at(-1),previous=group.at(-2);if(!alias||!previous||alias.string||!alias.quoted&&(!/^[\p{L}_][\p{L}\p{N}_$]*$/u.test(alias.value)||reserved.has(alias.upper)))continue;
+ const explicit=is(previous,'AS')&&group.length>2;
+ const implicit=!is(previous,'AS','COLLATE')&&previous.endnames.findIndex(other=>other.toLowerCase()===name.toLowerCase())===index&&names.filter(other=>other.toLowerCase()===name.toLowerCase()).length===1).map(spelling);
+ }
function joinCondition(a,b){
const possibilities=[];
for(const [child,parent]of [[a,b],[b,a]]){
@@ -110,7 +126,8 @@
const qualifier=last?.value==='.'?tokens.at(-2)?.value:null;
const wildcard=upper==='*'&&(is(tokens.at(-2),'SELECT','DISTINCT')||tokens.at(-2)?.value==='.'||clause==='SELECT'&&!insideExpression&&tokens.at(-2)?.value===',');
const operand=last&&(wildcard||last.quoted||last.value===')'||is(last,'END','NULL','TRUE','FALSE')||/^[\p{L}\p{N}_]/u.test(last.value)&&!reserved.has(last.upper));
- const expression=[...columnNames,...funcs,'CASE','NULL'];
+ const aliases=!windowScope&&['ORDER','HAVING'].includes(clause)?outputAliases(allSame,position):[];
+ const expression=[...aliases,...columnNames,...funcs,'CASE','NULL'];
const frame=windowScope&&['ROWS','RANGE','GROUPS'].includes(clause);
const frameTokens=frame?local.slice(local.findLastIndex(t=>is(t,'ROWS','RANGE','GROUPS'))+1):[];
const frameBetween=frameTokens.some(t=>is(t,'BETWEEN')),frameEnd=frameTokens.some(t=>is(t,'AND'));
diff --git a/docs/architecture/architecture.json b/docs/architecture/architecture.json
index 239d99c..c1af9ac 100644
--- a/docs/architecture/architecture.json
+++ b/docs/architecture/architecture.json
@@ -1,6 +1,6 @@
{
- "version": "3.4.269",
- "date": "2026-10-21",
+ "version": "3.4.270",
+ "date": "2026-10-27",
"defaultLocale": "en",
"views": [
{
@@ -1159,7 +1159,7 @@
"references": {
".github/SECURITY.md": "470353c7e0447bd45fea3e4040507f03253c3cc762bac12ed424e9f6f93d648d",
"LICENSE": "c03af9304b8b44d8f65921a325a7f2077ebb89e5c64fc309106b3c547ef02d09",
- "README.md": "05a90e92c8829fe475fec2b158fb8c3f4a1b110940230caf1266486a49d24200",
+ "README.md": "74e2b60b292faa39f6624a22429f01d9e234a993e9056ad889ecfb320e20281c",
"app/ai-rate-limit.js": "fdf24873dfb664444f28728abe483a3f363e3aa61d16201a2cb1429ef67a2ff8",
"app/chatgpt-account.js": "1ea42388b92788b93804a9f0b6fc3441039ce26af2b837e9e5e732c160a99f32",
"app/database-inspection/runner.cjs": "32265046ec123e4248845436a523f67ff069bb7ebd8bb83102fc43e249d11d20",
diff --git a/docs/architecture/index.html b/docs/architecture/index.html
index 836ace7..1dddae0 100644
--- a/docs/architecture/index.html
+++ b/docs/architecture/index.html
@@ -1,6 +1,6 @@
PractiSQL · How it works
PractiSQL
How the application works
Local SQL · AI tutor · Actual results
From a question to SQL. From SQL to learning.
The model interprets intent. The application supplies scoped context, runs eligible SQL in local SQLite and brings actual results back into the conversation. Choose a workflow and component below to explore its responsibilities.
01 / 08
Main flow
The model selects the SQL approach and teaching explanation. The application harness prepares context, applies eligible output, checks specific violations and carries actual results into the next turn.
The current request, history and database evidence are labelled separately. The schema is not a copy of every row; missing record information can be retrieved with a tool.
These are the two read-only tools in normal SQL chat. Final SQL follows a separate execution path. Data-project discovery and review tools appear in the project view.
Successful execution does not prove that the business request or explanation is entirely correct. Specific violations are checked; not every semantic or teaching error can be detected.
The editor, chat, exams, projects and lesson PDFs have separate contracts. Lesson PDFs use their own local fixtures, rather than reading a personal workspace.
An optional, separate workflow. Source, SQL and numeric checks provide different evidence from AI judgments about business meaning. Numeric success does not prove a flawless interpretation or assignment.
This map explains component responsibilities. Successful SQL execution alone does not prove that every user intention or AI explanation is correct. Signing infrastructure is prepared; the current local installer is unsigned.