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
28 changes: 28 additions & 0 deletions lib/core/database/mysql_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,34 @@ class MysqlConnection {
return rs.rows.map((r) => r.colAt(0)!).toList();
}

/// Lists stored procedures in [schema] (database name).
Future<List<String>> listProcedures({required String schema}) async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to MySQL');
}
final rs = await execute(
'SELECT ROUTINE_NAME FROM information_schema.ROUTINES '
"WHERE ROUTINE_SCHEMA = :schema AND ROUTINE_TYPE = 'PROCEDURE' "
'ORDER BY ROUTINE_NAME',
{'schema': schema},
);
return rs.rows.map((r) => r.colAt(0)!).toList();
}

/// Lists stored functions in [schema] (database name).
Future<List<String>> listFunctions({required String schema}) async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to MySQL');
}
final rs = await execute(
'SELECT ROUTINE_NAME FROM information_schema.ROUTINES '
"WHERE ROUTINE_SCHEMA = :schema AND ROUTINE_TYPE = 'FUNCTION' "
'ORDER BY ROUTINE_NAME',
{'schema': schema},
);
return rs.rows.map((r) => r.colAt(0)!).toList();
}

Future<String> serverVersion() async {
if (!isConnected || _conn == null) {
throw StateError('Not connected to MySQL');
Expand Down
36 changes: 36 additions & 0 deletions lib/core/database/sqlite_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,42 @@ class SqliteConnection {
return rows.map((r) => r['name'] as String).toList();
}

/// Returns the DDL (`sql`) of a table or view from sqlite_master.
Future<String> getObjectDdl(String objectName) async {
final rows = await execute(
"SELECT sql FROM sqlite_master WHERE name = :name",
[objectName],
);
if (rows.isEmpty) return '-- No definition found for $objectName';
return (rows.first['sql'] as String?) ?? '-- Empty definition';
}

/// Returns database overview info (`page_count`, `page_size`, `journal_mode`, version, etc.).
Future<Map<String, dynamic>> databaseOverview() async {
final verRows = await execute('SELECT sqlite_version() AS ver');
final ver = (verRows.isNotEmpty ? verRows.first['ver'] : '') ?? '';

final pcRows = await execute('PRAGMA page_count');
final pc = (pcRows.isNotEmpty ? pcRows.first.values.first : 0) ?? 0;

final psRows = await execute('PRAGMA page_size');
final ps = (psRows.isNotEmpty ? psRows.first.values.first : 0) ?? 0;

final jmRows = await execute('PRAGMA journal_mode');
final jm = (jmRows.isNotEmpty ? jmRows.first.values.first : '') ?? '';

final tblCountRows = await execute("SELECT count(*) AS c FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
final tblCount = (tblCountRows.isNotEmpty ? tblCountRows.first['c'] : 0) ?? 0;

return {
'version': ver,
'page_count': pc,
'page_size': ps,
'journal_mode': jm,
'table_count': tblCount,
};
}

/// Helper to quote SQLite identifiers safely.
static String quoteIdentifier(String id) {
return '"${id.replaceAll('"', '""')}"';
Expand Down
58 changes: 56 additions & 2 deletions lib/features/connections/connections_panel_mysql.dart
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,17 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> {
bool _loading = false;
List<String> _tables = [];
List<String> _views = [];
List<String> _procedures = [];
List<String> _functions = [];

void _toggle() {
setState(() => _expanded = !_expanded);
if (_expanded && _tables.isEmpty && _views.isEmpty && !_loading) {
if (_expanded &&
_tables.isEmpty &&
_views.isEmpty &&
_procedures.isEmpty &&
_functions.isEmpty &&
!_loading) {
_loadTables();
}
}
Expand All @@ -394,10 +401,16 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> {
await lease.connection.listTables(schema: widget.databaseName);
final views =
await lease.connection.listViews(schema: widget.databaseName);
final procs =
await lease.connection.listProcedures(schema: widget.databaseName);
final funcs =
await lease.connection.listFunctions(schema: widget.databaseName);
if (!mounted) return;
setState(() {
_tables = tables;
_views = views;
_procedures = procs;
_functions = funcs;
_loading = false;
});
} catch (e) {
Expand Down Expand Up @@ -468,7 +481,10 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> {
],
),
),
if (_tables.isNotEmpty || _views.isNotEmpty)
if (_tables.isNotEmpty ||
_views.isNotEmpty ||
_procedures.isNotEmpty ||
_functions.isNotEmpty)
material.Padding(
padding: const material.EdgeInsets.only(left: 16),
child: material.Column(
Expand Down Expand Up @@ -512,6 +528,44 @@ class _MysqlDatabaseNodeState extends State<_MysqlDatabaseNode> {
MysqlObjectKind.view,
),
),
if (_procedures.isNotEmpty)
_MysqlObjectGroup(
connection: widget.connection,
databaseName: widget.databaseName,
objectKind: MysqlObjectKind.procedure,
onRefresh: _loadTables,
label: 'Procedures',
icon: material.Icons.functions_rounded,
itemIcon: material.Icons.code_rounded,
items: _procedures,
onItemTap: widget.onMysqlObjectSelected == null
? null
: (name) => widget.onMysqlObjectSelected!(
widget.connection,
widget.databaseName,
name,
MysqlObjectKind.procedure,
),
),
if (_functions.isNotEmpty)
_MysqlObjectGroup(
connection: widget.connection,
databaseName: widget.databaseName,
objectKind: MysqlObjectKind.function,
onRefresh: _loadTables,
label: 'Functions',
icon: material.Icons.functions_rounded,
itemIcon: material.Icons.code_rounded,
items: _functions,
onItemTap: widget.onMysqlObjectSelected == null
? null
: (name) => widget.onMysqlObjectSelected!(
widget.connection,
widget.databaseName,
name,
MysqlObjectKind.function,
),
),
],
),
),
Expand Down
46 changes: 30 additions & 16 deletions lib/features/main_screen/workspace_panel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import 'package:querya_desktop/core/storage/local_db.dart';
import 'package:querya_desktop/shared/widgets/widgets.dart';

import 'package:querya_desktop/features/mysql/mysql_object_kind.dart';
import 'package:querya_desktop/features/mysql/mysql_routine_view.dart';
import 'package:querya_desktop/features/mysql/mysql_table_view.dart';
import 'package:querya_desktop/features/mysql/mysql_workspace_home.dart';
import 'package:querya_desktop/features/mongodb/mongo_explorer_view.dart';
Expand Down Expand Up @@ -183,22 +184,35 @@ class _WorkspacePanelState extends State<WorkspacePanel> {
break;
case 'mysql':
final my = widget.selectedMysqlObject;
driverWorkspace = my == null
? MysqlWorkspaceHome(
key: ValueKey('mysql_home_${activeConn.id}'),
connectionRow: activeConn,
sqlTabRequestToken: widget.mysqlSqlTabRequestToken,
isReadOnly: widget.isReadOnly,
)
: MysqlTableView(
key: ValueKey(
'mysql_${activeConn.id}_${my.database}_${my.name}_${my.kind}',
),
connectionRow: activeConn,
database: my.database,
tableName: my.name,
isView: my.kind == MysqlObjectKind.view,
);
if (my == null) {
driverWorkspace = MysqlWorkspaceHome(
key: ValueKey('mysql_home_${activeConn.id}'),
connectionRow: activeConn,
sqlTabRequestToken: widget.mysqlSqlTabRequestToken,
isReadOnly: widget.isReadOnly,
);
} else if (my.kind == MysqlObjectKind.procedure ||
my.kind == MysqlObjectKind.function) {
driverWorkspace = MysqlRoutineView(
key: ValueKey(
'mysql_${activeConn.id}_${my.database}_${my.name}_${my.kind}',
),
connectionRow: activeConn,
database: my.database,
routineName: my.name,
isFunction: my.kind == MysqlObjectKind.function,
);
} else {
driverWorkspace = MysqlTableView(
key: ValueKey(
'mysql_${activeConn.id}_${my.database}_${my.name}_${my.kind}',
),
connectionRow: activeConn,
database: my.database,
tableName: my.name,
isView: my.kind == MysqlObjectKind.view,
);
}
break;
case 'mongodb':
final mongoDb = widget.selectedMongoDb;
Expand Down
2 changes: 2 additions & 0 deletions lib/features/mysql/mysql_object_kind.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
enum MysqlObjectKind {
table,
view,
procedure,
function,
}
Loading
Loading