SqlView, SqlSp, SqlFunction kaldırıldı

This commit is contained in:
Sedat Öztürk 2026-03-02 21:31:49 +03:00
parent a87c06c2ff
commit fe19cacedc
33 changed files with 552 additions and 4808 deletions

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
@ -6,38 +5,20 @@ using Volo.Abp.Application.Services;
namespace Sozsoft.SqlQueryManager.Application.Contracts; namespace Sozsoft.SqlQueryManager.Application.Contracts;
/// <summary> /// <summary>
/// Unified service for SQL Object Explorer and CRUD operations /// SQL Query Manager - executes T-SQL and provides database metadata.
/// Does not persist SQL objects to its own tables.
/// </summary> /// </summary>
public interface ISqlObjectManagerAppService : IApplicationService public interface ISqlObjectManagerAppService : IApplicationService
{ {
/// <summary> /// <summary>
/// Get all SQL objects for Object Explorer (Queries, SPs, Views, Functions, Tables, Templates) /// Returns tables (and optionally templates) available on the given data source.
/// </summary> /// </summary>
/// <param name="dataSourceCode">Data source code to filter objects</param>
/// <returns>Combined response with all object types</returns>
Task<SqlObjectExplorerDto> GetAllObjectsAsync(string dataSourceCode); Task<SqlObjectExplorerDto> GetAllObjectsAsync(string dataSourceCode);
// Query Operations /// <summary>
Task<SqlQueryDto> CreateQueryAsync(CreateSqlQueryDto input); /// Executes raw T-SQL against the specified data source.
Task<SqlQueryDto> UpdateQueryAsync(Guid id, UpdateSqlQueryDto input); /// </summary>
Task DeleteQueryAsync(Guid id);
Task<SqlQueryExecutionResultDto> ExecuteQueryAsync(ExecuteSqlQueryDto input); Task<SqlQueryExecutionResultDto> ExecuteQueryAsync(ExecuteSqlQueryDto input);
Task<SqlQueryExecutionResultDto> ExecuteSavedQueryAsync(Guid id);
// Stored Procedure Operations
Task<SqlStoredProcedureDto> UpdateStoredProcedureAsync(Guid id, UpdateSqlStoredProcedureDto input);
Task DeleteStoredProcedureAsync(Guid id);
Task<SqlQueryExecutionResultDto> DeployStoredProcedureAsync(DeployStoredProcedureDto input);
// View Operations
Task<SqlViewDto> UpdateViewAsync(Guid id, UpdateSqlViewDto input);
Task DeleteViewAsync(Guid id);
Task<SqlQueryExecutionResultDto> DeployViewAsync(DeployViewDto input);
// Function Operations
Task<SqlFunctionDto> UpdateFunctionAsync(Guid id, UpdateSqlFunctionDto input);
Task DeleteFunctionAsync(Guid id);
Task<SqlQueryExecutionResultDto> DeployFunctionAsync(DeployFunctionDto input);
// Database Metadata Operations // Database Metadata Operations
Task<List<DatabaseColumnDto>> GetTableColumnsAsync(string dataSourceCode, string schemaName, string tableName); Task<List<DatabaseColumnDto>> GetTableColumnsAsync(string dataSourceCode, string schemaName, string tableName);
@ -46,7 +27,4 @@ public interface ISqlObjectManagerAppService : IApplicationService
/// Gets the SQL definition/body of a native SQL Server object (Stored Procedure, View, or Function) /// Gets the SQL definition/body of a native SQL Server object (Stored Procedure, View, or Function)
/// </summary> /// </summary>
Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName); Task<string> GetNativeObjectDefinitionAsync(string dataSourceCode, string schemaName, string objectName);
// Smart Save - Analyzes SQL and saves to appropriate table with auto-deploy
Task<SmartSaveResultDto> SmartSaveAsync(SmartSaveInputDto input);
} }

View file

@ -1,25 +0,0 @@
using System;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
/// <summary>
/// Input for smart save operation
/// </summary>
public class SmartSaveInputDto
{
public string SqlText { get; set; }
public string DataSourceCode { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
/// <summary>
/// Result of smart save operation
/// </summary>
public class SmartSaveResultDto
{
public string ObjectType { get; set; }
public Guid ObjectId { get; set; }
public bool Deployed { get; set; }
public string Message { get; set; }
}

View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
public class ExecuteSqlQueryDto
{
public string QueryText { get; set; }
public string DataSourceCode { get; set; }
public Dictionary<string, object> Parameters { get; set; }
}
public class SqlQueryExecutionResultDto
{
public bool Success { get; set; }
public string Message { get; set; }
public IEnumerable<dynamic> Data { get; set; }
public int RowsAffected { get; set; }
public long ExecutionTimeMs { get; set; }
public Dictionary<string, object> Metadata { get; set; }
}

View file

@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Application.Dtos;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
public class SqlFunctionDto : FullAuditedEntityDto<Guid>
{
public string FunctionName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public SqlFunctionType FunctionType { get; set; }
public string FunctionBody { get; set; }
public string ReturnType { get; set; }
public string DataSourceCode { get; set; }
public SqlQueryStatus Status { get; set; }
public string Category { get; set; }
public bool IsDeployed { get; set; }
public DateTime? LastDeployedAt { get; set; }
public string Parameters { get; set; }
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
}
public class CreateSqlFunctionDto
{
public string FunctionName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public SqlFunctionType FunctionType { get; set; }
public string FunctionBody { get; set; }
public string ReturnType { get; set; }
public string DataSourceCode { get; set; }
public string Category { get; set; }
public string Parameters { get; set; }
}
public class UpdateSqlFunctionDto
{
public string DisplayName { get; set; }
public string Description { get; set; }
public string FunctionBody { get; set; }
public string ReturnType { get; set; }
public string Category { get; set; }
public string Parameters { get; set; }
}
public class DeployFunctionDto
{
public Guid Id { get; set; }
public bool DropIfExists { get; set; }
}

View file

@ -3,37 +3,23 @@ using System.Collections.Generic;
namespace Sozsoft.SqlQueryManager.Application.Contracts; namespace Sozsoft.SqlQueryManager.Application.Contracts;
/// <summary> /// <summary>
/// Combined DTO for Object Explorer containing all SQL objects /// A native SQL Server object (view, stored procedure, or function) discovered from system catalogs.
/// </summary>
public class SqlNativeObjectDto
{
public string SchemaName { get; set; } = "dbo";
public string ObjectName { get; set; } = "";
public string FullName => $"[{SchemaName}].[{ObjectName}]";
}
/// <summary>
/// Object Explorer DTO - reflects the live SQL Server catalog structure.
/// </summary> /// </summary>
public class SqlObjectExplorerDto public class SqlObjectExplorerDto
{ {
/// <summary>
/// SQL Queries
/// </summary>
public List<SqlQueryDto> Queries { get; set; } = new();
/// <summary>
/// Stored Procedures
/// </summary>
public List<SqlStoredProcedureDto> StoredProcedures { get; set; } = new();
/// <summary>
/// Views
/// </summary>
public List<SqlViewDto> Views { get; set; } = new();
/// <summary>
/// Functions
/// </summary>
public List<SqlFunctionDto> Functions { get; set; } = new();
/// <summary>
/// Database Tables
/// </summary>
public List<DatabaseTableDto> Tables { get; set; } = new(); public List<DatabaseTableDto> Tables { get; set; } = new();
public List<SqlNativeObjectDto> Views { get; set; } = new();
/// <summary> public List<SqlNativeObjectDto> StoredProcedures { get; set; } = new();
/// Query Templates public List<SqlNativeObjectDto> Functions { get; set; } = new();
/// </summary>
public List<SqlTemplateDto> Templates { get; set; } = new(); public List<SqlTemplateDto> Templates { get; set; } = new();
} }

View file

@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Application.Dtos;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
public class SqlQueryDto : FullAuditedEntityDto<Guid>
{
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string QueryText { get; set; }
public string DataSourceCode { get; set; }
public SqlQueryStatus Status { get; set; }
public string Category { get; set; }
public string Tags { get; set; }
public DateTime? LastExecutedAt { get; set; }
public int ExecutionCount { get; set; }
public bool IsModifyingData { get; set; }
public string Parameters { get; set; }
}
public class CreateSqlQueryDto
{
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string QueryText { get; set; }
public string DataSourceCode { get; set; }
public string Category { get; set; }
public string Tags { get; set; }
public bool IsModifyingData { get; set; }
public string Parameters { get; set; }
}
public class UpdateSqlQueryDto
{
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string QueryText { get; set; }
public string DataSourceCode { get; set; }
public string Category { get; set; }
public string Tags { get; set; }
public bool IsModifyingData { get; set; }
public string Parameters { get; set; }
}
public class ExecuteSqlQueryDto
{
public string QueryText { get; set; }
public string DataSourceCode { get; set; }
public Dictionary<string, object> Parameters { get; set; }
}
public class SqlQueryExecutionResultDto
{
public bool Success { get; set; }
public string Message { get; set; }
public IEnumerable<dynamic> Data { get; set; }
public int RowsAffected { get; set; }
public long ExecutionTimeMs { get; set; }
public Dictionary<string, object> Metadata { get; set; }
}

View file

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Application.Dtos;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
public class SqlStoredProcedureDto : FullAuditedEntityDto<Guid>
{
public string ProcedureName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string ProcedureBody { get; set; }
public string DataSourceCode { get; set; }
public SqlQueryStatus Status { get; set; }
public string Category { get; set; }
public bool IsDeployed { get; set; }
public DateTime? LastDeployedAt { get; set; }
public string Parameters { get; set; }
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
}
public class CreateSqlStoredProcedureDto
{
public string ProcedureName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string ProcedureBody { get; set; }
public string DataSourceCode { get; set; }
public string Category { get; set; }
public string Parameters { get; set; }
}
public class UpdateSqlStoredProcedureDto
{
public string DisplayName { get; set; }
public string Description { get; set; }
public string ProcedureBody { get; set; }
public string Category { get; set; }
public string Parameters { get; set; }
}
public class DeployStoredProcedureDto
{
public Guid Id { get; set; }
public bool DropIfExists { get; set; }
}

View file

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Application.Dtos;
namespace Sozsoft.SqlQueryManager.Application.Contracts;
public class SqlViewDto : FullAuditedEntityDto<Guid>
{
public string ViewName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string ViewDefinition { get; set; }
public string DataSourceCode { get; set; }
public SqlQueryStatus Status { get; set; }
public string Category { get; set; }
public bool IsDeployed { get; set; }
public DateTime? LastDeployedAt { get; set; }
public bool WithSchemaBinding { get; set; }
public bool IsCustom { get; set; } // true = stored in database, false = native SQL Server object
}
public class CreateSqlViewDto
{
public string ViewName { get; set; }
public string SchemaName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string ViewDefinition { get; set; }
public string DataSourceCode { get; set; }
public string Category { get; set; }
public bool WithSchemaBinding { get; set; }
}
public class UpdateSqlViewDto
{
public string DisplayName { get; set; }
public string Description { get; set; }
public string ViewDefinition { get; set; }
public string Category { get; set; }
public bool WithSchemaBinding { get; set; }
}
public class DeployViewDto
{
public Guid Id { get; set; }
public bool DropIfExists { get; set; }
}

View file

@ -1,88 +1,12 @@
using AutoMapper; using AutoMapper;
using Sozsoft.SqlQueryManager.Application.Contracts;
using Sozsoft.SqlQueryManager.Domain.Entities;
using Volo.Abp.AutoMapper;
namespace Sozsoft.SqlQueryManager.Application; namespace Sozsoft.SqlQueryManager.Application;
// No entity-to-DTO mappings needed; SQL objects are no longer persisted in this module.
public class SqlQueryManagerAutoMapperProfile : Profile public class SqlQueryManagerAutoMapperProfile : Profile
{ {
public SqlQueryManagerAutoMapperProfile() public SqlQueryManagerAutoMapperProfile()
{ {
CreateMap<SqlQuery, SqlQueryDto>(); // intentionally empty
CreateMap<CreateSqlQueryDto, SqlQuery>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.Status)
.Ignore(x => x.LastExecutedAt)
.Ignore(x => x.ExecutionCount);
CreateMap<UpdateSqlQueryDto, SqlQuery>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.Status)
.Ignore(x => x.LastExecutedAt)
.Ignore(x => x.ExecutionCount);
CreateMap<SqlStoredProcedure, SqlStoredProcedureDto>().Ignore(x => x.IsCustom);
CreateMap<CreateSqlStoredProcedureDto, SqlStoredProcedure>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
CreateMap<UpdateSqlStoredProcedureDto, SqlStoredProcedure>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.ProcedureName)
.Ignore(x => x.SchemaName)
.Ignore(x => x.DataSourceCode)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
CreateMap<SqlView, SqlViewDto>().Ignore(x => x.IsCustom);
CreateMap<CreateSqlViewDto, SqlView>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
CreateMap<UpdateSqlViewDto, SqlView>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.ViewName)
.Ignore(x => x.SchemaName)
.Ignore(x => x.DataSourceCode)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
CreateMap<SqlFunction, SqlFunctionDto>().Ignore(x => x.IsCustom);
CreateMap<CreateSqlFunctionDto, SqlFunction>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
CreateMap<UpdateSqlFunctionDto, SqlFunction>()
.IgnoreFullAuditedObjectProperties()
.Ignore(x => x.Id)
.Ignore(x => x.TenantId)
.Ignore(x => x.FunctionName)
.Ignore(x => x.SchemaName)
.Ignore(x => x.FunctionType)
.Ignore(x => x.DataSourceCode)
.Ignore(x => x.Status)
.Ignore(x => x.IsDeployed)
.Ignore(x => x.LastDeployedAt);
} }
} }

View file

@ -1,23 +1,4 @@
namespace Sozsoft.SqlQueryManager.Domain.Shared; namespace Sozsoft.SqlQueryManager.Domain.Shared;
public enum SqlObjectType // SqlObjectType, SqlFunctionType and SqlQueryStatus enums removed:
{ // The module no longer persists SQL objects and therefore does not need these enums.
Query = 1,
StoredProcedure = 2,
View = 3,
Function = 4
}
public enum SqlFunctionType
{
ScalarFunction = 1,
TableValuedFunction = 2,
InlineTableValuedFunction = 3
}
public enum SqlQueryStatus
{
Draft = 1,
Active = 2,
Archived = 3
}

View file

@ -6,50 +6,12 @@ public static class SqlQueryManagerPermissions
{ {
public const string GroupName = "SqlQueryManager"; public const string GroupName = "SqlQueryManager";
public static class SqlQuery public static class SqlExecution
{ {
public const string Default = GroupName + ".SqlQuery"; public const string Default = GroupName + ".SqlExecution";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Execute = Default + ".Execute"; public const string Execute = Default + ".Execute";
} }
public static class SqlStoredProcedure
{
public const string Default = GroupName + ".SqlStoredProcedure";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Deploy = Default + ".Deploy";
public const string Drop = Default + ".Drop";
}
public static class SqlView
{
public const string Default = GroupName + ".SqlView";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Deploy = Default + ".Deploy";
public const string Drop = Default + ".Drop";
}
public static class SqlFunction
{
public const string Default = GroupName + ".SqlFunction";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
public const string Deploy = Default + ".Deploy";
public const string Drop = Default + ".Drop";
}
public static class Templates
{
public const string Default = GroupName + ".Templates";
}
public static string[] GetAll() public static string[] GetAll()
{ {
return ReflectionHelper.GetPublicConstantsRecursively(typeof(SqlQueryManagerPermissions)); return ReflectionHelper.GetPublicConstantsRecursively(typeof(SqlQueryManagerPermissions));

View file

@ -1,120 +0,0 @@
using System;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.SqlQueryManager.Domain.Entities;
/// <summary>
/// SQL Function entity for creating and managing database functions
/// </summary>
public class SqlFunction : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
/// <summary>
/// Function name in database
/// </summary>
public string FunctionName { get; set; }
/// <summary>
/// Schema name (default: dbo)
/// </summary>
public string SchemaName { get; set; }
/// <summary>
/// Display name
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Function type
/// </summary>
public SqlFunctionType FunctionType { get; set; }
/// <summary>
/// Full function definition (CREATE/ALTER)
/// </summary>
public string FunctionBody { get; set; }
/// <summary>
/// Return type definition
/// </summary>
public string ReturnType { get; set; }
/// <summary>
/// DataSource code
/// </summary>
public string DataSourceCode { get; set; }
/// <summary>
/// Status
/// </summary>
public SqlQueryStatus Status { get; set; }
/// <summary>
/// Category
/// </summary>
public string Category { get; set; }
/// <summary>
/// Whether function exists in database
/// </summary>
public bool IsDeployed { get; set; }
/// <summary>
/// Last deployment time
/// </summary>
public DateTime? LastDeployedAt { get; set; }
/// <summary>
/// Parameter definitions (JSON)
/// </summary>
public string Parameters { get; set; }
protected SqlFunction()
{
}
public SqlFunction(
Guid id,
string functionName,
string schemaName,
string displayName,
SqlFunctionType functionType,
string functionBody,
string returnType,
string dataSourceCode,
Guid? tenantId = null) : base(id)
{
FunctionName = functionName;
SchemaName = schemaName ?? "dbo";
DisplayName = displayName;
FunctionType = functionType;
FunctionBody = functionBody;
ReturnType = returnType;
DataSourceCode = dataSourceCode;
TenantId = tenantId;
Status = SqlQueryStatus.Draft;
IsDeployed = false;
}
public void UpdateBody(string body)
{
FunctionBody = body;
}
public void MarkAsDeployed()
{
IsDeployed = true;
LastDeployedAt = DateTime.UtcNow;
Status = SqlQueryStatus.Active;
}
public string GetFullName() => $"{SchemaName}.{FunctionName}";
}

View file

@ -1,116 +0,0 @@
using System;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.SqlQueryManager.Domain.Entities;
/// <summary>
/// SQL Query entity for storing and managing SQL queries
/// </summary>
public class SqlQuery : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
/// <summary>
/// Unique code for the query
/// </summary>
public string Code { get; set; }
/// <summary>
/// Display name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Description of the query
/// </summary>
public string Description { get; set; }
/// <summary>
/// SQL query content
/// </summary>
public string QueryText { get; set; }
/// <summary>
/// DataSource code to use for execution
/// </summary>
public string DataSourceCode { get; set; }
/// <summary>
/// Query status
/// </summary>
public SqlQueryStatus Status { get; set; }
/// <summary>
/// Category for organization
/// </summary>
public string Category { get; set; }
/// <summary>
/// Tags for filtering
/// </summary>
public string Tags { get; set; }
/// <summary>
/// Last execution time
/// </summary>
public DateTime? LastExecutedAt { get; set; }
/// <summary>
/// Execution count
/// </summary>
public int ExecutionCount { get; set; }
/// <summary>
/// Whether query modifies data
/// </summary>
public bool IsModifyingData { get; set; }
/// <summary>
/// Expected parameter definitions (JSON format)
/// </summary>
public string Parameters { get; set; }
protected SqlQuery()
{
}
public SqlQuery(
Guid id,
string code,
string name,
string queryText,
string dataSourceCode,
Guid? tenantId = null) : base(id)
{
Code = code;
Name = name;
QueryText = queryText;
DataSourceCode = dataSourceCode;
TenantId = tenantId;
Status = SqlQueryStatus.Draft;
ExecutionCount = 0;
}
public void UpdateQueryText(string queryText)
{
QueryText = queryText;
}
public void MarkAsExecuted()
{
LastExecutedAt = DateTime.UtcNow;
ExecutionCount++;
}
public void Activate()
{
Status = SqlQueryStatus.Active;
}
public void Archive()
{
Status = SqlQueryStatus.Archived;
}
}

View file

@ -1,106 +0,0 @@
using System;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.SqlQueryManager.Domain.Entities;
/// <summary>
/// SQL Stored Procedure entity for creating and managing stored procedures
/// </summary>
public class SqlStoredProcedure : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
/// <summary>
/// Procedure name in database
/// </summary>
public string ProcedureName { get; set; }
/// <summary>
/// Schema name (default: dbo)
/// </summary>
public string SchemaName { get; set; }
/// <summary>
/// Display name
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// Full procedure definition (CREATE/ALTER)
/// </summary>
public string ProcedureBody { get; set; }
/// <summary>
/// DataSource code
/// </summary>
public string DataSourceCode { get; set; }
/// <summary>
/// Status
/// </summary>
public SqlQueryStatus Status { get; set; }
/// <summary>
/// Category
/// </summary>
public string Category { get; set; }
/// <summary>
/// Whether procedure exists in database
/// </summary>
public bool IsDeployed { get; set; }
/// <summary>
/// Last deployment time
/// </summary>
public DateTime? LastDeployedAt { get; set; }
/// <summary>
/// Parameter definitions (JSON)
/// </summary>
public string Parameters { get; set; }
protected SqlStoredProcedure()
{
}
public SqlStoredProcedure(
Guid id,
string procedureName,
string schemaName,
string displayName,
string procedureBody,
string dataSourceCode,
Guid? tenantId = null) : base(id)
{
ProcedureName = procedureName;
SchemaName = schemaName ?? "dbo";
DisplayName = displayName;
ProcedureBody = procedureBody;
DataSourceCode = dataSourceCode;
TenantId = tenantId;
Status = SqlQueryStatus.Draft;
IsDeployed = false;
}
public void UpdateBody(string body)
{
ProcedureBody = body;
}
public void MarkAsDeployed()
{
IsDeployed = true;
LastDeployedAt = DateTime.UtcNow;
Status = SqlQueryStatus.Active;
}
public string GetFullName() => $"{SchemaName}.{ProcedureName}";
}

View file

@ -1,107 +0,0 @@
using System;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace Sozsoft.SqlQueryManager.Domain.Entities;
/// <summary>
/// SQL View entity for creating and managing database views
/// </summary>
public class SqlView : FullAuditedEntity<Guid>, IMultiTenant
{
public Guid? TenantId { get; set; }
/// <summary>
/// View name in database
/// </summary>
public string ViewName { get; set; }
/// <summary>
/// Schema name (default: dbo)
/// </summary>
public string SchemaName { get; set; }
/// <summary>
/// Display name
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// Description
/// </summary>
public string Description { get; set; }
/// <summary>
/// View definition (SELECT statement)
/// </summary>
public string ViewDefinition { get; set; }
/// <summary>
/// DataSource code
/// </summary>
public string DataSourceCode { get; set; }
/// <summary>
/// Status
/// </summary>
public SqlQueryStatus Status { get; set; }
/// <summary>
/// Category
/// </summary>
public string Category { get; set; }
/// <summary>
/// Whether view exists in database
/// </summary>
public bool IsDeployed { get; set; }
/// <summary>
/// Last deployment time
/// </summary>
public DateTime? LastDeployedAt { get; set; }
/// <summary>
/// Whether to use WITH SCHEMABINDING
/// </summary>
public bool WithSchemaBinding { get; set; }
protected SqlView()
{
}
public SqlView(
Guid id,
string viewName,
string schemaName,
string displayName,
string viewDefinition,
string dataSourceCode,
Guid? tenantId = null) : base(id)
{
ViewName = viewName;
SchemaName = schemaName ?? "dbo";
DisplayName = displayName;
ViewDefinition = viewDefinition;
DataSourceCode = dataSourceCode;
TenantId = tenantId;
Status = SqlQueryStatus.Draft;
IsDeployed = false;
WithSchemaBinding = false;
}
public void UpdateDefinition(string definition)
{
ViewDefinition = definition;
}
public void MarkAsDeployed()
{
IsDeployed = true;
LastDeployedAt = DateTime.UtcNow;
Status = SqlQueryStatus.Active;
}
public string GetFullName() => $"{SchemaName}.{ViewName}";
}

View file

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Sozsoft.SqlQueryManager.Domain.Services; namespace Sozsoft.SqlQueryManager.Domain.Services;
@ -22,75 +22,28 @@ public class SqlExecutionResult
} }
/// <summary> /// <summary>
/// Service for executing SQL commands /// Service for executing T-SQL commands against configured data sources.
/// </summary> /// </summary>
public interface ISqlExecutorService public interface ISqlExecutorService
{ {
/// <summary> /// <summary>Execute a SELECT query and return results.</summary>
/// Execute a SELECT query and return results
/// </summary>
Task<SqlExecutionResult> ExecuteQueryAsync( Task<SqlExecutionResult> ExecuteQueryAsync(
string sql, string sql,
string dataSourceCode, string dataSourceCode,
Dictionary<string, object> parameters = null); Dictionary<string, object> parameters = null);
/// <summary> /// <summary>Execute a non-query command (INSERT, UPDATE, DELETE, DDL).</summary>
/// Execute a non-query command (INSERT, UPDATE, DELETE)
/// </summary>
Task<SqlExecutionResult> ExecuteNonQueryAsync( Task<SqlExecutionResult> ExecuteNonQueryAsync(
string sql, string sql,
string dataSourceCode, string dataSourceCode,
Dictionary<string, object> parameters = null); Dictionary<string, object> parameters = null);
/// <summary> /// <summary>Execute scalar query (returns single value).</summary>
/// Execute scalar query (returns single value)
/// </summary>
Task<SqlExecutionResult> ExecuteScalarAsync<T>( Task<SqlExecutionResult> ExecuteScalarAsync<T>(
string sql, string sql,
string dataSourceCode, string dataSourceCode,
Dictionary<string, object> parameters = null); Dictionary<string, object> parameters = null);
/// <summary> /// <summary>Validate SQL syntax (basic validation).</summary>
/// Deploy stored procedure to database
/// </summary>
Task<SqlExecutionResult> DeployStoredProcedureAsync(
string procedureBody,
string dataSourceCode);
/// <summary>
/// Deploy view to database
/// </summary>
Task<SqlExecutionResult> DeployViewAsync(
string viewDefinition,
string dataSourceCode);
/// <summary>
/// Deploy function to database
/// </summary>
Task<SqlExecutionResult> DeployFunctionAsync(
string functionBody,
string dataSourceCode);
/// <summary>
/// Check if database object exists
/// </summary>
Task<bool> CheckObjectExistsAsync(
string objectName,
string objectType,
string dataSourceCode,
string schemaName = "dbo");
/// <summary>
/// Drop database object
/// </summary>
Task<SqlExecutionResult> DropObjectAsync(
string objectName,
string objectType,
string dataSourceCode,
string schemaName = "dbo");
/// <summary>
/// Validate SQL syntax (basic validation)
/// </summary>
Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql); Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql);
} }

View file

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using Sozsoft.SqlQueryManager.Domain.Shared;
namespace Sozsoft.SqlQueryManager.Domain.Services; namespace Sozsoft.SqlQueryManager.Domain.Services;
@ -8,29 +7,21 @@ namespace Sozsoft.SqlQueryManager.Domain.Services;
/// </summary> /// </summary>
public interface ISqlTemplateProvider public interface ISqlTemplateProvider
{ {
/// <summary> /// <summary>Get stored procedure template.</summary>
/// Get stored procedure template
/// </summary>
string GetStoredProcedureTemplate(string procedureName, string schemaName = "dbo"); string GetStoredProcedureTemplate(string procedureName, string schemaName = "dbo");
/// <summary> /// <summary>Get view template.</summary>
/// Get view template
/// </summary>
string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false); string GetViewTemplate(string viewName, string schemaName = "dbo", bool withSchemaBinding = false);
/// <summary> /// <summary>
/// Get function template /// Get function template. functionType: "Scalar" | "TableValued" | "InlineTableValued"
/// </summary> /// </summary>
string GetFunctionTemplate(string functionName, SqlFunctionType functionType, string schemaName = "dbo"); string GetFunctionTemplate(string functionName, string functionType = "Scalar", string schemaName = "dbo");
/// <summary> /// <summary>Get query template with common patterns.</summary>
/// Get query template with common patterns
/// </summary>
string GetQueryTemplate(string queryType); string GetQueryTemplate(string queryType);
/// <summary> /// <summary>Get available query template types.</summary>
/// Get available query template types
/// </summary>
List<QueryTemplateInfo> GetAvailableQueryTemplates(); List<QueryTemplateInfo> GetAvailableQueryTemplates();
} }

View file

@ -179,81 +179,6 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
return result; return result;
} }
public async Task<SqlExecutionResult> DeployStoredProcedureAsync(
string procedureBody,
string dataSourceCode)
{
return await ExecuteNonQueryAsync(procedureBody, dataSourceCode);
}
public async Task<SqlExecutionResult> DeployViewAsync(
string viewDefinition,
string dataSourceCode)
{
return await ExecuteNonQueryAsync(viewDefinition, dataSourceCode);
}
public async Task<SqlExecutionResult> DeployFunctionAsync(
string functionBody,
string dataSourceCode)
{
return await ExecuteNonQueryAsync(functionBody, dataSourceCode);
}
public async Task<bool> CheckObjectExistsAsync(
string objectName,
string objectType,
string dataSourceCode,
string schemaName = "dbo")
{
try
{
var sql = $@"
SELECT COUNT(*)
FROM sys.objects o
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE s.name = @SchemaName
AND o.name = @ObjectName
AND o.type_desc = @ObjectType";
var parameters = new Dictionary<string, object>
{
{ "SchemaName", schemaName },
{ "ObjectName", objectName },
{ "ObjectType", objectType.ToUpperInvariant() }
};
var result = await ExecuteScalarAsync<int>(sql, dataSourceCode, parameters);
return result.Success && result.Metadata.ContainsKey("ScalarValue")
&& Convert.ToInt32(result.Metadata["ScalarValue"]) > 0;
}
catch
{
return false;
}
}
public async Task<SqlExecutionResult> DropObjectAsync(
string objectName,
string objectType,
string dataSourceCode,
string schemaName = "dbo")
{
var dropCommand = objectType.ToUpperInvariant() switch
{
"SQL_STORED_PROCEDURE" => $"DROP PROCEDURE IF EXISTS [{schemaName}].[{objectName}]",
"VIEW" => $"DROP VIEW IF EXISTS [{schemaName}].[{objectName}]",
"SQL_SCALAR_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]",
"SQL_TABLE_VALUED_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]",
"SQL_INLINE_TABLE_VALUED_FUNCTION" => $"DROP FUNCTION IF EXISTS [{schemaName}].[{objectName}]",
_ => throw new BusinessException("SqlQueryManager:UnsupportedObjectType")
.WithData("ObjectType", objectType)
};
return await ExecuteNonQueryAsync(dropCommand, dataSourceCode);
}
public Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql) public Task<(bool IsValid, string ErrorMessage)> ValidateSqlAsync(string sql)
{ {
try try
@ -299,3 +224,4 @@ public class SqlExecutorService : DomainService, ISqlExecutorService
} }
} }
} }

View file

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Sozsoft.SqlQueryManager.Domain.Shared;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Sozsoft.SqlQueryManager.Domain.Services; namespace Sozsoft.SqlQueryManager.Domain.Services;
@ -70,13 +69,12 @@ AS
GO"; GO";
} }
public string GetFunctionTemplate(string functionName, SqlFunctionType functionType, string schemaName = "dbo") public string GetFunctionTemplate(string functionName, string functionType = "Scalar", string schemaName = "dbo")
{ {
return functionType switch return functionType switch
{ {
SqlFunctionType.ScalarFunction => GetScalarFunctionTemplate(functionName, schemaName), "TableValued" => GetTableValuedFunctionTemplate(functionName, schemaName),
SqlFunctionType.TableValuedFunction => GetTableValuedFunctionTemplate(functionName, schemaName), "InlineTableValued" => GetInlineTableValuedFunctionTemplate(functionName, schemaName),
SqlFunctionType.InlineTableValuedFunction => GetInlineTableValuedFunctionTemplate(functionName, schemaName),
_ => GetScalarFunctionTemplate(functionName, schemaName) _ => GetScalarFunctionTemplate(functionName, schemaName)
}; };
} }

View file

@ -1,4 +1,3 @@
using Sozsoft.SqlQueryManager.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
@ -8,11 +7,6 @@ namespace Sozsoft.SqlQueryManager.EntityFrameworkCore;
[ConnectionStringName("Default")] [ConnectionStringName("Default")]
public class SqlQueryManagerDbContext : AbpDbContext<SqlQueryManagerDbContext> public class SqlQueryManagerDbContext : AbpDbContext<SqlQueryManagerDbContext>
{ {
public DbSet<SqlQuery> SqlQueries { get; set; }
public DbSet<SqlStoredProcedure> SqlStoredProcedures { get; set; }
public DbSet<SqlView> SqlViews { get; set; }
public DbSet<SqlFunction> SqlFunctions { get; set; }
public SqlQueryManagerDbContext(DbContextOptions<SqlQueryManagerDbContext> options) public SqlQueryManagerDbContext(DbContextOptions<SqlQueryManagerDbContext> options)
: base(options) : base(options)
{ {
@ -21,7 +15,6 @@ public class SqlQueryManagerDbContext : AbpDbContext<SqlQueryManagerDbContext>
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.ConfigureSqlQueryManager(); builder.ConfigureSqlQueryManager();
} }
} }

View file

@ -1,7 +1,5 @@
using Sozsoft.SqlQueryManager.Domain.Entities; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Sozsoft.SqlQueryManager.EntityFrameworkCore; namespace Sozsoft.SqlQueryManager.EntityFrameworkCore;
@ -10,95 +8,7 @@ public static class SqlQueryManagerDbContextModelCreatingExtensions
public static void ConfigureSqlQueryManager(this ModelBuilder builder) public static void ConfigureSqlQueryManager(this ModelBuilder builder)
{ {
Check.NotNull(builder, nameof(builder)); Check.NotNull(builder, nameof(builder));
// SQL object entities (SqlQuery, SqlStoredProcedure, SqlView, SqlFunction) have been
// SqlQuery // removed. The module no longer persists SQL objects to its own tables.
builder.Entity<SqlQuery>(b =>
{
b.ToTable(TablePrefix.ByName(nameof(SqlQuery)), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Code).IsRequired().HasMaxLength(128);
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
b.Property(x => x.Description).HasMaxLength(1024);
b.Property(x => x.QueryText).IsRequired();
b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128);
b.Property(x => x.Status).IsRequired();
b.Property(x => x.Category).HasMaxLength(128);
b.Property(x => x.Tags).HasMaxLength(512);
b.Property(x => x.Parameters).HasMaxLength(4096);
b.HasIndex(x => x.Code);
b.HasIndex(x => x.DataSourceCode);
b.HasIndex(x => x.Status);
b.HasIndex(x => x.Category);
});
// SqlStoredProcedure
builder.Entity<SqlStoredProcedure>(b =>
{
b.ToTable(TablePrefix.ByName(nameof(SqlStoredProcedure)), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.ProcedureName).IsRequired().HasMaxLength(128);
b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128);
b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256);
b.Property(x => x.Description).HasMaxLength(1024);
b.Property(x => x.ProcedureBody).IsRequired();
b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128);
b.Property(x => x.Status).IsRequired();
b.Property(x => x.Category).HasMaxLength(128);
b.Property(x => x.Parameters).HasMaxLength(4096);
b.HasIndex(x => new { x.SchemaName, x.ProcedureName });
b.HasIndex(x => x.DataSourceCode);
b.HasIndex(x => x.Status);
b.HasIndex(x => x.IsDeployed);
});
// SqlView
builder.Entity<SqlView>(b =>
{
b.ToTable(TablePrefix.ByName(nameof(SqlView)), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.ViewName).IsRequired().HasMaxLength(128);
b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128);
b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256);
b.Property(x => x.Description).HasMaxLength(1024);
b.Property(x => x.ViewDefinition).IsRequired();
b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128);
b.Property(x => x.Status).IsRequired();
b.Property(x => x.Category).HasMaxLength(128);
b.HasIndex(x => new { x.SchemaName, x.ViewName });
b.HasIndex(x => x.DataSourceCode);
b.HasIndex(x => x.Status);
b.HasIndex(x => x.IsDeployed);
});
// SqlFunction
builder.Entity<SqlFunction>(b =>
{
b.ToTable(TablePrefix.ByName(nameof(SqlFunction)), Prefix.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.FunctionName).IsRequired().HasMaxLength(128);
b.Property(x => x.SchemaName).IsRequired().HasMaxLength(128);
b.Property(x => x.DisplayName).IsRequired().HasMaxLength(256);
b.Property(x => x.Description).HasMaxLength(1024);
b.Property(x => x.FunctionType).IsRequired();
b.Property(x => x.FunctionBody).IsRequired();
b.Property(x => x.ReturnType).IsRequired().HasMaxLength(256);
b.Property(x => x.DataSourceCode).IsRequired().HasMaxLength(128);
b.Property(x => x.Status).IsRequired();
b.Property(x => x.Category).HasMaxLength(128);
b.Property(x => x.Parameters).HasMaxLength(4096);
b.HasIndex(x => new { x.SchemaName, x.FunctionName });
b.HasIndex(x => x.DataSourceCode);
b.HasIndex(x => x.Status);
b.HasIndex(x => x.FunctionType);
b.HasIndex(x => x.IsDeployed);
});
} }
} }

View file

@ -10254,6 +10254,12 @@
"tr": "Çalıştır", "tr": "Çalıştır",
"en": "Execute" "en": "Execute"
}, },
{
"resourceName": "Platform",
"key": "App.Platform.NewQuery",
"tr": "Yeni Sorgu",
"en": "New Query"
},
{ {
"resourceName": "Platform", "resourceName": "Platform",
"key": "App.Platform.Save", "key": "App.Platform.Save",

View file

@ -31,10 +31,6 @@ public enum TableNameEnum
ForumPost, ForumPost,
SqlTable, SqlTable,
SqlTableField, SqlTableField,
SqlView,
SqlStoredProcedure,
SqlFunction,
SqlQuery,
CrudEndpoint, CrudEndpoint,
CustomEndpoint, CustomEndpoint,
CustomComponent, CustomComponent,

View file

@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Sozsoft.Platform.Migrations namespace Sozsoft.Platform.Migrations
{ {
[DbContext(typeof(PlatformDbContext))] [DbContext(typeof(PlatformDbContext))]
[Migration("20260302111924_Initial")] [Migration("20260302174517_Initial")]
partial class Initial partial class Initial
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -4486,409 +4486,6 @@ namespace Sozsoft.Platform.Migrations
b.ToTable("Sas_H_SettingDefinition", (string)null); b.ToTable("Sas_H_SettingDefinition", (string)null);
}); });
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlFunction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("FunctionBody")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FunctionName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("FunctionType")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("ReturnType")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("FunctionType");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "FunctionName");
b.ToTable("Adm_T_SqlFunction", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlQuery", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<int>("ExecutionCount")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsModifyingData")
.HasColumnType("bit");
b.Property<DateTime?>("LastExecutedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("QueryText")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("Tags")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("Code");
b.HasIndex("DataSourceCode");
b.HasIndex("Status");
b.ToTable("Adm_T_SqlQuery", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlStoredProcedure", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("ProcedureBody")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ProcedureName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "ProcedureName");
b.ToTable("Adm_T_SqlStoredProcedure", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlView", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("ViewDefinition")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ViewName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("WithSchemaBinding")
.HasColumnType("bit");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "ViewName");
b.ToTable("Adm_T_SqlView", (string)null);
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")

View file

@ -986,99 +986,6 @@ namespace Sozsoft.Platform.Migrations
table.PrimaryKey("PK_Adm_T_SkillType", x => x.Id); table.PrimaryKey("PK_Adm_T_SkillType", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "Adm_T_SqlFunction",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
FunctionName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
SchemaName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
FunctionType = table.Column<int>(type: "int", nullable: false),
FunctionBody = table.Column<string>(type: "nvarchar(max)", nullable: false),
ReturnType = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
Category = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
IsDeployed = table.Column<bool>(type: "bit", nullable: false),
LastDeployedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
Parameters = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_SqlFunction", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Adm_T_SqlQuery",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Code = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
QueryText = table.Column<string>(type: "nvarchar(max)", nullable: false),
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
Category = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
Tags = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
LastExecutedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
ExecutionCount = table.Column<int>(type: "int", nullable: false),
IsModifyingData = table.Column<bool>(type: "bit", nullable: false),
Parameters = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_SqlQuery", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Adm_T_SqlStoredProcedure",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ProcedureName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
SchemaName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
ProcedureBody = table.Column<string>(type: "nvarchar(max)", nullable: false),
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
Category = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
IsDeployed = table.Column<bool>(type: "bit", nullable: false),
LastDeployedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
Parameters = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_SqlStoredProcedure", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Adm_T_SqlTable", name: "Adm_T_SqlTable",
columns: table => new columns: table => new
@ -1107,36 +1014,6 @@ namespace Sozsoft.Platform.Migrations
table.PrimaryKey("PK_Adm_T_SqlTable", x => x.Id); table.PrimaryKey("PK_Adm_T_SqlTable", x => x.Id);
}); });
migrationBuilder.CreateTable(
name: "Adm_T_SqlView",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ViewName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
SchemaName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
DisplayName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
Description = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
ViewDefinition = table.Column<string>(type: "nvarchar(max)", nullable: false),
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
Category = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
IsDeployed = table.Column<bool>(type: "bit", nullable: false),
LastDeployedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
WithSchemaBinding = table.Column<bool>(type: "bit", nullable: false),
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifierId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
DeleterId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
DeletionTime = table.Column<DateTime>(type: "datetime2", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Adm_T_SqlView", x => x.Id);
});
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "Adm_T_UomCategory", name: "Adm_T_UomCategory",
columns: table => new columns: table => new
@ -2994,96 +2871,11 @@ namespace Sozsoft.Platform.Migrations
table: "Adm_T_SkillLevel", table: "Adm_T_SkillLevel",
column: "SkillTypeId"); column: "SkillTypeId");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlFunction_DataSourceCode",
table: "Adm_T_SqlFunction",
column: "DataSourceCode");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlFunction_FunctionType",
table: "Adm_T_SqlFunction",
column: "FunctionType");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlFunction_IsDeployed",
table: "Adm_T_SqlFunction",
column: "IsDeployed");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlFunction_SchemaName_FunctionName",
table: "Adm_T_SqlFunction",
columns: new[] { "SchemaName", "FunctionName" });
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlFunction_Status",
table: "Adm_T_SqlFunction",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlQuery_Category",
table: "Adm_T_SqlQuery",
column: "Category");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlQuery_Code",
table: "Adm_T_SqlQuery",
column: "Code");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlQuery_DataSourceCode",
table: "Adm_T_SqlQuery",
column: "DataSourceCode");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlQuery_Status",
table: "Adm_T_SqlQuery",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlStoredProcedure_DataSourceCode",
table: "Adm_T_SqlStoredProcedure",
column: "DataSourceCode");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlStoredProcedure_IsDeployed",
table: "Adm_T_SqlStoredProcedure",
column: "IsDeployed");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlStoredProcedure_SchemaName_ProcedureName",
table: "Adm_T_SqlStoredProcedure",
columns: new[] { "SchemaName", "ProcedureName" });
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlStoredProcedure_Status",
table: "Adm_T_SqlStoredProcedure",
column: "Status");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlTableField_EntityId", name: "IX_Adm_T_SqlTableField_EntityId",
table: "Adm_T_SqlTableField", table: "Adm_T_SqlTableField",
column: "EntityId"); column: "EntityId");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlView_DataSourceCode",
table: "Adm_T_SqlView",
column: "DataSourceCode");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlView_IsDeployed",
table: "Adm_T_SqlView",
column: "IsDeployed");
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlView_SchemaName_ViewName",
table: "Adm_T_SqlView",
columns: new[] { "SchemaName", "ViewName" });
migrationBuilder.CreateIndex(
name: "IX_Adm_T_SqlView_Status",
table: "Adm_T_SqlView",
column: "Status");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Adm_T_Uom_UomCategoryId", name: "IX_Adm_T_Uom_UomCategoryId",
table: "Adm_T_Uom", table: "Adm_T_Uom",
@ -3359,21 +3151,9 @@ namespace Sozsoft.Platform.Migrations
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Adm_T_SkillLevel"); name: "Adm_T_SkillLevel");
migrationBuilder.DropTable(
name: "Adm_T_SqlFunction");
migrationBuilder.DropTable(
name: "Adm_T_SqlQuery");
migrationBuilder.DropTable(
name: "Adm_T_SqlStoredProcedure");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Adm_T_SqlTableField"); name: "Adm_T_SqlTableField");
migrationBuilder.DropTable(
name: "Adm_T_SqlView");
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "Adm_T_Uom"); name: "Adm_T_Uom");

View file

@ -4483,409 +4483,6 @@ namespace Sozsoft.Platform.Migrations
b.ToTable("Sas_H_SettingDefinition", (string)null); b.ToTable("Sas_H_SettingDefinition", (string)null);
}); });
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlFunction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("FunctionBody")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FunctionName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("FunctionType")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("ReturnType")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("FunctionType");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "FunctionName");
b.ToTable("Adm_T_SqlFunction", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlQuery", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<int>("ExecutionCount")
.HasColumnType("int");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsModifyingData")
.HasColumnType("bit");
b.Property<DateTime?>("LastExecutedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("QueryText")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<string>("Tags")
.HasMaxLength(512)
.HasColumnType("nvarchar(512)");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("Category");
b.HasIndex("Code");
b.HasIndex("DataSourceCode");
b.HasIndex("Status");
b.ToTable("Adm_T_SqlQuery", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlStoredProcedure", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("Parameters")
.HasMaxLength(4096)
.HasColumnType("nvarchar(max)");
b.Property<string>("ProcedureBody")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ProcedureName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "ProcedureName");
b.ToTable("Adm_T_SqlStoredProcedure", (string)null);
});
modelBuilder.Entity("Sozsoft.SqlQueryManager.Domain.Entities.SqlView", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("Category")
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime2")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uniqueidentifier")
.HasColumnName("CreatorId");
b.Property<string>("DataSourceCode")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<Guid?>("DeleterId")
.HasColumnType("uniqueidentifier")
.HasColumnName("DeleterId");
b.Property<DateTime?>("DeletionTime")
.HasColumnType("datetime2")
.HasColumnName("DeletionTime");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("IsDeleted")
.ValueGeneratedOnAdd()
.HasColumnType("bit")
.HasDefaultValue(false)
.HasColumnName("IsDeleted");
b.Property<bool>("IsDeployed")
.HasColumnType("bit");
b.Property<DateTime?>("LastDeployedAt")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime2")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uniqueidentifier")
.HasColumnName("LastModifierId");
b.Property<string>("SchemaName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("uniqueidentifier")
.HasColumnName("TenantId");
b.Property<string>("ViewDefinition")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ViewName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<bool>("WithSchemaBinding")
.HasColumnType("bit");
b.HasKey("Id");
b.HasIndex("DataSourceCode");
b.HasIndex("IsDeployed");
b.HasIndex("Status");
b.HasIndex("SchemaName", "ViewName");
b.ToTable("Adm_T_SqlView", (string)null);
});
modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")

View file

@ -1,214 +1,10 @@
import type { FullAuditedEntityDto, PagedAndSortedResultRequestDto } from '../index' // SQL Execution
export enum SqlObjectType {
Query = 1,
StoredProcedure = 2,
View = 3,
Function = 4,
}
export enum SqlFunctionType {
ScalarFunction = 1,
TableValuedFunction = 2,
InlineTableValuedFunction = 3,
}
export enum SqlQueryStatus {
Draft = 1,
Active = 2,
Archived = 3,
}
// SQL Function DTOs
export interface SqlFunctionDto extends FullAuditedEntityDto<string> {
functionName: string
schemaName: string
displayName: string
description: string
functionType: SqlFunctionType
functionBody: string
returnType: string
dataSourceCode: string
status: SqlQueryStatus
category: string
isDeployed: boolean
lastDeployedAt?: string
parameters: string
isCustom: boolean // true = stored in database, false = native SQL Server object
}
export interface CreateSqlFunctionDto {
functionName: string
schemaName: string
displayName: string
description: string
functionType: SqlFunctionType
functionBody: string
returnType: string
dataSourceCode: string
category: string
parameters: string
}
export interface UpdateSqlFunctionDto {
displayName: string
description: string
functionBody: string
returnType: string
category: string
parameters: string
}
export interface DeployFunctionDto {
id: string
dropIfExists: boolean
}
// SQL Query DTOs
export interface SqlQueryDto extends FullAuditedEntityDto<string> {
code: string
name: string
description: string
queryText: string
dataSourceCode: string
status: SqlQueryStatus
category: string
tags: string
lastExecutedAt?: string
executionCount: number
isModifyingData: boolean
parameters: string
}
export interface CreateSqlQueryDto {
code: string
name: string
description: string
queryText: string
dataSourceCode: string
category: string
tags: string
isModifyingData: boolean
parameters: string
}
export interface UpdateSqlQueryDto {
code: string
name: string
description: string
queryText: string
dataSourceCode: string
category: string
tags: string
isModifyingData: boolean
parameters: string
}
export interface ExecuteSqlQueryDto { export interface ExecuteSqlQueryDto {
queryText: string queryText: string
dataSourceCode: string dataSourceCode: string
parameters?: Record<string, any> parameters?: Record<string, any>
} }
export interface ExecuteSavedQueryDto {
id: string
parameters?: Record<string, any>
}
export interface ValidateQueryDto {
queryText: string
dataSourceCode: string
}
// SQL Stored Procedure DTOs
export interface SqlStoredProcedureDto extends FullAuditedEntityDto<string> {
procedureName: string
schemaName: string
displayName: string
description: string
procedureBody: string
dataSourceCode: string
status: SqlQueryStatus
category: string
isDeployed: boolean
lastDeployedAt?: string
parameters: string
isCustom: boolean // true = stored in database, false = native SQL Server object
}
export interface CreateSqlStoredProcedureDto {
procedureName: string
schemaName: string
displayName: string
description: string
procedureBody: string
dataSourceCode: string
category: string
parameters: string
}
export interface UpdateSqlStoredProcedureDto {
displayName: string
description: string
procedureBody: string
category: string
parameters: string
}
export interface DeployStoredProcedureDto {
id: string
dropIfExists: boolean
}
// SQL View DTOs
export interface SqlViewDto extends FullAuditedEntityDto<string> {
viewName: string
schemaName: string
displayName: string
description: string
viewDefinition: string
dataSourceCode: string
status: SqlQueryStatus
category: string
isDeployed: boolean
lastDeployedAt?: string
withSchemaBinding: boolean
isCustom: boolean // true = stored in database, false = native SQL Server object
}
export interface CreateSqlViewDto {
viewName: string
schemaName: string
displayName: string
description: string
viewDefinition: string
dataSourceCode: string
category: string
withSchemaBinding: boolean
}
export interface UpdateSqlViewDto {
displayName: string
description: string
viewDefinition: string
category: string
withSchemaBinding: boolean
}
export interface DeployViewDto {
id: string
dropIfExists: boolean
}
// SQL Template DTOs
export interface SqlTemplateDto {
name: string
description: string
template: string
category: string
}
// SQL Execution Result
export interface SqlQueryExecutionResultDto { export interface SqlQueryExecutionResultDto {
success: boolean success: boolean
message: string message: string
@ -219,42 +15,27 @@ export interface SqlQueryExecutionResultDto {
error?: string error?: string
} }
// Request DTOs // SQL Templates
export interface GetSqlFunctionsInput extends PagedAndSortedResultRequestDto { export interface SqlTemplateDto {
filter?: string type: string
dataSourceCode?: string name: string
status?: SqlQueryStatus description: string
category?: string template: string
} }
export interface GetSqlQueriesInput extends PagedAndSortedResultRequestDto { // Database Metadata
filter?: string
dataSourceCode?: string
status?: SqlQueryStatus
category?: string
}
export interface GetSqlStoredProceduresInput extends PagedAndSortedResultRequestDto {
filter?: string
dataSourceCode?: string
status?: SqlQueryStatus
category?: string
}
export interface GetSqlViewsInput extends PagedAndSortedResultRequestDto {
filter?: string
dataSourceCode?: string
status?: SqlQueryStatus
category?: string
}
// Database Metadata DTOs
export interface DatabaseTableDto { export interface DatabaseTableDto {
schemaName: string schemaName: string
tableName: string tableName: string
fullName: string fullName: string
} }
export interface SqlNativeObjectDto {
schemaName: string
objectName: string
fullName: string
}
export interface DatabaseColumnDto { export interface DatabaseColumnDto {
columnName: string columnName: string
dataType: string dataType: string
@ -262,12 +43,11 @@ export interface DatabaseColumnDto {
maxLength?: number maxLength?: number
} }
// Unified Object Explorer Response // Object Explorer Response — mirrors live SQL Server catalog
export interface SqlObjectExplorerDto { export interface SqlObjectExplorerDto {
queries: SqlQueryDto[]
storedProcedures: SqlStoredProcedureDto[]
views: SqlViewDto[]
functions: SqlFunctionDto[]
tables: DatabaseTableDto[] tables: DatabaseTableDto[]
views: SqlNativeObjectDto[]
storedProcedures: SqlNativeObjectDto[]
functions: SqlNativeObjectDto[]
templates: SqlTemplateDto[] templates: SqlTemplateDto[]
} }

View file

@ -1,30 +1,14 @@
import apiService, { Config } from '@/services/api.service' import apiService, { Config } from '@/services/api.service'
import type { import type {
SqlFunctionDto,
UpdateSqlFunctionDto,
SqlQueryDto,
UpdateSqlQueryDto,
CreateSqlQueryDto,
ExecuteSqlQueryDto, ExecuteSqlQueryDto,
SqlStoredProcedureDto,
UpdateSqlStoredProcedureDto,
DeployStoredProcedureDto,
SqlViewDto,
UpdateSqlViewDto,
DeployViewDto,
DeployFunctionDto,
SqlQueryExecutionResultDto, SqlQueryExecutionResultDto,
DatabaseColumnDto, DatabaseColumnDto,
DatabaseTableDto,
SqlObjectExplorerDto, SqlObjectExplorerDto,
} from '@/proxy/sql-query-manager/models' } from '@/proxy/sql-query-manager/models'
export class SqlObjectManagerService { export class SqlObjectManagerService {
apiName = 'Default' apiName = 'Default'
/**
* Get all SQL objects for Object Explorer in a single call
*/
getAllObjects = (dataSourceCode: string, config?: Partial<Config>) => getAllObjects = (dataSourceCode: string, config?: Partial<Config>) =>
apiService.fetchData<SqlObjectExplorerDto, void>( apiService.fetchData<SqlObjectExplorerDto, void>(
{ {
@ -35,36 +19,6 @@ export class SqlObjectManagerService {
{ apiName: this.apiName, ...config }, { apiName: this.apiName, ...config },
) )
// Query Operations
createQuery = (input: CreateSqlQueryDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryDto, CreateSqlQueryDto>(
{
method: 'POST',
url: '/api/app/sql-object-manager/query',
data: input,
},
{ apiName: this.apiName, ...config },
)
updateQuery = (id: string, input: UpdateSqlQueryDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryDto, UpdateSqlQueryDto>(
{
method: 'PUT',
url: `/api/app/sql-object-manager/query/${id}`,
data: input,
},
{ apiName: this.apiName, ...config },
)
deleteQuery = (id: string, config?: Partial<Config>) =>
apiService.fetchData<void, void>(
{
method: 'DELETE',
url: `/api/app/sql-object-manager/${id}/query`,
},
{ apiName: this.apiName, ...config },
)
executeQuery = (input: ExecuteSqlQueryDto, config?: Partial<Config>) => executeQuery = (input: ExecuteSqlQueryDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryExecutionResultDto, ExecuteSqlQueryDto>( apiService.fetchData<SqlQueryExecutionResultDto, ExecuteSqlQueryDto>(
{ {
@ -75,106 +29,6 @@ export class SqlObjectManagerService {
{ apiName: this.apiName, ...config }, { apiName: this.apiName, ...config },
) )
executeSavedQuery = (id: string, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryExecutionResultDto, void>(
{
method: 'POST',
url: `/api/app/sql-object-manager/execute-saved-query/${id}`,
},
{ apiName: this.apiName, ...config },
)
// Stored Procedure Operations
updateStoredProcedure = (id: string, input: UpdateSqlStoredProcedureDto, config?: Partial<Config>) =>
apiService.fetchData<SqlStoredProcedureDto, UpdateSqlStoredProcedureDto>(
{
method: 'PUT',
url: `/api/app/sql-object-manager/stored-procedure/${id}`,
data: input,
},
{ apiName: this.apiName, ...config },
)
deleteStoredProcedure = (id: string, config?: Partial<Config>) =>
apiService.fetchData<void, void>(
{
method: 'DELETE',
url: `/api/app/sql-object-manager/${id}/stored-procedure`,
},
{ apiName: this.apiName, ...config },
)
deployStoredProcedure = (input: DeployStoredProcedureDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryExecutionResultDto, DeployStoredProcedureDto>(
{
method: 'POST',
url: '/api/app/sql-object-manager/deploy-stored-procedure',
data: input,
},
{ apiName: this.apiName, ...config },
)
// View Operations
updateView = (id: string, input: UpdateSqlViewDto, config?: Partial<Config>) =>
apiService.fetchData<SqlViewDto, UpdateSqlViewDto>(
{
method: 'PUT',
url: `/api/app/sql-object-manager/view/${id}`,
data: input,
},
{ apiName: this.apiName, ...config },
)
deleteView = (id: string, config?: Partial<Config>) =>
apiService.fetchData<void, void>(
{
method: 'DELETE',
url: `/api/app/sql-object-manager/${id}/view`,
},
{ apiName: this.apiName, ...config },
)
deployView = (input: DeployViewDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryExecutionResultDto, DeployViewDto>(
{
method: 'POST',
url: '/api/app/sql-object-manager/deploy-view',
data: input,
},
{ apiName: this.apiName, ...config },
)
// Function Operations
updateFunction = (id: string, input: UpdateSqlFunctionDto, config?: Partial<Config>) =>
apiService.fetchData<SqlFunctionDto, UpdateSqlFunctionDto>(
{
method: 'PUT',
url: `/api/app/sql-object-manager/function/${id}`,
data: input,
},
{ apiName: this.apiName, ...config },
)
deleteFunction = (id: string, config?: Partial<Config>) =>
apiService.fetchData<void, void>(
{
method: 'DELETE',
url: `/api/app/sql-object-manager/${id}/function`,
},
{ apiName: this.apiName, ...config },
)
deployFunction = (input: DeployFunctionDto, config?: Partial<Config>) =>
apiService.fetchData<SqlQueryExecutionResultDto, DeployFunctionDto>(
{
method: 'POST',
url: '/api/app/sql-object-manager/deploy-function',
data: input,
},
{ apiName: this.apiName, ...config },
)
// Database Metadata Operations
getTableColumns = (dataSourceCode: string, schemaName: string, tableName: string, config?: Partial<Config>) => getTableColumns = (dataSourceCode: string, schemaName: string, tableName: string, config?: Partial<Config>) =>
apiService.fetchData<DatabaseColumnDto[], void>( apiService.fetchData<DatabaseColumnDto[], void>(
{ {
@ -194,18 +48,6 @@ export class SqlObjectManagerService {
}, },
{ apiName: this.apiName, ...config }, { apiName: this.apiName, ...config },
) )
// Smart Save - Analyzes SQL and saves to appropriate table with auto-deploy
smartSave = (input: { sqlText: string; dataSourceCode: string; name?: string; description?: string }, config?: Partial<Config>) =>
apiService.fetchData<{ objectType: string; objectId: string; deployed: boolean; message: string }, typeof input>(
{
method: 'POST',
url: '/api/app/sql-object-manager/smart-save',
data: input,
},
{ apiName: this.apiName, ...config },
)
} }
// Export service instance
export const sqlObjectManagerService = new SqlObjectManagerService() export const sqlObjectManagerService = new SqlObjectManagerService()

View file

@ -1,454 +1,244 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { Dialog, Button, Notification, toast } from '@/components/ui'
import { import {
FaRegFolder, FaRegFolder,
FaRegFolderOpen, FaRegFolderOpen,
FaRegFileAlt,
FaCog,
FaColumns, FaColumns,
FaCode,
FaSyncAlt, FaSyncAlt,
FaEdit,
FaTrash,
FaTable, FaTable,
FaPlus, FaPlus,
FaEye,
FaCog,
FaCode,
FaDatabase,
FaTrash,
} from 'react-icons/fa' } from 'react-icons/fa'
import type { import type { DatabaseTableDto, SqlNativeObjectDto } from '@/proxy/sql-query-manager/models'
SqlFunctionDto,
SqlQueryDto,
SqlStoredProcedureDto,
SqlViewDto,
SqlObjectType,
} from '@/proxy/sql-query-manager/models'
import { sqlObjectManagerService } from '@/services/sql-query-manager.service' import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
export type SqlObject = SqlFunctionDto | SqlQueryDto | SqlStoredProcedureDto | SqlViewDto type FolderKey = 'tables' | 'views' | 'procedures' | 'functions'
interface TreeNode { interface TreeNode {
id: string id: string
label: string label: string
type: 'root' | 'folder' | 'object' | 'column' type: 'root' | 'folder' | 'object'
objectType?: SqlObjectType folder?: FolderKey
data?: SqlObject | any data?: DatabaseTableDto | SqlNativeObjectDto
children?: TreeNode[] children?: TreeNode[]
expanded?: boolean
isColumn?: boolean
parentTable?: { schemaName: string; tableName: string }
} }
interface SqlObjectExplorerProps { interface SqlObjectExplorerProps {
dataSource: string | null dataSource: string | null
onObjectSelect: (object: SqlObject | null, objectType: SqlObjectType | null) => void
selectedObject: SqlObject | null
onTemplateSelect?: (template: string, templateType: string) => void onTemplateSelect?: (template: string, templateType: string) => void
onShowTableColumns?: (schemaName: string, tableName: string) => void onViewDefinition?: (schemaName: string, objectName: string) => void
onDesignTable?: (schemaName: string, tableName: string) => void onDesignTable?: (schemaName: string, tableName: string) => void
onNewTable?: () => void onNewTable?: () => void
refreshTrigger?: number refreshTrigger?: number
} }
const FOLDER_META: Record<FolderKey, { label: string; color: string }> = {
tables: { label: 'Tables', color: 'text-teal-500' },
views: { label: 'Views', color: 'text-purple-500' },
procedures: { label: 'Stored Procedures', color: 'text-green-600' },
functions: { label: 'Functions', color: 'text-orange-500' },
}
const SqlObjectExplorer = ({ const SqlObjectExplorer = ({
dataSource, dataSource,
onObjectSelect,
selectedObject,
onTemplateSelect, onTemplateSelect,
onShowTableColumns, onViewDefinition,
onDesignTable, onDesignTable,
onNewTable, onNewTable,
refreshTrigger, refreshTrigger,
}: SqlObjectExplorerProps) => { }: SqlObjectExplorerProps) => {
const { translate } = useLocalization() const { translate } = useLocalization()
const [treeData, setTreeData] = useState<TreeNode[]>([]) const [treeData, setTreeData] = useState<TreeNode[]>([])
const [expandedNodes, setExpandedNodes] = useState<Set<string>>( const [expandedNodes, setExpandedNodes] = useState<Set<string>>(new Set(['root']))
new Set(['root']), // Only root expanded by default
)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [filterText, setFilterText] = useState('') const [filterText, setFilterText] = useState('')
const [dropConfirm, setDropConfirm] = useState<{ node: TreeNode } | null>(null)
const [dropping, setDropping] = useState(false)
const [contextMenu, setContextMenu] = useState<{ const [contextMenu, setContextMenu] = useState<{
show: boolean show: boolean; x: number; y: number; node: TreeNode | null
x: number
y: number
node: TreeNode | null
}>({ show: false, x: 0, y: 0, node: null }) }>({ show: false, x: 0, y: 0, node: null })
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
const [objectToDelete, setObjectToDelete] = useState<{
object: SqlObject
type: SqlObjectType
} | null>(null)
useEffect(() => { useEffect(() => {
if (dataSource) { if (dataSource) loadObjects()
loadObjects() else setTreeData([])
} else { }, [dataSource, refreshTrigger])
setTreeData([])
}
}, [dataSource, refreshTrigger]) // refreshTrigger değişince de yenile
const loadObjects = async () => { const loadObjects = async () => {
if (!dataSource) return if (!dataSource) return
setLoading(true) setLoading(true)
try { try {
// Single API call to get all objects const { data } = await sqlObjectManagerService.getAllObjects(dataSource)
const response = await sqlObjectManagerService.getAllObjects(dataSource || '')
const allObjects = response.data
const tree: TreeNode[] = [ const makeObjectNode = (folder: FolderKey, obj: SqlNativeObjectDto): TreeNode => ({
{ id: `${folder}-${obj.schemaName}-${obj.objectName}`,
label: obj.fullName ?? `[${obj.schemaName}].[${obj.objectName}]`,
type: 'object',
folder,
data: obj,
})
const tree: TreeNode[] = [{
id: 'root', id: 'root',
label: dataSource || 'Database', label: dataSource,
type: 'root', type: 'root',
expanded: true,
children: [ children: [
{ {
id: 'tables', id: 'tables',
label: `${translate('::App.Platform.Tables')} (${allObjects.tables.length})`, label: `Tables (${data.tables.length})`,
type: 'folder', type: 'folder',
expanded: expandedNodes.has('tables'), folder: 'tables',
children: children: data.tables.map((t) => ({
allObjects.tables.map((t) => ({ id: `tables-${t.schemaName}-${t.tableName}`,
id: `table-${t.schemaName}-${t.tableName}`, label: t.fullName ?? `[${t.schemaName}].[${t.tableName}]`,
label: t.fullName,
type: 'object' as const, type: 'object' as const,
folder: 'tables' as FolderKey,
data: t, data: t,
})) || [], })),
},
{
id: 'procedures',
label: `${translate('::App.Platform.StoredProcedures')} (${allObjects.storedProcedures.length})`,
type: 'folder',
objectType: 2,
expanded: expandedNodes.has('procedures'),
children:
allObjects.storedProcedures.map((p) => {
const deployInfo = p.isDeployed && p.lastDeployedAt
? ` (${new Date(p.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})`
: '';
const customBadge = p.isCustom ? ' 📝' : ' 🗄️';
return {
id: p.id!,
label: `${p.displayName || p.procedureName}${customBadge}${p.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
type: 'object' as const,
objectType: 2 as SqlObjectType,
data: p,
};
}) || [],
}, },
{ {
id: 'views', id: 'views',
label: `${translate('::App.Platform.Views')} (${allObjects.views.length})`, label: `Views (${data.views.length})`,
type: 'folder', type: 'folder',
objectType: 3, folder: 'views',
expanded: expandedNodes.has('views'), children: data.views.map((v) => makeObjectNode('views', v)),
children: },
allObjects.views.map((v) => { {
const deployInfo = v.isDeployed && v.lastDeployedAt id: 'procedures',
? ` (${new Date(v.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})` label: `Stored Procedures (${data.storedProcedures.length})`,
: ''; type: 'folder',
const customBadge = v.isCustom ? ' 📝' : ' 🗄️'; folder: 'procedures',
return { children: data.storedProcedures.map((p) => makeObjectNode('procedures', p)),
id: v.id!,
label: `${v.displayName || v.viewName}${customBadge}${v.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
type: 'object' as const,
objectType: 3 as SqlObjectType,
data: v,
};
}) || [],
}, },
{ {
id: 'functions', id: 'functions',
label: `${translate('::App.Platform.Functions')} (${allObjects.functions.length})`, label: `Functions (${data.functions.length})`,
type: 'folder', type: 'folder',
objectType: 4, folder: 'functions',
expanded: expandedNodes.has('functions'), children: data.functions.map((f) => makeObjectNode('functions', f)),
children:
allObjects.functions.map((f) => {
const deployInfo = f.isDeployed && f.lastDeployedAt
? ` (${new Date(f.lastDeployedAt).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })})`
: '';
const customBadge = f.isCustom ? ' 📝' : ' 🗄️';
return {
id: f.id!,
label: `${f.displayName || f.functionName}${customBadge}${f.isDeployed ? ' ✅' : ' ❌'}${deployInfo}`,
type: 'object' as const,
objectType: 4 as SqlObjectType,
data: f,
};
}) || [],
},
{
id: 'queries',
label: `${translate('::App.Platform.Queries')} (${allObjects.queries.length})`,
type: 'folder',
objectType: 1,
expanded: expandedNodes.has('queries'),
children:
allObjects.queries.map((q) => ({
id: q.id || '',
label: q.name,
type: 'object' as const,
objectType: 1 as SqlObjectType,
data: q,
})) || [],
}, },
], ],
}, }]
]
setTreeData(tree) setTreeData(tree)
} catch (error: any) { } catch (error: any) {
toast.push( console.error('Failed to load objects', error)
<Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToLoadObjects')}
</Notification>,
{ placement: 'top-center' },
)
} finally { } finally {
setLoading(false) setLoading(false)
} }
} }
const loadTableColumns = async (schemaName: string, tableName: string): Promise<TreeNode[]> => { const toggleNode = (nodeId: string) => {
try { setExpandedNodes((prev) => {
const response = await sqlObjectManagerService.getTableColumns( const next = new Set(prev)
dataSource || '', next.has(nodeId) ? next.delete(nodeId) : next.add(nodeId)
schemaName, return next
tableName,
)
return response.data.map((col) => ({
id: `column-${schemaName}-${tableName}-${col.columnName}`,
label: `${col.columnName} (${col.dataType}${col.maxLength ? `(${col.maxLength})` : ''})${col.isNullable ? '' : ' NOT NULL'}`,
type: 'column' as const,
isColumn: true,
data: col,
parentTable: { schemaName, tableName },
}))
} catch (error) {
return []
}
}
const toggleNode = async (nodeId: string) => {
const newSet = new Set(expandedNodes)
if (newSet.has(nodeId)) {
newSet.delete(nodeId)
setExpandedNodes(newSet)
} else {
newSet.add(nodeId)
setExpandedNodes(newSet)
}
}
const findNodeById = (nodes: TreeNode[], id: string): TreeNode | null => {
for (const node of nodes) {
if (node.id === id) return node
if (node.children) {
const found = findNodeById(node.children, id)
if (found) return found
}
}
return null
}
const updateNodeChildren = (
nodes: TreeNode[],
nodeId: string,
children: TreeNode[],
): TreeNode[] => {
return nodes.map((node) => {
if (node.id === nodeId) {
return { ...node, children }
}
if (node.children) {
return { ...node, children: updateNodeChildren(node.children, nodeId, children) }
}
return node
}) })
} }
const filterTree = (nodes: TreeNode[], searchText: string): TreeNode[] => { const filterTree = (nodes: TreeNode[], search: string): TreeNode[] => {
if (!searchText.trim()) return nodes if (!search.trim()) return nodes
const q = search.toLowerCase()
const search = searchText.toLowerCase() return nodes
const filtered = nodes
.map((node) => { .map((node) => {
const matchesSearch = node.label.toLowerCase().includes(search) const match = node.label.toLowerCase().includes(q)
const filteredChildren = node.children ? filterTree(node.children, searchText) : [] const kids = node.children ? filterTree(node.children, search) : []
if (match || kids.length > 0)
if (matchesSearch || filteredChildren.length > 0) { return { ...node, children: kids.length > 0 ? kids : node.children } as TreeNode
return {
...node,
children: filteredChildren.length > 0 ? filteredChildren : node.children,
} as TreeNode
}
return null return null
}) })
.filter((node) => node !== null) as TreeNode[] .filter(Boolean) as TreeNode[]
return filtered
} }
const handleNodeClick = (node: TreeNode) => { const handleNodeClick = (node: TreeNode) => {
if (node.type === 'folder' || node.type === 'root') { if (node.type !== 'object') { toggleNode(node.id); return }
toggleNode(node.id)
} else if (node.type === 'column') { if (node.folder === 'tables') {
// Column clicked - do nothing or show info // Generate SELECT template for tables
return const t = node.data as DatabaseTableDto
} else if (node.type === 'object' && node.data) { onTemplateSelect?.(`SELECT TOP 10 *\nFROM ${t.fullName ?? `[${t.schemaName}].[${t.tableName}]`};`, 'table-select')
// Check if it's a table - generate SELECT template } else {
if (node.id.startsWith('table-') && onTemplateSelect) { // Load native object definition into editor
const table = node.data as any const obj = node.data as SqlNativeObjectDto
const selectQuery = `SELECT TOP 10 * \nFROM ${table.fullName || `[${table.schemaName}].[${table.tableName}]`};` onViewDefinition?.(obj.schemaName, obj.objectName)
onTemplateSelect(selectQuery, 'table-select')
} else if (node.objectType) {
onObjectSelect(node.data, node.objectType)
}
} }
} }
const handleContextMenu = (e: React.MouseEvent, node: TreeNode) => { const buildDropSql = (node: TreeNode): string => {
e.preventDefault() if (node.folder === 'tables') {
const t = node.data as DatabaseTableDto
// Don't show context menu for columns return `DROP TABLE ${t.fullName ?? `[${t.schemaName}].[${t.tableName}]`};`
if (node.type === 'column') { }
return const obj = node.data as SqlNativeObjectDto
const keyword =
node.folder === 'views' ? 'VIEW' :
node.folder === 'procedures' ? 'PROCEDURE' :
'FUNCTION'
return `DROP ${keyword} ${obj.fullName ?? `[${obj.schemaName}].[${obj.objectName}]`};`
} }
setContextMenu({ const handleDrop = async () => {
show: true, if (!dropConfirm || !dataSource) return
x: e.clientX, setDropping(true)
y: e.clientY,
node,
})
}
const handleDelete = async () => {
if (!objectToDelete || !objectToDelete.object.id) return
try { try {
const { object, type } = objectToDelete await sqlObjectManagerService.executeQuery({
queryText: buildDropSql(dropConfirm.node),
switch (type) { dataSourceCode: dataSource,
case 1: })
await sqlObjectManagerService.deleteQuery(object.id!) setDropConfirm(null)
break
case 2:
await sqlObjectManagerService.deleteStoredProcedure(object.id!)
break
case 3:
await sqlObjectManagerService.deleteView(object.id!)
break
case 4:
await sqlObjectManagerService.deleteFunction(object.id!)
break
}
toast.push(
<Notification type="success" title={translate('::App.Platform.Success')}>
{translate('::App.Platform.ObjectDeletedSuccessfully')}
</Notification>,
{ placement: 'top-center' },
)
setShowDeleteDialog(false)
setObjectToDelete(null)
loadObjects() loadObjects()
} catch (err: any) {
console.error('Drop failed', err)
} finally {
setDropping(false)
}
}
if (selectedObject?.id === object.id) { const closeCtx = () => setContextMenu({ show: false, x: 0, y: 0, node: null })
onObjectSelect(null, null)
}
} catch (error: any) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToDeleteObject')}
</Notification>,
{ placement: 'top-center' },
)
}
}
const getIcon = (node: TreeNode) => { const getIcon = (node: TreeNode) => {
if (node.type === 'root') return <FaRegFolder className="text-blue-500" /> if (node.type === 'root') return <FaDatabase className="text-blue-500" />
if (node.type === 'folder') { if (node.type === 'folder') {
const isExpanded = expandedNodes.has(node.id) const open = expandedNodes.has(node.id)
const cls = FOLDER_META[node.folder!]?.color ?? 'text-blue-500'
// Tables folder return open
if (node.id === 'tables') ? <FaRegFolderOpen className={cls} />
return isExpanded ? ( : <FaRegFolder className={cls} />
<FaRegFolderOpen className="text-blue-500" />
) : (
<FaRegFolder className="text-blue-500" />
)
if (node.objectType === 1)
return isExpanded ? (
<FaRegFolderOpen className="text-yellow-500" />
) : (
<FaRegFolder className="text-yellow-500" />
)
if (node.objectType === 2)
return isExpanded ? (
<FaRegFolderOpen className="text-green-500" />
) : (
<FaRegFolder className="text-green-500" />
)
if (node.objectType === 3)
return isExpanded ? (
<FaRegFolderOpen className="text-purple-500" />
) : (
<FaRegFolder className="text-purple-500" />
)
if (node.objectType === 4)
return isExpanded ? (
<FaRegFolderOpen className="text-red-500" />
) : (
<FaRegFolder className="text-red-500" />
)
} }
if (node.folder === 'tables') return <FaTable className="text-teal-500" />
if (node.type === 'object') { if (node.folder === 'views') return <FaEye className="text-purple-500" />
if (node.objectType === 1) return <FaRegFileAlt className="text-gray-500" /> if (node.folder === 'procedures') return <FaCog className="text-green-600" />
if (node.objectType === 2) return <FaCog className="text-gray-500" /> if (node.folder === 'functions') return <FaCode className="text-orange-500" />
if (node.objectType === 3) return <FaColumns className="text-gray-500" /> return <FaColumns className="text-gray-400" />
if (node.objectType === 4) return <FaCode className="text-gray-500" />
}
if (node.type === 'column') {
return <FaColumns className="text-gray-400 text-sm" />
}
return <FaRegFolder />
} }
const renderNode = (node: TreeNode, level = 0) => { const renderNode = (node: TreeNode, level = 0) => {
const isExpanded = expandedNodes.has(node.id) const isExpanded = expandedNodes.has(node.id)
const isSelected = node.type === 'object' && selectedObject?.id === node.id
const isColumn = node.type === 'column'
return ( return (
<div key={node.id}> <div key={node.id}>
<div <div
className={`flex items-center gap-2 py-1 px-2 ${isColumn ? 'cursor-default' : 'cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700'} rounded ${ className="group flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 rounded"
isSelected ? 'bg-blue-100 dark:bg-blue-900' : ''
}`}
style={{ paddingLeft: `${level * 16 + 8}px` }} style={{ paddingLeft: `${level * 16 + 8}px` }}
onClick={() => !isColumn && handleNodeClick(node)} onClick={() => handleNodeClick(node)}
onContextMenu={(e) => !isColumn && handleContextMenu(e, node)} onContextMenu={(e) => { e.preventDefault(); setContextMenu({ show: true, x: e.clientX, y: e.clientY, node }) }}
> >
{getIcon(node)} {getIcon(node)}
<span className={`text-sm flex-1 ${isColumn ? 'text-gray-600 dark:text-gray-400' : ''}`}> <span className="text-sm flex-1 truncate">{node.label}</span>
{node.label} {node.type === 'object' && (
</span> <button
title="Drop"
className="opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-red-100 dark:hover:bg-red-900 text-red-500 transition-opacity flex-shrink-0"
onClick={(e) => { e.stopPropagation(); setDropConfirm({ node }) }}
>
<FaTrash className="text-xs" />
</button>
)}
</div> </div>
{isExpanded && node.children && ( {isExpanded && node.children && (
<div>{node.children.map((child) => renderNode(child, level + 1))}</div> <div>{node.children.map((c) => renderNode(c, level + 1))}</div>
)} )}
</div> </div>
) )
@ -456,11 +246,20 @@ const SqlObjectExplorer = ({
const filteredTree = filterTree(treeData, filterText) const filteredTree = filterTree(treeData, filterText)
// Context menu items per folder
const ctxNode = contextMenu.node
const isTableObj = ctxNode?.type === 'object' && ctxNode.folder === 'tables'
const isNativeObj = ctxNode?.type === 'object' && ctxNode.folder !== 'tables'
const isTablesDir = ctxNode?.id === 'tables'
const isViewsDir = ctxNode?.id === 'views'
const isProcsDir = ctxNode?.id === 'procedures'
const isFuncsDir = ctxNode?.id === 'functions'
const isFolderNode = ctxNode?.type === 'folder'
return ( return (
<div className="h-full flex flex-col"> <div className="flex-1 flex flex-col min-h-0">
{/* Filter and Refresh Controls */} {/* Search + refresh */}
<div className="p-2 border-b space-y-2 flex-shrink-0"> <div className="p-2 border-b flex gap-2 flex-shrink-0">
<div className="flex gap-2">
<input <input
type="text" type="text"
placeholder={translate('::App.Platform.Search')} placeholder={translate('::App.Platform.Search')}
@ -477,183 +276,89 @@ const SqlObjectExplorer = ({
<FaSyncAlt className={loading ? 'animate-spin' : ''} /> <FaSyncAlt className={loading ? 'animate-spin' : ''} />
</button> </button>
</div> </div>
{/* Tree */}
<div className="flex-1 overflow-auto">
{loading && <div className="text-center py-8 text-gray-500 text-sm">{translate('::App.Platform.Loading')}</div>}
{!loading && treeData.length === 0 && <div className="text-center py-8 text-gray-500 text-sm">{translate('::App.Platform.NoDataSourceSelected')}</div>}
{!loading && filteredTree.length > 0 && <div className="p-1">{filteredTree.map((n) => renderNode(n))}</div>}
{!loading && treeData.length > 0 && filteredTree.length === 0 && <div className="text-center py-8 text-gray-500 text-sm">{translate('::App.Platform.NoResultsFound')}</div>}
</div> </div>
{/* Tree Content */} {/* Context menu */}
<div className="h-[calc(100vh-265px)] overflow-auto">
{loading && (
<div className="text-center py-8 text-gray-500">
{translate('::App.Platform.Loading')}
</div>
)}
{!loading && treeData.length === 0 && (
<div className="text-center py-8 text-gray-500">
{translate('::App.Platform.NoDataSourceSelected')}
</div>
)}
{!loading && filteredTree.length > 0 && (
<div className="space-y-1 p-2">{filteredTree.map((node) => renderNode(node))}</div>
)}
{!loading && treeData.length > 0 && filteredTree.length === 0 && (
<div className="text-center py-8 text-gray-500">
{translate('::App.Platform.NoResultsFound')}
</div>
)}
</div>
{contextMenu.show && ( {contextMenu.show && (
<> <>
<div className="fixed inset-0 z-40" onClick={closeCtx} />
<div <div
className="fixed inset-0 z-40" className="fixed z-50 bg-white dark:bg-gray-800 shadow-lg rounded border border-gray-200 dark:border-gray-700 py-1 min-w-[180px]"
onClick={() => setContextMenu({ show: false, x: 0, y: 0, node: null })}
/>
<div
className="fixed z-50 bg-white dark:bg-gray-800 shadow-lg rounded border border-gray-200 dark:border-gray-700 py-1"
style={{ top: contextMenu.y, left: contextMenu.x }} style={{ top: contextMenu.y, left: contextMenu.x }}
> >
{contextMenu.node?.id?.startsWith('table-') && ( {/* TABLE object <20> Design */}
<button {isTableObj && (
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm" <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
onClick={() => { onClick={() => {
const tableData = contextMenu.node?.data as any const t = ctxNode!.data as DatabaseTableDto
if (tableData && onDesignTable) { onDesignTable?.(t.schemaName, t.tableName)
onDesignTable(tableData.schemaName, tableData.tableName) closeCtx()
} }}>
setContextMenu({ show: false, x: 0, y: 0, node: null }) <FaTable className="text-teal-600" /> Design Table
}}
>
<FaTable className="inline mr-2 text-teal-600" />
Design
</button> </button>
)} )}
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && contextMenu.node?.data?.isCustom && ( {/* NATIVE object <20> View Definition */}
<> {isNativeObj && (
<button <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => { onClick={() => {
if (contextMenu.node?.data && contextMenu.node?.objectType) { const obj = ctxNode!.data as SqlNativeObjectDto
onObjectSelect(contextMenu.node.data, contextMenu.node.objectType) onViewDefinition?.(obj.schemaName, obj.objectName)
} closeCtx()
setContextMenu({ show: false, x: 0, y: 0, node: null }) }}>
}} {ctxNode!.folder === 'views' && <FaEye className="text-purple-500" />}
> {ctxNode!.folder === 'procedures' && <FaCog className="text-green-600" />}
<FaEdit className="inline mr-2" /> {ctxNode!.folder === 'functions' && <FaCode className="text-orange-500" />}
{translate('::App.Platform.Edit')} View Definition
</button> </button>
)}
<button {/* FOLDER <20> New ... */}
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm text-red-600" {isTablesDir && (
onClick={() => { <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
if (contextMenu.node?.data && contextMenu.node?.objectType) { onClick={() => { onNewTable?.(); closeCtx() }}>
setObjectToDelete({ <FaPlus className="text-teal-600" /> New Table
object: contextMenu.node.data, </button>
type: contextMenu.node.objectType, )}
}) {isViewsDir && (
setShowDeleteDialog(true) <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
} onClick={() => { onTemplateSelect?.('', 'create-view'); closeCtx() }}>
setContextMenu({ show: false, x: 0, y: 0, node: null }) <FaPlus className="text-purple-500" /> New View
}} </button>
> )}
<FaTrash className="inline mr-2" /> {isProcsDir && (
{translate('::App.Platform.Delete')} <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
onClick={() => { onTemplateSelect?.('', 'create-procedure'); closeCtx() }}>
<FaPlus className="text-green-600" /> New Stored Procedure
</button>
)}
{isFuncsDir && (
<>
<button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
onClick={() => { onTemplateSelect?.('', 'create-scalar-function'); closeCtx() }}>
<FaPlus className="text-orange-500" /> New Scalar Function
</button>
<button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
onClick={() => { onTemplateSelect?.('', 'create-table-function'); closeCtx() }}>
<FaPlus className="text-orange-500" /> New Table-Valued Function
</button> </button>
</> </>
)} )}
{contextMenu.node?.type === 'object' && contextMenu.node?.objectType && !contextMenu.node?.data?.isCustom && ( {/* Separator + Refresh for folders */}
<div className="px-4 py-2 text-xs text-gray-500 italic"> {isFolderNode && (
{translate('::App.Platform.NativeObjectViewOnly')}
</div>
)}
{contextMenu.node?.type === 'folder' && (
<> <>
{/* Tables folder */}
{contextMenu.node.id === 'tables' && (
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
onNewTable?.()
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaPlus className="inline mr-2 text-teal-600" />
New Table
</button>
)}
{/* Stored Procedures folder */}
{contextMenu.node.id === 'procedures' && (
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
onTemplateSelect?.('', 'create-procedure')
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaPlus className="inline mr-2 text-green-600" />
New Stored Procedure
</button>
)}
{/* Views folder */}
{contextMenu.node.id === 'views' && (
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
onTemplateSelect?.('', 'create-view')
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaPlus className="inline mr-2 text-purple-600" />
New View
</button>
)}
{/* Functions folder */}
{contextMenu.node.id === 'functions' && (
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
onTemplateSelect?.('', 'create-scalar-function')
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaPlus className="inline mr-2 text-red-500" />
New Function
</button>
)}
{/* Queries folder */}
{contextMenu.node.id === 'queries' && (
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
onTemplateSelect?.('', 'select')
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaPlus className="inline mr-2 text-yellow-600" />
New Query
</button>
)}
{/* Separator */}
{contextMenu.node.id !== 'root' && (
<div className="my-1 border-t border-gray-100 dark:border-gray-700" /> <div className="my-1 border-t border-gray-100 dark:border-gray-700" />
)} <button className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm flex items-center gap-2"
onClick={() => { loadObjects(); closeCtx() }}>
{/* Refresh — all folders */} <FaSyncAlt /> {translate('::App.Platform.Refresh')}
<button
className="w-full px-4 py-2 text-left hover:bg-gray-100 dark:hover:bg-gray-700 text-sm"
onClick={() => {
loadObjects()
setContextMenu({ show: false, x: 0, y: 0, node: null })
}}
>
<FaSyncAlt className="inline mr-2" />
{translate('::App.Platform.Refresh')}
</button> </button>
</> </>
)} )}
@ -661,22 +366,42 @@ const SqlObjectExplorer = ({
</> </>
)} )}
<Dialog {/* Drop Confirm Dialog */}
isOpen={showDeleteDialog} {dropConfirm && (
onClose={() => setShowDeleteDialog(false)} <>
onRequestClose={() => setShowDeleteDialog(false)} <div className="fixed inset-0 z-50 bg-black/40 flex items-center justify-center" onClick={() => !dropping && setDropConfirm(null)}>
> <div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 max-w-sm w-full mx-4" onClick={(e) => e.stopPropagation()}>
<h5 className="mb-4">{translate('::App.Platform.ConfirmDelete')}</h5> <div className="flex items-center gap-3 mb-3">
<p className="mb-4">{translate('::App.Platform.DeleteConfirmationMessage')}</p> <FaTrash className="text-red-500 text-lg flex-shrink-0" />
<div className="flex justify-end gap-2"> <h6 className="font-semibold text-gray-900 dark:text-gray-100">Drop Object</h6>
<Button variant="plain" onClick={() => setShowDeleteDialog(false)}>
{translate('::Cancel')}
</Button>
<Button variant="solid" onClick={handleDelete}>
{translate('::App.Platform.DeleteAction')}
</Button>
</div> </div>
</Dialog> <p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
The following object will be permanently dropped:
</p>
<code className="block text-sm bg-gray-100 dark:bg-gray-700 rounded px-3 py-2 mb-4 break-all">
{buildDropSql(dropConfirm.node)}
</code>
<div className="flex justify-end gap-2">
<button
disabled={dropping}
className="px-4 py-1.5 text-sm rounded border hover:bg-gray-100 dark:hover:bg-gray-700 disabled:opacity-50"
onClick={() => setDropConfirm(null)}
>
Cancel
</button>
<button
disabled={dropping}
className="px-4 py-1.5 text-sm rounded bg-red-600 text-white hover:bg-red-700 disabled:opacity-50 flex items-center gap-2"
onClick={handleDrop}
>
{dropping && <FaSyncAlt className="animate-spin text-xs" />}
Drop
</button>
</div>
</div>
</div>
</>
)}
</div> </div>
) )
} }

View file

@ -1,212 +1,21 @@
import type { interface SqlObjectPropertiesProps {
SqlFunctionDto, object?: any
SqlQueryDto,
SqlStoredProcedureDto,
SqlViewDto,
SqlObjectType,
} from '@/proxy/sql-query-manager/models'
import { useLocalization } from '@/utils/hooks/useLocalization'
import dayjs from 'dayjs'
export type SqlObject = SqlFunctionDto | SqlQueryDto | SqlStoredProcedureDto | SqlViewDto
interface SqlObjectPropertiesProps {
object: SqlObject | null
type: SqlObjectType | null
} }
const SqlObjectProperties = ({ object, type }: SqlObjectPropertiesProps) => { const SqlObjectProperties = ({ object }: SqlObjectPropertiesProps) => {
const { translate } = useLocalization() if (!object) {
if (!object || !type) {
return ( return (
<div className="p-4 text-center text-gray-500"> <div className="flex items-center justify-center h-full text-gray-400 text-sm">
{translate('::App.Platform.SelectAnObjectToViewProperties')} Select an object to view properties
</div> </div>
) )
} }
const PropertyRow = ({ label, value }: { label: string; value: any }) => (
<div className="mb-3">
<div className="text-xs text-gray-500 dark:text-gray-400 mb-1">{label}</div>
<div className="text-sm font-medium">{value || '-'}</div>
</div>
)
const getObjectTypeName = () => {
switch (type) {
case 1:
return translate('::App.Platform.Query')
case 2:
return translate('::App.Platform.StoredProcedure')
case 3:
return translate('::App.Platform.View')
case 4:
return translate('::App.Platform.Function')
default:
return translate('::App.Platform.Object')
}
}
const getStatusBadge = (status: number) => {
switch (status) {
case 1:
return <span className="px-2 py-1 text-xs rounded bg-gray-200 dark:bg-gray-700">{translate('::App.Platform.Draft')}</span>
case 2:
return <span className="px-2 py-1 text-xs rounded bg-green-200 dark:bg-green-700">{translate('::App.Platform.Active')}</span>
case 3:
return <span className="px-2 py-1 text-xs rounded bg-orange-200 dark:bg-orange-700">{translate('::App.Platform.Archived')}</span>
default:
return <span className="px-2 py-1 text-xs rounded bg-gray-200 dark:bg-gray-700">{translate('::App.Platform.Unknown')}</span>
}
}
const renderCommonProperties = () => (
<>
<PropertyRow label={translate('::App.Platform.ObjectType')} value={getObjectTypeName()} />
<PropertyRow label={translate('::App.Platform.ID')} value={object.id} />
{object.creationTime && (
<PropertyRow
label={translate('::App.Platform.Created')}
value={dayjs(object.creationTime).format('DD/MM/YYYY HH:mm')}
/>
)}
{object.lastModificationTime && (
<PropertyRow
label={translate('::App.Platform.Modified')}
value={dayjs(object.lastModificationTime).format('DD/MM/YYYY HH:mm')}
/>
)}
</>
)
const renderQueryProperties = () => {
const query = object as SqlQueryDto
return ( return (
<> <div className="p-4 text-sm">
<PropertyRow label={translate('::App.Platform.Code')} value={query.code} /> <pre className="text-xs text-gray-600 dark:text-gray-400 overflow-auto">
<PropertyRow label={translate('::App.Platform.Name')} value={query.name} /> {JSON.stringify(object, null, 2)}
<PropertyRow label={translate('::App.Platform.Description')} value={query.description} /> </pre>
<PropertyRow label={translate('::App.Platform.DataSource')} value={query.dataSourceCode} />
<PropertyRow label={translate('::App.Platform.Status')} value={getStatusBadge(query.status)} />
<PropertyRow label={translate('::App.Platform.Category')} value={query.category} />
<PropertyRow label={translate('::App.Platform.Tags')} value={query.tags} />
<PropertyRow
label={translate('::App.Platform.ModifiesData')}
value={query.isModifyingData ? translate('::App.Platform.Yes') : translate('::App.Platform.No')}
/>
<PropertyRow label={translate('::App.Platform.ExecutionCount')} value={query.executionCount} />
{query.lastExecutedAt && (
<PropertyRow
label={translate('::App.Platform.LastExecuted')}
value={dayjs(query.lastExecutedAt).format('DD/MM/YYYY HH:mm')}
/>
)}
{renderCommonProperties()}
</>
)
}
const renderStoredProcedureProperties = () => {
const sp = object as SqlStoredProcedureDto
return (
<>
<PropertyRow label={translate('::App.Platform.ProcedureName')} value={sp.procedureName} />
<PropertyRow label={translate('::App.Platform.Schema')} value={sp.schemaName} />
<PropertyRow label={translate('::App.Platform.DisplayName')} value={sp.displayName} />
<PropertyRow label={translate('::App.Platform.Description')} value={sp.description} />
<PropertyRow label={translate('::App.Platform.DataSource')} value={sp.dataSourceCode} />
<PropertyRow label={translate('::App.Platform.Status')} value={getStatusBadge(sp.status)} />
<PropertyRow label={translate('::App.Platform.Category')} value={sp.category} />
<PropertyRow label={translate('::App.Platform.Deployed')} value={sp.isDeployed ? translate('::App.Platform.Yes') : translate('::App.Platform.No')} />
{sp.lastDeployedAt && (
<PropertyRow
label={translate('::App.Platform.LastDeployed')}
value={dayjs(sp.lastDeployedAt).format('DD/MM/YYYY HH:mm')}
/>
)}
{renderCommonProperties()}
</>
)
}
const renderViewProperties = () => {
const view = object as SqlViewDto
return (
<>
<PropertyRow label={translate('::App.Platform.ViewName')} value={view.viewName} />
<PropertyRow label={translate('::App.Platform.Schema')} value={view.schemaName} />
<PropertyRow label={translate('::App.Platform.DisplayName')} value={view.displayName} />
<PropertyRow label={translate('::App.Platform.Description')} value={view.description} />
<PropertyRow label={translate('::App.Platform.DataSource')} value={view.dataSourceCode} />
<PropertyRow label={translate('::App.Platform.Status')} value={getStatusBadge(view.status)} />
<PropertyRow label={translate('::App.Platform.Category')} value={view.category} />
<PropertyRow label={translate('::App.Platform.Deployed')} value={view.isDeployed ? translate('::App.Platform.Yes') : translate('::App.Platform.No')} />
<PropertyRow
label={translate('::App.Platform.SchemaBinding')}
value={view.withSchemaBinding ? translate('::App.Platform.Yes') : translate('::App.Platform.No')}
/>
{view.lastDeployedAt && (
<PropertyRow
label={translate('::App.Platform.LastDeployed')}
value={dayjs(view.lastDeployedAt).format('DD/MM/YYYY HH:mm')}
/>
)}
{renderCommonProperties()}
</>
)
}
const renderFunctionProperties = () => {
const func = object as SqlFunctionDto
const getFunctionType = (funcType: number) => {
switch (funcType) {
case 1:
return translate('::App.Platform.ScalarFunction')
case 2:
return translate('::App.Platform.TableValuedFunction')
case 3:
return translate('::App.Platform.InlineTableValuedFunction')
default:
return translate('::App.Platform.Unknown')
}
}
return (
<>
<PropertyRow label={translate('::App.Platform.FunctionName')} value={func.functionName} />
<PropertyRow label={translate('::App.Platform.Schema')} value={func.schemaName} />
<PropertyRow label={translate('::App.Platform.DisplayName')} value={func.displayName} />
<PropertyRow label={translate('::App.Platform.Description')} value={func.description} />
<PropertyRow label={translate('::App.Platform.FunctionType')} value={getFunctionType(func.functionType)} />
<PropertyRow label={translate('::App.Platform.ReturnType')} value={func.returnType} />
<PropertyRow label={translate('::App.Platform.DataSource')} value={func.dataSourceCode} />
<PropertyRow label={translate('::App.Platform.Status')} value={getStatusBadge(func.status)} />
<PropertyRow label={translate('::App.Platform.Category')} value={func.category} />
<PropertyRow label={translate('::App.Platform.Deployed')} value={func.isDeployed ? translate('::App.Platform.Yes') : translate('::App.Platform.No')} />
{func.lastDeployedAt && (
<PropertyRow
label={translate('::App.Platform.LastDeployed')}
value={dayjs(func.lastDeployedAt).format('DD/MM/YYYY HH:mm')}
/>
)}
{renderCommonProperties()}
</>
)
}
return (
<div className="h-full flex flex-col">
<div className="mb-4 pb-2 border-b">
<h6 className="font-bold">{translate('::App.Platform.Properties')}</h6>
</div>
<div className="flex-1 overflow-auto p-2">
{type === 1 && renderQueryProperties()}
{type === 2 && renderStoredProcedureProperties()}
{type === 3 && renderViewProperties()}
{type === 4 && renderFunctionProperties()}
</div>
</div> </div>
) )
} }

View file

@ -1,45 +1,31 @@
import { useState, useCallback, useEffect, useRef } from 'react' import { useState, useCallback, useEffect, useRef } from 'react'
import { Button, Dialog, Input, Notification, toast } from '@/components/ui' import { Button, Dialog, Notification, toast } from '@/components/ui'
import Container from '@/components/shared/Container' import Container from '@/components/shared/Container'
import AdaptableCard from '@/components/shared/AdaptableCard'
import { getDataSources } from '@/services/data-source.service' import { getDataSources } from '@/services/data-source.service'
import type { DataSourceDto } from '@/proxy/data-source' import type { DataSourceDto } from '@/proxy/data-source'
import type { import type { SqlQueryExecutionResultDto } from '@/proxy/sql-query-manager/models'
SqlFunctionDto,
SqlQueryDto,
SqlStoredProcedureDto,
SqlViewDto,
SqlQueryExecutionResultDto,
} from '@/proxy/sql-query-manager/models'
import { SqlObjectType } from '@/proxy/sql-query-manager/models'
import { sqlObjectManagerService } from '@/services/sql-query-manager.service' import { sqlObjectManagerService } from '@/services/sql-query-manager.service'
import { FaDatabase, FaPlay, FaSave, FaCloudUploadAlt, FaCode } from 'react-icons/fa' import { FaDatabase, FaPlay, FaFileAlt } from 'react-icons/fa'
import { FaCheckCircle } from 'react-icons/fa' import { FaCheckCircle } from 'react-icons/fa'
import { useLocalization } from '@/utils/hooks/useLocalization' import { useLocalization } from '@/utils/hooks/useLocalization'
import SqlObjectExplorer from './SqlObjectExplorer' import SqlObjectExplorer from './SqlObjectExplorer'
import SqlEditor, { SqlEditorRef } from './SqlEditor' import SqlEditor, { SqlEditorRef } from './SqlEditor'
import SqlResultsGrid from './SqlResultsGrid' import SqlResultsGrid from './SqlResultsGrid'
import SqlObjectProperties from './SqlObjectProperties'
import SqlTableDesignerDialog from './SqlTableDesignerDialog' import SqlTableDesignerDialog from './SqlTableDesignerDialog'
import { Splitter } from '@/components/codeLayout/Splitter' import { Splitter } from '@/components/codeLayout/Splitter'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { useStoreState } from '@/store/store' import { useStoreState } from '@/store/store'
import { APP_NAME } from '@/constants/app.constant' import { APP_NAME } from '@/constants/app.constant'
export type SqlObject = SqlFunctionDto | SqlQueryDto | SqlStoredProcedureDto | SqlViewDto
interface SqlManagerState { interface SqlManagerState {
dataSources: DataSourceDto[] dataSources: DataSourceDto[]
selectedDataSource: string | null selectedDataSource: string | null
selectedObject: SqlObject | null
selectedObjectType: SqlObjectType | null
editorContent: string editorContent: string
isExecuting: boolean isExecuting: boolean
executionResult: SqlQueryExecutionResultDto | null executionResult: SqlQueryExecutionResultDto | null
showProperties: boolean showProperties: boolean
isDirty: boolean isDirty: boolean
tableColumns: any | null tableColumns: any | null
isSaved: boolean
refreshTrigger: number refreshTrigger: number
} }
@ -51,8 +37,6 @@ const SqlQueryManager = () => {
const [state, setState] = useState<SqlManagerState>({ const [state, setState] = useState<SqlManagerState>({
dataSources: [], dataSources: [],
selectedDataSource: tenantName ?? null, selectedDataSource: tenantName ?? null,
selectedObject: null,
selectedObjectType: null,
editorContent: '', editorContent: '',
isExecuting: false, isExecuting: false,
refreshTrigger: 0, refreshTrigger: 0,
@ -60,23 +44,17 @@ const SqlQueryManager = () => {
showProperties: false, showProperties: false,
isDirty: false, isDirty: false,
tableColumns: null, tableColumns: null,
isSaved: false,
}) })
const [showSaveDialog, setShowSaveDialog] = useState(false)
const [saveDialogData, setSaveDialogData] = useState({
name: '',
description: '',
detectedType: '',
detectedName: '',
isExistingObject: false,
})
const [showTemplateConfirmDialog, setShowTemplateConfirmDialog] = useState(false) const [showTemplateConfirmDialog, setShowTemplateConfirmDialog] = useState(false)
const [pendingTemplate, setPendingTemplate] = useState<{ content: string; type: string } | null>( const [pendingTemplate, setPendingTemplate] = useState<{ content: string; type: string } | null>(
null, null,
) )
const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false) const [showTableDesignerDialog, setShowTableDesignerDialog] = useState(false)
const [designTableData, setDesignTableData] = useState<{ schemaName: string; tableName: string } | null>(null) const [designTableData, setDesignTableData] = useState<{
schemaName: string
tableName: string
} | null>(null)
useEffect(() => { useEffect(() => {
loadDataSources() loadDataSources()
@ -107,7 +85,6 @@ const SqlQueryManager = () => {
setState((prev) => ({ setState((prev) => ({
...prev, ...prev,
selectedDataSource: dataSource.code ?? null, selectedDataSource: dataSource.code ?? null,
selectedObject: null,
editorContent: '', editorContent: '',
executionResult: null, executionResult: null,
isDirty: false, isDirty: false,
@ -124,111 +101,11 @@ const SqlQueryManager = () => {
setShowTableDesignerDialog(true) setShowTableDesignerDialog(true)
}, []) }, [])
const handleObjectSelect = useCallback(
async (object: SqlObject | null, objectType: SqlObjectType | null) => {
if (state.isDirty) {
if (!confirm(translate('::App.Platform.UnsavedChangesConfirmation'))) {
return
}
}
let content = ''
if (object) {
if (objectType === 1) {
// Query
content = (object as SqlQueryDto).queryText || ''
} else if (objectType === 2) {
// Stored Procedure
const sp = object as SqlStoredProcedureDto
if (sp.isCustom) {
content = sp.procedureBody || ''
} else {
// Native SP - fetch definition from SQL Server
try {
const result = await sqlObjectManagerService.getNativeObjectDefinition(
state.selectedDataSource || '',
sp.schemaName,
sp.procedureName
)
content = result.data || ''
} catch (error) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{translate('::App.Platform.FailedToLoadDefinition')}
</Notification>,
{ placement: 'top-center' },
)
}
}
} else if (objectType === 3) {
// View
const view = object as SqlViewDto
if (view.isCustom) {
content = view.viewDefinition || ''
} else {
// Native View - fetch definition from SQL Server
try {
const result = await sqlObjectManagerService.getNativeObjectDefinition(
state.selectedDataSource || '',
view.schemaName,
view.viewName
)
content = result.data || ''
} catch (error) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{translate('::App.Platform.FailedToLoadDefinition')}
</Notification>,
{ placement: 'top-center' },
)
}
}
} else if (objectType === 4) {
// Function
const func = object as SqlFunctionDto
if (func.isCustom) {
content = func.functionBody || ''
} else {
// Native Function - fetch definition from SQL Server
try {
const result = await sqlObjectManagerService.getNativeObjectDefinition(
state.selectedDataSource || '',
func.schemaName,
func.functionName
)
content = result.data || ''
} catch (error) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{translate('::App.Platform.FailedToLoadDefinition')}
</Notification>,
{ placement: 'top-center' },
)
}
}
}
}
setState((prev) => ({
...prev,
selectedObject: object,
selectedObjectType: objectType,
editorContent: content,
executionResult: null,
tableColumns: null,
isDirty: false,
isSaved: false,
}))
},
[state.isDirty, state.selectedDataSource, translate],
)
const handleEditorChange = useCallback((value: string | undefined) => { const handleEditorChange = useCallback((value: string | undefined) => {
setState((prev) => ({ setState((prev) => ({
...prev, ...prev,
editorContent: value || '', editorContent: value || '',
isDirty: true, isDirty: true,
isSaved: false,
})) }))
}, []) }, [])
@ -350,69 +227,14 @@ GO`,
return templates[templateType] || templates['select'] return templates[templateType] || templates['select']
} }
// SQL analiz fonksiyonu - SQL metnini analiz edip nesne türünü ve adını tespit eder const applyTemplate = useCallback((templateContent: string) => {
const detectSqlObject = (sql: string): { type: string; name: string } => {
const upperSql = sql.trim().toUpperCase()
// VIEW tespiti
if (upperSql.includes('CREATE VIEW') || upperSql.includes('ALTER VIEW')) {
// Son kelimeyi al (schema varsa sonraki kelime, yoksa ilk kelime)
const viewMatch = sql.match(
/(?:CREATE|ALTER)\s+VIEW\s+(?:[\[\]]*\w+[\[\]]*\.)?\s*[\[]?(\w+)[\]]?/i,
)
return {
type: 'View',
name: viewMatch ? viewMatch[1] : '',
}
}
// STORED PROCEDURE tespiti
if (
upperSql.includes('CREATE PROCEDURE') ||
upperSql.includes('CREATE PROC') ||
upperSql.includes('ALTER PROCEDURE') ||
upperSql.includes('ALTER PROC')
) {
const procMatch = sql.match(
/(?:CREATE|ALTER)\s+(?:PROCEDURE|PROC)\s+(?:[\[\]]*\w+[\[\]]*\.)?\s*[\[]?(\w+)[\]]?/i,
)
return {
type: 'StoredProcedure',
name: procMatch ? procMatch[1] : '',
}
}
// FUNCTION tespiti
if (upperSql.includes('CREATE FUNCTION') || upperSql.includes('ALTER FUNCTION')) {
const funcMatch = sql.match(
/(?:CREATE|ALTER)\s+FUNCTION\s+(?:[\[\]]*\w+[\[\]]*\.)?\s*[\[]?(\w+)[\]]?/i,
)
return {
type: 'Function',
name: funcMatch ? funcMatch[1] : '',
}
}
// Default: Query
return {
type: 'Query',
name: '',
}
}
const applyTemplate = useCallback(
(templateContent: string) => {
setState((prev) => ({ setState((prev) => ({
...prev, ...prev,
editorContent: templateContent, editorContent: templateContent,
selectedObject: null,
selectedObjectType: null,
executionResult: null, executionResult: null,
isDirty: false, isDirty: false,
})) }))
}, }, [])
[],
)
const handleUseTemplateFromDialog = useCallback( const handleUseTemplateFromDialog = useCallback(
(templateContent: string, templateType: string) => { (templateContent: string, templateType: string) => {
@ -464,6 +286,16 @@ GO`,
setPendingTemplate(null) setPendingTemplate(null)
}, []) }, [])
const handleNewQuery = useCallback(() => {
setState((prev) => ({
...prev,
editorContent: '',
executionResult: null,
tableColumns: null,
isDirty: false,
}))
}, [])
const handleExecute = async () => { const handleExecute = async () => {
if (!state.selectedDataSource) { if (!state.selectedDataSource) {
toast.push( toast.push(
@ -532,246 +364,29 @@ GO`,
} }
} }
const handleSave = async () => { const handleViewDefinition = async (schemaName: string, objectName: string) => {
if (!state.selectedDataSource) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseSelectDataSource')}
</Notification>,
{ placement: 'top-center' },
)
return
}
if (!state.editorContent?.trim()) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseEnterContentToSave')}
</Notification>,
{ placement: 'top-center' },
)
return
}
if (state.selectedObject && state.selectedObjectType) {
// Update existing object - open dialog with existing data
const typeMap: Record<SqlObjectType, string> = {
[SqlObjectType.Query]: 'Query',
[SqlObjectType.View]: 'View',
[SqlObjectType.StoredProcedure]: 'StoredProcedure',
[SqlObjectType.Function]: 'Function',
}
// Get name based on object type
let objectName = ''
if ('viewName' in state.selectedObject) {
objectName = state.selectedObject.viewName || state.selectedObject.displayName || ''
} else if ('procedureName' in state.selectedObject) {
objectName = state.selectedObject.procedureName || state.selectedObject.displayName || ''
} else if ('functionName' in state.selectedObject) {
objectName = state.selectedObject.functionName || state.selectedObject.displayName || ''
} else if ('name' in state.selectedObject) {
objectName = state.selectedObject.name || ''
}
setSaveDialogData({
name: objectName,
description: state.selectedObject.description || '',
detectedType: typeMap[state.selectedObjectType] || '',
detectedName: objectName,
isExistingObject: true,
})
setShowSaveDialog(true)
} else {
// New object - analyze SQL and show dialog with detection
const detection = detectSqlObject(state.editorContent)
setSaveDialogData({
name: detection.name,
description: '',
detectedType: detection.type,
detectedName: detection.name,
isExistingObject: false,
})
setShowSaveDialog(true)
}
}
const handleCreateNewQuery = async () => {
if (!state.selectedDataSource || !saveDialogData.name) return
try {
// Smart save ile kaydet
const result = await sqlObjectManagerService.smartSave({
sqlText: state.editorContent,
dataSourceCode: state.selectedDataSource || '',
name: saveDialogData.name,
description: saveDialogData.description,
})
// Kaydedilen objeyi state'e set et
const savedObject: any = {
id: result.data.objectId,
displayName: saveDialogData.name,
description: saveDialogData.description,
isDeployed: result.data.deployed,
}
// ObjectType'a göre ekstra alanlar ekle
let objectType: SqlObjectType | null = null
if (result.data.objectType === 'View') {
objectType = SqlObjectType.View
savedObject.viewName = saveDialogData.name
savedObject.viewDefinition = state.editorContent
} else if (result.data.objectType === 'StoredProcedure') {
objectType = SqlObjectType.StoredProcedure
savedObject.procedureName = saveDialogData.name
savedObject.procedureBody = state.editorContent
} else if (result.data.objectType === 'Function') {
objectType = SqlObjectType.Function
savedObject.functionName = saveDialogData.name
savedObject.functionBody = state.editorContent
} else if (result.data.objectType === 'Query') {
objectType = SqlObjectType.Query
savedObject.queryText = state.editorContent
}
setState((prev) => ({
...prev,
isDirty: false,
isSaved: true,
selectedObject: savedObject,
selectedObjectType: objectType,
refreshTrigger: prev.refreshTrigger + 1,
}))
setShowSaveDialog(false)
toast.push(
<Notification type="success" title={translate('::App.Platform.Success')}>
{result.data.message || translate('::App.Platform.SavedSuccessfully')}
</Notification>,
{ placement: 'top-center' },
)
} catch (error: any) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToSaveQuery')}
</Notification>,
{ placement: 'top-center' },
)
}
}
const handleDeploy = async () => {
if (!state.selectedObject || !state.selectedObjectType) {
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseSelectAnObjectToDeploy')}
</Notification>,
{ placement: 'top-center' },
)
return
}
if (!state.selectedObject.id) return
try {
const objectId = state.selectedObject.id
let result: any
switch (state.selectedObjectType) {
case SqlObjectType.StoredProcedure:
result = await sqlObjectManagerService.deployStoredProcedure({
id: objectId,
dropIfExists: true,
})
break
case SqlObjectType.View:
result = await sqlObjectManagerService.deployView({ id: objectId, dropIfExists: true })
break
case SqlObjectType.Function:
result = await sqlObjectManagerService.deployFunction({
id: objectId,
dropIfExists: true,
})
break
default:
toast.push(
<Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.ThisObjectTypeCannotBeDeployed')}
</Notification>,
{ placement: 'top-center' },
)
return
}
// Update selectedObject's isDeployed status
setState((prev) => ({
...prev,
selectedObject: prev.selectedObject ? { ...prev.selectedObject, isDeployed: true } : null,
refreshTrigger: prev.refreshTrigger + 1,
}))
toast.push(
<Notification type="success" title={translate('::App.Platform.Success')}>
{translate('::App.Platform.ObjectDeployedSuccessfully')}
</Notification>,
{ placement: 'top-center' },
)
} catch (error: any) {
toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToDeployObject')}
</Notification>,
{ placement: 'top-center' },
)
}
}
const handleShowTableColumns = async (schemaName: string, tableName: string) => {
if (!state.selectedDataSource) return if (!state.selectedDataSource) return
try { try {
const response = await sqlObjectManagerService.getTableColumns( const result = await sqlObjectManagerService.getNativeObjectDefinition(
state.selectedDataSource || '', state.selectedDataSource,
schemaName, schemaName,
tableName, objectName,
) )
if (result.data) {
// Transform API response to match display format const definition = result.data.replace(/\bCREATE\b/i, 'ALTER')
const transformedData = response.data.map((col: any) => ({
ColumnName: col.columnName,
DataType: col.dataType,
MaxLength: col.maxLength || '-',
IsNullable: col.isNullable,
IsPrimaryKey: col.isPrimaryKey || false,
}))
// Create a result object that looks like execution result for display
const columnsResult = {
success: true,
message: `Columns for ${schemaName}.${tableName}`,
data: transformedData,
rowsAffected: transformedData.length,
executionTimeMs: 0,
metadata: {
columns: [
{ name: 'ColumnName', dataType: 'string' },
{ name: 'DataType', dataType: 'string' },
{ name: 'MaxLength', dataType: 'string' },
{ name: 'IsNullable', dataType: 'boolean' },
{ name: 'IsPrimaryKey', dataType: 'boolean' },
],
},
}
setState((prev) => ({ setState((prev) => ({
...prev, ...prev,
tableColumns: columnsResult, editorContent: definition,
executionResult: null, // Clear query results when showing columns executionResult: null,
tableColumns: null,
isDirty: false,
})) }))
}
} catch (error: any) { } catch (error: any) {
toast.push( toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToLoadColumns')} {error.response?.data?.error?.message ||
translate('::App.Platform.FailedToLoadDefinition')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'top-center' },
) )
@ -779,14 +394,14 @@ GO`,
} }
return ( return (
<Container className="h-full overflow-hidden"> <Container className="flex flex-col overflow-hidden" style={{ height: 'calc(100vh - 130px)' }}>
<Helmet <Helmet
titleTemplate={`%s | ${APP_NAME}`} titleTemplate={`%s | ${APP_NAME}`}
title={translate('::' + 'App.SqlQueryManager')} title={translate('::' + 'App.SqlQueryManager')}
defaultTitle={APP_NAME} defaultTitle={APP_NAME}
></Helmet> ></Helmet>
<div className="flex flex-col h-full p-1"> <div className="flex flex-col flex-1 min-h-0 p-1">
{/* Toolbar */} {/* Toolbar */}
<div className="flex-shrink-0 shadow-sm mb-4"> <div className="flex-shrink-0 shadow-sm mb-4">
<div className="flex items-center justify-between px-1 py-1"> <div className="flex items-center justify-between px-1 py-1">
@ -813,6 +428,15 @@ GO`,
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Button
size="sm"
variant="default"
icon={<FaFileAlt />}
onClick={handleNewQuery}
className="shadow-sm"
>
{translate('::App.Platform.NewQuery') || 'New Query'}
</Button>
<Button <Button
size="sm" size="sm"
variant="solid" variant="solid"
@ -826,40 +450,6 @@ GO`,
{translate('::App.Platform.Execute')} {translate('::App.Platform.Execute')}
<span className="ml-1 text-xs opacity-75">(F5)</span> <span className="ml-1 text-xs opacity-75">(F5)</span>
</Button> </Button>
<Button
size="sm"
variant="solid"
icon={<FaSave />}
onClick={handleSave}
disabled={
!state.selectedDataSource ||
!state.editorContent?.trim() ||
(state.isSaved && !state.isDirty) ||
!state.executionResult?.success
}
className="shadow-sm"
>
{translate('::App.Platform.Save')}
<span className="ml-1 text-xs opacity-75">(Ctrl+S)</span>
</Button>
<Button
size="sm"
variant="solid"
color="green-600"
icon={<FaCloudUploadAlt />}
onClick={handleDeploy}
disabled={
!state.selectedObject ||
!state.selectedObjectType ||
state.selectedObjectType === SqlObjectType.Query ||
(state.selectedObject &&
'isDeployed' in state.selectedObject &&
state.selectedObject.isDeployed)
}
className="shadow-sm"
>
{translate('::App.Platform.Deploy')}
</Button>
</div> </div>
</div> </div>
</div> </div>
@ -867,26 +457,22 @@ GO`,
{/* Main Content Area */} {/* Main Content Area */}
<div className="flex-1 flex min-h-0"> <div className="flex-1 flex min-h-0">
{/* Left Panel - Object Explorer */} {/* Left Panel - Object Explorer */}
<div className="w-1/3 flex-shrink-0 flex flex-col min-h-0 mr-4"> <div className="w-1/3 flex-shrink-0 flex flex-col min-h-0 mr-4 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-600 shadow">
<AdaptableCard className="h-full" bodyClass="p-0"> <div className="border-b px-4 py-2 bg-gray-50 dark:bg-gray-800 flex-shrink-0 rounded-t-lg">
<div className="h-full flex flex-col">
<div className="border-b px-4 py-2 bg-gray-50 dark:bg-gray-800 flex-shrink-0">
<h6 className="font-semibold text-sm"> <h6 className="font-semibold text-sm">
{translate('::App.Platform.ObjectExplorer')} {translate('::App.Platform.ObjectExplorer')}
</h6> </h6>
</div> </div>
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
<SqlObjectExplorer <SqlObjectExplorer
dataSource={state.selectedDataSource} dataSource={state.selectedDataSource}
onObjectSelect={handleObjectSelect}
selectedObject={state.selectedObject}
onTemplateSelect={handleTemplateSelect} onTemplateSelect={handleTemplateSelect}
onShowTableColumns={handleShowTableColumns} onViewDefinition={handleViewDefinition}
refreshTrigger={state.refreshTrigger} refreshTrigger={state.refreshTrigger}
onNewTable={handleNewTable} onNewTable={handleNewTable}
onDesignTable={handleDesignTable} onDesignTable={handleDesignTable}
/> />
</div> </div>
</AdaptableCard>
</div> </div>
{/* Center Panel - Editor and Results */} {/* Center Panel - Editor and Results */}
@ -905,7 +491,6 @@ GO`,
value={state.editorContent} value={state.editorContent}
onChange={handleEditorChange} onChange={handleEditorChange}
onExecute={handleExecute} onExecute={handleExecute}
onSave={handleSave}
readOnly={state.isExecuting} readOnly={state.isExecuting}
/> />
</div> </div>
@ -958,20 +543,12 @@ GO`,
value={state.editorContent} value={state.editorContent}
onChange={handleEditorChange} onChange={handleEditorChange}
onExecute={handleExecute} onExecute={handleExecute}
onSave={handleSave}
readOnly={state.isExecuting} readOnly={state.isExecuting}
/> />
</div> </div>
</div> </div>
)} )}
</div> </div>
{/* Right Panel - Properties (Optional) */}
{state.showProperties && state.selectedObject && (
<div className="w-80 flex-shrink-0 flex flex-col min-h-0">
<SqlObjectProperties object={state.selectedObject} type={state.selectedObjectType} />
</div>
)}
</div> </div>
</div> </div>
@ -1008,85 +585,6 @@ GO`,
setState((prev) => ({ ...prev, refreshTrigger: prev.refreshTrigger + 1 })) setState((prev) => ({ ...prev, refreshTrigger: prev.refreshTrigger + 1 }))
}} }}
/> />
{/* Save Dialog */}
<Dialog
isOpen={showSaveDialog}
onClose={() => setShowSaveDialog(false)}
onRequestClose={() => setShowSaveDialog(false)}
>
<h5 className="mb-4">{translate('::App.Platform.SaveQuery')}</h5>
<div className="space-y-4">
{/* Detected Object Type */}
{saveDialogData.detectedType && (
<div className="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-md border border-blue-200 dark:border-blue-800">
<div className="flex items-center gap-2">
<FaCheckCircle className="text-blue-600 dark:text-blue-400 text-xl" />
<div>
<div className="text-sm font-semibold text-blue-900 dark:text-blue-100">
{translate('::App.Platform.DetectedObjectType')}
</div>
<div className="text-sm text-blue-700 dark:text-blue-300">
{saveDialogData.detectedType === 'View' && translate('::App.Platform.View')}
{saveDialogData.detectedType === 'StoredProcedure' &&
translate('::App.Platform.StoredProcedure')}
{saveDialogData.detectedType === 'Function' &&
translate('::App.Platform.Function')}
{saveDialogData.detectedType === 'Query' && translate('::App.Platform.Query')}
</div>
{saveDialogData.detectedName && (
<div className="text-xs text-blue-600 dark:text-blue-400 mt-1">
{translate('::App.Platform.DetectedName')}:{' '}
<span className="font-mono">{saveDialogData.detectedName}</span>
</div>
)}
</div>
</div>
</div>
)}
<div>
<label className="block mb-2">
{translate('::App.Platform.Name')} <span className="text-red-500">*</span>
{saveDialogData.isExistingObject && (
<span className="text-xs text-gray-500 ml-2">
({translate('::App.Platform.CannotBeChanged')})
</span>
)}
</label>
<Input
autoFocus={!saveDialogData.isExistingObject}
value={saveDialogData.name}
onChange={(e) => setSaveDialogData((prev) => ({ ...prev, name: e.target.value }))}
placeholder={saveDialogData.detectedName || translate('::App.Platform.Name')}
invalid={!saveDialogData.name.trim()}
disabled={saveDialogData.isExistingObject}
/>
</div>
<div>
<label className="block mb-2">{translate('::App.Platform.Description')}</label>
<Input
value={saveDialogData.description}
onChange={(e) =>
setSaveDialogData((prev) => ({ ...prev, description: e.target.value }))
}
placeholder={translate('::App.Platform.Description')}
/>
</div>
<div className="flex justify-end gap-2">
<Button variant="plain" onClick={() => setShowSaveDialog(false)}>
{translate('::Cancel')}
</Button>
<Button
variant="solid"
onClick={handleCreateNewQuery}
disabled={!saveDialogData.name.trim()}
>
{translate('::App.Platform.Save')}
</Button>
</div>
</div>
</Dialog>
</Container> </Container>
) )
} }

View file

@ -957,12 +957,13 @@ const SqlTableDesignerDialog = ({
const STEP_LABELS = ['Sütun Tasarımı', 'Entity Ayarları', 'İlişkiler', 'T-SQL Önizleme'] const STEP_LABELS = ['Sütun Tasarımı', 'Entity Ayarları', 'İlişkiler', 'T-SQL Önizleme']
const renderStepIndicator = () => ( const renderStepIndicator = () => (
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center justify-between mb-2">
{STEP_LABELS.map((label, i) => { {STEP_LABELS.map((label, i) => {
const isDone = i < step const isDone = i < step
const isActive = i === step const isActive = i === step
return ( return (
<div key={i} className="flex items-center gap-2"> <div key={i} className="flex items-center flex-1">
<div className="flex items-center gap-2">
<div <div
className={`w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold transition-colors className={`w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold transition-colors
${isDone ? 'bg-green-500 text-white' : isActive ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400'}`} ${isDone ? 'bg-green-500 text-white' : isActive ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-500 dark:bg-gray-700 dark:text-gray-400'}`}
@ -970,12 +971,13 @@ const SqlTableDesignerDialog = ({
{isDone ? <FaCheck /> : i + 1} {isDone ? <FaCheck /> : i + 1}
</div> </div>
<span <span
className={`text-sm font-medium ${isActive ? 'text-blue-600 dark:text-blue-400' : 'text-gray-500'}`} className={`text-sm font-medium whitespace-nowrap ${isActive ? 'text-blue-600 dark:text-blue-400' : 'text-gray-500'}`}
> >
{label} {label}
</span> </span>
</div>
{i < STEP_LABELS.length - 1 && ( {i < STEP_LABELS.length - 1 && (
<FaChevronRight className="text-gray-300 dark:text-gray-600 mx-1" /> <div className="flex-1 mx-3 h-px bg-gray-200 dark:bg-gray-700" />
)} )}
</div> </div>
) )