Skip to content

Commit

Permalink
Add support for passing parameters to the query as constant literals (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
victor-sushko committed Mar 27, 2022
1 parent 51ccbf1 commit bd68dba
Show file tree
Hide file tree
Showing 11 changed files with 1,201 additions and 1,049 deletions.
14 changes: 8 additions & 6 deletions src/ConnectionSettingsHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#region License Apache 2.0
/* Copyright 2020-2021 Octonica
/* Copyright 2020-2022 Octonica
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,17 +22,17 @@ namespace Octonica.ClickHouseClient
{
internal static class ConnectionSettingsHelper
{
public static ClickHouseConnectionSettings GetConnectionSettings()
public static ClickHouseConnectionSettings GetConnectionSettings(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)
{
return GetConnectionSettingsInternal().settings;
return GetConnectionSettingsInternal(updateSettings).settings;
}

public static string GetConnectionString()
public static string GetConnectionString(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)
{
return GetConnectionSettingsInternal().connectionString;
return GetConnectionSettingsInternal(updateSettings).connectionString;
}

private static (ClickHouseConnectionSettings settings, string connectionString) GetConnectionSettingsInternal()
private static (ClickHouseConnectionSettings settings, string connectionString) GetConnectionSettingsInternal(Action<ClickHouseConnectionStringBuilder>? updateSettings)
{
const string envVariableName = "CLICKHOUSE_TEST_CONNECTION";
const string configFileName = "clickHouse.dbconfig";
Expand All @@ -44,6 +44,7 @@ private static (ClickHouseConnectionSettings settings, string connectionString)
try
{
var builder = new ClickHouseConnectionStringBuilder(configTextFromEnvVar);
updateSettings?.Invoke(builder);
return (builder.BuildSettings(), configTextFromEnvVar);
}
catch (Exception ex)
Expand All @@ -65,6 +66,7 @@ private static (ClickHouseConnectionSettings settings, string connectionString)
try
{
var builder = new ClickHouseConnectionStringBuilder(configText);
updateSettings?.Invoke(builder);
return (builder.BuildSettings(), configText);
}
catch (Exception ex)
Expand Down
6 changes: 4 additions & 2 deletions src/Octonica.ClickHouseClient.Tests/ClickHouseCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ public async Task InsertWithParameters()
{
try
{
await using var connection = await OpenConnectionAsync();
await using var connection = await OpenConnectionAsync(builder => builder.ParametersMode = ClickHouseParameterMode.Interpolate);

var cmd = connection.CreateCommand("DROP TABLE IF EXISTS insert_with_parameters_test");
await cmd.ExecuteNonQueryAsync();
Expand Down Expand Up @@ -505,7 +505,7 @@ public async Task DeleteWithParameters()
{
try
{
await using var connection = await OpenConnectionAsync();
await using var connection = await OpenConnectionAsync(builder => builder.ParametersMode = ClickHouseParameterMode.Interpolate);

var cmd = connection.CreateCommand("DROP TABLE IF EXISTS delete_with_parameters_test");
await cmd.ExecuteNonQueryAsync();
Expand Down Expand Up @@ -548,6 +548,7 @@ public async Task UpdateWithParameters()
p.ParameterName = "str_param";
var insertedValue = "IZyy8d\\'\"\n\t\v\b\rLsVeTtdfk6MjJl";
p.Value = insertedValue;
((ClickHouseParameter)p).ParameterMode = ClickHouseParameterMode.Interpolate;
cmd.Parameters.Add(p);
await cmd.ExecuteNonQueryAsync();
// Assert: Not Throws
Expand All @@ -566,6 +567,7 @@ public async Task SelectWithOffsetLimitParameters()
await using var connection = await OpenConnectionAsync();

await using var cmd = connection.CreateCommand("select cast(42 as UInt64) limit @Limit offset @Offset");
cmd.ParametersMode = ClickHouseParameterMode.Interpolate;
var p_limit = cmd.CreateParameter();
var p_offset = cmd.CreateParameter();
p_limit.ParameterName = "Limit";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#region License Apache 2.0
/* Copyright 2019-2021 Octonica
/* Copyright 2019-2022 Octonica
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -131,6 +131,9 @@ public void Default()
Assert.True(settings.ServerCertificateHash.IsEmpty);
++checkedPropertiesCount;

Assert.Equal(ClickHouseConnectionStringBuilder.DefaultParametersMode, settings.ParametersMode);
++checkedPropertiesCount;

Assert.Equal(checkedPropertiesCount, settings.GetType().GetProperties().Length);
}

Expand All @@ -151,7 +154,8 @@ public void Clone()
"CommandTimeout=123;" +
"TLSMode=rEqUIrE;" +
"RootCertificate=/usr/local/share/ca-certificates/Yandex/YandexInternalRootCA.pem;" +
"ServerCertificateHash=1234-5678 9abc-def0");
"ServerCertificateHash=1234-5678 9abc-def0;" +
"ParametersMode=Interpolate");

var settings = builder.BuildSettings();

Expand Down Expand Up @@ -202,6 +206,9 @@ public void Clone()
Assert.Equal(new byte[] { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 }, settings.ServerCertificateHash.ToArray());
++checkedPropertiesCount;

Assert.Equal(ClickHouseParameterMode.Interpolate, settings.ParametersMode);
++checkedPropertiesCount;

Assert.Equal(checkedPropertiesCount, settings.GetType().GetProperties().Length);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#region License Apache 2.0
/* Copyright 2021 Octonica
/* Copyright 2021-2022 Octonica
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -50,7 +50,7 @@ static ClickHouseParameterTests()
[Fact]
public void Clone()
{
var p1 = new ClickHouseParameter("p1") { Value = new[] { 42 }, ClickHouseDbType = ClickHouseDbType.VarNumeric, Precision = 19, Scale = 7 };
var p1 = new ClickHouseParameter("p1") { Value = new[] { 42 }, ClickHouseDbType = ClickHouseDbType.VarNumeric, Precision = 19, Scale = 7, ParameterMode = ClickHouseParameterMode.Interpolate };

var p2 = p1.Clone();

Expand All @@ -61,6 +61,7 @@ public void Clone()
Assert.Equal(ClickHouseDbType.VarNumeric, p2.ClickHouseDbType);
Assert.Equal(19, p2.Precision);
Assert.Equal(7, p2.Scale);
Assert.Equal(ClickHouseParameterMode.Interpolate, p2.ParameterMode);

p2.TimeZone = TimeZoneInfo.Local;
p2.Size = 35;
Expand Down
14 changes: 7 additions & 7 deletions src/Octonica.ClickHouseClient.Tests/ClickHouseTestsBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#region License Apache 2.0
/* Copyright 2019-2021 Octonica
/* Copyright 2019-2022 Octonica
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,14 +25,14 @@ public abstract class ClickHouseTestsBase
{
private ClickHouseConnectionSettings? _settings;

public ClickHouseConnectionSettings GetDefaultConnectionSettings()
public ClickHouseConnectionSettings GetDefaultConnectionSettings(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)
{
if (_settings != null)
{
return _settings;
}

_settings = ConnectionSettingsHelper.GetConnectionSettings();
_settings = ConnectionSettingsHelper.GetConnectionSettings(updateSettings);
return _settings;
}

Expand All @@ -44,14 +44,14 @@ public async Task<ClickHouseConnection> OpenConnectionAsync(ClickHouseConnection
return connection;
}

public async Task<ClickHouseConnection> OpenConnectionAsync()
public async Task<ClickHouseConnection> OpenConnectionAsync(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)
{
return await OpenConnectionAsync(GetDefaultConnectionSettings(), CancellationToken.None);
return await OpenConnectionAsync(GetDefaultConnectionSettings(updateSettings), CancellationToken.None);
}

public ClickHouseConnection OpenConnection()
public ClickHouseConnection OpenConnection(Action<ClickHouseConnectionStringBuilder>? updateSettings = null)
{
ClickHouseConnection connection = new ClickHouseConnection(GetDefaultConnectionSettings());
ClickHouseConnection connection = new ClickHouseConnection(GetDefaultConnectionSettings(updateSettings));
connection.Open();

return connection;
Expand Down
53 changes: 41 additions & 12 deletions src/Octonica.ClickHouseClient/ClickHouseCommand.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#region License Apache 2.0
/* Copyright 2019-2021 Octonica
/* Copyright 2019-2022 Octonica
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -153,6 +153,12 @@ protected override DbTransaction? DbTransaction
/// if the data reader should return profile events as a recordset. The default value is <see langword="true"/>.</returns>
public bool IgnoreProfileEvents { get; set; } = true;

/// <summary>
/// Gets or sets the mode of passing parameters to the query. The value of this property overrides <see cref="ClickHouseConnection.ParametersMode"/>.
/// </summary>
/// <returns>The mode of passing parameters to the query. The default value is <see cref="ClickHouseParameterMode.Inherit"/>.</returns>
public ClickHouseParameterMode ParametersMode { get; set; } = ClickHouseParameterMode.Inherit;

/// <summary>
/// Creates a new instance of <see cref="ClickHouseCommand"/>.
/// </summary>
Expand Down Expand Up @@ -818,6 +824,15 @@ private TimeSpan GetCommandTimeout(ClickHouseConnection? connection)
return _commandTimeout ?? connection?.CommandTimeSpan ?? TimeSpan.FromSeconds(ClickHouseConnectionStringBuilder.DefaultCommandTimeout);
}

private ClickHouseParameterMode GetParametersMode(ClickHouseConnection? connection)
{
var mode = ParametersMode;
if (mode != ClickHouseParameterMode.Inherit)
return mode;

return connection?.ParametersMode ?? ClickHouseParameterMode.Default;
}

private IClickHouseTableWriter CreateParameterTableWriter(IClickHouseTypeInfoProvider typeInfoProvider, string tableName)
{
return new ClickHouseTableWriter(tableName, 1, Parameters.Select(p => p.CreateParameterColumnWriter(typeInfoProvider)));
Expand Down Expand Up @@ -856,6 +871,7 @@ private string PrepareCommandText(IClickHouseTypeInfoProvider typeInfoProvider,
}

parameters = null;
var inheritParameterMode = GetParametersMode(Connection);
var queryStringBuilder = new StringBuilder(query.Length);
for (int i = 0; i < parameterPositions.Count; i++)
{
Expand All @@ -868,21 +884,34 @@ private string PrepareCommandText(IClickHouseTypeInfoProvider typeInfoProvider,
if (!Parameters.TryGetValue(parameterName, out var parameter))
throw new ClickHouseException(ClickHouseErrorCodes.QueryParameterNotFound, $"Parameter \"{parameterName}\" not found.");

var specifiedType = typeSeparatorIdx >= 0 ? query.AsMemory().Slice(offset + typeSeparatorIdx + 1, length - typeSeparatorIdx - 2) : ReadOnlyMemory<char>.Empty;
parameter.OutputParameterValue(queryStringBuilder, specifiedType, typeInfoProvider);
var parameterMode = parameter.GetParameterMode(inheritParameterMode);
switch (parameterMode)
{
case ClickHouseParameterMode.Interpolate:
var specifiedType = typeSeparatorIdx >= 0 ? query.AsMemory().Slice(offset + typeSeparatorIdx + 1, length - typeSeparatorIdx - 2) : ReadOnlyMemory<char>.Empty;
parameter.OutputParameterValue(queryStringBuilder, specifiedType, typeInfoProvider);
break;

/* TODO: add parameter inline mode
if (!parameters.Contains(parameter.ParameterName))
parameters.Add(parameter.ParameterName);
case ClickHouseParameterMode.Default:
case ClickHouseParameterMode.Binary:
if (parameters == null)
parameters = new HashSet<string>();

if (typeSeparatorIdx >= 0)
queryStringBuilder.Append("(CAST(");
if (!parameters.Contains(parameter.ParameterName))
parameters.Add(parameter.ParameterName);

queryStringBuilder.Append("(SELECT ").Append(parametersTable).Append('.').Append(parameter.Id).Append(" FROM ").Append(parametersTable).Append(')');
if (typeSeparatorIdx >= 0)
queryStringBuilder.Append("(CAST(");

if (typeSeparatorIdx >= 0)
queryStringBuilder.Append(" AS ").Append(query, offset + typeSeparatorIdx + 1, length - typeSeparatorIdx - 2).Append("))");
*/
queryStringBuilder.Append("(SELECT ").Append(parametersTable).Append('.').Append(parameter.Id).Append(" FROM ").Append(parametersTable).Append(')');

if (typeSeparatorIdx >= 0)
queryStringBuilder.Append(" AS ").Append(query, offset + typeSeparatorIdx + 1, length - typeSeparatorIdx - 2).Append("))");
break;

default:
throw new ClickHouseException(ClickHouseErrorCodes.InternalError, $"Internal error. Unexpected parameter mode: {parameterMode}.");
}
}

var lastPartStart = parameterPositions[^1].offset + parameterPositions[^1].length;
Expand Down
Loading

0 comments on commit bd68dba

Please sign in to comment.