Как выглядят костыли в коде

В ADO.NET есть стандартная схема метаданных DataSourceInformation, в которой определена колонка ParameterMarkerFormat:

A format string that represents how to format a parameter.

If named parameters are supported by the data source, the first placeholder in this string should be where the parameter name should be formatted.

(1) For example, if the data source expects parameters to be named and prefixed with an ‘:’ this would be «:{0}». When formatting this with a parameter name of «p1» the resulting string is «:p1».

(2) If the data source expects parameters to be prefixed with the ‘@’, but the names already include them, this would be ‘{0}’, and the result of formatting a parameter named «@p1» would simply be «@p1».

Провайдер данных MSSQL попадает под вторую категорию — в имена параметров включается префикс. Ну, наверное, вы такое уже видели, что в ADODB, что в ADO.NET. Под него косит халявный ADO.NET провайдер для Firebird. То есть, ParameterMarkerFormat у MSSQL провайдера содержит «{0}».

IBProvider может работать и так и сяк, в зависимости от настройки строки подключения named_param_rules.

В первом случае (режим по-умолчанию) named_param_rules=0. Во втором случае: named_param_rules=1.

Теперь перейдем к костылям, которые я обещал показать.

В попытках заставить SSIS (Sql Server Intergration Services) работать с моим ADO.NET провайдером я немного растерялся. Если named_param_rules=0 — ругается провайдер на неизвестное имя параметра «:p1». А если named_param_rules=1, ругается сервер на неизвестную колонку «p1».

Сначала я, чертыхаясь, настроил Firebird на трассировку запросов.

2018-11-03T19:14:45.1420 (6312:0000000002F424C0) PREPARE_STATEMENT
	D:\DATABASE\RAM\IBP_TEST_FB30_D3.GDB (ATT_16190, GAMER:NONE, NONE, TCPv4:127.0.0.1/59880)
	C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\SSIS\140\Binn\DTExec.exe:19952
		(TRA_278781, CONCURRENCY | NOWAIT | READ_WRITE)

-------------------------------------------------------------------------------
INSERT INTO "TEST_MODIFY_ROW_1" ("COL_INTEGER") VALUES (p1)
      0 ms

Потом, поймав отладчиком установку проблемного запроса, я посмотрел откуда ко мне пришли.

// C:\WINDOWS\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.ADONETDest\v4.0_14.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ADONETDest.dll
// Microsoft.SqlServer.ADONETDest, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319

Метод ADONETDestination::PreExecute

Смотрим декомпилятором (ILSpy) в код этого метода:

string parmameterMarkerFormat = getParmameterMarkerFormat();
PostDiagnosticMessage(Microsoft.SqlServer.Dts.Pipeline.Localized.DiagnosticPre("DbProviderFactory.CreateParameter"));
string empty = string.Empty;
for (int k = 0; k <= num; k++)
{
	empty = string.Format(CultureInfo.InvariantCulture, "{0}{1:d}", new object[2]
	{
		"p",
		k + 1
	});
	empty = string.Format(CultureInfo.InvariantCulture, parmameterMarkerFormat, new object[1]
	{
		empty
	});
	DbParameter dbParameter = m_DbFactory.CreateParameter();
	dbParameter.ParameterName = empty;
	dbParameter.SourceColumn = m_table.Columns[k].ColumnName;
	AdjustParameterType(dbParameter, m_table.Columns[k].DataType);
	m_insertCmd.Parameters.Add(dbParameter);
	stringBuilder.Append(empty);
	if (k == num)
	{
		stringBuilder.Append(")");
	}
	else
	{
		stringBuilder.Append(", ");
	}
}

Ага, это они берут имя параметра, применяют к нему этот самый parmameterMarkerFormat, а потом полученный маркер снова делают именем. Клоуны.

А что из себя представляет getParmameterMarkerFormat?

private string getParmameterMarkerFormat()
{
	if (m_DbConnection.GetType().Equals(typeof(SqlConnection)))
	{
		return "@{0}";
	}
	DataTable schema = m_DbConnection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation);
	return (string)schema.Rows[0]["ParameterMarkerFormat"];
}

Молодцы!

Под свой сервер они прикрутили костыли. А остальные — да как хотят.

Leave a Comment