using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Npgsql.BackendMessages; namespace Npgsql { /// /// Represents a single SQL statement within Npgsql. /// /// Instances aren't constructed directly; users should construct an /// object and populate its property as in standard ADO.NET. /// Npgsql will analyze that property and construct instances of /// internally. /// /// Users can retrieve instances from /// and access information about statement execution (e.g. affected rows). /// public sealed class NpgsqlStatement { /// /// The SQL text of the statement. /// public string SQL { get; set; } = string.Empty; /// /// Specifies the type of query, e.g. SELECT. /// public StatementType StatementType { get; internal set; } /// /// The number of rows affected or retrieved. /// /// /// See the command tag in the CommandComplete message, /// https://www.postgresql.org/docs/current/static/protocol-message-formats.html /// public uint Rows => (uint)LongRows; /// /// The number of rows affected or retrieved. /// /// /// See the command tag in the CommandComplete message, /// https://www.postgresql.org/docs/current/static/protocol-message-formats.html /// public ulong LongRows { get; internal set; } /// /// For an INSERT, the object ID of the inserted row if is 1 and /// the target table has OIDs; otherwise 0. /// public uint OID { get; internal set; } /// /// The input parameters sent with this statement. /// public List InputParameters { get => _inputParameters ??= _ownedInputParameters ??= new(); internal set => _inputParameters = value; } List? _ownedInputParameters; List? _inputParameters; /// /// The RowDescription message for this query. If null, the query does not return rows (e.g. INSERT) /// internal RowDescriptionMessage? Description { get => PreparedStatement == null ? _description : PreparedStatement.Description; set { if (PreparedStatement == null) _description = value; else PreparedStatement.Description = value; } } RowDescriptionMessage? _description; /// /// If this statement has been automatically prepared, references the . /// Null otherwise. /// internal PreparedStatement? PreparedStatement { get => _preparedStatement != null && _preparedStatement.State == PreparedState.Unprepared ? _preparedStatement = null : _preparedStatement; set => _preparedStatement = value; } PreparedStatement? _preparedStatement; internal bool IsPreparing; /// /// Holds the server-side (prepared) statement name. Empty string for non-prepared statements. /// internal string StatementName => PreparedStatement?.Name ?? ""; /// /// Whether this statement has already been prepared (including automatic preparation). /// internal bool IsPrepared => PreparedStatement?.IsPrepared == true; internal void Reset() { SQL = string.Empty; StatementType = StatementType.Select; _description = null; LongRows = 0; OID = 0; PreparedStatement = null; if (ReferenceEquals(_inputParameters, _ownedInputParameters)) InputParameters.Clear(); else if (_inputParameters is not null) _inputParameters = null; // We're pointing at a user's NpgsqlParameterCollection Debug.Assert(_inputParameters is null || _inputParameters.Count == 0); Debug.Assert(_ownedInputParameters is null || _ownedInputParameters.Count == 0); } internal void ApplyCommandComplete(CommandCompleteMessage msg) { StatementType = msg.StatementType; LongRows = msg.Rows; OID = msg.OID; } /// /// Returns the SQL text of the statement. /// public override string ToString() => SQL ?? ""; } }