#nullable enable using System.Data; using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW.DbWriteSmoke; /// /// The legacy deletion workflows remove child rows by numeric parent key. This /// read-only guard proves a newly suggested key has no pre-existing orphaned /// children before a smoke record can adopt that key. /// internal sealed class ExistingChildRowGuard { private const string ResultColumn = "ROW_COUNT"; private readonly IDataQueryExecutor _executor; internal ExistingChildRowGuard(IDataQueryExecutor executor) { _executor = executor ?? throw new ArgumentNullException(nameof(executor)); } internal Task EnsureNamedPlaylistChildrenAbsentAsync( string programCode, CancellationToken cancellationToken) => EnsureZeroAsync( DataSourceKind.Oracle, "DB_WRITE_SMOKE_PLAYLIST_CHILD_PREFLIGHT", "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM PLAY_LIST WHERE PG_CODE = :parent_code", programCode, cancellationToken); internal Task EnsureThemeChildrenAbsentAsync( ThemeMarket market, string themeCode, CancellationToken cancellationToken) => market switch { ThemeMarket.Krx => EnsureZeroAsync( DataSourceKind.Oracle, "DB_WRITE_SMOKE_KRX_THEME_CHILD_PREFLIGHT", "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM SB_ITEM WHERE SB_M_CODE = :parent_code", themeCode, cancellationToken), ThemeMarket.Nxt => EnsureZeroAsync( DataSourceKind.MariaDb, "DB_WRITE_SMOKE_NXT_THEME_CHILD_PREFLIGHT", "SELECT CAST(COUNT(*) AS CHAR) ROW_COUNT FROM SB_ITEM WHERE SB_M_CODE = @parent_code", themeCode, cancellationToken), _ => throw new ArgumentOutOfRangeException(nameof(market)) }; internal Task EnsureExpertChildrenAbsentAsync( string expertCode, CancellationToken cancellationToken) => EnsureZeroAsync( DataSourceKind.Oracle, "DB_WRITE_SMOKE_EXPERT_CHILD_PREFLIGHT", "SELECT TO_CHAR(COUNT(*)) ROW_COUNT FROM RECOMMEND_LIST WHERE RC_CODE = :parent_code", expertCode, cancellationToken); private async Task EnsureZeroAsync( DataSourceKind source, string queryName, string sql, string parentCode, CancellationToken cancellationToken) { ArgumentException.ThrowIfNullOrWhiteSpace(parentCode); var spec = new DataQuerySpec( sql, [new DataQueryParameter("parent_code", parentCode, DbType.String)]); spec.ValidateFor(source); var table = await _executor.ExecuteAsync( source, queryName, spec, cancellationToken).ConfigureAwait(false); if (table is null || table.Columns.Count != 1 || table.Rows.Count != 1 || !string.Equals( table.Columns[0].ColumnName, ResultColumn, StringComparison.Ordinal) || table.Columns[0].DataType != typeof(string) || table.Rows[0][0] is not string count || !string.Equals(count, "0", StringComparison.Ordinal)) { throw new InvalidOperationException( "A suggested numeric key has existing child rows or an invalid preflight result."); } } }