Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- iCloud Sync between the iPhone and Mac apps: the iOS app now uses the Production CloudKit environment, so a development build no longer syncs into a separate database the Mac never reads.
- Exports no longer fail mid-table on servers that enforce a statement time limit; the export session disables the limit and restores it afterwards, the same way mysqldump does. (#1633)
- Refreshing a table now reloads its data even when the previous load is still running; before, the refresh was silently dropped and the grid kept stale rows. (#1637)
- SQL autocomplete now suggests tables after JOIN. It detects the clause at the cursor across multi-join and multi-clause queries, so columns no longer appear where a table is expected, and tables lead the list. (#1646)

### Security

Expand Down
3 changes: 2 additions & 1 deletion TablePro/Core/Autocomplete/CompletionEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ final class CompletionEngine {
cteNames: context.cteNames,
nestingLevel: context.nestingLevel,
currentFunction: context.currentFunction,
isAfterComma: context.isAfterComma
isAfterComma: context.isAfterComma,
expectsObjectName: context.expectsObjectName
)

return CompletionContext(
Expand Down
23 changes: 19 additions & 4 deletions TablePro/Core/Autocomplete/SQLCompletionProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,16 @@ final class SQLCompletionProvider {
items += filterKeywords([
"AND", "OR", "NOT", "IS", "NULL", "TRUE", "FALSE"
])
// Continuations once the join condition is written: another join or
// the next clause. Without these, typing the next keyword (e.g. a
// second INNER JOIN) only fuzzy-matches columns.
items += filterKeywords([
"INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN",
"LEFT OUTER JOIN", "RIGHT OUTER JOIN", "FULL OUTER JOIN",
"CROSS JOIN", "NATURAL JOIN", "JOIN",
"WHERE", "ORDER BY", "GROUP BY", "HAVING", "LIMIT",
"UNION", "INTERSECT", "EXCEPT"
])

case .where_, .and, .having:
// HP-8: Columns, operators, logical keywords + clause transitions
Expand Down Expand Up @@ -739,11 +749,16 @@ final class SQLCompletionProvider {
score -= 1_000
}

// When prefix is empty and tables are in scope, user is at a clause
// transition point (e.g., "FROM users |" or "WHERE id > 1 |").
// Boost keywords so they appear alongside context-specific items.
// When prefix is empty and tables are in scope, the user is either in a
// table-operand slot (e.g. "... JOIN |") or at a clause transition point
// (e.g. "FROM users |" or "WHERE id > 1 |"). In the operand slot, tables
// lead; otherwise keywords lead so clause transitions surface.
if prefix.isEmpty && !context.tableReferences.isEmpty && !context.isAfterComma {
if item.kind == .keyword {
if context.expectsObjectName {
if item.kind == .table || item.kind == .view || item.kind == .schema {
score -= 300
}
} else if item.kind == .keyword {
score -= 300
}
} else {
Expand Down
Loading
Loading