From 4f9f7b54c4f5b9a25a4ae656f756d922fd1b4591 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 07:30:36 +0000 Subject: [PATCH 1/2] perf: pre-compute hasOutputParameters flag at construction time Avoid Seq.cast + Seq.exists traversal on every Execute call by computing the hasOutputParameters flag once from cfg.Parameters at ISqlCommand Implementation construction time. For the common case of queries with no output parameters, this eliminates an O(n) linear scan of the parameter collection per execution. The flag is threaded through to ExecuteSeq<'TItem> (via reflection invocation) and ExecuteNonQuery (via partial application), and the output-parameter writeback loop in ExecuteNonQuery is now guarded by it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/SqlClient/ISqlCommand.fs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/SqlClient/ISqlCommand.fs b/src/SqlClient/ISqlCommand.fs index ec6b76a7..3e2a85c6 100644 --- a/src/SqlClient/ISqlCommand.fs +++ b/src/SqlClient/ISqlCommand.fs @@ -87,6 +87,11 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio let notImplemented _ : _ = raise <| NotImplementedException() + // Pre-compute whether any parameters are output parameters once at construction time, + // avoiding repeated Seq.cast + Seq.exists traversal on every Execute call. + let hasOutputParameters = + cfg.Parameters |> Array.exists (fun p -> p.Direction.HasFlag(ParameterDirection.Output)) + let execute, asyncExecute, executeSingle, asyncExecuteSingle = match cfg.ResultType with | ResultType.DataReader -> @@ -102,7 +107,7 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio | ResultType.Records | ResultType.Tuples -> match box cfg.RowMapping, cfg.ItemTypeName with | null, null -> - ``ISqlCommand Implementation``.ExecuteNonQuery manageConnection >> box, + ``ISqlCommand Implementation``.ExecuteNonQuery manageConnection hasOutputParameters >> box, ``ISqlCommand Implementation``.AsyncExecuteNonQuery manageConnection >> box, notImplemented, notImplemented @@ -120,9 +125,9 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio .GetMethod("AsyncExecuteSeq", BindingFlags.NonPublic ||| BindingFlags.Static) .MakeGenericMethod(itemType) - executeHandle.Invoke(null, [| cfg.Rank; cfg.RowMapping |]) |> unbox >> box, + executeHandle.Invoke(null, [| cfg.Rank; cfg.RowMapping; hasOutputParameters |]) |> unbox >> box, asyncExecuteHandle.Invoke(null, [| cfg.Rank; cfg.RowMapping |]) |> unbox >> box, - executeHandle.Invoke(null, [| ResultRank.SingleRow; cfg.RowMapping |]) |> unbox >> box, + executeHandle.Invoke(null, [| ResultRank.SingleRow; cfg.RowMapping; hasOutputParameters |]) |> unbox >> box, asyncExecuteHandle.Invoke(null, [| ResultRank.SingleRow; cfg.RowMapping |]) |> unbox >> box | unexpected -> failwithf "Unexpected ResultType value: %O" unexpected @@ -298,9 +303,7 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio return result } - static member internal ExecuteSeq<'TItem> (rank, rowMapper) = fun(cmd: SqlCommand, getReaderBehavior, parameters, expectedDataReaderColumns) -> - let hasOutputParameters = cmd.Parameters |> Seq.cast |> Seq.exists (fun x -> x.Direction.HasFlag( ParameterDirection.Output)) - + static member internal ExecuteSeq<'TItem> (rank, rowMapper, hasOutputParameters: bool) = fun(cmd: SqlCommand, getReaderBehavior, parameters, expectedDataReaderColumns) -> if not hasOutputParameters then let xs = Seq.delay <| fun() -> @@ -360,16 +363,17 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio assert (rank = ResultRank.Sequence) box xs - static member internal ExecuteNonQuery manageConnection (cmd, _, parameters, _) = + static member internal ExecuteNonQuery manageConnection hasOutputParameters (cmd, _, parameters, _) = ``ISqlCommand Implementation``.SetParameters(cmd, parameters) use openedConnection = cmd.Connection.UseLocally(manageConnection ) let recordsAffected = cmd.ExecuteNonQuery() - for i = 0 to parameters.Length - 1 do - let name, _ = parameters.[i] - let p = cmd.Parameters.[name] - if p.Direction.HasFlag( ParameterDirection.Output) - then - parameters.[i] <- name, p.Value + if hasOutputParameters then + for i = 0 to parameters.Length - 1 do + let name, _ = parameters.[i] + let p = cmd.Parameters.[name] + if p.Direction.HasFlag( ParameterDirection.Output) + then + parameters.[i] <- name, p.Value recordsAffected static member internal AsyncExecuteNonQuery manageConnection (cmd, _, parameters, _) = From b122de9b714f629d36d5a313a483931156b3d02d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 07:37:02 +0000 Subject: [PATCH 2/2] ci: trigger checks