Try your search with a different keyword or use * as a wildcard.
using System.Data;
using System.Data.Common;
using LinqToDB;
using LinqToDB.Common;
using LinqToDB.Data;
using LinqToDB.DataProvider;
using LinqToDB.SqlQuery;
using Nop.Core;
using Nop.Data.DataProviders.LinqToDB;
using Nop.Data.Mapping;
using Npgsql;
namespace Nop.Data.DataProviders;
public partial class PostgreSqlDataProvider : BaseDataProvider, INopDataProvider
{
#region Fields
protected static readonly Lazy _dataProvider = new(() => new LinqToDBPostgreSQLDataProvider(), true);
#endregion
#region Utilities
///
/// Creates the database connection by the current data configuration
///
protected override DataConnection CreateDataConnection()
{
var dataContext = CreateDataConnection(LinqToDbDataProvider);
dataContext.MappingSchema.SetDataType(
typeof(string),
new SqlDataType(new DbDataType(typeof(string), "citext")));
return dataContext;
}
///
/// Gets the connection string builder
///
/// The connection string builder
protected static NpgsqlConnectionStringBuilder GetConnectionStringBuilder()
{
return new NpgsqlConnectionStringBuilder(GetCurrentConnectionString());
}
///
/// Gets a connection to the database for a current data provider
///
/// Connection string
/// Connection to a database
protected override DbConnection GetInternalDbConnection(string connectionString)
{
ArgumentException.ThrowIfNullOrEmpty(connectionString);
return new NpgsqlConnection(connectionString);
}
///
/// Get the name of the sequence associated with a identity column
///
/// A database connection object
/// Entity type
/// Returns the name of the sequence, or NULL if no sequence is associated with the column
protected virtual string GetSequenceName(DataConnection dataConnection) where TEntity : BaseEntity
{
ArgumentNullException.ThrowIfNull(dataConnection);
var descriptor = NopMappingSchema.GetEntityDescriptor(typeof(TEntity))
?? throw new NopException($"Mapped entity descriptor is not found: {typeof(TEntity).Name}");
var tableName = descriptor.EntityName;
var columnName = descriptor.Fields.FirstOrDefault(x => x.IsIdentity && x.IsPrimaryKey)?.Name;
if (string.IsNullOrEmpty(columnName))
throw new NopException("A table's primary key does not have an identity constraint");
return dataConnection.Query($"SELECT pg_get_serial_sequence('\"{tableName}\"', '{columnName}');")
.FirstOrDefault();
}
#endregion
#region Methods
///
/// Creates the database by using the loaded connection string
///
///
///
public void CreateDatabase(string collation, int triesToConnect = 10)
{
if (DatabaseExists())
return;
var builder = GetConnectionStringBuilder();
//gets database name
var databaseName = builder.Database;
//now create connection string to 'postgres' - default administrative connection database.
builder.Database = "postgres";
using (var connection = GetInternalDbConnection(builder.ConnectionString))
{
var query = $"CREATE DATABASE \"{databaseName}\" WITH OWNER = '{builder.Username}'";
if (!string.IsNullOrWhiteSpace(collation))
query = $"{query} LC_COLLATE = '{collation}'";
var command = connection.CreateCommand();
command.CommandText = query;
command.Connection.Open();
command.ExecuteNonQuery();
}
//try connect
if (triesToConnect <= 0)
return;
//sometimes on slow servers (hosting) there could be situations when database requires some time to be created.
//but we have already started creation of tables and sample data.
//as a result there is an exception thrown and the installation process cannot continue.
//that's why we are in a cycle of "triesToConnect" times trying to connect to a database with a delay of one second.
for (var i = 0; i <= triesToConnect; i++)
{
if (i == triesToConnect)
throw new Exception("Unable to connect to the new database. Please try one more time");
if (!DatabaseExists())
{
Thread.Sleep(1000);
}
else
{
builder.Database = databaseName;
using var connection = GetInternalDbConnection(builder.ConnectionString) as NpgsqlConnection;
var command = connection.CreateCommand();
command.CommandText = "CREATE EXTENSION IF NOT EXISTS citext; CREATE EXTENSION IF NOT EXISTS pgcrypto;";
command.Connection.Open();
command.ExecuteNonQuery();
connection.ReloadTypes();
break;
}
}
}
///
/// Checks if the specified database exists, returns true if database exists
///
/// Returns true if the database exists.
public bool DatabaseExists()
{
try
{
using var connection = GetInternalDbConnection(GetCurrentConnectionString());
//just try to connect
connection.Open();
return true;
}
catch
{
return false;
}
}
///
/// Checks if the specified database exists, returns true if database exists
///
///
/// A task that represents the asynchronous operation
/// The task result contains the returns true if the database exists.
///
public async Task DatabaseExistsAsync()
{
try
{
await using var connection = GetInternalDbConnection(GetCurrentConnectionString());
//just try to connect
await connection.OpenAsync();
return true;
}
catch
{
return false;
}
}
///
/// Get the current identity value
///
/// Entity type
///
/// A task that represents the asynchronous operation
/// The task result contains the integer identity; null if cannot get the result
///
public virtual Task GetTableIdentAsync() where TEntity : BaseEntity
{
using var currentConnection = CreateDataConnection();
var seqName = GetSequenceName(currentConnection);
var result = currentConnection.Query($"SELECT COALESCE(last_value + CASE WHEN is_called THEN 1 ELSE 0 END, 1) as Value FROM {seqName};")
.FirstOrDefault();
return Task.FromResult(result);
}
///
/// Set table identity (is supported)
///
/// Entity type
/// Identity value
/// A task that represents the asynchronous operation
public virtual async Task SetTableIdentAsync(int ident) where TEntity : BaseEntity
{
var currentIdent = await GetTableIdentAsync();
if (!currentIdent.HasValue || ident <= currentIdent.Value)
return;
using var currentConnection = CreateDataConnection();
var seqName = GetSequenceName(currentConnection);
await currentConnection.ExecuteAsync($"select setval('{seqName}', {ident}, false);");
}
///
/// Creates a backup of the database
///
/// A task that represents the asynchronous operation
public virtual Task BackupDatabaseAsync(string fileName)
{
throw new DataException("This database provider does not support backup");
}
///
/// Inserts record into table. Returns inserted entity with identity
///
///
///
/// Inserted entity
public override TEntity InsertEntity(TEntity entity)
{
using var dataContext = CreateDataConnection();
try
{
entity.Id = dataContext.InsertWithInt32Identity(entity);
}
// Ignore when we try insert foreign entity via InsertWithInt32IdentityAsync method
catch (SqlException ex) when (ex.Message.StartsWith("Identity field must be defined for"))
{
dataContext.Insert(entity);
}
return entity;
}
///
/// Inserts record into table. Returns inserted entity with identity
///
///
///
///
/// A task that represents the asynchronous operation
/// The task result contains the inserted entity
///
public override async Task InsertEntityAsync(TEntity entity)
{
using var dataContext = CreateDataConnection();
try
{
entity.Id = await dataContext.InsertWithInt32IdentityAsync(entity);
}
// Ignore when we try insert foreign entity via InsertWithInt32IdentityAsync method
catch (SqlException ex) when (ex.Message.StartsWith("Identity field must be defined for"))
{
await dataContext.InsertAsync(entity);
}
return entity;
}
///
/// Restores the database from a backup
///
/// The name of the backup file
/// A task that represents the asynchronous operation
public virtual Task RestoreDatabaseAsync(string backupFileName)
{
throw new DataException("This database provider does not support backup");
}
///
/// Re-index database tables
///
/// A task that represents the asynchronous operation
public virtual async Task ReIndexTablesAsync()
{
using var currentConnection = CreateDataConnection();
await currentConnection.ExecuteAsync($"REINDEX DATABASE \"{currentConnection.Connection.Database}\";");
}
///
/// Build the connection string
///
/// Connection string info
/// Connection string
public virtual string BuildConnectionString(INopConnectionStringInfo nopConnectionString)
{
ArgumentNullException.ThrowIfNull(nopConnectionString);
if (nopConnectionString.IntegratedSecurity)
throw new NopException("Data provider supports connection only with login and password");
var builder = new NpgsqlConnectionStringBuilder
{
Host = nopConnectionString.ServerName,
//Cast DatabaseName to lowercase to avoid case-sensitivity problems
Database = nopConnectionString.DatabaseName.ToLowerInvariant(),
Username = nopConnectionString.Username,
Password = nopConnectionString.Password,
};
return builder.ConnectionString;
}
///
/// Gets the name of a foreign key
///
/// Foreign key table
/// Foreign key column name
/// Primary table
/// Primary key column name
/// Name of a foreign key
public virtual string CreateForeignKeyName(string foreignTable, string foreignColumn, string primaryTable, string primaryColumn)
{
return $"FK_{foreignTable}_{foreignColumn}_{primaryTable}_{primaryColumn}";
}
///
/// Gets the name of an index
///
/// Target table name
/// Target column name
/// Name of an index
public virtual string GetIndexName(string targetTable, string targetColumn)
{
return $"IX_{targetTable}_{targetColumn}";
}
#endregion
#region Properties
protected override IDataProvider LinqToDbDataProvider => _dataProvider.Value;
public int SupportedLengthOfBinaryHash => 0;
public bool BackupSupported => false;
#endregion
}