Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
3387126
fix(macos): correct SPM package source path for refresh_rate plugin (…
ZhuchkaTriplesix Aug 23, 2026
5c397a3
Merge branch 'fix/555-macos-spm-refresh-rate-path' into dev
ZhuchkaTriplesix Aug 23, 2026
888099d
fix(macos): add missing network.client and user-selected file entitle…
ZhuchkaTriplesix Aug 23, 2026
05e33e9
Merge branch 'fix/556-macos-app-sandbox-entitlements' into dev
ZhuchkaTriplesix Aug 23, 2026
45df673
feat(grid): manual column drag-resizing in VirtualResultGrid (#548)
ZhuchkaTriplesix Aug 23, 2026
abd7b3f
Merge branch 'feat/548-grid-column-drag-resizing' into dev
ZhuchkaTriplesix Aug 23, 2026
2b95ff5
feat(grid): client-side column sorting in VirtualResultGrid (#549)
ZhuchkaTriplesix Aug 23, 2026
8b7d6fe
Merge branch 'feat/549-grid-column-sorting' into dev
ZhuchkaTriplesix Aug 23, 2026
76f471e
feat(sidebar): rich context menus for database objects (#551)
ZhuchkaTriplesix Aug 23, 2026
0923088
Merge branch 'feat/551-rich-object-context-menus' into dev
ZhuchkaTriplesix Aug 23, 2026
7095c3b
feat(grid): multi-cell selection and TSV/CSV clipboard copy (#550)
ZhuchkaTriplesix Aug 23, 2026
a005702
Merge branch 'feat/550-grid-multi-cell-selection-copy' into dev
ZhuchkaTriplesix Aug 23, 2026
dbe9ea6
feat(a11y): full keyboard navigation in database tree views (#552)
ZhuchkaTriplesix Aug 23, 2026
829288d
Merge branch 'feat/552-tree-keyboard-navigation' into dev
ZhuchkaTriplesix Aug 23, 2026
1eea454
feat(forms): real-time inline validation in connection dialogs (#553)
ZhuchkaTriplesix Aug 23, 2026
844a67c
Merge branch 'feat/553-connection-dialogs-inline-validation' into dev
ZhuchkaTriplesix Aug 23, 2026
92578d0
feat(sidebar): fluid motion collapsible connections tree sidebar (#557)
ZhuchkaTriplesix Aug 23, 2026
f490dd4
Merge branch 'feat/557-fluid-collapsible-sidebar' into dev
ZhuchkaTriplesix Aug 23, 2026
ebe9378
feat(onboarding): interactive welcome tour & 1-click demo playground …
ZhuchkaTriplesix Aug 23, 2026
96fd543
Merge branch 'feat/558-interactive-welcome-tour-playground' into dev
ZhuchkaTriplesix Aug 23, 2026
6e03c1d
fix(ui): smooth sidebar collapse motion, enhanced tutorial dialog wit…
ZhuchkaTriplesix Aug 23, 2026
ffd1978
Merge branch 'feat/559-sidebar-motion-and-tutorial-polish' into dev
ZhuchkaTriplesix Aug 23, 2026
a972e1c
fix(ui): enhance tip banner contrast, smooth dialog animated resizing…
ZhuchkaTriplesix Aug 23, 2026
0e84a33
chore(deps): safe upgrade of direct and transitive dependencies (fixe…
ZhuchkaTriplesix Aug 24, 2026
dda9f16
Merge branch 'chore/559-safe-dependencies-upgrade' into dev
ZhuchkaTriplesix Aug 24, 2026
63d844b
fix(sidebar): dynamically collapse split handle width to eliminate re…
ZhuchkaTriplesix Aug 24, 2026
3a67958
feat(shortcuts): bind Ctrl+Enter, Cmd+Enter, F5 across all SQL editor…
ZhuchkaTriplesix Aug 24, 2026
1e3dc74
fix(shortcuts): add HardwareKeyboard global listener with physical ke…
ZhuchkaTriplesix Aug 24, 2026
e79d11f
feat(shortcuts): toggle WelcomeTourDialog on hotkey and prevent stack…
ZhuchkaTriplesix Aug 24, 2026
09f8f6f
feat(data-grid): staging buffer engine and visual change tracking (#560)
ZhuchkaTriplesix Aug 24, 2026
6f9f490
Merge branch 'feat/560-data-grid-staging-buffer' into dev
ZhuchkaTriplesix Aug 24, 2026
963f7d4
feat(data-grid): type-aware inline cell editors and popover inspector…
ZhuchkaTriplesix Aug 24, 2026
331a6ca
Merge branch 'feat/561-type-aware-grid-editors' into dev
ZhuchkaTriplesix Aug 24, 2026
6fe8085
fix(deps): downgrade sqflite_common_ffi constraint to ^2.4.0+3 for Da…
ZhuchkaTriplesix Aug 24, 2026
d34ee69
feat(database): primary key detection and atomic DML mutation engine …
ZhuchkaTriplesix Aug 24, 2026
e195870
Merge branch 'feat/562-database-mutation-engine' into dev
ZhuchkaTriplesix Aug 24, 2026
5acbcd7
fix(ui): wrap ResultsTab toolbar in horizontal scroll to prevent over…
ZhuchkaTriplesix Aug 25, 2026
ad977f8
Merge pull request #580 from QueryaHub/issue/577-resultstab-overflow-fix
ZhuchkaTriplesix Aug 25, 2026
c0e3dcb
feat(data-grid): integrate TableMutationPlan generator with staging b…
ZhuchkaTriplesix Aug 25, 2026
7cb80fc
Merge pull request #581 from QueryaHub/issue/564-data-grid-dml-generator
ZhuchkaTriplesix Aug 25, 2026
c254f41
feat(workspaces): wire staging buffer and save handler in SQLite, Pos…
ZhuchkaTriplesix Aug 25, 2026
d5fc4c2
Merge pull request #582 from QueryaHub/issue/565-staging-buffer-sql-w…
ZhuchkaTriplesix Aug 25, 2026
3d4e5eb
feat(grid): add atomic DML preview and confirmation dialog before sav…
ZhuchkaTriplesix Aug 25, 2026
b0d9bad
Merge pull request #583 from QueryaHub/issue/566-dml-preview-confirma…
ZhuchkaTriplesix Aug 25, 2026
295663d
feat(grid): add in-cell data type validator, visual error cues, and b…
ZhuchkaTriplesix Aug 25, 2026
9371366
Merge pull request #584 from QueryaHub/issue/567-in-cell-validation-s…
ZhuchkaTriplesix Aug 25, 2026
f7e3d93
feat(filter): add multi-clause query parser with AND, OR, NOT, and pa…
ZhuchkaTriplesix Aug 25, 2026
4edfaca
Merge pull request #585 from QueryaHub/issue/568-multi-clause-query-p…
ZhuchkaTriplesix Aug 25, 2026
40956a8
feat(filter): support LIKE, ILIKE, IN, IS NULL, BETWEEN, and escaped …
ZhuchkaTriplesix Aug 25, 2026
8dd11b6
Merge pull request #586 from QueryaHub/issue/569-string-literals-exte…
ZhuchkaTriplesix Aug 25, 2026
3e823ea
feat(filter): add autocomplete and syntax suggestions popup to filter…
ZhuchkaTriplesix Aug 25, 2026
9111336
Merge pull request #587 from QueryaHub/issue/570-autocomplete-filter-bar
ZhuchkaTriplesix Aug 25, 2026
0087ae4
feat(value-panel): add syntax highlighting and XML/HTML formatter wit…
ZhuchkaTriplesix Aug 25, 2026
8d8398e
Merge pull request #588 from QueryaHub/issue/571-syntax-highlighting-…
ZhuchkaTriplesix Aug 25, 2026
15558b9
feat(calc): add distinct count, median, std dev, and copy stats summa…
ZhuchkaTriplesix Aug 25, 2026
f0dd16c
Merge pull request #589 from QueryaHub/issue/573-advanced-selection-calc
ZhuchkaTriplesix Aug 25, 2026
617ef2b
feat(groupings): add multi-column grouping, custom aggregations, sort…
ZhuchkaTriplesix Aug 25, 2026
b3a8f6d
Merge pull request #590 from QueryaHub/issue/574-groupings-aggregatio…
ZhuchkaTriplesix Aug 25, 2026
02d0fbf
test(grid): add end-to-end integration tests for data grid staging, D…
ZhuchkaTriplesix Aug 25, 2026
8a6cab2
Merge pull request #591 from QueryaHub/issue/578-data-grid-e2e-integr…
ZhuchkaTriplesix Aug 25, 2026
f9f5c93
feat(a11y): add global keyboard shortcuts (Ctrl+F, Ctrl+S, Ctrl+G), f…
ZhuchkaTriplesix Aug 25, 2026
3626188
Merge pull request #592 from QueryaHub/issue/579-visual-polish-access…
ZhuchkaTriplesix Aug 25, 2026
90f681c
chore: add const to samplePlan in dml_preview_dialog_test
ZhuchkaTriplesix Aug 25, 2026
4bc5db9
fix: resolve unawaited_return_in_try_block and const warnings in anal…
ZhuchkaTriplesix Aug 25, 2026
6602a8f
fix(sqlite): expand table view columns to fill available viewport width
ZhuchkaTriplesix Aug 25, 2026
4af6c54
refactor: polish mysql pagination controls, optimize media query, and…
ZhuchkaTriplesix Aug 25, 2026
2caaa81
fix(test): restore CI baseline golden images
ZhuchkaTriplesix Aug 25, 2026
86ea3f1
chore(release): prepare release 0.4.13
ZhuchkaTriplesix Aug 25, 2026
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.13] - 2026-08-25

Production-ready UI polish, complete interactive Data Grid editing suite, advanced query filtering, fluid collapsible navigation, and platform hardening.

### Added

- **In-Place Cell Editing & Staging Engine (#560, #561, #564, #565)** — Double-click cell to edit in-place with type-aware inline editors, dirty state indicators, staging buffer for staged mutations (inserts/updates/deletions), and keyboard navigation (Tab/Shift+Tab, Enter, Escape).
- **Atomic DML Preview & Multi-Dialect Generation (#562, #566)** — Visual DML confirmation modal displaying compiled atomic `UPDATE`, `INSERT`, and `DELETE` statements with primary key resolution before committing to SQLite, PostgreSQL, and MySQL.
- **In-Cell Validation & Safety Guardrails (#567)** — Real-time cell validation for integer, float, boolean, UUID, JSON, date, and timestamp data types with visual error cues.
- **Advanced Query Filter Engine (#568, #569, #570)** — Compound predicate filter bar with `AND`, `OR`, `NOT`, parentheses, `LIKE`, `ILIKE`, `IN`, `IS NULL`, `BETWEEN`, escaped quotes, and intelligent popup autocomplete suggestions.
- **Syntax Highlighting & Value Inspector (#571, #572)** — Dedicated inspector panel with syntax highlighting for JSON, XML, YAML, and automated XML/HTML formatting and validation.
- **Selection Statistics & Quick Calc (#573)** — Extended selection calculations in the status bar (Count, Distinct, Sum, Avg, Min, Max, Median, Standard Deviation) with one-click clipboard summary export.
- **Multi-Column Grouping & Pivot View (#574, #575, #576)** — Hierarchical multi-level grouping, custom aggregations (SUM, AVG, MIN, MAX, COUNT), sorting, and CSV export for grouped summaries.
- **Column Drag-Resizing & 3-Phase Sorting (#548, #549)** — Interactive column width drag-resizing with divider handles and 3-phase client-side sorting (`natural` -> `asc` -> `desc`).
- **Multi-Cell Range Selection & Clipboard (#550)** — Rectangular multi-cell selection (Shift+Click) with TSV/CSV clipboard copy for Excel/Google Sheets.
- **Fluid Collapsible Sidebar (#557, #559)** — Physics-driven animated sidebar toggle with `QueryaSpring`, global `Cmd+B` / `Ctrl+B` hotkey, titlebar toggle button, and width persistence.
- **Rich Object Context Menus (#551)** — Right-click native context menus for database tables, views, procedures, and connections (*«Select TOP 100»*, *«Copy SELECT statement»*, *«Copy name»*, *«Open in SQL»*).
- **Tree Keyboard Navigation (#552)** — Full arrow-key navigation (Left/Right to expand/collapse, Up/Down, Enter/Space) across database trees.
- **Interactive Welcome Tour & 1-Click Playground (#558)** — Built-in onboarding tour and 1-click SQLite demo database playground.
- **Global Shortcuts & Focus Polish (#579)** — Added global shortcuts (`Ctrl+F` for filter bar, `Ctrl+S` for staging commit, `Ctrl+G` for groupings panel) and enhanced focus accessibility.

### Fixed

- **Connection Dialogs Inline Validation (#553)** — Real-time URI parser and validation with dynamic database driver and host badges.
- **Toolbar Layout Responsive Scroll (#577)** — Wrapped data grid action bars in horizontal scroll to eliminate overflow on compact viewports.
- **macOS SPM Path (#555)** — Corrected relative path to `refresh_rate` plugin source in `Package.swift` for Swift Package Manager builds.
- **macOS App Sandbox Entitlements (#556)** — Added `network.client` and `files.user-selected.read-write` entitlements for outbound TCP connections and local file access.

## [0.4.11-c] - 2026-07-29

Flutter SDK & dependency compatibility update, image downsampling memory optimization, and UI polish.
Expand Down
6 changes: 6 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ analyzer:
exclude:
- "third_party/**"
- "build/**"
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**
errors:
unnecessary_underscores: ignore
prefer_initializing_formals: ignore
Expand Down
455 changes: 455 additions & 0 deletions docs/data-grid-interactive-editor-issues.md

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion lib/core/actions/sql_editor_command_bridge.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/foundation.dart';

enum SqlEditorPendingAction { none, newQuery, openFile, saveFile }
enum SqlEditorPendingAction { none, newQuery, openFile, saveFile, executeQuery }

/// Lets the active SQL workspace handle File menu commands when menu focus is
/// outside the editor subtree (e.g. title bar).
Expand All @@ -13,21 +13,25 @@ class SqlEditorCommandBridge {
VoidCallback? _onNew;
VoidCallback? _onOpen;
VoidCallback? _onSave;
VoidCallback? _onExecute;

SqlEditorPendingAction pendingAction = SqlEditorPendingAction.none;

bool get isActive => _onNew != null;
bool get canExecute => _onExecute != null;

void register({
required int? connectionId,
required VoidCallback onNew,
required VoidCallback onOpen,
required VoidCallback onSave,
VoidCallback? onExecute,
}) {
_ownerConnectionId = connectionId;
_onNew = onNew;
_onOpen = onOpen;
_onSave = onSave;
_onExecute = onExecute;
_flushPending();
}

Expand All @@ -37,6 +41,7 @@ class SqlEditorCommandBridge {
_onNew = null;
_onOpen = null;
_onSave = null;
_onExecute = null;
}

void queuePending(SqlEditorPendingAction action) {
Expand Down Expand Up @@ -67,6 +72,14 @@ class SqlEditorCommandBridge {
pendingAction = SqlEditorPendingAction.saveFile;
}

void invokeExecute() {
if (_onExecute != null) {
_onExecute!();
return;
}
pendingAction = SqlEditorPendingAction.executeQuery;
}

void _flushPending() {
final action = pendingAction;
if (action == SqlEditorPendingAction.none) return;
Expand All @@ -81,6 +94,8 @@ class SqlEditorCommandBridge {
_onOpen?.call();
case SqlEditorPendingAction.saveFile:
_onSave?.call();
case SqlEditorPendingAction.executeQuery:
_onExecute?.call();
}
}

Expand All @@ -90,6 +105,7 @@ class SqlEditorCommandBridge {
_onNew = null;
_onOpen = null;
_onSave = null;
_onExecute = null;
pendingAction = SqlEditorPendingAction.none;
}
}
21 changes: 13 additions & 8 deletions lib/core/database/connection_pool_lock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,26 @@ class PoolEntryLock<T> {
}
final completer = Completer<void>();
_lock = completer.future;
Future<T> result;
try {
final existing = _pending[key];
if (existing != null) return existing;
final future = create();
_pending[key] = future;
// Ensure the pending entry is removed once the creation finishes, and
// swallow errors on the cleanup chain so they don't become unhandled.
final guarded = future.whenComplete(() => _pending.remove(key));
guarded.then((_) {}, onError: (_) {});
return future;
if (existing != null) {
result = existing;
} else {
final future = create();
_pending[key] = future;
// Ensure the pending entry is removed once the creation finishes, and
// swallow errors on the cleanup chain so they don't become unhandled.
final guarded = future.whenComplete(() => _pending.remove(key));
guarded.then((_) {}, onError: (_) {});
result = future;
}
} finally {
completer.complete();
if (_lock == completer.future) {
_lock = null;
}
}
return result;
}
}
71 changes: 71 additions & 0 deletions lib/core/database/mysql_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:mysql_client/mysql_client.dart';
import 'package:querya_desktop/core/database/table_schema_meta.dart';
import 'package:querya_desktop/core/storage/local_db.dart';
import 'package:querya_desktop/features/connections/ssl_certificate_support.dart';

Expand Down Expand Up @@ -349,6 +350,76 @@ class MysqlConnection {
return rs.rows.map((r) => r.colAt(0)!).toList();
}

/// Retrieves schema metadata and primary keys for [table] in [database].
Future<TableSchemaMeta> getTableSchema({
required String database,
required String table,
}) async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to MySQL');
}
final colsRs = await execute(
'SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT '
'FROM information_schema.COLUMNS '
'WHERE TABLE_SCHEMA = :database AND TABLE_NAME = :table '
'ORDER BY ORDINAL_POSITION',
{
'database': database,
'table': table,
},
);

final pkRs = await execute(
'SELECT COLUMN_NAME '
'FROM information_schema.KEY_COLUMN_USAGE '
"WHERE CONSTRAINT_NAME = 'PRIMARY' AND TABLE_SCHEMA = :database AND TABLE_NAME = :table "
'ORDER BY ORDINAL_POSITION',
{
'database': database,
'table': table,
},
);

final primaryKeys = pkRs.rows.map((r) => r.colAt(0)!).toList();
final columns = <TableColumnMeta>[];

for (final r in colsRs.rows) {
final name = r.colByName('COLUMN_NAME') ?? '';
final dataType = r.colByName('DATA_TYPE') ?? '';
final isNullable = (r.colByName('IS_NULLABLE') ?? 'YES').toUpperCase() == 'YES';
final isPk = primaryKeys.contains(name);
final pkPos = isPk ? primaryKeys.indexOf(name) + 1 : null;
final dflt = r.colByName('COLUMN_DEFAULT');

columns.add(
TableColumnMeta(
name: name,
dataType: dataType,
isNullable: isNullable,
isPrimaryKey: isPk,
primaryKeyPosition: pkPos,
defaultValue: dflt,
),
);
}

return TableSchemaMeta(
tableName: table,
schema: database,
columns: columns,
primaryKeys: primaryKeys,
);
}

/// Returns primary key column names for [table] in [database].
Future<List<String>> getPrimaryKeys({
required String database,
required String table,
}) async {
final schema = await getTableSchema(database: database, table: table);
return schema.primaryKeys;
}

/// Lists base tables in [schema] (MySQL: database name).
Future<List<String>> listTables({required String schema}) async {
if (!isConnected || _conn == null) {
Expand Down
74 changes: 74 additions & 0 deletions lib/core/database/postgres_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:querya_desktop/core/storage/local_db.dart';
import 'package:postgres/src/connection_string.dart' show parseConnectionString;

import 'postgres_metadata.dart';
import 'table_schema_meta.dart';

/// Replaces the database in a `postgresql://` / `postgres://` URI (path or
/// `database=` query param). Used when switching DB while keeping URI auth/SSL.
Expand Down Expand Up @@ -329,6 +330,79 @@ class PostgresConnection {
return result.map((row) => row[0] as String).toList();
}

/// Retrieves schema metadata and primary keys for [table] in [schema].
Future<TableSchemaMeta> getTableSchema({
String schema = 'public',
required String table,
}) async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to PostgreSQL');
}
final colsRs = await _conn!.execute(
Sql.named(
'SELECT column_name, data_type, is_nullable, column_default '
'FROM information_schema.columns '
'WHERE table_schema = @schema AND table_name = @table '
'ORDER BY ordinal_position',
),
parameters: {'schema': schema, 'table': table},
);

final pkRs = await _conn!.execute(
Sql.named(
'SELECT kcu.column_name '
'FROM information_schema.table_constraints tc '
'JOIN information_schema.key_column_usage kcu '
' ON tc.constraint_name = kcu.constraint_name '
' AND tc.table_schema = kcu.table_schema '
"WHERE tc.constraint_type = 'PRIMARY KEY' "
' AND tc.table_schema = @schema '
' AND tc.table_name = @table '
'ORDER BY kcu.ordinal_position',
),
parameters: {'schema': schema, 'table': table},
);

final primaryKeys = pkRs.map((r) => r[0] as String).toList();
final columns = <TableColumnMeta>[];

for (final r in colsRs) {
final name = r[0] as String? ?? '';
final dataType = r[1] as String? ?? '';
final isNullable = (r[2] as String? ?? 'YES').toUpperCase() == 'YES';
final isPk = primaryKeys.contains(name);
final pkPos = isPk ? primaryKeys.indexOf(name) + 1 : null;
final dflt = r[3]?.toString();

columns.add(
TableColumnMeta(
name: name,
dataType: dataType,
isNullable: isNullable,
isPrimaryKey: isPk,
primaryKeyPosition: pkPos,
defaultValue: dflt,
),
);
}

return TableSchemaMeta(
tableName: table,
schema: schema,
columns: columns,
primaryKeys: primaryKeys,
);
}

/// Returns primary key column names for [table] in [schema].
Future<List<String>> getPrimaryKeys({
String schema = 'public',
required String table,
}) async {
final s = await getTableSchema(schema: schema, table: table);
return s.primaryKeys;
}

Future<List<String>> listViews({String schema = 'public'}) async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to PostgreSQL');
Expand Down
52 changes: 52 additions & 0 deletions lib/core/database/sql_table_target_extractor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/// Result of extracting target table and schema from an SQL query.
class SqlTableTarget {
const SqlTableTarget({
required this.tableName,
this.schema,
});

final String tableName;
final String? schema;

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SqlTableTarget &&
tableName == other.tableName &&
schema == other.schema;

@override
int get hashCode => Object.hash(tableName, schema);
}

/// Helper utility to infer the primary target table from a simple SELECT query.
abstract final class SqlTableTargetExtractor {
static final _fromTableRegex = RegExp(
r'\bfrom\s+(?:(?:"([^"]+)"|`([^`]+)`|([a-zA-Z_]\w*))\.)?(?:"([^"]+)"|`([^`]+)`|([a-zA-Z_]\w*))',
caseSensitive: false,
);

/// Extracts the target schema and table name from [sql].
/// Returns `null` if no simple target table can be determined (e.g. subqueries, joins).
static SqlTableTarget? extract(String sql) {
final trimmed = sql.trim();
if (trimmed.isEmpty) return null;

// If query has JOIN or multiple tables separated by comma, avoid auto-generating DML
final hasJoin = RegExp(r'\bjoin\b', caseSensitive: false).hasMatch(trimmed);
if (hasJoin) return null;

final match = _fromTableRegex.firstMatch(trimmed);
if (match == null) return null;

final schema = match.group(1) ?? match.group(2) ?? match.group(3);
final table = match.group(4) ?? match.group(5) ?? match.group(6);

if (table == null || table.isEmpty) return null;

return SqlTableTarget(
tableName: table,
schema: schema,
);
}
}
Loading
Loading