using System; using System.Data; using System.Data.Common; using System.Diagnostics.CodeAnalysis; using System.Globalization; using NpgsqlTypes; namespace Npgsql; /// /// This class is responsible to create database commands for automatic insert, update and delete operations. /// [System.ComponentModel.DesignerCategory("")] public sealed class NpgsqlCommandBuilder : DbCommandBuilder { // Commented out because SetRowUpdatingHandler() is commented, and causes an "is never used" warning // private NpgsqlRowUpdatingEventHandler rowUpdatingHandler; /// /// Initializes a new instance of the class. /// public NpgsqlCommandBuilder() : this(null) { } /// /// Initializes a new instance of the class. /// /// The adapter. public NpgsqlCommandBuilder(NpgsqlDataAdapter? adapter) { DataAdapter = adapter; QuotePrefix = "\""; QuoteSuffix = "\""; } /// /// Gets or sets the beginning character or characters to use when specifying database objects (for example, tables or columns) whose names contain characters such as spaces or reserved tokens. /// /// /// The beginning character or characters to use. The default is an empty string. /// [AllowNull] public override string QuotePrefix { get => base.QuotePrefix; // TODO: Why should it be possible to remove the QuotePrefix? set { if (string.IsNullOrEmpty(value)) { base.QuotePrefix = value; } else { base.QuotePrefix = "\""; } } } /// /// Gets or sets the ending character or characters to use when specifying database objects (for example, tables or columns) whose names contain characters such as spaces or reserved tokens. /// /// /// The ending character or characters to use. The default is an empty string. /// [AllowNull] public override string QuoteSuffix { get => base.QuoteSuffix; // TODO: Why should it be possible to remove the QuoteSuffix? set { if (string.IsNullOrEmpty(value)) { base.QuoteSuffix = value; } else { base.QuoteSuffix = "\""; } } } /// /// /// This method is responsible to derive the command parameter list with values obtained from function definition. /// It clears the Parameters collection of command. Also, if there is any parameter type which is not supported by Npgsql, an InvalidOperationException will be thrown. /// Parameters name will be parameter1, parameter2, ... for CommandType.StoredProcedure and named after the placeholder for CommandType.Text /// /// NpgsqlCommand whose function parameters will be obtained. public static void DeriveParameters(NpgsqlCommand command) => command.DeriveParameters(); /// /// Gets the automatically generated object required /// to perform insertions at the data source. /// /// /// The automatically generated object required to perform insertions. /// public new NpgsqlCommand GetInsertCommand() => GetInsertCommand(false); /// /// Gets the automatically generated object required to perform insertions /// at the data source, optionally using columns for parameter names. /// /// /// If , generate parameter names matching column names, if possible. /// If , generate @p1, @p2, and so on. /// /// /// The automatically generated object required to perform insertions. /// public new NpgsqlCommand GetInsertCommand(bool useColumnsForParameterNames) { var cmd = (NpgsqlCommand) base.GetInsertCommand(useColumnsForParameterNames); cmd.UpdatedRowSource = UpdateRowSource.None; return cmd; } /// /// Gets the automatically generated System.Data.Common.DbCommand object required /// to perform updates at the data source. /// /// /// The automatically generated System.Data.Common.DbCommand object required to perform updates. /// public new NpgsqlCommand GetUpdateCommand() => GetUpdateCommand(false); /// /// Gets the automatically generated object required to perform updates /// at the data source, optionally using columns for parameter names. /// /// /// If , generate parameter names matching column names, if possible. /// If , generate @p1, @p2, and so on. /// /// /// The automatically generated object required to perform updates. /// public new NpgsqlCommand GetUpdateCommand(bool useColumnsForParameterNames) { var cmd = (NpgsqlCommand)base.GetUpdateCommand(useColumnsForParameterNames); cmd.UpdatedRowSource = UpdateRowSource.None; return cmd; } /// /// Gets the automatically generated System.Data.Common.DbCommand object required /// to perform deletions at the data source. /// /// /// The automatically generated System.Data.Common.DbCommand object required to perform deletions. /// public new NpgsqlCommand GetDeleteCommand() => GetDeleteCommand(false); /// /// Gets the automatically generated object required to perform deletions /// at the data source, optionally using columns for parameter names. /// /// /// If , generate parameter names matching column names, if possible. /// If , generate @p1, @p2, and so on. /// /// /// The automatically generated object required to perform deletions. /// public new NpgsqlCommand GetDeleteCommand(bool useColumnsForParameterNames) { var cmd = (NpgsqlCommand) base.GetDeleteCommand(useColumnsForParameterNames); cmd.UpdatedRowSource = UpdateRowSource.None; return cmd; } //never used //private string QualifiedTableName(string schema, string tableName) //{ // if (schema == null || schema.Length == 0) // { // return tableName; // } // else // { // return schema + "." + tableName; // } //} /* private static void SetParameterValuesFromRow(NpgsqlCommand command, DataRow row) { foreach (NpgsqlParameter parameter in command.Parameters) { parameter.Value = row[parameter.SourceColumn, parameter.SourceVersion]; } } */ /// /// Applies the parameter information. /// /// The parameter. /// The row. /// Type of the statement. /// If set to [where clause]. protected override void ApplyParameterInfo(DbParameter p, DataRow row, System.Data.StatementType statementType, bool whereClause) { var param = (NpgsqlParameter)p; param.NpgsqlDbType = (NpgsqlDbType)row[SchemaTableColumn.ProviderType]; } /// /// Returns the name of the specified parameter in the format of @p#. /// /// The number to be included as part of the parameter's name.. /// /// The name of the parameter with the specified number appended as part of the parameter name. /// protected override string GetParameterName(int parameterOrdinal) => string.Format(CultureInfo.InvariantCulture, "@p{0}", parameterOrdinal); /// /// Returns the full parameter name, given the partial parameter name. /// /// The partial name of the parameter. /// /// The full parameter name corresponding to the partial parameter name requested. /// protected override string GetParameterName(string parameterName) => string.Format(CultureInfo.InvariantCulture, "@{0}", parameterName); /// /// Returns the placeholder for the parameter in the associated SQL statement. /// /// The number to be included as part of the parameter's name. /// /// The name of the parameter with the specified number appended. /// protected override string GetParameterPlaceholder(int parameterOrdinal) => GetParameterName(parameterOrdinal); /// /// Registers the to handle the event for a . /// /// The to be used for the update. protected override void SetRowUpdatingHandler(DbDataAdapter adapter) { var npgsqlAdapter = adapter as NpgsqlDataAdapter; if (npgsqlAdapter == null) throw new ArgumentException("adapter needs to be a NpgsqlDataAdapter", nameof(adapter)); // Being called twice for the same adapter means unregister if (adapter == DataAdapter) npgsqlAdapter.RowUpdating -= RowUpdatingHandler; else npgsqlAdapter.RowUpdating += RowUpdatingHandler; } /// /// Adds an event handler for the event. /// /// The sender /// A instance containing information about the event. void RowUpdatingHandler(object sender, NpgsqlRowUpdatingEventArgs e) => base.RowUpdatingHandler(e); /// /// Given an unquoted identifier in the correct catalog case, returns the correct quoted form of that identifier, including properly escaping any embedded quotes in the identifier. /// /// The original unquoted identifier. /// /// The quoted version of the identifier. Embedded quotes within the identifier are properly escaped. /// /// Unquoted identifier parameter cannot be null public override string QuoteIdentifier(string unquotedIdentifier) => unquotedIdentifier == null ? throw new ArgumentNullException(nameof(unquotedIdentifier), "Unquoted identifier parameter cannot be null") : $"{QuotePrefix}{unquotedIdentifier.Replace(QuotePrefix, QuotePrefix + QuotePrefix)}{QuoteSuffix}"; /// /// Given a quoted identifier, returns the correct unquoted form of that identifier, including properly un-escaping any embedded quotes in the identifier. /// /// The identifier that will have its embedded quotes removed. /// /// The unquoted identifier, with embedded quotes properly un-escaped. /// /// Quoted identifier parameter cannot be null public override string UnquoteIdentifier(string quotedIdentifier) { if (quotedIdentifier == null) throw new ArgumentNullException(nameof(quotedIdentifier), "Quoted identifier parameter cannot be null"); var unquotedIdentifier = quotedIdentifier.Trim().Replace(QuotePrefix + QuotePrefix, QuotePrefix); if (unquotedIdentifier.StartsWith(QuotePrefix, StringComparison.Ordinal)) unquotedIdentifier = unquotedIdentifier.Remove(0, 1); if (unquotedIdentifier.EndsWith(QuoteSuffix, StringComparison.Ordinal)) unquotedIdentifier = unquotedIdentifier.Remove(unquotedIdentifier.Length - 1, 1); return unquotedIdentifier; } }