//ирПортативный Перем ирПортативный Экспорт;
//ирПортативный Перем ирОбщий Экспорт;
//ирПортативный Перем ирСервер Экспорт;
//ирПортативный Перем ирКэш Экспорт;
//ирПортативный Перем ирКлиент Экспорт;

Перем мТаблицаКолонок Экспорт;
Перем мСписокКолонок Экспорт;
//Перем КлючиЗагруженныхСтрок;
Перем мСвойстваСИменамиБД Экспорт;
Перем мРегВыражениеПараметры;
Перем мРегВыражениеМета;
Перем шБуква;
Перем шГраничныйСимволИмени;
//Перем МинимальнаяДатаЗагрузки Экспорт;
//Перем мЧисловыеСвойства;

Перем мИменаВозвращаемыхСвойств Экспорт;
Перем мТипСУБД Экспорт;
Перем мПлатформа Экспорт;
Перем мКартыФайлов Экспорт;
Перем мНепустыеКолонкиЖурнала Экспорт;

Перем мСерверныеТипыПроцессов Экспорт;
//Перем мСоставСвойствСобытий Экспорт;
Перем мСвойстваСобытий Экспорт;
Перем мАдресЧужойСхемыБД Экспорт;

Перем КонецПериодаКлиента Экспорт;
Перем НачалоПериодаКлиента Экспорт;
Перем КонецПериодаСервера Экспорт;
Перем НачалоПериодаСервера Экспорт;
Перем мИдентификаторТрассы Экспорт;
Перем мЛиТрассаПоПользователю Экспорт;
Перем мТрассаСУБДXML;
Перем мСоответствиеКолонок;
Перем мИдентификаторБазыДанных;
Перем мУдержательСоединенияБД;
Перем мИдентификаторСоединенияБД;
Перем мЛиСерверMSSQLПоддерживаетТрассировку;
Перем мТаблицаЖурнала;
Перем мСтруктураХраненияСРазмерами;
Перем мСоединениеЭтойСУБД;

// СдвигВремени - Число - в секундах задается, нужно для компенсации разницы времени между компьютерами
// ОтборПоПроцессу частично игнорируются когда ЗагружатьТолькоТекущийСеанс = Истина
Функция ПрочитатьПроизвольныйЖурнал(СообщитьРазмер = Неопределено, СдвигВремени = 0, ОтборПоПроцессу = Неопределено, ОтборПоСеансу = Неопределено,
	НаСервере = Неопределено)
	
	#Если Сервер И Не Сервер Тогда
		мПлатформа = Обработки.ирПлатформа.Создать();
	#КонецЕсли
	НеизвестныеСвойства = Новый Соответствие();
	Если Ложь
		Или (НаСервере <> Неопределено И Не мЛиТрассаПоПользователю)
		Или Не ЗначениеЗаполнено(КаталогЖурнала) 
	Тогда
		КаталогЖурнала = ПолучитьКаталогТекущегоЖурнала(НаСервере = Истина); 
		Если Не ЗначениеЗаполнено(КаталогЖурнала) Тогда
			Возврат Ложь;
		КонецЕсли; 
	КонецЕсли;
	Если Не ирОбщий.ЛиКаталогДоступенЛкс(КаталогЖурнала) Тогда
		Возврат Ложь;
	КонецЕсли;
	Если НаСервере <> Истина Тогда
		ОтборПоСеансу = Неопределено; 
	КонецЕсли; 
	Если мЛиТрассаПоПользователю Тогда
		ОтборПоПользователю = ИмяПользователя();
	КонецЕсли; 
	Если ЗагружатьТолькоТекущийСеанс Тогда
		Если Ложь
			Или ирКэш.ЛиФайловаяБазаЛкс()
			Или НаСервере <> Истина
		Тогда
			ОтборПоПроцессу = ирКэш.ИдентификаторПроцессаОСЛкс();
		КонецЕсли;
		Если ОтборПоСеансу = Неопределено Тогда
			ОтборПоПользователю = ИмяПользователя();
		КонецЕсли; 
	КонецЕсли; 
	лПоследнееВремяНачалаЗагрузки = ТекущаяДата();
	Если ПериодПоследниеМинуты > 0 Тогда
		НачалоПериода = лПоследнееВремяНачалаЗагрузки - 60 * ПериодПоследниеМинуты;
		КонецПериода = Неопределено;
	КонецЕсли;
	//Если ТаблицаЖурнала.Количество() = 0 Тогда
	//	МинимальнаяДатаЗагрузки = ТекущаяДата() + 100000;
	//КонецЕсли; 
	//Если МинимальнаяДатаЗагрузки > НачалоПериода Тогда
	//	ТаблицаЖурнала.Очистить();
	//КонецЕсли; 
	//РежимДозагрузки = Истина
	//	И ТаблицаЖурнала.Количество() > 0
	//	И МинимальнаяДатаЗагрузки <= НачалоПериода;
	Если СообщитьРазмер = Неопределено Тогда
		СообщитьРазмер = (мКартыФайлов.Количество() = 0) Или КомментироватьЗагрузку;
	КонецЕсли; 
	
	ирОбщий.ЛиКаталогТехножурналаНедоступенЛкс(КаталогЖурнала);
	ФайлыЖурнала = НайтиФайлы(КаталогЖурнала, "*.log", Истина);
	ОбщийРазмер = 0;
	Для Каждого ФайлЖурнала Из ФайлыЖурнала Цикл
		ОбщийРазмер = ОбщийРазмер + ФайлЖурнала.Размер();
	КонецЦикла;
	Если СообщитьРазмер Тогда
		ирОбщий.СообщитьЛкс("В каталоге """ + КаталогЖурнала + """ обнаружено для обработки " + Формат(Цел(ОбщийРазмер / 1024), "ЧН=") + "КБ логов");
		ПредставлениеОтбора = "";
		Если СписокЗагружаемыхТиповСобытий.Количество() > 0 Тогда
			ПредставлениеОтбора = ПредставлениеОтбора + " события " + СписокЗагружаемыхТиповСобытий;
		КонецЕсли;
		ПредставлениеПериода = ирОбщий.ПредставлениеПериодаЛкс(НачалоПериода, КонецПериода);
		Если ЗначениеЗаполнено(ПредставлениеПериода) Тогда
			ПредставлениеОтбора = ПредставлениеОтбора + " " + ПредставлениеПериода;
		КонецЕсли; 
		Если ЗначениеЗаполнено("" + ФильтрЗагрузки) Тогда
			Если ЗначениеЗаполнено(ПредставлениеОтбора) Тогда
				ПредставлениеОтбора = ПредставлениеОтбора + " И";
			КонецЕсли; 
			ПредставлениеОтбора = ПредставлениеОтбора + " " + ФильтрЗагрузки;
		КонецЕсли; 
		Если ЗначениеЗаполнено(ОтборПоПроцессу) Тогда
			Если ЗначениеЗаполнено(ПредставлениеОтбора) Тогда
				ПредставлениеОтбора = ПредставлениеОтбора + " И";
			КонецЕсли; 
			ПредставлениеОтбора = ПредставлениеОтбора + " Процесс=" + XMLСтрока(ОтборПоПроцессу);
		КонецЕсли; 
		Если ЗначениеЗаполнено(ОтборПоСеансу) Тогда
			Если ЗначениеЗаполнено(ПредставлениеОтбора) Тогда
				ПредставлениеОтбора = ПредставлениеОтбора + " И";
			КонецЕсли; 
			ПредставлениеОтбора = ПредставлениеОтбора + " Сеанс=" + XMLСтрока(ОтборПоСеансу);
		КонецЕсли; 
		Если ЗначениеЗаполнено(ОтборПоПользователю) Тогда
			Если ЗначениеЗаполнено(ПредставлениеОтбора) Тогда
				ПредставлениеОтбора = ПредставлениеОтбора + " И";
			КонецЕсли; 
			ПредставлениеОтбора = ПредставлениеОтбора + " Пользователь=""" + ОтборПоПользователю + """";
		КонецЕсли; 
		Если ЗначениеЗаполнено(ПредставлениеОтбора) Тогда
			ирОбщий.СообщитьЛкс("Отбор загрузки:" + ПредставлениеОтбора);
		КонецЕсли;
		// Проверяем только на клиенте для снижения вероятности ошибки "Не найден файл внешней компоненты" https://www.hostedredmine.com/issues/937706
		#Если Клиент Тогда 
		Если мПлатформа.ИдентификаторыПроцессовОтладчиков().Количество() > 0 Тогда 
			ирОбщий.СообщитьЛкс("Возможно подключен отладчик и тогда он замедляет загрузку логов. Отключите отладчик для ускорения загрузки.");
		КонецЕсли; 
		#КонецЕсли 
	КонецЕсли; 
	//лНачалоПериода = НачалоПериода;
	//Если Не РежимДозагрузки Тогда
	//	//ТаблицаЖурнала.Очистить();
	//	КлючиЗагруженныхСтрок = Новый Соответствие;
	//Иначе
	//	Если НаСервере = Истина Тогда
	//		лДатаЗагрузки = ПоследнееВремяНачалаЗагрузкиСервера;
	//	Иначе
	//		лДатаЗагрузки = ПоследнееВремяНачалаЗагрузки;
	//	КонецЕсли; 
	//	Если ЗначениеЗаполнено(лДатаЗагрузки) Тогда
	//		лНачалоПериода = лДатаЗагрузки - НаложениеПриДозагрузкеСекунд;
	//	КонецЕсли; 
	//КонецЕсли; 
	Если УдалятьДанныеВнеПериода Тогда
		ТаблицаЖурнала.Сортировать("МоментВремени");
		КоличествоСтрок = ТаблицаЖурнала.Количество();
		Пока Истина
			И КоличествоСтрок > 0
			И ТаблицаЖурнала[0].Дата < НачалоПериода
		Цикл
			ТаблицаЖурнала.Удалить(0);
			КоличествоСтрок = КоличествоСтрок - 1;
		КонецЦикла;
		Если ЗначениеЗаполнено(КонецПериода)  Тогда
			ИндексСтроки = КоличествоСтрок - 1;
			Пока Истина
				И КоличествоСтрок > 0
				И ТаблицаЖурнала[ИндексСтроки].Дата > КонецПериода
			Цикл
				ТаблицаЖурнала.Удалить(ИндексСтроки);
				ИндексСтроки = ИндексСтроки - 1;
			КонецЦикла;
		КонецЕсли; 
	КонецЕсли; 
	#Если Сервер И Не Сервер Тогда
		ЗагрузитьРезультатЧтенияФайлаВПотоке();
		ПрочитатьФайлЖурнала();
	#КонецЕсли
	СтруктураПотоков = ирОбщий.НоваяСтруктураМногопоточнойОбработкиЛкс("ПрочитатьФайлЖурнала", ЭтотОбъект, "ЗагрузитьРезультатЧтенияФайлаВПотоке", 1, КоличествоПотоков);
	Если СтруктураПотоков.ФактическоеКоличествоПотоков > 1 Тогда
		ЗаголовокИндикации = "Выполняем загрузку в " + СтруктураПотоков.ФактическоеКоличествоПотоков + " потоков";
	КонецЕсли; 
	Индикатор = ирОбщий.ПолучитьИндикаторПроцессаЛкс(ОбщийРазмер, "Загрузка журнала");
	РазмерОбработанныхДанных = 0;
	Для Каждого ФайлЖурнала Из ФайлыЖурнала Цикл
		#Если Сервер И Не Сервер Тогда
			ФайлЖурнала = Новый Файл;
		#КонецЕсли
		Попытка
			РазмерФайла = ФайлЖурнала.Размер();
		Исключение
			ирОбщий.СообщитьЛкс("Пропускаем недоступный файл " + ФайлЖурнала.ПолноеИмя, СтатусСообщения.Внимание);
			Продолжить;
		КонецПопытки; 
		РазмерОбработанныхДанных = РазмерОбработанныхДанных + РазмерФайла;
		Если ирОбщий.ОбработатьИндикаторЛкс(Индикатор, РазмерОбработанныхДанных) Тогда 
			Если ирОбщий.ЛиПрерватьФоновоеЗаданиеЛкс(Индикатор) Тогда 
				Прервать;
			КонецЕсли;
		КонецЕсли;
		ПолноеИмяКаталогаПроцесса = Лев(ФайлЖурнала.Путь, СтрДлина(ФайлЖурнала.Путь) - 1);
		Длина1 = СтрДлина(ПолноеИмяКаталогаПроцесса);
		СтрокаЧасаЗаписи = "20" + Сред(ФайлЖурнала.ПолноеИмя, Длина1 + 2, 8); 
		ДатаЧасЗаписи = Дата(СтрокаЧасаЗаписи + "0000");
		ИдентификаторПроцессаСтрока = ирОбщий.СтрокаБезКонцаЛкс(ирОбщий.ПоследнийФрагментЛкс(ФайлЖурнала.Путь, "_"), 1);
		Попытка
			ИдентификаторПроцесса = Число(ИдентификаторПроцессаСтрока);
		Исключение
			Если КомментироватьЗагрузку Тогда
				ирОбщий.СообщитьЛкс("Пропускаем файл " + ФайлЖурнала.ПолноеИмя + " из каталога с некорректным именем", СтатусСообщения.Внимание);
			КонецЕсли;
			Продолжить;
		КонецПопытки; 
		//ЛиДатаВИнтервале = ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(ДатаЧасЗаписи, НачалоЧаса(лНачалоПериода), НачалоЧаса(КонецПериода));
		ЛиДатаВИнтервале = ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(ДатаЧасЗаписи, НачалоЧаса(НачалоПериода), НачалоЧаса(КонецПериода));
		Если Ложь
			Или Не ЛиДатаВИнтервале 
			Или (Истина
				И ОтборПоПроцессу <> Неопределено
				И ИдентификаторПроцесса <> ОтборПоПроцессу)
		Тогда
			Продолжить;
		КонецЕсли; 
		Попытка
			ЧтениеТекста = Новый ЧтениеТекста(ФайлЖурнала.ПолноеИмя, КодировкаТекста.UTF8, ,,Ложь);
			//ТекстовыйДокумент.Прочитать(ФайлЖурнала.ПолноеИмя, КодировкаТекста.UTF8);
		Исключение
			ирОбщий.СообщитьЛкс("Не удалось прочитать открыть для чтения файл " + ФайлЖурнала.ПолноеИмя, СтатусСообщения.Внимание);
			Продолжить;
		КонецПопытки;
		СигнатураФайла = ЧтениеТекста.Прочитать(20); // Для идентификации файла
		Если ПустаяСтрока(СигнатураФайла) Тогда
			Продолжить;
		КонецЕсли;
		ЧтениеТекста.Закрыть();
		ПараметрыОбработкиФайла = НовыеПараметрыОбработкиФайла(СдвигВремени, ОтборПоПроцессу, ОтборПоСеансу, ОтборПоПользователю, ФайлЖурнала);
		ЛокальныеПараметры = Новый Структура;
		ЛокальныеПараметры.Вставить("Индикатор", Индикатор);
		ЛокальныеПараметры.Вставить("РазмерОбработанныхДанных", РазмерОбработанныхДанных);
		ирОбщий.ДобавитьОбъектВОчередьМногопоточнойОбработкиЛкс(СтруктураПотоков, ПараметрыОбработкиФайла, ЛокальныеПараметры, РазмерФайла < 100000);
		#Если Сервер И Не Сервер Тогда
			//ПрочитатьФайлЖурнала(ПараметрыОбработкиФайла);
		#КонецЕсли
		Если ТаблицаЖурнала.Количество() > МаксТысячСобытий * 1000 Тогда
			ирОбщий.СообщитьЛкс("Загрузка прервана по достижению предела числа загружаемых событий (""Не более тыс."")");
			Прервать;
		КонецЕсли; 
	КонецЦикла;
	ирОбщий.ОжидатьЗавершенияВсехПотоковОбработкиЛкс(СтруктураПотоков);
	ирОбщий.ОсвободитьИндикаторПроцессаЛкс();

	УдалитьСтрокиВнеФильтра();
	//КлючиЗагруженныхСтрок = НовыеКлючиЗагруженныхСтрок;
	//Если НаСервере = Истина Тогда
	//	ЭтотОбъект.ПоследнееВремяНачалаЗагрузкиСервера = лПоследнееВремяНачалаЗагрузки;
	//Иначе
	//	ЭтотОбъект.ПоследнееВремяНачалаЗагрузки = лПоследнееВремяНачалаЗагрузки;
	//КонецЕсли; 
	//МинимальнаяДатаЗагрузки = Мин(НачалоПериода, МинимальнаяДатаЗагрузки);
	ТаблицаЖурнала.Сортировать("МоментВремени");
	//ТаблицаЖурнала.Сортировать("МоментВремениНачала");
	
	ОпределитьНепустыеКолонки();
	Возврат Истина;
	
КонецФункции

//.
// Параметры:
//    СдвигВремени - Число - 
//    ОтборПоПроцессу - Число - 
//    ОтборПоСеансу - Число, Неопределено - 
//    ОтборПоПользователю - Строка - 
//    ФайлЖурнала - Файл - 
// Возвращаемое значение:
//    Структура - 
Функция НовыеПараметрыОбработкиФайла(Знач СдвигВремени, Знач ОтборПоПроцессу, Знач ОтборПоСеансу, Знач ОтборПоПользователю, Знач ФайлЖурнала) Экспорт
	ПараметрыОбработкиФайла = Новый Структура;
	ПараметрыОбработкиФайла.Вставить("ОтборПоПроцессу", ОтборПоПроцессу);
	ПараметрыОбработкиФайла.Вставить("ОтборПоСеансу", ОтборПоСеансу);
	ПараметрыОбработкиФайла.Вставить("ОтборПоПользователю", ОтборПоПользователю);
	ПараметрыОбработкиФайла.Вставить("СдвигВремени", СдвигВремени);
	ПараметрыОбработкиФайла.Вставить("ФильтрЗагрузки", ФильтрЗагрузки);
	ПараметрыОбработкиФайла.Вставить("НомерСеанса", НомерСеансаИнформационнойБазы());
	ПараметрыОбработкиФайла.Вставить("мКартыФайлов", мКартыФайлов);
	ПараметрыОбработкиФайла.Вставить("КоличествоЗагруженныхСобытий", мТаблицаЖурнала.Количество());
	ПараметрыОбработкиФайла.Вставить("ФайлЖурнала", ФайлЖурнала.ПолноеИмя);
	Возврат ПараметрыОбработкиФайла;
КонецФункции

// .
// Параметры:
//  Параметры	 - см. НовыеПараметрыОбработкиФайла - 
// 
// Возвращаемое значение:
//   - 
Функция ПрочитатьФайлЖурнала(Параметры) Экспорт 
	
	мКартыФайлов = Параметры.мКартыФайлов;
	ОтборПоПроцессу = Параметры.ОтборПоПроцессу;
	ОтборПоСеансу = Параметры.ОтборПоСеансу;
	ОтборПоПользователю = Параметры.ОтборПоПользователю;
	СдвигВремени = Параметры.СдвигВремени;
	ФайлЖурнала = Параметры.ФайлЖурнала;
	ФильтрЗагрузки = Параметры.ФильтрЗагрузки;
	КоличествоЗагруженныхСобытий = Параметры.КоличествоЗагруженныхСобытий;
	ВозвращатьРезультат = Параметры.НомерСеанса <> НомерСеансаИнформационнойБазы();
	ДостигнутоПредельноеКоличество = Ложь;
	ДобавитьОбязательныеНепустыеКолонки();
	ФайлЖурнала = Новый Файл(ФайлЖурнала);
	РазмерФайла = ФайлЖурнала.Размер();
	РегВыражение1 = мПлатформа.мРегВыражение;
	РегВыражение2 = мПлатформа.мРегВыражение2;  
	#Если Сервер И Не Сервер Тогда
		РегВыражение1 = Обработки.ирОболочкаРегВыражение.Создать();
		РегВыражение2 = РегВыражение1;
		мПлатформа = Обработки.ирПлатформа.Создать();
		мТаблицаЖурнала = ТаблицаЖурнала;
	#КонецЕсли
	шСимвол = "[" + шБуква + "\d\._#{}-]";
	//ШаблонСвойствоЗначение = "([\w\:]+)=(?:'\s*((?:.|\n|\r)*?)'|""\s*([^""]*)""|([^'""\n\r,]*))(?:,|\r|$)"; // Так были очень большие трассы вычисления мРегВыражение
	//ШаблонСвойствоЗначение = "([\w\:]+)=(?:'\s*((?:''|\s|\S)*?)'|""\s*((?:""""|[^""])*)""|([^'""\n\r,]*))(?:,|\r|\n|$)+"; // Дает ложное позитивное на фрагменте <<',>> внутри значения свойства
	ШаблонСвойствоЗначение = "([\w\:]+)=(?:'\s*((?:''|[^'])*?)'|""\s*((?:""""|[^""])*)""|([^'""\n\r,]*))(?:,|\r|\n|$)+"; // Дает ложное позитивное на фрагменте <<',>> внутри значения свойства

	ШаблонСобытия = "(\d\d:\d\d)\.(\d+)-(\d+),(" + шСимвол + "+),(\d+),((?:" + ШаблонСвойствоЗначение + ")*)"; // Избыточный и кажется менее надежный, но почему то более быстрый
	ШаблонСобытияЖСОН = "(\{""ts"":""[^}]*\})(?=\n)";
	РегВыражение1.Pattern = ШаблонСобытия + "|" + ШаблонСобытияЖСОН; 
	//
	//ШаблонСобытия = "(?:^|\n)(\d\d:\d\d)\.(\d+)-(\d+),([_ЁА-ЯA-Z\d\._#{}-]+),(\d+),"; // Только начала находит
	//РегВыражение1.Pattern = ШаблонСобытия + "([\s\S]*?)(?=$|" + ШаблонСобытия + ")"; 
	
	РегВыражение1.Global = Истина;
	РегВыражение2.Pattern = ШаблонСвойствоЗначение;
	РегВыражение2.Global = Истина;
	ПустаяДата = Дата("00010101");
	МаксКоличествоСобытий = КоличествоЗагруженныхСобытий + МаксТысячСобытий * 1000;
	//НовыеКлючиЗагруженныхСтрок = Новый Соответствие();
	Если КомментироватьЗагрузку Тогда
		ирОбщий.СообщитьЛкс("Читаем " + ФайлЖурнала.ПолноеИмя + ", размер - " + РазмерФайла);
	КонецЕсли; 
	ПолноеИмяКаталогаПроцесса = Лев(ФайлЖурнала.Путь, СтрДлина(ФайлЖурнала.Путь) - 1);
	Длина1 = СтрДлина(ПолноеИмяКаталогаПроцесса);
	СтрокаЧасаЗаписи = "20" + Сред(ФайлЖурнала.ПолноеИмя, Длина1 + 2, 8); 
	ДатаЧасЗаписи = Дата(СтрокаЧасаЗаписи + "0000");
	ИдентификаторПроцессаСтрока = ирОбщий.СтрокаБезКонцаЛкс(ирОбщий.ПоследнийФрагментЛкс(ФайлЖурнала.Путь, "_"), 1);
	ИдентификаторПроцесса = Число(ИдентификаторПроцессаСтрока);
	//ЛиФайлВИнтервалеПолностью = ирОбщий.ЛиДатаВИнтервалеБезГраницЛкс(ДатаЧасЗаписи, НачалоЧаса(лНачалоПериода), НачалоЧаса(КонецПериода));
	//ЛиДатаВИнтервале = ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(ДатаЧасЗаписи, НачалоЧаса(лНачалоПериода), НачалоЧаса(КонецПериода));
	ЛиФайлВИнтервалеПолностью = ирОбщий.ЛиДатаВИнтервалеБезГраницЛкс(ДатаЧасЗаписи, НачалоЧаса(НачалоПериода), НачалоЧаса(КонецПериода));
	ЛиДатаВИнтервале = ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(ДатаЧасЗаписи, НачалоЧаса(НачалоПериода), НачалоЧаса(КонецПериода));
	ТекстФайла = ""; // Уничтожаем старый текст, т.к. он может быть очень большим
	Попытка
		ЧтениеТекста = Новый ЧтениеТекста(ФайлЖурнала.ПолноеИмя, КодировкаТекста.UTF8, ,,Ложь);
		//ТекстовыйДокумент.Прочитать(ФайлЖурнала.ПолноеИмя, КодировкаТекста.UTF8);
	Исключение
		ирОбщий.СообщитьЛкс("Не удалось прочитать данные из файла " + ФайлЖурнала.ПолноеИмя, СтатусСообщения.Внимание);
		Возврат Неопределено;
	КонецПопытки;
	СигнатураФайла = ЧтениеТекста.Прочитать(20); // Для идентификации файла
	Если ПустаяСтрока(СигнатураФайла) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	РазмерПорции = 2 * 1000 * 1000; // Подобрано экспериментально
	КраткоеИмяКаталогаПроцесса = ирОбщий.ПоследнийФрагментЛкс(ПолноеИмяКаталогаПроцесса, "\");
	ЧислоПорций = Цел(РазмерФайла / РазмерПорции) + 1;
	ЧтениеТекста = Новый ЧтениеТекста(ФайлЖурнала.ПолноеИмя, КодировкаТекста.UTF8, ,,Ложь);
	АбсолютнаяПозицияВФайле = 0;
	КлючКарты = Новый Структура;
	КлючКарты.Вставить("ИмяКаталогаПроцесса", КраткоеИмяКаталогаПроцесса);
	КлючКарты.Вставить("ИмяТекущегоФайла", ФайлЖурнала.Имя);
	КлючКарты.Вставить("СигнатураТекущегоФайла", СигнатураФайла);
	КлючКарты.Вставить("ОтборПоСеансу", "" + ЗагружатьТолькоТекущийСеанс + ОтборПоСеансу);
	СтрокиКарт = мКартыФайлов.НайтиСтроки(КлючКарты);
	Если СтрокиКарт.Количество() > 0 Тогда
		КартаФайла = СтрокиКарт[0];
	Иначе
		КартаФайла = мКартыФайлов.Добавить();
		ЗаполнитьЗначенияСвойств(КартаФайла, КлючКарты); 
		КартаФайла.КонецПериода = Дата(1000, 1, 1);
	КонецЕсли;
	Если ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(НачалоПериода, КартаФайла.НачалоПериода, КартаФайла.КонецПериода) Тогда
		АбсолютнаяПозицияВФайле = КартаФайла.ПозицияКонца;
	ИначеЕсли Ложь
		Или НачалоПериода > КартаФайла.НачалоПериода
		Или КонецПериода < КартаФайла.КонецПериода
	Тогда
		// Периоды не сшиваются. Очищаем карту.
		КартаФайла.ПозицияНачала = -1;
		КартаФайла.ПозицияКонца = -1;
		КартаФайла.НачалоПериода = НачалоПериода;
		КартаФайла.КонецПериода = КонецПериода;
		КартаФайла.ДатаИзмененияФайла = Неопределено;
	КонецЕсли;
	Если АбсолютнаяПозицияВФайле > 0 Тогда
		Если ЗначениеЗаполнено(КартаФайла.ДатаИзмененияФайла) Тогда
			Если КартаФайла.ДатаИзмененияФайла = ФайлЖурнала.ПолучитьВремяИзменения() + ирКэш.ПолучитьСмещениеВремениЛкс() Тогда
				Возврат Неопределено;
			КонецЕсли; 
		КонецЕсли; 
		ЧтениеТекста.Прочитать(АбсолютнаяПозицияВФайле);
	КонецЕсли; 
	ПозицияНачалаСчитанныхДанных = -1;
	Если ЧислоПорций > 1 Тогда
		ИндикаторФайла = ирОбщий.ПолучитьИндикаторПроцессаЛкс(ЧислоПорций, "" + КраткоеИмяКаталогаПроцесса + "\" + ФайлЖурнала.Имя);
	Иначе
		ИндикаторФайла = Неопределено;
	КонецЕсли; 
	РазрешенныеТипыСобытий = Неопределено;
	Если СписокЗагружаемыхТиповСобытий.Количество() > 0 Тогда
		РазрешенныеТипыСобытий = Новый Структура;
		Для Каждого ТипСобытия Из СписокЗагружаемыхТиповСобытий.ВыгрузитьЗначения() Цикл
			РазрешенныеТипыСобытий.Вставить(ТипСобытия);
		КонецЦикла;
	КонецЕсли;
	Если ирКэш.ДоступноЖСОНЛкс() Тогда
		ЧтениеЖСОН = ирОбщий.МойЧтениеJSON();
	КонецЕсли;
	СтрокаТЧ = Неопределено;
	РазмерТекущейПорции = 0;
	_РежимОтладки = ирКэш.РежимОтладкиЛкс();
	ПоследниеСобытияПоТипам = Новый Соответствие;
	Пока Истина Цикл
		КартаФайла.ДатаИзмененияФайла = ФайлЖурнала.ПолучитьВремяИзменения() + ирКэш.ПолучитьСмещениеВремениЛкс();
		ПорцияТекстаФайла = ЧтениеТекста.Прочитать(РазмерПорции);
		Если Ложь
			Или ПорцияТекстаФайла = Неопределено 
			Или ПустаяСтрока(ПорцияТекстаФайла)
		Тогда
			Прервать;
		КонецЕсли; 
		Если ИндикаторФайла <> Неопределено Тогда
			Если ирОбщий.ОбработатьИндикаторЛкс(ИндикаторФайла) Тогда 
				Если ирОбщий.ЛиПрерватьФоновоеЗаданиеЛкс(ИндикаторФайла) Тогда 
					Прервать;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли; 
		Если Истина
			И АбсолютнаяПозицияВФайле >= КартаФайла.ПозицияНачала 
			И АбсолютнаяПозицияВФайле + СтрДлина(ПорцияТекстаФайла) + СтрДлина(ТекстФайла) < КартаФайла.ПозицияКонца 
		Тогда 
			АбсолютнаяПозицияВФайле = АбсолютнаяПозицияВФайле + СтрДлина(ПорцияТекстаФайла) + СтрДлина(ТекстФайла);
			Продолжить;
		КонецЕсли; 
		ТекстФайла = ТекстФайла + ПорцияТекстаФайла;
		РазборТекстаРазрешен = Истина;
		ОписаниеОшибки = "";
		МаксимальныйРазмерТекстДляАнализа = 20000000; // Экспериментально подобрано http://devtool1c.ucoz.ru/forum/2-270-1#1145
		Если СтрДлина(ТекстФайла) > МаксимальныйРазмерТекстДляАнализа Тогда 
			Если Истина
				И РазмерТекущейПорции = РазмерПорции
				И СтрокаТЧ <> Неопределено
			Тогда
				СтрокаТЧ.ТекстSDBL = "<?>";
				СтрокаТЧ.ТекстСУБД = "<?>";
				ирОбщий.СообщитьЛкс("При анализе файла """ + ФайлЖурнала.ПолноеИмя + """ некоторые свойства события пропущены, т.к. оно превышает допустимый размер "
					+ XMLСтрока(Цел(МаксимальныйРазмерТекстДляАнализа/1024/1024)) + "МБ: " + Лев(СокрЛ(СтрЗаменить(ТекстФайла, Символы.ПС, "")), 80) + "...");
			Иначе
				ОписаниеОшибки = "Событие превышает допустимый размер 20МБ";
			КонецЕсли; 
			РазборТекстаРазрешен = Ложь;
		Иначе
			Если Истина
				И РазмерТекущейПорции = РазмерПорции
				И СтрокаТЧ <> Неопределено
			Тогда
				мТаблицаЖурнала.Удалить(СтрокаТЧ);
			КонецЕсли; 
		КонецЕсли; 
		Вхождения = РегВыражение1.НайтиВхождения("");
		Если РазборТекстаРазрешен Тогда
			Попытка
				Вхождения = РегВыражение1.НайтиВхождения(ТекстФайла);
			Исключение
				ОписаниеОшибки = ОписаниеОшибки();
			КонецПопытки;
		КонецЕсли; 
		Если ОписаниеОшибки <> "" Тогда
			ирОбщий.СообщитьЛкс("При анализе файла """ + ФайлЖурнала.ПолноеИмя + """ пропущена порция """ + Лев(СокрЛ(СтрЗаменить(ТекстФайла, Символы.ПС, "")), 80) + "..."":
			|	" + ОписаниеОшибки);
		Иначе
			Если КомментироватьЗагрузку Тогда
				ирОбщий.СообщитьЛкс("Анализ порции " + СтрДлина(ТекстФайла) + " символов обнаружил " + Вхождения.Количество() + " событий");
			КонецЕсли;
		КонецЕсли; 
		СтрокаТекущегоКонтекста = Неопределено;
		СтрокаТЧ = Неопределено;
		Вхождение = Неопределено;
		Для Каждого Вхождение Из Вхождения Цикл
			#Если Клиент Тогда
			ОбработкаПрерыванияПользователя();
			#КонецЕсли 
			СтрокаТЧ = Неопределено;
			АбсолютнаяПозицияВхождения = АбсолютнаяПозицияВФайле + Вхождение.FirstIndex;
			Если Истина
				И КартаФайла.ПозицияНачала > -1
				И АбсолютнаяПозицияВхождения >= КартаФайла.ПозицияНачала
				И АбсолютнаяПозицияВхождения < КартаФайла.ПозицияКонца
			Тогда
				Продолжить;
			КонецЕсли;
			ЭтоЖСОН = Вхождение.SubMatches(0) = Неопределено;
			Если ЭтоЖСОН Тогда
				// ЖСОН
				СобытиеЖСОН = Вхождение.SubMatches(10);
				ЧтениеЖСОН.УстановитьСтроку(СобытиеЖСОН);
				СвойстваЖСОН = Вычислить("ПрочитатьJSON(ЧтениеЖСОН, Истина)"); // см. ОбразецСобытияЖСОН
				ДатаТекущегоСобытия = XMLЗначение(Тип("Дата"), СвойстваЖСОН["ts"]);
				ТипСобытия = ВРег(СвойстваЖСОН["name"]);
			Иначе
				СтрокаВремениЗаписи = СтрокаЧасаЗаписи + СтрЗаменить(Вхождение.SubMatches(0), ":", "");
				ДатаТекущегоСобытия = Дата(СтрокаВремениЗаписи);
				ТипСобытия = ВРег(Вхождение.SubMatches(3));
			КонецЕсли;
			Если Не ЛиФайлВИнтервалеПолностью Тогда
				//ЛиДатаВИнтервале = ирОбщий.ЛиДатаВИнтервалеСГраницамиЛкс(ДатаТекущегоСобытия, лНачалоПериода, КонецПериода);
				//Если Не ЛиДатаВИнтервале Тогда
				//	Продолжить;
				//КонецЕсли; 
				Если Истина
					И ЗначениеЗаполнено(НачалоПериода)
					И ДатаТекущегоСобытия < НачалоПериода
				Тогда
					Продолжить;
				КонецЕсли; 
				Если Истина
					И ЗначениеЗаполнено(КонецПериода)
					И ДатаТекущегоСобытия > КонецПериода
				Тогда
					Вхождение = Неопределено;
					Прервать;
				КонецЕсли; 
			КонецЕсли;
			Если Истина
				И РазрешенныеТипыСобытий <> Неопределено
				И Не РазрешенныеТипыСобытий.Свойство(ТипСобытия) 
			Тогда
				Продолжить;
			КонецЕсли; 
			Если ПозицияНачалаСчитанныхДанных = -1 Тогда
				ПозицияНачалаСчитанныхДанных = АбсолютнаяПозицияВхождения;
			КонецЕсли; 
			Если ЭтоЖСОН Тогда
				КонецМикросекунды = Число(ирОбщий.ПоследнийФрагментЛкс(СвойстваЖСОН["ts"], "."));
				ДлительностьМикросекунд = Число(СвойстваЖСОН["duration"]);
				Вложенность = Число(СвойстваЖСОН["depth"]);
				ТекстДинамическихСвойств = ирОбщий.ПоследнийФрагментЛкс(СобытиеЖСОН, """,""depth"":""");
			Иначе
				КонецМикросекунды = Вхождение.SubMatches(1);
				//КлючСтроки = МоментВремени + ";" + ФайлЖурнала.ПолноеИмя;
				//Если РежимДозагрузки Тогда
				//	Если КлючиЗагруженныхСтрок[КлючСтроки] = 1 Тогда
				//		Продолжить;
				//	КонецЕсли; 
				//КонецЕсли; 
				Если СтрДлина(КонецМикросекунды) = 6 Тогда
					//Это 8.3
					КонецМикросекунды = Число(КонецМикросекунды);
					ДлительностьМикросекунд = Число(Вхождение.SubMatches(2));
				Иначе
					//Это 8.2
					КонецМикросекунды = Число(КонецМикросекунды) * 100;
					ДлительностьМикросекунд = Число(Вхождение.SubMatches(2)) * 100;
				КонецЕсли;
				Вложенность = Число(Вхождение.SubMatches(4));
				ТекстДинамическихСвойств = Вхождение.SubMatches(5);
			КонецЕсли;
			Если мТаблицаЖурнала.Количество() = МаксКоличествоСобытий Тогда
				УдалитьСтрокиВнеФильтра();
				Если мТаблицаЖурнала.Количество() = МаксКоличествоСобытий Тогда
					ДостигнутоПредельноеКоличество = Истина;
					Прервать;
				КонецЕсли; 
			КонецЕсли; 
			СтрокаТЧ = мТаблицаЖурнала.Добавить();
			ПоследнееСобытиеТипа = ПоследниеСобытияПоТипам[ТипСобытия];
			ДинамическиеСвойстваВзятыИзКэша = Истина
				И ПоследнееСобытиеТипа <> Неопределено
				И ПоследнееСобытиеТипа.ТекстДинамическихСвойств = ТекстДинамическихСвойств
				И мТаблицаЖурнала.Индекс(ПоследнееСобытиеТипа.СтрокаТаблицы) >= 0; // Строку, на которую ссылается кэш, могли удалить на стыке порций. Поэтому проверяем ее существование
			Если ДинамическиеСвойстваВзятыИзКэша Тогда
				Если ТипЗнч(ПоследнееСобытиеТипа.СписокСвойств) = Тип("Массив") Тогда
					ПоследнееСобытиеТипа.СписокСвойств = ирОбщий.СтрСоединитьЛкс(ПоследнееСобытиеТипа.СписокСвойств);
				КонецЕсли; 
				ЗаполнитьЗначенияСвойств(СтрокаТЧ, ПоследнееСобытиеТипа.СтрокаТаблицы, ПоследнееСобытиеТипа.СписокСвойств); 
			КонецЕсли; 
			СтрокаТЧ.ИмяФайлаЛога = ФайлЖурнала.ПолноеИмя;
			СтрокаТЧ.ПроцессОС = ИдентификаторПроцесса;
			СтрокаТЧ.Событие = ТипСобытия;
			СтрокаТЧ.Дата = ДатаТекущегоСобытия - СдвигВремени;
			СтрокаТЧ.МоментВремени = ПолучитьМоментВремени(СтрокаТЧ.Дата, КонецМикросекунды);
			//СтрокаТЧ.ТекстЖурнала = Вхождение.Value; // Теперь это только для отладки будем включать
			СтрокаТЧ.Длительность = ДлительностьМикросекунд / 1000; // Переводим длительность в миллисекунды
			Длительность1Секунды = Цел(ДлительностьМикросекунд / 1000000);
			Длительность1Микросекунды = ДлительностьМикросекунд - Длительность1Секунды * 1000000;
			ДатаНачала = СтрокаТЧ.Дата - Длительность1Секунды;
			НачалоМикросекунды = КонецМикросекунды - Длительность1Микросекунды;
			Если НачалоМикросекунды < 0 Тогда
				ДатаНачала = ДатаНачала - 1;
				НачалоМикросекунды = 1000000 + НачалоМикросекунды;
			КонецЕсли; 
			СтрокаТЧ.ДатаНачала = ДатаНачала;
			СтрокаТЧ.МоментВремениНачала = ПолучитьМоментВремени(ДатаНачала, НачалоМикросекунды);
			СтрокаТЧ.Вложенность = Вложенность;
			Если Не ДинамическиеСвойстваВзятыИзКэша Тогда
				СписокСвойств = Новый Массив;
				Если ЭтоЖСОН Тогда
					Для Каждого КлючИЗначение Из СвойстваЖСОН Цикл
						ИмяСвойства = КлючИЗначение.Ключ;
						Если Ложь
							Или ИмяСвойства = "ts"
							Или ИмяСвойства = "name"
							Или ИмяСвойства = "duration"
							Или ИмяСвойства = "depth"
						Тогда
							Продолжить;
						КонецЕсли;
						ЗначениеСвойства = КлючИЗначение.Значение;
						
						// Мультиметка241103_155155
						ИмяКолонкиТЧ = мСоответствиеКолонок[НРег(ИмяСвойства)];
						Если ИмяКолонкиТЧ = Неопределено Тогда
							СтрокаТЧ.НеизвестныеСвойства = СтрокаТЧ.НеизвестныеСвойства + ИмяСвойства + "=" + ЗначениеСвойства + ", ";
							СписокСвойств.Добавить("НеизвестныеСвойства");
						Иначе
							Если ТипЗнч(СтрокаТЧ[ИмяКолонкиТЧ]) = Тип("Булево") Тогда
								ЗначениеСвойства = ?(ЗначениеСвойства = "1" Или Нрег(ЗначениеСвойства) = "true", Истина, Ложь);
							КонецЕсли; 
							Если Не мНепустыеКолонкиЖурнала.Свойство(ИмяКолонкиТЧ) Тогда
								Если ЗначениеЗаполнено(ЗначениеСвойства) Тогда
									мНепустыеКолонкиЖурнала.Вставить(ИмяКолонкиТЧ);
								КонецЕсли; 
							КонецЕсли; 
							Попытка
								СтрокаТЧ[ИмяКолонкиТЧ] = ЗначениеСвойства;
							Исключение
								ВызватьИсключение "Некорректное представление """ + ЗначениеСвойства + """ значения свойства """ + ИмяКолонкиТЧ + """";
							КонецПопытки;
							СписокСвойств.Добавить(ИмяКолонкиТЧ);
						КонецЕсли; 
					КонецЦикла;  
				Иначе
					ВхожденияСвойств = РегВыражение2.НайтиВхождения(ТекстДинамическихСвойств,, Истина);
					Если _РежимОтладки Тогда // Можно менять на Истина в точке останова, например условием ирОбщий.Пр(_РежимОтладки, 1, 1) 
						// Пассивный оригинал расположенного ниже однострочного кода. Выполняйте изменения синхронно в обоих вариантах.
						Для Каждого ВхождениеСвойства Из ВхожденияСвойств Цикл
							//Для Индекс = 0 По мТаблицаКолонок.Количество() - 1 Цикл
							ИмяСвойства = ВхождениеСвойства.SubMatches(0);
							ЗначениеСвойства = ВхождениеСвойства.SubMatches(3);
							Если ЗначениеСвойства = Неопределено Тогда // Скорость критична
								ЗначениеСвойства = ВхождениеСвойства.SubMatches(2);
							КонецЕсли; 
							Если ЗначениеСвойства = Неопределено Тогда // Скорость критична
								ЗначениеСвойства = ВхождениеСвойства.SubMatches(1);
							КонецЕсли; 
							Если ЗначениеСвойства = Неопределено Тогда // Скорость критична
								Продолжить;
							КонецЕсли;
							
							// Мультиметка241103_155155
							ИмяКолонкиТЧ = мСоответствиеКолонок[НРег(ИмяСвойства)];
							Если ИмяКолонкиТЧ = Неопределено Тогда
								СтрокаТЧ.НеизвестныеСвойства = СтрокаТЧ.НеизвестныеСвойства + ИмяСвойства + "=" + ЗначениеСвойства + ", ";
								СписокСвойств.Добавить("НеизвестныеСвойства");
							Иначе
								//Если мЧисловыеСвойства.Свойство(ИмяКолонкиТЧ) Тогда
								//	Если Не ЗначениеЗаполнено(ЗначениеСвойства) Тогда
								//		ЗначениеСвойства = 0;
								//	Иначе
								//		Попытка
								//			ЗначениеСвойства = Число(ЗначениеСвойства);
								//		Исключение
								//			ВызватьИсключение "Некорректное представление """ + ЗначениеСвойства + """ значения числового свойства """ + ИмяКолонкиТЧ + """";
								//		КонецПопытки;
								//	КонецЕсли; 
								//КонецЕсли;
								Если ТипЗнч(СтрокаТЧ[ИмяКолонкиТЧ]) = Тип("Булево") Тогда
									ЗначениеСвойства = ?(ЗначениеСвойства = "1" Или Нрег(ЗначениеСвойства) = "true", Истина, Ложь);
								КонецЕсли; 
								Если Не мНепустыеКолонкиЖурнала.Свойство(ИмяКолонкиТЧ) Тогда
									Если ЗначениеЗаполнено(ЗначениеСвойства) Тогда
										мНепустыеКолонкиЖурнала.Вставить(ИмяКолонкиТЧ);
									КонецЕсли; 
								КонецЕсли; 
								Попытка
									СтрокаТЧ[ИмяКолонкиТЧ] = ЗначениеСвойства;
								Исключение
									ВызватьИсключение "Некорректное представление """ + ЗначениеСвойства + """ значения свойства """ + ИмяКолонкиТЧ + """";
								КонецПопытки;
								СписокСвойств.Добавить(ИмяКолонкиТЧ);
							КонецЕсли; 
						КонецЦикла;
					Иначе
						// Однострочный код использован для ускорения. Выше расположен оригинал. Выполняйте изменения синхронно в обоих вариантах. Преобразовано консолью кода из подсистемы "Инструменты разработчика" (http://devtool1c.ucoz.ru)
						Для Каждого ВхождениеСвойства Из ВхожденияСвойств Цикл    					ИмяСвойства = ВхождениеСвойства.SubMatches(0);  					ЗначениеСвойства = ВхождениеСвойства.SubMatches(3);  					Если ЗначениеСвойства = Неопределено Тогда  						ЗначениеСвойства = ВхождениеСвойства.SubMatches(2);  					КонецЕсли;  					Если ЗначениеСвойства = Неопределено Тогда  						ЗначениеСвойства = ВхождениеСвойства.SubMatches(1);  					КонецЕсли;  					Если ЗначениеСвойства = Неопределено Тогда  						Продолжить;  					КонецЕсли;  					ИмяКолонкиТЧ = мСоответствиеКолонок[НРег(ИмяСвойства)];  					Если ИмяКолонкиТЧ = Неопределено Тогда  						СтрокаТЧ.НеизвестныеСвойства = СтрокаТЧ.НеизвестныеСвойства +  ИмяСвойства + "=" + ЗначениеСвойства + ", ";  						СписокСвойств.Добавить("НеизвестныеСвойства");  					Иначе                        						Если ТипЗнч(СтрокаТЧ[ИмяКолонкиТЧ]) = Тип("Булево") Тогда  							ЗначениеСвойства = ?(ЗначениеСвойства = "1" Или Нрег(ЗначениеСвойства) = "true", Истина, Ложь);  						КонецЕсли;  						Если Не мНепустыеКолонкиЖурнала.Свойство(ИмяКолонкиТЧ) Тогда  							Если ЗначениеЗаполнено(ЗначениеСвойства) Тогда  								мНепустыеКолонкиЖурнала.Вставить(ИмяКолонкиТЧ);  							КонецЕсли;  						КонецЕсли;  						Попытка  							СтрокаТЧ[ИмяКолонкиТЧ] = ЗначениеСвойства;  						Исключение  							ВызватьИсключение "Некорректное представление """ + ЗначениеСвойства + """ значения свойства """ + ИмяКолонкиТЧ + """";  						КонецПопытки;  						СписокСвойств.Добавить(ИмяКолонкиТЧ);  					КонецЕсли;  				КонецЦикла;  
					КонецЕсли; 
				КонецЕсли;
				//АбсолютнаяПозицияВФайле = АбсолютнаяПозицияВФайле + Вхождение.Length;
				Если Ложь
					Или (Истина
						И ОтборПоСеансу <> Неопределено
						И СтрокаТЧ.Сеанс <> ОтборПоСеансу)
					Или (Истина
						И ОтборПоПользователю <> Неопределено
						И СтрокаТЧ.Пользователь <> ОтборПоПользователю)
				Тогда
					мТаблицаЖурнала.Удалить(СтрокаТЧ);
					СтрокаТЧ = Неопределено;
					#Если Сервер И Не Сервер Тогда
					    СтрокаТЧ = мТаблицаЖурнала.Добавить();
					#КонецЕсли
					Продолжить;
				КонецЕсли; 
				ПоследниеСобытияПоТипам[ТипСобытия] = Новый Структура("СтрокаТаблицы, ТекстДинамическихСвойств, СписокСвойств", СтрокаТЧ, ТекстДинамическихСвойств, СписокСвойств);
			КонецЕсли;
			//Если НаСервере <> Неопределено Тогда
			//	СтрокаТЧ.НаСервере = НаСервере;
			//Иначе
				Если Ложь
					Или ТипСобытия = "SCOM" 
				Тогда
				    СтрокаТЧ.НаСервере = Истина;
				Иначе
					ИмяТипаПроцесса = "_" + СтрЗаменить(СтрокаТЧ.ТипПроцессаОС, "#", "");
					Попытка
						СтрокаТЧ.НаСервере = мСерверныеТипыПроцессов.Свойство(ИмяТипаПроцесса);
					Исключение
						// Бывают типы процессов с UID, к сожалению тогда невозможно определить
					КонецПопытки; 
				КонецЕсли; 
			//КонецЕсли; 
			Если СтрокаТЧ.Контекст <> "" Тогда
				КонтекстТД = Новый ТекстовыйДокумент;
				КонтекстТД.УстановитьТекст(СтрокаТЧ.Контекст);
				СтрокаТЧ.СтрокаМодуля = СокрЛП(КонтекстТД.ПолучитьСтроку(КонтекстТД.КоличествоСтрок()));
				Если БазовыйУровеньСтека > 0 Тогда
					ЗаполнитьСтрокуБазовогоУровня(СтрокаТЧ);
				КонецЕсли; 
			КонецЕсли; 
			//Если ДатаТекущегоСобытия > лПоследнееВремяНачалаЗагрузки - НаложениеПриДозагрузкеСекунд Тогда
			//	НовыеКлючиЗагруженныхСтрок[КлючСтроки] = 1;
			//КонецЕсли; 
			Если ирОбщий.СтрокиРавныЛкс(ТипСобытия, "Context") Тогда
				// Встроим контекст в предыдущие события
				Индекс = мТаблицаЖурнала.Количество() - 2;
				Пока Индекс >= 0 Цикл
					СтрокаБезКонтекста = мТаблицаЖурнала[Индекс];
					Индекс = Индекс - 1;
					Если Ложь
						Или СтрокаБезКонтекста.Сеанс <> СтрокаТЧ.Сеанс
						Или СтрокаБезКонтекста.Инфобаза <> СтрокаТЧ.Инфобаза
					Тогда
						Прервать;
					КонецЕсли;
					Если СтрокаБезКонтекста.СтрокаМодуля <> "" Тогда
						Прервать;
					КонецЕсли; 
					ЗаполнитьЗначенияСвойств(СтрокаБезКонтекста, СтрокаТЧ, "СтрокаМодуля, Контекст"); 
				КонецЦикла; 
			КонецЕсли;
		КонецЦикла;
		Если ДостигнутоПредельноеКоличество Тогда
			Прервать;
		КонецЕсли; 
		// Для отката к началу последнего события
		РазмерТекущейПорции = СтрДлина(ПорцияТекстаФайла);
		АбсолютнаяПозицияВФайле = АбсолютнаяПозицияВФайле + СтрДлина(ТекстФайла);
		Если РазмерТекущейПорции = РазмерПорции Тогда
			//Если СтрокаТЧ <> Неопределено Тогда
			//	ТаблицаЖурнала.Удалить(ТаблицаЖурнала.Количество() - 1);
			//КонецЕсли; 
			Если Вхождение <> Неопределено Тогда
				ТекстФайла = Сред(ТекстФайла, Вхождение.FirstIndex);
			Иначе
				ТекстФайла = Прав(ТекстФайла, 1000); // Страховка
			КонецЕсли; 
			АбсолютнаяПозицияВФайле = АбсолютнаяПозицияВФайле - СтрДлина(ТекстФайла);
		Иначе
			ТекстФайла = "";
		КонецЕсли;
	КонецЦикла;
	Если ИндикаторФайла <> Неопределено Тогда
		ирОбщий.ОсвободитьИндикаторПроцессаЛкс();
	КонецЕсли; 
	Если ДостигнутоПредельноеКоличество Тогда
		ТекстСообщения = "Загрузка остановлена по достижению максимального количества событий в таблице";
		Если Не ЗначениеЗаполнено("" + ФильтрЗагрузки) Тогда
			ТекстСообщения = ТекстСообщения + ". Если задать фильтр отображения таблицы, то будет предложено применить его при загрузке нового журнала.";
		КонецЕсли; 
		ирОбщий.СообщитьЛкс(ТекстСообщения);
	Иначе
		КартаФайла.ПозицияКонца = АбсолютнаяПозицияВФайле;
		Если Истина
			И ПозицияНачалаСчитанныхДанных <> -1
			И (Ложь
				Или КартаФайла.ПозицияНачала = -1
				Или КартаФайла.ПозицияНачала > ПозицияНачалаСчитанныхДанных)
		Тогда
			КартаФайла.ПозицияНачала = ПозицияНачалаСчитанныхДанных;
		КонецЕсли; 
		Если Ложь
			Или Не ЗначениеЗаполнено(КонецПериода)
			Или КонецПериода > КартаФайла.КонецПериода
		Тогда
			КартаФайла.КонецПериода = КонецПериода;
		КонецЕсли;
		Если Ложь
			Или Не ЗначениеЗаполнено(НачалоПериода)
			Или НачалоПериода < КартаФайла.НачалоПериода
		Тогда
			КартаФайла.НачалоПериода = НачалоПериода;
		КонецЕсли;
	КонецЕсли;
	Результат = Неопределено;
	Если ВозвращатьРезультат Тогда
		ПодготовитьТаблицуЖурналаКСериализации();
		Результат = Новый Структура;
		Результат.Вставить("КлючКарты", КлючКарты);
		Результат.Вставить("КартаФайла", ирОбщий.СтруктураИзСтрокиТаблицыИлиДереваИлиВыборкиЛкс(КартаФайла));
		Результат.Вставить("ТаблицаЖурнала", мТаблицаЖурнала);
		Результат.Вставить("РазмерФайла", РазмерФайла);
		Результат.Вставить("НепустыеКолонкиЖурнала", мНепустыеКолонкиЖурнала);
	КонецЕсли; 
	Возврат Результат;
КонецФункции 

Функция ОбразецСобытияЖСОН()
	//! Возврат ПрочитатьJSON("{""ts"":""2024-11-03T09:31:40.282002"",""duration"":""0"",""name"":""EXCP"",""depth"":""6""}");
КонецФункции
	
Процедура ЗагрузитьРезультатЧтенияФайлаВПотоке(Знач Результат, Знач ЛокальныеПараметры) Экспорт 
	
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли; 
	мТаблицаЖурнала = ирОбщий.ОбъединитьТаблицыЗначенийЛкс(Результат.ТаблицаЖурнала, мТаблицаЖурнала, Результат.НепустыеКолонкиЖурнала, мНепустыеКолонкиЖурнала);
	ирОбщий.СкопироватьКоллекциюЛкс(Результат.НепустыеКолонкиЖурнала, мНепустыеКолонкиЖурнала);
	СтрокиКарт = мКартыФайлов.НайтиСтроки(Результат.КлючКарты);
	Если СтрокиКарт.Количество() > 0 Тогда
		КартаФайла = СтрокиКарт[0];
	Иначе
		КартаФайла = мКартыФайлов.Добавить();
	КонецЕсли;
	ЗаполнитьЗначенияСвойств(КартаФайла, Результат.КартаФайла); 

КонецПроцедуры

Функция РеквизитыДляСервера(Параметры) Экспорт 
	
	//Результат = ирОбщий.РеквизитыОбработкиЛкс(ЭтотОбъект);
	//
	ИменаПередаваемыхСвойств = мИменаВозвращаемыхСвойств + ",
	|мАдресЧужойСхемыБД,
	|НачалоПериодаКлиента,
	|КонецПериодаКлиента,
	|НачалоПериодаСервера,
	|КонецПериодаСервера,
	|мЛиТрассаПоПользователю,
	|мИдентификаторТрассы";
	Результат = Новый Структура(ИменаПередаваемыхСвойств);
	ЗаполнитьЗначенияСвойств(Результат, ЭтотОбъект, ИменаПередаваемыхСвойств); 
	Для Каждого МетаРеквизит Из Метаданные().Реквизиты Цикл
		Результат.Вставить(МетаРеквизит.Имя, ЭтотОбъект[МетаРеквизит.Имя]);
	КонецЦикла;
	Возврат Результат;
	
КонецФункции

Процедура УдалитьСтрокиВнеФильтра()
	
	Если ЗначениеЗаполнено("" + ФильтрЗагрузки) Тогда
		Построитель = Новый ПостроительЗапроса;
		Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТаблицаЖурнала);
		ирОбщий.СкопироватьОтборПостроителяЛкс(Построитель.Отбор, ФильтрЗагрузки);
		ТаблицаЖурнала.Загрузить(Построитель.Результат.Выгрузить());
	КонецЕсли;

КонецПроцедуры

Процедура ОпределитьНепустыеКолонки() Экспорт 
	
	Если мНепустыеКолонкиЖурнала.Количество() = 0 Тогда // Загрузили данные из файла
		// Долгая операция
		Для Каждого Реквизит Из Метаданные().ТабличныеЧасти.ТаблицаЖурнала.Реквизиты Цикл
			Если ТаблицаЖурнала.НайтиСтроки(Новый Структура(Реквизит.Имя, Реквизит.Тип.ПривестиЗначение())).Количество() < ТаблицаЖурнала.Количество() Тогда
				мНепустыеКолонкиЖурнала.Вставить(Реквизит.Имя);
			КонецЕсли; 
		КонецЦикла;
	КонецЕсли; 
	ДобавитьОбязательныеНепустыеКолонки();
	//Пустота производных колонок
	Если мНепустыеКолонкиЖурнала.Свойство("Контекст") Тогда
		мНепустыеКолонкиЖурнала.Вставить("СтрокаМодуля");
		мНепустыеКолонкиЖурнала.Вставить("СтрокаБазовогоМодуля");
	КонецЕсли;

КонецПроцедуры

Процедура ДобавитьОбязательныеНепустыеКолонки() Экспорт 
	
	мНепустыеКолонкиЖурнала.Вставить("Событие");
	мНепустыеКолонкиЖурнала.Вставить("ИмяФайлаЛога");
	мНепустыеКолонкиЖурнала.Вставить("ПроцессОС");
	мНепустыеКолонкиЖурнала.Вставить("НаСервере");
	мНепустыеКолонкиЖурнала.Вставить("Вложенность");
	мНепустыеКолонкиЖурнала.Вставить("Длительность");
	мНепустыеКолонкиЖурнала.Вставить("Дата");
	мНепустыеКолонкиЖурнала.Вставить("ДатаНачала");
	мНепустыеКолонкиЖурнала.Вставить("МоментВремени");
	мНепустыеКолонкиЖурнала.Вставить("МоментВремениНачала");
	мНепустыеКолонкиЖурнала.Вставить("СтрокаМодуля");
	мНепустыеКолонкиЖурнала.Вставить("НеизвестныеСвойства");
	//мНепустыеКолонкиЖурнала.Вставить("Картинка");

КонецПроцедуры

Функция ПолучитьМоментВремени(Дата, Микросекунды) Экспорт 
	
	Результат = Формат(Дата, "ДФ=yyyyMMddHHmmss") + Формат(Микросекунды, "ЧЦ=6; ЧВН=; ЧГ=");
	Возврат Результат;
	
КонецФункции

Функция РазностьМоментовВремени(МоментВремениУменьшаемый, МоментВремениВычитаемый) Экспорт 

	Если СтрДлина(МоментВремениВычитаемый) <> СтрДлина(МоментВремениВычитаемый) Тогда
		ВызватьИсключение "Размерность моментов времени не совпадает";
	КонецЕсли; 
	ДлинаЧастиДаты = 14;
	ДатаУменьшаемая = Дата(Лев(МоментВремениУменьшаемый, ДлинаЧастиДаты));
	ДатаВычитаемая = Дата(Лев(МоментВремениВычитаемый, ДлинаЧастиДаты));
	РазрядностьДолейСекунд = СтрДлина(МоментВремениУменьшаемый) - ДлинаЧастиДаты;
	РазностьСекунд = ДатаУменьшаемая - ДатаВычитаемая;
	Результат = РазностьСекунд;
	Для Счетчик = 1 По РазрядностьДолейСекунд Цикл
		Результат = Результат * 10;
	КонецЦикла;
	Результат = Результат + Число(Сред(МоментВремениУменьшаемый, ДлинаЧастиДаты + 1)) - Число(Сред(МоментВремениВычитаемый, ДлинаЧастиДаты + 1));
	Возврат Результат;

КонецФункции // РазностьМоментовВремени()

Функция ЗаполнитьСтрокуБазовогоУровня(СтрокаТЧ)

	Если БазовыйУровеньСтека > 0 Тогда
		//МаркерБазовогоМодуля = Символы.Таб + мИмяБазовогоМодуля + " : ";
		//ПозицияМаркераМодуля = Найти(СтрокаТЧ.Контекст, МаркерБазовогоМодуля);
		//КонецСтроки = Сред(СтрокаТЧ.Контекст, ПозицияМаркераМодуля + СтрДлина(МаркерБазовогоМодуля));
		НовоеЗначение = СокрЛ(СтрПолучитьСтроку(СтрокаТЧ.Контекст, БазовыйУровеньСтека));
	Иначе
		НовоеЗначение = "";
	КонецЕсли;
	ирОбщий.ПрисвоитьЕслиНеРавноЛкс(СтрокаТЧ.СтрокаМодуляБазовогоУровня, НовоеЗначение);
	Возврат Неопределено;

КонецФункции

// ПопытокЧтения - Число, используется только при РежимТрассы = Истина
Функция ПрочитатьСобственныйЖурналДвухСторон(УдалитьДополнительныеСобытия = Истина, ИменаНеполезныхКолонок = "", РежимТрассы = Истина, ЖдатьСекунд = 2, СмещениеБазовогоУровня = Неопределено,
	ОтборПоСеансу = Неопределено)
	
	Если РежимТрассы Тогда
		Если мИдентификаторТрассы = Неопределено Тогда
			Если Не ирКэш.ЛиФайловаяБазаЛкс() Тогда
				ирОбщий.ЛиТехножурналВключенЛкс(Истина, Истина, Истина, мЛиТрассаПоПользователю);
			КонецЕсли; 
			ирОбщий.ЛиТехножурналВключенЛкс(, Истина, Истина, мЛиТрассаПоПользователю);
			Возврат Ложь;
		КонецЕсли; 
		//ЭтотОбъект.ЗагружатьЖурналКлиента = Истина;
		//ЭтотОбъект.ЗагружатьЖурналСервера = Не ирКэш.ЛиФайловаяБазаЛкс();
		//ЭтотОбъект.ЗагружатьТолькоТекущийСеанс = Истина;
	КонецЕсли; 
	ВыраниватьДатуПоСерверу = Ложь;
	#Если Клиент Тогда
	ВыраниватьДатуПоСерверу = ирОбщий.ВосстановитьЗначениеЛкс("ирАнализТехножурнала.ВыраниватьДатуПоСерверу") = Истина;
	#КонецЕсли
	Если РежимТрассы Тогда
		РазницаВремениКлиентСервер = НачалоПериодаКлиента - НачалоПериодаСервера;
	Иначе
		РазницаВремениКлиентСервер = ирОбщий.РазницаВремениКлиентСерверЛкс();
	КонецЕсли; 
	Если Истина
		И Не ирКэш.Получить().ЭтоФайловаяБаза 
		И ЗагружатьЖурналСервера
	Тогда
		ЭтотОбъект.КаталогЖурнала = "";
		Если ВыраниватьДатуПоСерверу Тогда
			СдвигВремени = 0;
		Иначе
			СдвигВремени = -РазницаВремениКлиентСервер;
		КонецЕсли; 
		РезультатСервера = ПрочитатьСобственныйЖурналОднойСтороны(НачалоПериодаСервера, КонецПериодаСервера, Истина, СдвигВремени, РежимТрассы, ЖдатьСекунд, СмещениеБазовогоУровня, ОтборПоСеансу);
		Если РезультатСервера = Неопределено Тогда
			ирОбщий.СообщитьЛкс("Не удалось найти маркеры трассы в логах техножурнала сервера " + КаталогЖурнала + ". Проверьте включенность регистрации события QERR в настройке техножурнала");
		КонецЕсли; 
	КонецЕсли;
	РезультатКлиента = Неопределено;
	Если Ложь
		Или Не РежимТрассы
		Или Не мЛиТрассаПоПользователю 
		Или ирКэш.Получить().ЭтоФайловаяБаза
	Тогда
		#Если Клиент Тогда
		Если ЗагружатьЖурналКлиента Тогда
			ЭтотОбъект.КаталогЖурнала = "";
			Если ВыраниватьДатуПоСерверу Тогда
				СдвигВремени = РазницаВремениКлиентСервер;
			Иначе
				СдвигВремени = 0;
			КонецЕсли;
			РезультатКлиента = ПрочитатьСобственныйЖурналОднойСтороны(НачалоПериодаКлиента, КонецПериодаКлиента, Ложь, СдвигВремени, РежимТрассы, ЖдатьСекунд, СмещениеБазовогоУровня, ОтборПоСеансу);
			Если РезультатКлиента = Неопределено Тогда
				ирОбщий.СообщитьЛкс("Не удалось найти маркеры трассы в логах техножурнала клиента " + КаталогЖурнала + ". Проверьте включенность регистрации события QERR в настройке техножурнала");
			КонецЕсли; 
		КонецЕсли; 
		#КонецЕсли
	КонецЕсли;
	Если УдалитьДополнительныеСобытия Тогда
		УдалитьСтрокиПоОтборуЛкс(Новый Структура("Действие", "getExecSQLStatistics"));
		УдалитьСтрокиПоОтборуЛкс(Новый Структура("Событие", "CONTEXT"));
	КонецЕсли; 
	Если РежимТрассы Тогда
		ЭтотОбъект.ВключитьСвойстваСИменамиМетаданных = Истина;
	КонецЕсли; 
	Если Истина
		И РезультатКлиента = Неопределено
		И РезультатСервера = Неопределено
	Тогда
		Если мТаблицаЖурнала.Количество() = 0 Тогда
			Возврат Ложь;
		КонецЕсли; 
	КонецЕсли; 
	//Если ВключитьСвойстваСИменамиМетаданных Тогда
	//	ОбновитьСвойстваВТерминахМетаданных();
	//КонецЕсли; 
	//ОбновитьСтрокиБазовогоУровня();
	Возврат Истина;

КонецФункции

Функция ПрочитатьСобственныйЖурналОднойСтороны(НачалоПериода = Неопределено, КонецПериода = Неопределено, НаСервере = Истина, СдвигВремени = 0, 
	РежимТрассы = Истина, ЖдатьСекунд = 2, СмещениеБазовогоУровня = Неопределено, ОтборПоСеансу = Неопределено)

	Если НачалоПериода <> Неопределено Тогда
		ЭтотОбъект.НачалоПериода = НачалоПериода;
	КонецЕсли; 
	Если КонецПериода <> Неопределено Тогда
		ЭтотОбъект.КонецПериода = КонецПериода;
	КонецЕсли; 
	ТехножурналВключен = ирОбщий.ЛиТехножурналВключенЛкс(НаСервере,, Истина, РежимТрассы И мЛиТрассаПоПользователю);
	Если Истина
		И Не ТехножурналВключен 
		И РежимТрассы
	Тогда
		Возврат Неопределено;
	КонецЕсли; 
	МассивТиповСУБД = Новый Массив();
	Если ирКэш.ЛиФайловаяБазаЛкс() Тогда
		МассивТиповСУБД.Добавить("DBV8DBENG");
		//ОтборТЧ = Новый Структура("Событие", "SDBL");
	Иначе
		МассивТиповСУБД.Добавить("DBMSSQL");
		МассивТиповСУБД.Добавить("DBPOSTGRS");
		МассивТиповСУБД.Добавить("DBORACLE");
		МассивТиповСУБД.Добавить("DB2");
		//ОтборТЧ = Новый Структура("Событие", "CONTEXT");
	КонецЕсли; 
	СообщитьРазмер = Неопределено;
	МаркерНачалаНайден = Ложь;
	ДлительностьОднойПаузы = 0.5;
	Для Счетчик = 1 По ЖдатьСекунд / ДлительностьОднойПаузы + 1 Цикл
		#Если Клиент Тогда
		ОбработкаПрерыванияПользователя();
		#КонецЕсли
		Если Счетчик > 1 Тогда
			ирОбщий.СостояниеЛкс("Ожидание техножурнала " + КаталогЖурнала + "...");
			ирОбщий.ПаузаЛкс(ДлительностьОднойПаузы);
			СообщитьРазмер = Ложь;
		КонецЕсли; 
		ЖурналСчитан = ПрочитатьПроизвольныйЖурнал(СообщитьРазмер, СдвигВремени, , ОтборПоСеансу, НаСервере);
		Если Не ЖурналСчитан Тогда
			Возврат Неопределено;
		КонецЕсли;
		//ТехножурналВключен = ирОбщий.ЛиТехножурналВключенЛкс(Истина,, Истина);
		//Если ТехножурналВключен Тогда
		//	Если АвтоочисткаТехножурнала Тогда
		//		КаталогТекущегоЖурнала = ПолучитьКаталогТекущегоЖурнала();
		//		Если ЗначениеЗаполнено(КаталогТекущегоЖурнала) Тогда
		//			ирОбщий.ОчиститьКаталогТехножурналаЛкс(КаталогТекущегоЖурнала, Истина, Ложь);
		//		КонецЕсли; 
		//	КонецЕсли; 
		//КонецЕсли; 
		Если РежимТрассы Тогда
			Если Ложь
				Или Не ирКэш.ЛиПортативныйРежимЛкс() 
				Или Не НаСервере
			Тогда
				МаркерНачала = Новый Структура("Описание", "{(1, 1)}: Ожидается выражение ""ВЫБРАТЬ""
					|<<?>>НачалоТрассы_" + мИдентификаторТрассы);
				МаркерКонца = Новый Структура("Описание", "{(1, 1)}: Ожидается выражение ""ВЫБРАТЬ""
					|<<?>>КонецТрассы_" + мИдентификаторТрассы);
			Иначе
				МаркерНачала = Новый Структура("ТекстSDBL", "SELECT
					|""НачалоТрассы_" + мИдентификаторТрассы + """
					|");
				МаркерКонца = Новый Структура("ТекстSDBL", "SELECT
					|""КонецТрассы_" + мИдентификаторТрассы + """
					|");
			КонецЕсли; 		
			
			// Удаляем лишние строки до маркера начала и после маркера конца
			Если Не МаркерНачалаНайден Тогда
				НайденныеСтроки = мТаблицаЖурнала.НайтиСтроки(МаркерНачала);
				Если НайденныеСтроки.Количество() = 0 Тогда
					Продолжить;
				КонецЕсли; 
				НачальнаяСтрока = НайденныеСтроки[0];
				#Если Сервер И Не Сервер Тогда
				    НачальнаяСтрока = мТаблицаЖурнала.Добавить();
				#КонецЕсли
				Если СмещениеБазовогоУровня <> Неопределено Тогда
					ЭтотОбъект.БазовыйУровеньСтека = СтрЧислоСтрок(НачальнаяСтрока.Контекст) + СмещениеБазовогоУровня;
				КонецЕсли; 
				КонечныйИндекс = мТаблицаЖурнала.Индекс(НачальнаяСтрока);
				Для Индекс = 0 По КонечныйИндекс Цикл
					//Если мТаблицаЖурнала[0].НаСервере = НаСервере Тогда
						мТаблицаЖурнала.Удалить(0);
					//КонецЕсли; 
				КонецЦикла;
				МаркерНачалаНайден = Истина;
			КонецЕсли; 
			
			НайденныеСтроки = мТаблицаЖурнала.НайтиСтроки(МаркерКонца);
			Если НайденныеСтроки.Количество() = 0 Тогда
				Продолжить;
			КонецЕсли; 
			КонечнаяСтрока = НайденныеСтроки[0];
			НачальныйИндекс = мТаблицаЖурнала.Индекс(КонечнаяСтрока);
			Если Ложь
				Или НаСервере 
				Или ирКэш.ЛиФайловаяБазаЛкс()
			Тогда
				КонечнаяСтрока1 = мТаблицаЖурнала[НачальныйИндекс];
				Если МассивТиповСУБД.Найти(КонечнаяСтрока1.Событие) <> Неопределено Тогда // Опасно. Учитывается регистр букв
					мТипСУБД = КонечнаяСтрока1.Событие;
					НачальныйИндекс = НачальныйИндекс - 1;
				КонецЕсли; 
			КонецЕсли; 
			НачальноеКоличество = мТаблицаЖурнала.Количество(); 
			Для Индекс = 1 По НачальноеКоличество - НачальныйИндекс Цикл
				СтрокаЖурнала = мТаблицаЖурнала[НачальноеКоличество - Индекс];
				Если СтрокаЖурнала.НаСервере = НаСервере Тогда
					мТаблицаЖурнала.Удалить(СтрокаЖурнала);
				КонецЕсли; 
			КонецЦикла;
			
			Пока Истина
				И мТаблицаЖурнала.Количество() > 0
				И мТаблицаЖурнала[0].НаСервере = НаСервере
				И Найти(мТаблицаЖурнала[0].Контекст, "Обработка.ирАнализТехножурнала.МодульОбъекта") > 0 
			Цикл
				мТаблицаЖурнала.Удалить(0);
			КонецЦикла; 
			Пока Истина
				И мТаблицаЖурнала.Количество() > 0
				И мТаблицаЖурнала[мТаблицаЖурнала.Количество() - 1].НаСервере = НаСервере
				И Найти(мТаблицаЖурнала[мТаблицаЖурнала.Количество() - 1].Контекст, "Обработка.ирАнализТехножурнала.МодульОбъекта") > 0 
			Цикл
				мТаблицаЖурнала.Удалить(мТаблицаЖурнала.Количество() - 1);
			КонецЦикла; 
		КонецЕсли; 
		Прервать;
	КонецЦикла;
	Если РежимТрассы Тогда
		Если КонечнаяСтрока = Неопределено Тогда
			Возврат Неопределено;
		КонецЕсли; 
		//	ФрагментыНачальнойСтрокиМодуля = ирОбщий.СтрРазделитьЛкс(мТаблицаЖурнала[0].СтрокаМодуля, ":", Истина);
		//	ФрагментыКонечнойСтрокиМодуля = ирОбщий.СтрРазделитьЛкс(мТаблицаЖурнала[мТаблицаЖурнала.Количество() - 1].СтрокаМодуля, ":", Истина);
		//	ИмяМодуляНачальнойСтроки = ФрагментыНачальнойСтрокиМодуля[0];
		//	ИмяМодуляКонечнойСтроки = ФрагментыКонечнойСтрокиМодуля[0];
		//	НомерНачальнойСтроки = Число(ФрагментыНачальнойСтрокиМодуля[1]);
		//	НомерКонечнойСтроки = Число(ФрагментыКонечнойСтрокиМодуля[1]);
		//	ЕстьВызовыМетодовМодулей = Ложь
		//		Или ИмяМодуляНачальнойСтроки <> ИмяМодуляНачальнойСтроки
		//		Или НомерНачальнойСтроки > НомерКонечнойСтроки;
		//	СтрокиВызововМетодов = НайтиВызовыМетодовМодулей(ИмяМодуляНачальнойСтроки, НомерНачальнойСтроки, НомерКонечнойСтроки, Истина);
		//	Если СтрокиВызововМетодов.Количество() > 0 Тогда
		//		Ответ = Вопрос("В трассе обнаружены вызовы методов модулей. Хотите удалить их из трассы?", РежимДиалогаВопрос.ДаНет);
		//		Если Ответ = КодВозвратаДиалога.Да Тогда
		//			СтрокиВызововМетодов = НайтиВызовыМетодовМодулей(ИмяМодуляНачальнойСтроки, НомерНачальнойСтроки, НомерКонечнойСтроки, Ложь);
		//			Для Каждого СтрокаВызоваМетода Из СтрокиВызововМетодов Цикл
		//				мТаблицаЖурнала.Удалить(СтрокаВызоваМетода);
		//			КонецЦикла;
		//		КонецЕсли;
		//	КонецЕсли;
	КонецЕсли; 
	Возврат Истина;

КонецФункции

Функция ПрочитатьЖурнал(Параметры) Экспорт
	
	ЭтотОбъект.ФильтрЗагрузки = Параметры.ФильтрЗагрузки;
	УдалитьДополнительныеСобытия = Параметры.УдалитьДополнительныеСобытия;
	ИменаНеполезныхКолонок = Параметры.ИменаНеполезныхКолонок;
	РежимТрассы = Параметры.РежимТрассы;
	ЖдатьСекунд = Параметры.ЖдатьСекунд;
	ОтборПоСеансу = Параметры.ОтборПоСеансу;
	СмещениеБазовогоУровня = Параметры.СмещениеБазовогоУровня;
	Если Не РежимТрассы Тогда
		ЭтотОбъект.КонецПериодаСервера = Неопределено;
		ЭтотОбъект.КонецПериодаКлиента = Неопределено;
		ЭтотОбъект.НачалоПериодаСервера = Неопределено;
		ЭтотОбъект.НачалоПериодаКлиента = Неопределено;
	КонецЕсли; 
	Если Ложь
		Или ЗагружатьЖурналКлиента
		Или ЗагружатьЖурналСервера
	Тогда
		ЖурналПрочитан = ПрочитатьСобственныйЖурналДвухСторон(УдалитьДополнительныеСобытия, ИменаНеполезныхКолонок, РежимТрассы, ЖдатьСекунд, СмещениеБазовогоУровня, ОтборПоСеансу);
	Иначе
		ЖурналПрочитан = ПрочитатьПроизвольныйЖурнал(,,, ОтборПоСеансу);
		Если ВключитьСвойстваСИменамиМетаданных Тогда
			ОбновитьСвойстваВТерминахМетаданных();
		КонецЕсли; 
	КонецЕсли;
	Результат = Новый Структура(мИменаВозвращаемыхСвойств);
	ЗаполнитьЗначенияСвойств(Результат, ЭтотОбъект); 
	Результат.Вставить("Успех", ЖурналПрочитан);   
	ПодготовитьТаблицуЖурналаКСериализации();
	Результат.Вставить("ТаблицаЖурнала", мТаблицаЖурнала); 
	Возврат Результат;

КонецФункции

Процедура ПодготовитьТаблицуЖурналаКСериализации() Экспорт
	#Если Сервер И Не Клиент Тогда
		Если ТипЗнч(мТаблицаЖурнала) <> Тип("ТаблицаЗначений") Тогда
			мТаблицаЖурнала = мТаблицаЖурнала.Выгрузить();
		КонецЕсли;
	#КонецЕсли
КонецПроцедуры

// ТолькоПустые - заполнять только ранее не заполненные свойства
Функция ЗаполнитьСвойстваСИменамиМетаданных(ВыбраннаяСтрока, Принудительно = Ложь) Экспорт
	
	Если Не Принудительно И ВыбраннаяСтрока.СвойстваСИменамиМетаданныхАктуальны Тогда
		Возврат Ложь;
	КонецЕсли;
	Попытка
		Инфобаза = ВыбраннаяСтрока.Инфобаза;
	Исключение
		Инфобаза = "";
	КонецПопытки; 
	ЧужаяСхемаБД = ирОбщий.ПолучитьЧужуюСхемуБДЛкс(мАдресЧужойСхемыБД);
	ЭтоГарантированоСобытиеВыбраннойЧужойБазы = Истина
		И ЗначениеЗаполнено(Инфобаза)
		И ЧужаяСхемаБД <> Неопределено
		И ирОбщий.СтрокиРавныЛкс(Инфобаза, НСтр(ЧужаяСхемаБД.СтрокаСоединения, "Ref"));
	ЭтоГарантированоСобытиеСобственнойБазы = Истина
		И ЗначениеЗаполнено(Инфобаза) 
		И Не ЭтоГарантированоСобытиеВыбраннойЧужойБазы 
		И ирОбщий.СтрокиРавныЛкс(Инфобаза, НСтр(СтрокаСоединенияИнформационнойБазы(), "Ref"));
	НайденныеТаблицы = Новый ТаблицаЗначений;
	Для Каждого КлючИЗначение Из мСвойстваСИменамиБД Цикл
		ИмяСвойства = КлючИЗначение.Ключ;
		Если Найти(ИмяСвойства, "Шаблон") > 0 Тогда
			Продолжить;
		КонецЕсли; 
		Попытка
			Пустышка = ТипЗнч(ВыбраннаяСтрока[ИмяСвойства]); // т.к. строки длинные, то их копирование в новую переменную накладно
		Исключение
			// Такого свойства нет - пропускаем
			Продолжить;
		КонецПопытки;
		ЗначениеСвойства = ВыбраннаяСтрока[ИмяСвойства];
		// Берем только имя базы, т.к. кластер может быть по-разному написан
		Если Истина
			И ЗначениеЗаполнено(ЗначениеСвойства)
			И (Ложь
				Или Инфобаза = ""
				Или ЭтоГарантированоСобытиеСобственнойБазы
				Или ЭтоГарантированоСобытиеВыбраннойЧужойБазы)
		Тогда
			ЛиМенаБД = КлючИЗначение.Значение;
			Если Не ЛиМенаБД Тогда
				ТипСУБД = "";
			Иначе
				Попытка
					ТипСУБД = ВыбраннаяСтрока.Событие;
				Исключение
					ТипСУБД = Сред(ИмяСвойства, СтрДлина("Текст") + 1); // Опасно
				КонецПопытки; 
			КонецЕсли; 
			//Попытка
				ТекстМета = ПеревестиТекстБДВТерминыМетаданных(ЗначениеСвойства, , , ТипСУБД, НайденныеТаблицы,, Ложь,, ЭтоГарантированоСобытиеСобственнойБазы);
			//Исключение
			//	ТекстМета = "";
			//КонецПопытки; 
			//Если ТекстМета <> "" Тогда
				ВыбраннаяСтрока[ИмяСвойства + "Мета"] = ТекстМета;
			//КонецЕсли; 
		Иначе
			// База чужая. Не делаем преобразования
			//ВыбраннаяСтрока[ИмяСвойства + "Мета"] = ЗначениеСвойства;
		КонецЕсли; 
	КонецЦикла;
	Если НайденныеТаблицы.Количество() > 0 Тогда
		НайденныеТаблицы.Свернуть("ИмяМета");
		НайденныеТаблицы.Сортировать("ИмяМета");
		мНепустыеКолонкиЖурнала.Вставить("ТаблицыМетаданных");
		ИменаТаблицСтрокой = ирОбщий.СтрСоединитьЛкс(НайденныеТаблицы.ВыгрузитьКолонку("ИмяМета"));
		Попытка
			ВыбраннаяСтрока.ТаблицыМетаданных = ИменаТаблицСтрокой;
		Исключение
			// Строка итогов БлокировкиУпр
		КонецПопытки;
	КонецЕсли; 
	Попытка
		ТекстSDBLШаблонМета = ВыбраннаяСтрока.ТекстSDBLШаблонМета;
	Исключение
		ТекстSDBLШаблонМета = Неопределено;
	КонецПопытки;
	Если ТекстSDBLШаблонМета <> Неопределено Тогда
		Инфобаза = "";
		ТекстSDBLМета = "";
		Попытка
			Инфобаза = ВыбраннаяСтрока.Инфобаза;
			ТекстSDBLМета = ВыбраннаяСтрока.ТекстSDBLМета;
		Исключение
		КонецПопытки; 
		Если Ложь
			Или Инфобаза = ""
			Или ЭтоГарантированоСобытиеСобственнойБазы
			Или ЭтоГарантированоСобытиеВыбраннойЧужойБазы
		Тогда
			Если Истина
				И ТекстSDBLМета <> ""
			Тогда
				ВыбраннаяСтрока.ТекстSDBLШаблонМета = ПолучитьШаблонТекстаБД(ТекстSDBLМета);
			Иначе
				ВыбраннаяСтрока.ТекстSDBLШаблонМета = ПеревестиТекстБДВТерминыМетаданных(ВыбраннаяСтрока.ТекстSDBLШаблон,,,,,, Ложь,, ЭтоГарантированоСобытиеСобственнойБазы);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	Попытка
		ТекстСУБДШаблонМета = ВыбраннаяСтрока.ТекстСУБДШаблонМета;
	Исключение
		ТекстСУБДШаблонМета = Неопределено;
	КонецПопытки;
	Если ТекстСУБДШаблонМета <> Неопределено Тогда
		Инфобаза = "";
		ТекстСУБДМета = "";
		Попытка
			Инфобаза = ВыбраннаяСтрока.Инфобаза;
			ТекстСУБДМета = ВыбраннаяСтрока.ТекстСУБДМета;
		Исключение
		КонецПопытки; 
		Если Ложь
			Или Инфобаза = ""
			Или ЭтоГарантированоСобытиеСобственнойБазы
			Или ЭтоГарантированоСобытиеВыбраннойЧужойБазы
		Тогда
			Если Истина
				И ТекстСУБДМета <> ""
			Тогда
				ВыбраннаяСтрока.ТекстСУБДШаблонМета = ПолучитьШаблонТекстаБД(ТекстСУБДМета);
			Иначе
				ВыбраннаяСтрока.ТекстСУБДШаблонМета = ПеревестиТекстБДВТерминыМетаданных(ВыбраннаяСтрока.ТекстСУБДШаблон,,, мТипСУБД,,, Ложь,, ЭтоГарантированоСобытиеСобственнойБазы);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	ВыбраннаяСтрока.СвойстваСИменамиМетаданныхАктуальны = Истина;
	Возврат Истина;

КонецФункции

Функция ПолучитьКаталогТекущегоЖурнала(НаСервере) Экспорт
	
	Результат = ирОбщий.КаталогЧтенияТехножурналаЛкс(НаСервере, Истина, мЛиТрассаПоПользователю);
	Если Не ЗначениеЗаполнено(Результат) Тогда
		Если НаСервере Тогда
			ТекстКлиентСервер = "сервера";
		Иначе
			ТекстКлиентСервер = "клиента";
		КонецЕсли; 
		ирОбщий.СообщитьЛкс("Технологический журнал " + ТекстКлиентСервер + " выключен. Невозможно определить каталог журнала по умолчанию.");
		Возврат Неопределено;
	КонецЕсли; 
	Если НаСервере Тогда
		КлиентЗапущенНаКомпьютереСервера = ирОбщий.ЛиКлиентЗапущенНаКомпьютереСервераЛкс();
		Если Не КлиентЗапущенНаКомпьютереСервера Тогда 
			Если ЭтоЛокальныйПутьЛкс(Результат) Тогда
				ирОбщий.СообщитьЛкс("Клиент запущен не на компьютере сервера (" + ирСервер.ИмяКомпьютераЛкс() + 
					"), а в серверной настройке техножурнала указан локальный каталог. Необходимо указать сетевой путь к техножурналу сервера");
				#Если Клиент Тогда
					ФормаНастройки = ПолучитьФорму("НастройкаЧтения");
					РезультатНастройки = ФормаНастройки.ОткрытьМодально();
					Если РезультатНастройки = Истина Тогда
						Если мЛиТрассаПоПользователю Тогда
							Результат = ирОбщий.ВосстановитьЗначениеЛкс("ирАнализТехножурнала.КаталогТрассыПоПользователю");
						Иначе
							Результат = ирОбщий.ВосстановитьЗначениеЛкс("ирАнализТехножурнала.КаталогЖурналаСервера");
						КонецЕсли; 
					Иначе
						Результат = Неопределено;
					КонецЕсли; 
				#КонецЕсли
			КонецЕсли; 
		КонецЕсли; 
	КонецЕсли; 
	Возврат Результат;

КонецФункции

Функция ЭтоЛокальныйПутьЛкс(Путь) Экспорт 

	ЭтоЛокальныйРесурс = Ложь
		Или Найти(НРег(Путь), "\\localhost") = 1
		Или Найти(НРег(Путь), "\\127.0.0.1") = 1
		Или Лев(Путь, 2) <> "\\";
	Возврат ЭтоЛокальныйРесурс;

КонецФункции // ЭтоЛокальныйПуть()
 
Функция ОткрытьСПараметрами(пКаталогЖурнала) Экспорт 
	
	Форма = ПолучитьФорму();
	Форма.Открыть();
	ЭтотОбъект.ЗагружатьТолькоТекущийСеанс = Ложь;
	ЭтотОбъект.ЗагружатьЖурналСервера = Ложь;
	ЭтотОбъект.ЗагружатьЖурналКлиента = Ложь;
	Форма.ОбновитьДоступность();
	Форма.КаталогЖурнала = пКаталогЖурнала;
	Возврат Форма;
	
КонецФункции

Функция ОткрытьСОтбором(НачалоПериода = Неопределено, КонецПериода = Неопределено, СтруктураОтбора = Неопределено) Экспорт 
	
	Форма = ПолучитьФорму(,,);
	Форма.Открыть();
	Отбор = Форма.ЭлементыФормы.ТаблицаЖурнала.ОтборСтрок;
	Отбор.Сбросить();
	Если НачалоПериода <> Неопределено Тогда
		Форма.НачалоПериода = НачалоПериода;
	КонецЕсли; 
	Если КонецПериода <> Неопределено Тогда
		Форма.КонецПериода = КонецПериода;
	КонецЕсли; 
	Если СтруктураОтбора <> Неопределено Тогда
		Для Каждого КлючИЗначение Из СтруктураОтбора Цикл
			Отбор[КлючИЗначение.Ключ].Установить(КлючИЗначение.Значение);
		КонецЦикла; 
	КонецЕсли; 
	Возврат Форма;
	
КонецФункции

Функция ПолучитьИмяСвойстваБезМета(Знач МестноеИмя) Экспорт

	Если ирОбщий.СтрокиРавныЛкс(Прав(МестноеИмя, 4), "мета") Тогда
		МестноеИмя = Лев(МестноеИмя, СтрДлина(МестноеИмя) - 4);
	КонецЕсли; 
	Возврат МестноеИмя;

КонецФункции

Процедура ОбновитьСвойстваВТерминахМетаданных() Экспорт
	
	ВыбранныеСтроки = мТаблицаЖурнала.НайтиСтроки(Новый Структура("СвойстваСИменамиМетаданныхАктуальны", Ложь));
	Индикатор = ирОбщий.ПолучитьИндикаторПроцессаЛкс(ВыбранныеСтроки.Количество(), "Перевод в термины метаданных");
	Для Каждого СтрокаТаблицыЖурнала Из ВыбранныеСтроки Цикл
		ирОбщий.ОбработатьИндикаторЛкс(Индикатор);
		ЗаполнитьСвойстваСИменамиМетаданных(СтрокаТаблицыЖурнала);
	КонецЦикла;
	ирОбщий.ОсвободитьИндикаторПроцессаЛкс();
	
КонецПроцедуры

Процедура ОбновитьСтрокиБазовогоУровня() Экспорт
	
	//Индикатор = ирОбщий.ПолучитьИндикаторПроцессаЛкс(мТаблицаЖурнала.Количество());
	Для Каждого СтрокаТаблицыЖурнала Из мТаблицаЖурнала Цикл
		//ирОбщий.ОбработатьИндикаторЛкс(Индикатор);
		ЗаполнитьСтрокуБазовогоУровня(СтрокаТаблицыЖурнала);
	КонецЦикла;
	//Для Каждого СтрокаКонтекста Из Контексты Цикл
	//	ЗаполнитьСтрокуБазовогоУровня(СтрокаКонтекста);
	//КонецЦикла;
	
КонецПроцедуры

Функция ПолучитьШаблонТекстаБД(ТекстБД, Знач ЗаменаПеременныхФрагментов = "7") Экспорт 
	#Если Сервер И Не Сервер Тогда
		мРегВыражениеПараметры = Обработки.ирОболочкаРегВыражение.Создать();
	#КонецЕсли
	СложнаяЗамена = мРегВыражениеПараметры.ТекущийДвижок() = мРегВыражениеПараметры.ВсеДвижки().PCRE2;
	Если СложнаяЗамена Тогда
		// https://www.hostedredmine.com/issues/928414
		Префикс = "[!@!~=+]";
		ЗаменаПеременныхФрагментов = Префикс + ЗаменаПеременныхФрагментов;
	КонецЕсли; 
	ШаблонТекстаБД = мРегВыражениеПараметры.Заменить(ТекстБД, "$1$2" + ЗаменаПеременныхФрагментов);
	Если СложнаяЗамена Тогда
		ШаблонТекстаБД = СтрЗаменить(ШаблонТекстаБД, Префикс, "");
	КонецЕсли; 
	Возврат ШаблонТекстаБД;
	
КонецФункции

Процедура ЗаполнитьСвойстваШаблоны() Экспорт

	Индикатор = ирОбщий.ПолучитьИндикаторПроцессаЛкс(ТаблицаЖурнала.Количество());
	Для Каждого СтрокаТаблицыЖурнала Из ТаблицаЖурнала Цикл
		ирОбщий.ОбработатьИндикаторЛкс(Индикатор);
		Если Истина
			И СтрокаТаблицыЖурнала.ТекстSDBL <> ""
			И СтрокаТаблицыЖурнала.ТекстSDBLШаблон = ""
		Тогда
			СтрокаТаблицыЖурнала.ТекстSDBLШаблон = ПолучитьШаблонТекстаБД(СтрокаТаблицыЖурнала.ТекстSDBL);
		КонецЕсли; 
		Если Истина
			И СтрокаТаблицыЖурнала.ТекстСУБД <> ""
			И СтрокаТаблицыЖурнала.ТекстСУБДШаблон = ""
		Тогда
			СтрокаТаблицыЖурнала.ТекстСУБДШаблон = ПолучитьШаблонТекстаБД(СтрокаТаблицыЖурнала.ТекстСУБД);
		КонецЕсли; 
		Если Истина
			И СтрокаТаблицыЖурнала.Описание <> ""
			И СтрокаТаблицыЖурнала.ОписаниеШаблон = ""
		Тогда
			СтрокаТаблицыЖурнала.ОписаниеШаблон = ПолучитьШаблонТекстаБД(СтрокаТаблицыЖурнала.Описание);
		КонецЕсли; 
	КонецЦикла;
	
КонецПроцедуры

Процедура ОставитьТолькоСтрокиПоОтбору(СтруктураОтбора) Экспорт 
	
	НужныеСтроки = ТаблицаЖурнала.НайтиСтроки(СтруктураОтбора);
	КоличествоСтрок = ТаблицаЖурнала.Количество();
	Для Счетчик = 1 По КоличествоСтрок Цикл
		Индекс = КоличествоСтрок - Счетчик;
		СтрокаЖурнала = ТаблицаЖурнала[Индекс];
		Если НужныеСтроки.Найти(СтрокаЖурнала) = Неопределено Тогда
			ТаблицаЖурнала.Удалить(СтрокаЖурнала);
		КонецЕсли; 
	КонецЦикла;

КонецПроцедуры

Процедура УдалитьСтрокиПоОтборуЛкс(СтруктураОтбора) Экспорт 
	
	НенужныеСтроки = мТаблицаЖурнала.НайтиСтроки(СтруктураОтбора);
	Для Каждого НенужнаяСтрока Из НенужныеСтроки Цикл
		мТаблицаЖурнала.Удалить(НенужнаяСтрока);
	КонецЦикла;

КонецПроцедуры

Процедура ЗаписатьМаркерВТехножурнал(Маркер, ДляКлиента = Истина, ДляСервера = Истина, выхТекущаяДатаКлиента = Неопределено, выхТекущаяДатаСервера = Неопределено) Экспорт
	
	ЗапросМаркер = Новый Запрос();
	выхТекущаяДатаКлиента = ТекущаяДата();
	выхТекущаяДатаСервера = выхТекущаяДатаКлиента;
	Если ДляКлиента Тогда
		ЗапросМаркер.Текст = Маркер;
		Попытка
			ЗапросМаркер.Выполнить(); // Генерируем для клиента маркер-событие QERR
		Исключение
		КонецПопытки;
	КонецЕсли; 
	Если Истина
		И ДляСервера 
		И Не ирКэш.ЛиФайловаяБазаЛкс()
	Тогда
		Если Не ирКэш.ЛиПортативныйРежимЛкс() Тогда
			Попытка
				ирСервер.ВыполнитьЗапросЛкс(Маркер,,,, выхТекущаяДатаСервера); // Генерируем для сервера маркер-событие QERR
			Исключение
			КонецПопытки;
		Иначе
			ЗапросМаркер.Текст = "ВЫБРАТЬ """ + Маркер + """";
			Попытка
				ЗапросМаркер.Выполнить(); // Генерируем для клиента маркер-событие SDBL
			Исключение
				// Может возникнуть ошибка "В данной транзакции уже происходили ошибки!"
			КонецПопытки;
		КонецЕсли; 
	КонецЕсли; 
	
КонецПроцедуры

Функция НовыйИдентификаторТрассыБД()
	ДатаМиллисекунды = ТекущаяУниверсальнаяДатаВМиллисекундах();
	Генератор = Новый ГенераторСлучайныхЧисел(ДатаМиллисекунды);
	СгенерированноеЧисло = Генератор.СлучайноеЧисло(100000000, 900000000); 
	Возврат СгенерированноеЧисло;
КонецФункции

// Функция - Начать трассу
//
// Параметры:
//  ПрефиксТрассы						 - 	 - 
//  ВыводитьСообщения					 - 	 - 
//  выхТекущаяДатаСервера				 - 	 - 
//  ИспользоватьТрассуСУБД				 - 	 - 
//  ЗапроситьИдентификаторСоединенияБД	 - Булево - принудительно запросить актуальный идентификатор соединения БД, можно передавать Ложь если соединение БД удерживается снаружи
// 
// Возвращаемое значение:
//   - 
//
Функция НачатьТрассу(ПрефиксТрассы = "", ВыводитьСообщения = Ложь, выхТекущаяДатаСервера = Неопределено, Знач ТолькоТрассаСУБД = Ложь, Знач ЗапроситьИдентификаторСоединенияБД = Истина) Экспорт
	
	ЭтотОбъект.НачалоПериодаКлиента = Неопределено;
	ЭтотОбъект.НачалоПериодаСервера = Неопределено;
	ЭтотОбъект.КонецПериодаСервера = Неопределено;
	ЭтотОбъект.КонецПериодаКлиента = Неопределено;
	мТрассаСУБДXML = Неопределено;
	ИспользоватьТрассуСУБД = Истина
		И (Ложь
			Или ТолькоТрассаСУБД
			Или ирОбщий.ПараметрыСоединенияЭтойСУБДЛкс().СобиратьТрассу = Истина)
		#Если Клиент Тогда
		И ирКлиент.ЛиПровайдерАДОДляЭтойБазыГотовЛкс()
		#КонецЕсли
		И ЛиСерверMSSQLПоддерживаетТрассировку();
	Если Не ТолькоТрассаСУБД Тогда
		Если ВыводитьСообщения Тогда
			// Попробовать сделать проверку регистрации событий QERR в настройке техножурнала на сервере
			// Вызовы ЛиТехножурналВключенЛкс могут быть очень долгими, если в анализе техножурнала пользователь сохранил сетевой путь к логам на сервере и он недоступен
			Если Не ирКэш.ЛиФайловаяБазаЛкс() Тогда
				ТехножурналСервераВключен = ирОбщий.ЛиТехножурналВключенЛкс(Истина, ВыводитьСообщения,, мЛиТрассаПоПользователю);
			КонецЕсли; 
			ТехножурналКлиентаВключен = ирОбщий.ЛиТехножурналВключенЛкс(, ВыводитьСообщения,, мЛиТрассаПоПользователю);
			Если Истина
				И ТехножурналСервераВключен <> Истина
				И Не ТехножурналКлиентаВключен
				И Не ИспользоватьТрассуСУБД
			Тогда
				Возврат Ложь;
			КонецЕсли; 
		КонецЕсли; 
		мИдентификаторТрассы = ПрефиксТрассы + "_" + СтрЗаменить(Новый УникальныйИдентификатор(), "-", "");
		ЗаписатьМаркерВТехножурнал("НачалоТрассы_" + мИдентификаторТрассы,,, НачалоПериодаКлиента, НачалоПериодаСервера);
		выхТекущаяДатаСервера = НачалоПериодаСервера;
	КонецЕсли;
	мУдержательСоединенияБД = Неопределено;
	Если ЗапроситьИдентификаторСоединенияБД Тогда
		мИдентификаторСоединенияБД = Неопределено;
	КонецЕсли;
	Если ИспользоватьТрассуСУБД Тогда 
		// https://www.hostedredmine.com/issues/926150
		мИдентификаторБазыДанных = ИдентификаторБазыДанных();  
		ИдентификаторТрассы = XMLСтрока(НовыйИдентификаторТрассыБД());
		ТекстыСУБД = Новый Массив;
		Если мИдентификаторСоединенияБД = Неопределено Тогда
			// Удаляем все сессии этой базы, открытые инструментами
			ТекстыСУБД.Добавить(ПолучитьМакет("SQLServer_СкриптУдаленияСессийБазы").ПолучитьТекст());
			ТекстыСУБД.Добавить(ПолучитьМакет("SQLServer_СкриптСессииОпределенияСессииСУБД").ПолучитьТекст());
	        ВыполнитьЗапросТрассыЭтойСУБД(ТекстыСУБД, ИдентификаторТрассы);
			мУдержательСоединенияБД = Новый МенеджерВременныхТаблиц;
			ЗапросУдержаниеСоединения = Новый Запрос;
			ЗапросУдержаниеСоединения.МенеджерВременныхТаблиц = мУдержательСоединенияБД;
			ЗапросУдержаниеСоединения.Текст = 
			"ВЫБРАТЬ ПЕРВЫЕ " + XMLСтрока(ИдентификаторТрассы) + "
			|	" + ИдентификаторТрассы + " КАК Число,
			|	0293843834892983011884662710842 КАК ДополнительноеЧисло
			|ПОМЕСТИТЬ ВТ_ПроизвольныеЗначения";
			ЗапросУдержаниеСоединения.Выполнить();
			ЗапросДанныеСессии = ПолучитьМакет("SQLServer_СкриптПолученияДанныхРасширенныхСобытий").ПолучитьТекст();
			ЗапросДанныеСессии = ирОбщий.СтрЗаменитьЛкс(ЗапросДанныеСессии, "{ИмяСессии}", "getSessionForQuery");
			РезультатЗапроса = ВыполнитьЗапросТрассыЭтойСУБД(ЗапросДанныеСессии, ИдентификаторТрассы);
			//! РезультатЗапроса.Колонки.Добавить("Data")
			Для Каждого СтрокаРезультата Из РезультатЗапроса Цикл
				ЧтениеXML = Новый ЧтениеXML;
				ЧтениеXML.УстановитьСтроку(СтрокаРезультата.Data);
				ПостроительDOM = Новый ПостроительDOM;
				ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
				Разыменователь = Новый РазыменовательПространствИменDOM(ДокументDOM);
				НайденныеСобытия = ДокументDOM.ВычислитьВыражениеXPath("/RingBufferTarget/event", ДокументDOM, Разыменователь, ТипРезультатаDOMXPath.Любой);
				УзелСобытие = НайденныеСобытия.ПолучитьСледующий();
				Пока УзелСобытие <> Неопределено Цикл
					СвойстваСобытия = Новый Соответствие;
					Для Каждого ЭлементСобытия Из УзелСобытие.ДочерниеУзлы Цикл
						ИмяСвойства = СокрЛП(ЭлементСобытия.Атрибуты.ПолучитьИменованныйЭлемент("name").Значение);
						ЗначениеСвойства = СокрЛП(ЭлементСобытия.ПоследнийДочерний.ТекстовоеСодержимое);
						Если Не ЗначениеЗаполнено(ЗначениеСвойства) Тогда
							ЗначениеСвойства = СокрЛП(ЭлементСобытия.ТекстовоеСодержимое);
						КонецЕсли;
						СвойстваСобытия.Вставить(ИмяСвойства, ЗначениеСвойства);
					КонецЦикла;
					ТекстЗапроса = СвойстваСобытия["batch_text"];
					Если Не ЗначениеЗаполнено(ТекстЗапроса) Тогда
						ТекстЗапроса = СвойстваСобытия["statement"];	
					КонецЕсли;
					Если Не ЗначениеЗаполнено(ТекстЗапроса) Тогда
						ТекстЗапроса = СвойстваСобытия["sql_text"];	
					КонецЕсли;
					Если Найти(ТекстЗапроса, "CREATE EVENT SESSION") > 0 Тогда
						УзелСобытие = НайденныеСобытия.ПолучитьСледующий();
						Продолжить;
					ИначеЕсли Найти(ТекстЗапроса, ИдентификаторТрассы) > 0 Тогда
						мИдентификаторСоединенияБД = СвойстваСобытия["session_id"];
						Прервать;
					КонецЕсли;
					УзелСобытие = НайденныеСобытия.ПолучитьСледующий();
				КонецЦикла;
			КонецЦикла;
			ЗапросУдалениеСессии = ПолучитьМакет("SQLServer_СкриптУдаленияСессииСбораДанных").ПолучитьТекст();
			ЗапросУдалениеСессии = ирОбщий.СтрЗаменитьЛкс(ЗапросУдалениеСессии, "{ИмяСессии}", "getSessionForQuery");
			ТекстыСУБД.Добавить(ЗапросУдалениеСессии);
		КонецЕсли;
		Если мИдентификаторСоединенияБД = Неопределено Тогда
			мУдержательСоединенияБД = Неопределено;
			ирОбщий.СообщитьЛкс("Не удалось определить идентификатор соединения СУБД. Трасса СУБД будет недоступна. Проверьте наличие права ""Alter any event session"" у пользователя СУБД");
		Иначе
			ЗапросСозданияСессии = ПолучитьМакет("SQLServer_СкриптСбораДанныхОЗапросах").ПолучитьТекст();
			ЗапросСозданияСессии = ирОбщий.СтрЗаменитьЛкс(ЗапросСозданияСессии, "{ИдентификаторСоединения}", XMLСтрока(мИдентификаторСоединенияБД));
			ТекстыСУБД.Добавить(ЗапросСозданияСессии);
		КонецЕсли; 
		ВыполнитьЗапросТрассыЭтойСУБД(ТекстыСУБД, ИдентификаторТрассы);
	КонецЕсли;
	Возврат Истина;
	
КонецФункции

Функция ВыполнитьЗапросТрассыЭтойСУБД(Знач ТекстыСУБД, Знач ИдентификаторТрассы = "{0}")
	Если ТипЗнч(ТекстыСУБД) = Тип("Строка") Тогда
		ТекстЗапроса = ТекстыСУБД;
	Иначе
		#Если Сервер И Не Сервер Тогда
			ТекстыСУБД = Новый Массив;
		#КонецЕсли
		ТекстЗапроса = ирОбщий.СтрСоединитьЛкс(ТекстыСУБД, ";" + Символы.ПС);
		ТекстыСУБД.Очистить();
	КонецЕсли;
	ИдентификаторСессии = XMLСтрока(НомерСеансаИнформационнойБазы());
	ТекстЗапроса = ирОбщий.СтрЗаменитьЛкс(ТекстЗапроса, "{ИдентификаторСессии}", ИдентификаторСессии,, Ложь);
	ТекстЗапроса = ирОбщий.СтрЗаменитьЛкс(ТекстЗапроса, "{ИдентификатоЗапроса}", ИдентификаторТрассы,, Ложь);
	ТекстЗапроса = ирОбщий.СтрЗаменитьЛкс(ТекстЗапроса, "{ИдентификаторБазы}", мИдентификаторБазыДанных,, Ложь);
	РезультатЗапроса = ирОбщий.ВыполнитьЗапросЭтойСУБДЛкс(ТекстЗапроса,,,,,, мСоединениеЭтойСУБД);
	Возврат РезультатЗапроса;

КонецФункции

Функция ИдентификаторБазыДанных()
	
	Если мИдентификаторБазыДанных <> Неопределено Тогда
		Возврат мИдентификаторБазыДанных;
	КонецЕсли; 
	РезультатЗапроса = ирОбщий.ВыполнитьЗапросЭтойСУБДЛкс("SELECT DB_ID() AS DatabaseId",,,,,, мСоединениеЭтойСУБД);
	мИдентификаторБазыДанных = РезультатЗапроса[0][0];
	Возврат мИдентификаторБазыДанных;

КонецФункции

Функция ЛиСерверMSSQLПоддерживаетТрассировку() Экспорт 
	
	Если мЛиСерверMSSQLПоддерживаетТрассировку <> Неопределено Тогда
		Возврат мЛиСерверMSSQLПоддерживаетТрассировку;
	КонецЕсли; 
	мЛиСерверMSSQLПоддерживаетТрассировку = Ложь;
	РезультатЗапроса = ирОбщий.ВыполнитьЗапросЭтойСУБДЛкс("SELECT @@VERSION AS SQLServerVersion",,,,,, мСоединениеЭтойСУБД);
	Если РезультатЗапроса <> Неопределено Тогда
		ВерсияСУБД = РезультатЗапроса[0][0];
		Если Найти(ВерсияСУБД, "Microsoft SQL Server 2008") = 0 Тогда
			мЛиСерверMSSQLПоддерживаетТрассировку = Истина;
		КонецЕсли; 
	КонецЕсли;
	Если Не мЛиСерверMSSQLПоддерживаетТрассировку Тогда
		ирОбщий.СообщитьЛкс("Используемая версия СУБД не поддерживает нужный режим трассировки (расширенные события). Требуется MSSQL 2012+");
	КонецЕсли;
	Возврат мЛиСерверMSSQLПоддерживаетТрассировку;

КонецФункции

Функция КончитьТрассу(выхТекущаяДатаСервера = Неопределено) Экспорт
	
	Если ЗначениеЗаполнено(ЭтотОбъект.КонецПериодаКлиента) Тогда
		Возврат Ложь;
	КонецЕсли; 
	Если мИдентификаторСоединенияБД <> Неопределено Тогда
		Попытка
			ЗапросДанныеСессии = ПолучитьМакет("SQLServer_СкриптПолученияДанныхРасширенныхСобытий").ПолучитьТекст();
			ЗапросДанныеСессии = ирОбщий.СтрЗаменитьЛкс(ЗапросДанныеСессии, "{ИмяСессии}", "getQueriesInfo");
			РезультатБД = ВыполнитьЗапросТрассыЭтойСУБД(ЗапросДанныеСессии);
			Если РезультатБД.Количество() = 0 Тогда
				ирОбщий.СообщитьЛкс("Трасса СУБД пуста. Вероятно была получена другим экземляром инструмента.");
			Иначе
				мТрассаСУБДXML = РезультатБД[0][0];
			КонецЕсли; 
		Исключение
			ирОбщий.СообщитьЛкс(ОписаниеОшибки());
		КонецПопытки;
		УдалитьСессииТрассировкиСУБД();
	КонецЕсли;
	мУдержательСоединенияБД = Неопределено;
	//мИдентификаторСоединенияБД = Неопределено;
	Если ЗначениеЗаполнено(НачалоПериодаСервера) Тогда
		ЗаписатьМаркерВТехножурнал("КонецТрассы_" + мИдентификаторТрассы,,, КонецПериодаКлиента, КонецПериодаСервера);
		выхТекущаяДатаСервера = КонецПериодаСервера;
	КонецЕсли;
	Возврат Истина;
	
КонецФункции

Процедура УдалитьСессииТрассировкиСУБД()
	
	ЗапросОчисткиСессий = ПолучитьМакет("SQLServer_СкриптУдаленияСессийБазы").ПолучитьТекст();
	ВыполнитьЗапросТрассыЭтойСУБД(ЗапросОчисткиСессий);

КонецПроцедуры

Процедура ЗагрузитьТрассуСУБД(СПланамиЗапросов = Истина)  Экспорт 
	
	Если Не ЗначениеЗаполнено(мТрассаСУБДXML) Тогда
		Возврат;
	КонецЕсли; 
	ИдентификаторСессии = XMLСтрока(НомерСеансаИнформационнойБазы());
	НомерПоПорядку = 1;
	ЧтениеXML = Новый ЧтениеXML;
	ЧтениеXML.УстановитьСтроку(мТрассаСУБДXML);
	ПостроительDOM = Новый ПостроительDOM;
	ДокументDOM = ПостроительDOM.Прочитать(ЧтениеXML);
	Разыменователь = Новый РазыменовательПространствИменDOM(ДокументDOM);
	РезультатПоискаСобытий = ДокументDOM.ВычислитьВыражениеXPath("/RingBufferTarget/event", ДокументDOM, Разыменователь, ТипРезультатаDOMXPath.Любой);
	МассивОператоров = Новый Массив;
	МассивИДПланов = Новый Массив;
	УзелСобытие = РезультатПоискаСобытий.ПолучитьСледующий();  
	#Если Сервер И Не Сервер Тогда
		мТаблицаЖурнала = ТаблицаЖурнала;
	#КонецЕсли
	НачальныйИндексСтроки = мТаблицаЖурнала.Количество();
	Пока УзелСобытие <> Неопределено Цикл
		ИмяСобытия = СокрЛП(УзелСобытие.Атрибуты.ПолучитьИменованныйЭлемент("name").ТекстовоеСодержимое);
		СвойстваСобытия = Новый Соответствие;
		Для Каждого ЭлементСобытия Из УзелСобытие.ДочерниеУзлы Цикл
			ИмяСвойства = СокрЛП(ЭлементСобытия.Атрибуты.ПолучитьИменованныйЭлемент("name").Значение);
			ЗначениеСвойства = СокрЛП(ЭлементСобытия.ПоследнийДочерний.ТекстовоеСодержимое);
			Если НЕ ЗначениеЗаполнено(ЗначениеСвойства) Тогда
				ЗначениеСвойства = СокрЛП(ЭлементСобытия.ТекстовоеСодержимое);
			КонецЕсли;
			СвойстваСобытия.Вставить(ИмяСвойства, ЗначениеСвойства);
		КонецЦикла;
		ТекстЗапроса = СвойстваСобытия["statement"];
		Если НЕ ЗначениеЗаполнено(ТекстЗапроса) Тогда
			ТекстЗапроса = СвойстваСобытия["batch_text"];	
		КонецЕсли;
		Если НЕ ЗначениеЗаполнено(ТекстЗапроса) Тогда
			ТекстЗапроса = СвойстваСобытия["sql_text"];	
		КонецЕсли;
		Если Найти(ТекстЗапроса, "CREATE EVENT SESSION") > 0 Тогда
			УзелСобытие = РезультатПоискаСобытий.ПолучитьСледующий();
			Продолжить;
		КонецЕсли;
		Если ИмяСобытия = "rpc_completed" ИЛИ ИмяСобытия = "sql_batch_completed" Тогда
			КонецМилисекунды = "";
			ДатаСобытияСтрока = СокрЛП(УзелСобытие.Атрибуты.ПолучитьИменованныйЭлемент("timestamp").ТекстовоеСодержимое);
			ДатаСобытияСтрока = ирОбщий.ПервыйФрагментЛкс(ДатаСобытияСтрока, "Z");
			ДатаСобытия = МестноеВремя(ПреобразоватьКДате(ДатаСобытияСтрока, КонецМилисекунды));
			ДлительностьМикросекунд = СвойстваСобытия["duration"];
			ДатаНачала = ДатаСобытия - Цел(ДлительностьМикросекунд / 1000000);
			МоментВремени = Формат(ДатаСобытия, "ДФ=ггггММддЧЧммсс") + КонецМилисекунды + Формат(НомерПоПорядку, "ЧЦ=3; ЧВН=");
			НачалоМилисекунды = КонецМилисекунды - Цел(ДлительностьМикросекунд / 1000);
			Если НачалоМилисекунды < 0 Тогда
				ДатаНачала = ДатаНачала - 1;
				НачалоМилисекунды = 1000 + НачалоМилисекунды;
			КонецЕсли; 
			МоментВремениНачала = Формат(ДатаНачала, "ДФ=ггггММддЧЧммсс") + НачалоМилисекунды + Формат(НомерПоПорядку, "ЧЦ=3; ЧВН=");
			СтрокаТаблицы = мТаблицаЖурнала.Добавить();
			СтрокаТаблицы.МоментВремени = МоментВремени;
			СтрокаТаблицы.Дата = ДатаСобытия;
			СтрокаТаблицы.ДатаНачала = ДатаНачала;
			СтрокаТаблицы.Длительность = ДлительностьМикросекунд / 1000;
			СтрокаТаблицы.НаСервере = Истина;
			СтрокаТаблицы.ТипПроцессаОС = "sqlservr";
			СтрокаТаблицы.МоментВремениНачала = МоментВремениНачала;
			СтрокаТаблицы.CPUTime = СвойстваСобытия["cpu_time"] / 1000;
			СтрокаТаблицы.ЛогическиПрочитано = СвойстваСобытия["logical_reads"];
			СтрокаТаблицы.ФизическиПрочитано = СвойстваСобытия["physical_reads"];
			СтрокаТаблицы.ЛогическиЗаписано = СвойстваСобытия["writes"];
			//СтрокаТаблицы.ИмяОбъекта = СвойстваСобытия["object_name"];
			//СтрокаТаблицы.Приложение = СвойстваСобытия["client_app_name"];
			//СтрокаТаблицы.ИдентификаторЯдраПроцессора = СвойстваСобытия["cpu_id"];
			//СтрокаТаблицы.Соединение_ = СвойстваСобытия["client_pid"];
			//СтрокаТаблицы.Системный = ПреобразоватьКБулево(СвойстваСобытия["is_system"]);
			СтрокаТаблицы.ЧислоСтрок = СвойстваСобытия["row_count"];
			СтрокаТаблицы.ТекстСУБД = ТекстЗапроса;
			Если МассивОператоров.Количество() = 1 Тогда
				ИдентификаторПланаЗапроса = МассивОператоров.Получить(0).Получить("plan_handle");
				НачалоИдентификатораПланаЗапроса = "<plan handle='0x";
				Если Найти(ИдентификаторПланаЗапроса, НачалоИдентификатораПланаЗапроса) > 0 Тогда
					ДлинаИдентификатораПланаЗапроса = СтрДлина(ИдентификаторПланаЗапроса) - СтрДлина(НачалоИдентификатораПланаЗапроса) - 3;
					ИдентификаторПланаЗапроса = Сред(ИдентификаторПланаЗапроса, СтрДлина(НачалоИдентификатораПланаЗапроса) + 1, ДлинаИдентификатораПланаЗапроса);
				КонецЕсли;
				СтрокаТаблицы.ПланСУБД = "0x" + ВРег(ИдентификаторПланаЗапроса);
				МассивИДПланов.Добавить(СтрокаТаблицы.ПланСУБД);
			КонецЕсли;
			СтрокаТаблицы.Событие = ИмяСобытия;
			СтрокаТаблицы.Дата = ДатаСобытия;
			МассивОператоров.Очистить();
			НомерПоПорядку = НомерПоПорядку + 1;
		ИначеЕсли СПланамиЗапросов Тогда 
			МассивОператоров.Добавить(СвойстваСобытия);
		КонецЕсли;
		УзелСобытие = РезультатПоискаСобытий.ПолучитьСледующий();
	КонецЦикла;
	Если МассивИДПланов.Количество() > 0 Тогда
		ТекстЗапроса = ПолучитьМакет("SQLServer_СкриптЧтенияПланаЗапроса").ПолучитьТекст();
		ТекстЗапроса = ирОбщий.СтрЗаменитьЛкс(ТекстЗапроса, "{ИдентификаторПланаЗапроса}", ирОбщий.СтрСоединитьЛкс(МассивИДПланов));
		ТаблицаПлановЗапросов = ВыполнитьЗапросТрассыЭтойСУБД(ТекстЗапроса);
		//! ТаблицаПлановЗапросов.Колонки.Добавить("query_plan")
		//! ТаблицаПлановЗапросов.Колонки.Добавить("plan_handle")
		Для Индекс = НачальныйИндексСтроки По мТаблицаЖурнала.Количество() - 1 Цикл
			СтрокаРезультата = ТаблицаПлановЗапросов.Найти(мТаблицаЖурнала[Индекс].ПланСУБД, "plan_handle");   
			Если СтрокаРезультата <> Неопределено Тогда
				мТаблицаЖурнала[Индекс].ПланСУБД = СтрокаРезультата.query_plan;
			Иначе
				мТаблицаЖурнала[Индекс].ПланСУБД = МаркерПланЗапросаНеНайден();
			КонецЕсли; 
		КонецЦикла;
	КонецЕсли;
	//ЗапросУдалениеСессии = ПолучитьМакет("SQLServer_СкриптУдаленияСессииСбораДанных").ПолучитьТекст();
	//ЗапросУдалениеСессии = ирОбщий.СтрЗаменитьЛкс(ЗапросУдалениеСессии, "{ИмяСессии}", "getQueriesInfo");
	//ВыполнитьЗапросТрассыСУБД(ЗапросУдалениеСессии);
	мНепустыеКолонкиЖурнала.Очистить();
	ОпределитьНепустыеКолонки();

КонецПроцедуры

Функция МаркерПланЗапросаНеНайден() Экспорт 
	
	Возврат "<Не найден в кэше СУБД>";

КонецФункции

Функция ПреобразоватьКБулево(Знач ИсходноеЗначение)
	Если ТипЗнч(ИсходноеЗначение) = Тип("Строка") Тогда
		ИсходноеЗначение = ВРег(ИсходноеЗначение);
		Если ИсходноеЗначение = "TRUE" Тогда
			Возврат Истина;
		Иначе
			Возврат Ложь;
		КонецЕсли;
	ИначеЕсли ТипЗнч(ИсходноеЗначение) = Тип("Булево") Тогда
		Возврат ИсходноеЗначение;
	ИначеЕсли ТипЗнч(ИсходноеЗначение) = Тип("Число") Тогда 
		Возврат (ИсходноеЗначение > 0);
	Иначе
		Возврат Ложь;
	КонецЕсли;
КонецФункции

Функция ПреобразоватьКДате(Знач ИсходноеЗначение, выхТысячнаяДоляСекунды = "")
	
	ДатаСтрока = СтрЗаменить(ИсходноеЗначение, "-", "");
	ДатаСтрока = СтрЗаменить(ДатаСтрока, ":", "");
	ДатаСтрока = СтрЗаменить(ДатаСтрока, "T", "");
	Фрагменты = ирОбщий.СтрРазделитьЛкс(ДатаСтрока);
	выхТысячнаяДоляСекунды = Фрагменты[1];
	ДатаСтрока = Фрагменты[0];
	ДатаЗначение = Неопределено;
	Попытка
		ДатаЗначение = Дата(ДатаСтрока);
	Исключение
		ДатаЗначение = Дата(1,1,1);
	КонецПопытки;
	Возврат ДатаЗначение;
	
КонецФункции

Функция ПолучитьТекстSQLДляПоискаВТехножурнале(Текст) Экспорт 
	
	мРегВыражениеМета.Pattern = "([^A-ZА-ЯЁ_0-9]|^)(?:@P\d+)";
	Результат = мРегВыражениеМета.Заменить(Текст, "$1?");
	Возврат Результат;
	
КонецФункции

#Если Клиент Тогда
	
Функция ПоказатьТрассу(УдалитьДополнительныеСобытия = Истина, ИменаНеполезныхКолонок = "", ЖдатьСекунд = 2, СмещениеБазовогоУровня = Неопределено, ЗагружатьТолькоТекущийСеанс = Ложь,
	ЗагружатьЖурналКлиента = Истина, ЛиТрассаПоПользователю = Ложь) Экспорт
	
	Если ЭтотОбъект.КонецПериодаСервера = Неопределено Тогда 
		Предупреждение("Сформируйте трассу заново");
		Возврат Неопределено;
	КонецЕсли; 
	ОчиститьТаблицуЖурнала();
	лКаталогЖурнала = КаталогЖурнала;
	мЛиТрассаПоПользователю = ЛиТрассаПоПользователю Или ЗагружатьТолькоТекущийСеанс;
	ФормаАнализа = ПолучитьФорму();
	ФормаАнализа.ЭтоТрасса = Истина;
	ФормаАнализа.Открыть();
	ФормаАнализа.ЗагружатьТолькоТекущийСеанс = ЗагружатьТолькоТекущийСеанс;
	ФормаАнализа.ЗагружатьЖурналКлиента = ЗагружатьЖурналКлиента;
	ФормаАнализа.ЗагружатьЖурналСервера = Не ирКэш.ЛиФайловаяБазаЛкс();
	ВыраниватьДатуПоСерверу = ирОбщий.ВосстановитьЗначениеЛкс("ирАнализТехножурнала.ВыраниватьДатуПоСерверу") = Истина;
	Если ВыраниватьДатуПоСерверу Тогда
		ФормаАнализа.КонецПериода = КонецПериодаСервера;
		ФормаАнализа.НачалоПериода = НачалоПериодаСервера;
	Иначе
		ФормаАнализа.КонецПериода = КонецПериодаКлиента;
		ФормаАнализа.НачалоПериода = НачалоПериодаКлиента;
	КонецЕсли;
	ФормаАнализа.КаталогЖурнала = лКаталогЖурнала;
	ФормаАнализа.ПериодПоследниеМинуты = 0;
	ФормаАнализа.ОбновитьТаблицуЖурнала(УдалитьДополнительныеСобытия, ИменаНеполезныхКолонок, Истина, ЖдатьСекунд, СмещениеБазовогоУровня);
	ФормаАнализа.УстановитьРежимИтогов(ТаблицаЖурнала.Количество() > 30);
	ФормаАнализа.ОбновитьДоступность();
	ФормаАнализа.ЭлементыФормы.ПанельНастройки.ТекущаяСтраница = ФормаАнализа.ЭлементыФормы.ПанельНастройки.Страницы.Анализ;
	ФормаАнализа.УстановитьБесполезныеКолонки(ИменаНеполезныхКолонок);
	мЛиТрассаПоПользователю = Ложь;
	Возврат ФормаАнализа;

КонецФункции

Функция ЗагрузитьТрассуТехножурнала(Знач ЗагружатьЖурналКлиента = Истина, Знач ЗагружатьЖурналСервера = Истина, Знач ЖдатьСекунд = 1) Экспорт 
	мЛиТрассаПоПользователю = Истина;
	ЭтотОбъект.ЗагружатьЖурналКлиента = ЗагружатьЖурналКлиента;
	ЭтотОбъект.ЗагружатьЖурналСервера = ЗагружатьЖурналСервера;
	ЖурналПрочитан = ПрочитатьСобственныйЖурналДвухСторон(,, Истина, ЖдатьСекунд,, НомерСеансаИнформационнойБазы());
	мЛиТрассаПоПользователю = Ложь;
	Возврат ЖурналПрочитан;
КонецФункции

Функция ОчиститьТаблицуЖурнала() Экспорт

	мНепустыеКолонкиЖурнала = Новый Структура();
	ТаблицаЖурнала.Очистить();
	мКартыФайлов.Очистить();
	Возврат Неопределено;

КонецФункции

Функция ОткрытьТекстБДВКонверторе(ТекстБД, СразуПеревестиВМета = Истина, КлючУникальности = Неопределено, ЭтоТекстSDBL = Истина) Экспорт

	ФормаЗапроса = ПолучитьФорму("КонверторТекстаСУБД", , КлючУникальности);
	ФормаЗапроса.ЭтоТекстSDBL = ЭтоТекстSDBL;
	ФормаЗапроса.ЭлементыФормы.ТекстБД.УстановитьТекст(ТекстБД);
	Если СразуПеревестиВМета Тогда
		ФормаЗапроса.ПереводитьВМета = Истина;
	КонецЕсли;
	ФормаЗапроса.Открыть();
	Возврат Неопределено;

КонецФункции

Процедура ОткрытьТекстБДСтрокиЖурнала(Знач ВыбраннаяСтрока, Знач ИмяСвойства, Знач ПредложитьПеревод = Ложь) Экспорт 
	
	ИмяСвойстваБезМета = ПолучитьИмяСвойстваБезМета(ИмяСвойства);
	Если Истина
		И ирОбщий.СтрокиРавныЛкс(ИмяСвойстваБезМета, "ПланСУБД")
		И ирОбщий.СтрокиРавныЛкс(ВыбраннаяСтрока.Событие, "DBMSSQL")
	Тогда
		ТекстПланаЗапроса = ВыбраннаяСтрока[ИмяСвойства];
		Если ПредложитьПеревод И ИмяСвойстваБезМета = ИмяСвойства Тогда
			Ответ = Вопрос("Перевести план запроса в термины метаданных?", РежимДиалогаВопрос.ДаНет,, КодВозвратаДиалога.Да);
		КонецЕсли;
		Если Ложь
			Или ТекстПланаЗапроса = РезультатПереводаСлишкомБольшогоТекста() 
			Или Ответ = КодВозвратаДиалога.Да
		Тогда
			ТекстПланаЗапроса = ПеревестиТекстБДВТерминыМетаданных(ВыбраннаяСтрока.ПланСУБД, , , ВыбраннаяСтрока.Событие,, 20000000);
		КонецЕсли; 
		ФормаПросмотра = ПолучитьФорму("ПланЗапросаMSSQL", , "" + ВыбраннаяСтрока.МоментВремени);
		ФормаПросмотра.Текст = ТекстПланаЗапроса;
		ФормаПросмотра.Открыть();
	Иначе
		МаркерSDBL = "SDBL";
		//ЭтоТекстSDBL = ирОбщий.СтрокиРавныЛкс(МаркерSDBL, Прав(ИмяСвойстваБезМета, СтрДлина(МаркерSDBL)));
		ЭтоТекстSDBL = Не мСвойстваСИменамиБД[ИмяСвойстваБезМета];
		ОткрытьТекстБДВКонверторе(ВыбраннаяСтрока[ИмяСвойстваБезМета], Не ирОбщий.СтрокиРавныЛкс(ИмяСвойстваБезМета, ИмяСвойства),
				"" + ВыбраннаяСтрока.МоментВремени + ИмяСвойства, ЭтоТекстSDBL);
	КонецЕсли;

КонецПроцедуры

Процедура ОткрытьПланЗапросаСУБД(Знач ПланЗапроса) Экспорт 
	
	ИмяВременногоФайла = ПолучитьИмяВременногоФайла("sqlplan");
	//СтандартнаяОбработка = Ложь;
	Если ЗначениеЗаполнено(ПланЗапроса) Тогда
		ПланЗапроса = ОтформатироватьТекстXML(ПланЗапроса);
		Ответ = Вопрос("Перевести план запроса в термины метаданных?", РежимДиалогаВопрос.ДаНет,, КодВозвратаДиалога.Да);
		Если Ответ = КодВозвратаДиалога.Да Тогда
			ПланЗапроса = ирКэш.АнализТехножурналаЛкс().ПеревестиТекстБДВТерминыМетаданных(ПланЗапроса,,, "DBMSSQL",, 999999);
		КонецЕсли;
		ТекстовыйДокумент = Новый ТекстовыйДокумент;
		ТекстовыйДокумент.УстановитьТекст(ПланЗапроса);
		ТекстовыйДокумент.Записать(ИмяВременногоФайла);
		ЗапуститьПриложение(ИмяВременногоФайла);
	КонецЕсли;

КонецПроцедуры

Функция ОтформатироватьТекстXML(ТекстXML) Экспорт 

	Если Ложь
		Или Не ЗначениеЗаполнено(ТекстXML) 
		Или ТекстXML = МаркерПланЗапросаНеНайден()
	Тогда
		Возврат "";
	КонецЕсли; 
	Дом = ирОбщий.ТекстВДокументDOMЛкс("<_>" + ТекстXML + "</_>");
	ЗаписьXML = Новый ЗаписьXML;
	ЗаписьДОМ = Новый ЗаписьDOM;
	ОтформатированныйТекст = "";
	Для Каждого ДочернийУзел Из ДОМ.ПервыйДочерний.ДочерниеУзлы Цикл
		ЗаписьXML.УстановитьСтроку();
		ЗаписьДОМ.Записать(ДочернийУзел, ЗаписьXML);
		Если ОтформатированныйТекст <> "" Тогда
			ОтформатированныйТекст = ОтформатированныйТекст + Символы.ПС;
		КонецЕсли;
		ОтформатированныйТекст = ОтформатированныйТекст + ЗаписьXML.Закрыть();
	КонецЦикла;
	Возврат ОтформатированныйТекст;

КонецФункции

Процедура ОткрытьНастройкуТехножурналаДляРегистрацииВыполненияЗапроса(Знач ТекстБД, ЭтоТекстSDBL = Ложь, ТипСУБД = Неопределено, ИмяБазы = "") Экспорт 
    
	Если Не ЭтоТекстSDBL Тогда
		ТекстБД = ПолучитьТекстSQLДляПоискаВТехножурнале(ТекстБД);
	КонецЕсли; 
	Если СтрДлина(ТекстБД) > 1000 Тогда
		ТекстБД = Лев(ТекстБД , 1000) + "%";
	КонецЕсли; 
	ТекстБД = ПолучитьШаблонТекстаБД(ТекстБД, "%");
	ТекстБД = СтрЗаменить(ТекстБД, Символы.ПС, "%");
	ТекстБД = СтрЗаменить(ТекстБД, "%%", "%");
	ФормаНастройки = ирКлиент.ПолучитьФормуЛкс("Обработка.ирНастройкаТехножурнала.Форма");
	ФормаНастройки.Открыть();
	ФормаНастройки.НаСервере = Не ирКэш.Получить().ЭтоФайловаяБаза;
	ФормаНастройки.ПриИзмененииПравилаПолученияФайлаНастройки();
	Ответ = Вопрос("Хотите создать новую настройку?", РежимДиалогаВопрос.ДаНет);
	Если Ответ = КодВозвратаДиалога.Да Тогда
		ФормаНастройки.ЗагрузитьФайлНастройки("ШаблонЗаписьИсключительныхСитуаций", Истина, Истина, Ложь);
	КонецЕсли;
	СтрокаКаталога = ФормаНастройки.ТабличноеПолеЖурналы[0];
	ФормаНастройки.ЭлементыФормы.ТабличноеПолеЖурналы.ТекущаяСтрока = СтрокаКаталога;
	ФормаЖурнала = ФормаНастройки.ОткрытьФормуРедактированияЖурнала();
	Если ЭтоТекстSDBL Тогда
		УстановитьОтборПоСвойствуСобытияВФормеЖурнала(ФормаЖурнала, "SDBL", "sdbl", ТекстБД, , ИмяБазы);
	Иначе
		Если Не ЗначениеЗаполнено(ТипСУБД) Тогда
			ВызватьИсключение "Для текста СУБД необходимо указать тип СУБД";
		КонецЕсли; 
		УстановитьОтборПоСвойствуСобытияВФормеЖурнала(ФормаЖурнала, ТипСУБД, "sql", ТекстБД, , ИмяБазы);
	КонецЕсли;

КонецПроцедуры

// Сравнение - Строка - как оно задается в настройке техножурнала (eq, ne, like и т.д.)
Процедура УстановитьОтборПоСвойствуСобытияВФормеЖурнала(ФормаЖурнала, ИмяСобытия, Знач ИмяСвойства, Знач ШаблонЗапроса, Сравнение = "like", Знач ИмяБазы = "") Экспорт 
	
	ТабличноеПолеСписокСобытий = ФормаЖурнала.ЭлементыФормы.ТабличноеПолеСписокСобытий;
	ТабличноеПолеСписокСобытий.ВыделенныеСтроки.Очистить();
	СтрокаСобытия = ФормаЖурнала.УстановитьРегистрациюСобытия(ИмяСобытия, Истина);
	ТабличноеПолеСписокСобытий.ТекущаяСтрока = СтрокаСобытия;
	ФормаЖурнала.УстановитьЭлементОтбораВВыделенныхГруппахИ(ИмяСвойства, ШаблонЗапроса, Сравнение);
	Если Истина
		И ЗначениеЗаполнено(ИмяБазы) 
		И Не ирКэш.ЛиФайловаяБазаЛкс() 
	Тогда
		// К сожалению в файловой СУБД это свойство не заполняется
		//ФормаЖурнала.КП_ДетальныйФильтрСобытийТекущаяБаза();
		ФормаЖурнала.УстановитьЭлементОтбораВВыделенныхГруппахИ("p:processname", ИмяБазы);
	КонецЕсли; 

КонецПроцедуры

#КонецЕсли 

Функция ПолучитьВариантПросмотраТекстПоИмениРеквизита(ИмяРеквизита) Экспорт

	Если Ложь
		Или мСвойстваСИменамиБД.Свойство(ПолучитьИмяСвойстваБезМета(ИмяРеквизита)) 
		Или ирОбщий.СтрокиРавныЛкс(ИмяРеквизита, "ТекстЗапроса1С")
	Тогда
		ВариантПросмотра = "ЯзыкЗапросов";
	ИначеЕсли ирОбщий.СтрокиРавныЛкс(ИмяРеквизита, "Контекст") <> Неопределено Тогда
		ВариантПросмотра = "ВстроенныйЯзык";
	Иначе
		ВариантПросмотра = "Компактный";
	КонецЕсли; 
	Возврат ВариантПросмотра;

КонецФункции

Функция ПреобразоватьЗначениеВSDBL(Ссылка) Экспорт
	
	СтруктураБД = ирКэш.СтруктураХраненияБДЛкс(, мАдресЧужойСхемыБД);
	Если ТипЗнч(Ссылка) = Тип("Строка") Тогда
		Текст = "";
	Иначе
		СтруктураПоиска = Новый Структура("Метаданные, Назначение", Ссылка.Метаданные().ПолноеИмя(), "Основная");
		СтрокаТаблицы = СтруктураБД.НайтиСтроки(СтруктураПоиска)[0];
		мРегВыражение = ирКэш.Получить().мРегВыражение;
		мРегВыражение.Pattern = "\d+";
		РезультатПоиска = мРегВыражение.НайтиВхождения(СтрокаТаблицы.ИмяТаблицыХранения);
		Текст = РезультатПоиска[0].Value + ":" + ирОбщий.ПолучитьГУИДИнверсныйИзПрямогоЛкс("" + Ссылка.УникальныйИдентификатор());
	КонецЕсли; 
	Возврат Текст;
	
КонецФункции

Функция СтруктураЗапросаИзТекстаБД(Знач ТекстБД, Знач ТипСУБД = "", Знач ПересобратьТекст = Ложь, Знач ПереводСПотерейСинтаксиса = Истина, Знач ПереводитьВМета = Истина, Знач ЗаменитьHexЛитералы = Ложь) Экспорт
	
	Текст = ТекстБД;
	//ТаблицаПараметров = Новый ТаблицаЗначений;
	//ТаблицаПараметров.Колонки.Добавить("Значение1С", Новый ОписаниеТипов("Строка"));
	//ТаблицаПараметров.Колонки.Добавить("Значение");
	//ТаблицаПараметров.Колонки.Добавить("Имя", Новый ОписаниеТипов("Строка"));
	ТаблицаПараметров = ПараметрыЗапроса.ВыгрузитьКолонки();
	ТаблицаПараметров.Индексы.Добавить("Значение");
	ТаблицаПараметров.Индексы.Добавить("Имя");
	ТаблицаПараметров.Колонки.Удалить("ТипЗначения1С");
	ТаблицаПараметров.Колонки.Добавить("ТипЗначения1С");
	мРегВыражение = мПлатформа.мРегВыражение;
	мРегВыражение.Global = Истина;
	мРегВыражение.Multiline = Ложь;
    Если ЗначениеЗаполнено(ТипСУБД) Тогда
		Если ПереводитьВМета Тогда
			мРегВыражение.Pattern = "TRef\s*=\s*(0x[\da-z]+)"; // замена обращений к типам
			ВхожденияТипов = мРегВыражение.НайтиВхождения(Текст);
			ОбработанныеОбращения = Новый Соответствие;
			Для Каждого Вхождение Из ВхожденияТипов Цикл
				ЗначениеСУБД = Вхождение.Value;
				Если ОбработанныеОбращения[ЗначениеСУБД] = Неопределено Тогда
					ПолноеИмяМД = ирОбщий.МетаданныеПоНомеруСсылочнойТаблицыЛкс(ирОбщий.СтрокаHEXtoINTЛкс(Вхождение.SubMatches(0)), мАдресЧужойСхемыБД);
					Если ПолноеИмяМД <> Неопределено Тогда
						ЗаменаСтроки = "TRef = " + ПолноеИмяМД;
						ОбработанныеОбращения[ЗначениеСУБД] = ЗаменаСтроки;
						Текст = СтрЗаменить(Текст, ЗначениеСУБД, ЗаменаСтроки);
					Иначе
						ОбработанныеОбращения[ЗначениеСУБД] = 1;
					КонецЕсли; 
				КонецЕсли; 
			КонецЦикла;
		КонецЕсли; 
		мРегВыражение.Pattern = "^\s*\(@P\d+\s+\w+(?:\(\d+(?:,\d+)?\))?(,@P\d+\s+\w+(?:\(\d+(?:,\d+)?\))?)*\)"; // для удаления расположенных в начале типов параметров MSSQL
		Текст = мРегВыражение.Заменить(Текст, "");
		// sp_executesql
		// {ОписаниеРегулярногоВыражения.Начало} конструктор из подсистемы "Инструменты разработчика" (http://devtool1c.ucoz.ru)
		// Перем шЗначениеПараметра, шТипПараметра, шКорень;
		// {Шаблон.Начало}
		// + <шЗначениеПараметра> = (N?'([^']*)'|[^',]+)
		// + <шТипПараметра> = @([\w]+) ([^']+?)(?='|$|,@)
		// + <шКорень> = exec sp_executesql N'([^']+)',N'((?:<шТипПараметра>,)*<шТипПараметра>)'((?:,<шЗначениеПараметра>)+)
		// {Шаблон.Конец}
		шЗначениеПараметра = "(N?'([^']*)'|[^',]+)";
		шТипПараметра = "@([\w]+) ([^']+?)(?='|$|,@)";
		шКорень = "exec sp_executesql N'([^']+)',N'((?:" + шТипПараметра + ",)*" + шТипПараметра + ")'((?:," + шЗначениеПараметра + ")+)";
		// {ОписаниеРегулярногоВыражения.Конец}
		ВхожденияПараметра = ирОбщий.НайтиРегВыражениеЛкс(Текст, шКорень, "ТекстЗапроса, ТипыПараметров", Ложь,,, Ложь);
		Если ВхожденияПараметра.Количество() > 0 Тогда
			Вхождение = ВхожденияПараметра[0];
			Текст = Вхождение.ТекстЗапроса;
			ВложенныеВхождения = ирОбщий.НайтиРегВыражениеЛкс(Вхождение.ТипыПараметров, шТипПараметра, "Имя, Тип",,,, Ложь,,,, ВложенныеВхождения);
			Для каждого ВложенноеВхождение Из ВложенныеВхождения Цикл
				СтрокаПараметра = ТаблицаПараметров.Добавить();
				СтрокаПараметра.Имя = ВложенноеВхождение.Имя;
				СтрокаПараметра.ТипЗначения = ВложенноеВхождение.Тип; 
				Текст = ирОбщий.ЗаменитьРегВыражениеЛкс(Текст, "@" + СтрокаПараметра.Имя + "(?!\d)", "?");
			КонецЦикла;
			ВложенныеВхождения = ирОбщий.НайтиРегВыражениеЛкс(Вхождение.Группы[6], шЗначениеПараметра,,,,, Ложь,,,, ВложенныеВхождения);
			ИндексПараметра = 0;
			Для каждого ВложенноеВхождение Из ВложенныеВхождения Цикл
				СтрокаПараметра = ТаблицаПараметров[ИндексПараметра];
				ИндексПараметра = ИндексПараметра + 1;
				СтрокаПараметра.Значение = ВложенноеВхождение.ТекстВхождения;
			КонецЦикла;
		КонецЕсли;
    Иначе
		мРегВыражение.Pattern = "([A-F0-9]+):([A-F0-9]+|0x[A-F0-9]+)"; // замена ссылочных констант
		Вхождения = мРегВыражение.НайтиВхождения(Текст);
		Для Каждого Вхождение Из Вхождения Цикл
			ЗначениеSDBL = Вхождение.Value;
			Если СтрДлина(ЗначениеSDBL) < 32 Тогда
				// Защита от частей литерала Дата
				Продолжить;
			КонецЕсли; 
			Если ТаблицаПараметров.Найти(ЗначениеSDBL, "Значение") = Неопределено Тогда
				СтрокаПараметра = ирОбщий.НайтиДобавитьПараметрСсылкуВТаблицуЛкс(ТаблицаПараметров,,, ЗначениеSDBL);
				СтрокаПараметра.Значение1С = ирОбщий.ПреобразоватьЗначениеИзSDBLЛкс(ЗначениеSDBL, мАдресЧужойСхемыБД, Истина);
				СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов(ирОбщий.ЗначенияВМассивЛкс(ТипЗнч(СтрокаПараметра.Значение1С)));
				Если ПереводитьВМета Тогда
					Текст = СтрЗаменить(Текст, ЗначениеSDBL, "&" + СтрокаПараметра.Имя);
				КонецЕсли; 
			КонецЕсли; 
		КонецЦикла;
	КонецЕсли;
	Если ЗаменитьHexЛитералы Тогда
		мРегВыражение.Pattern = "0x[\da-z]+"; // замена значений констант
		Вхождения = мРегВыражение.НайтиВхождения(Текст);
		СоответствиеКонстант = Новый Соответствие;
		Для Каждого Вхождение Из Вхождения Цикл
			ЗначениеСУБД = Вхождение.Value;
			Если СоответствиеКонстант[ЗначениеСУБД] = Неопределено Тогда
				ЗаменаСтроки = "_" + ЗначениеСУБД;
				СоответствиеКонстант[ЗначениеСУБД] = ЗаменаСтроки;
				Текст = СтрЗаменить(Текст, ЗначениеСУБД, ЗаменаСтроки);
			КонецЕсли; 
		КонецЦикла;
	КонецЕсли; 
	//мРегВыражение.Pattern = "#(T[\d_A-Z]+)"; // имена временных таблиц
	//Текст = мРегВыражение.Заменить(Текст, "_$1");
	Если ПереводСПотерейСинтаксиса Тогда
		мРегВыражение.Pattern = "\{ts '(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)'\}"; // литералы Дата DBV8DBEng
		Текст = мРегВыражение.Заменить(Текст, "DATETIME($1,$2,$3,$4,$5,$6)");
		Если ПереводитьВМета Тогда
			мРегВыражение.Pattern = "([^""])N'([^']*)'"; // для замены юникодных литералов типа Строка MSSQL
			Текст = мРегВыражение.Заменить(Текст, "$1""$2""");
		КонецЕсли;
	КонецЕсли;
	ТаблицаТаблиц = Новый ТаблицаЗначений;
	ВсеСинонимы = Новый Структура;
	ТекстМета = ПеревестиТекстБДВТерминыМетаданных(Текст,,, ТипСУБД, ТаблицаТаблиц, 0, ПереводСПотерейСинтаксиса, ПереводитьВМета,, ВсеСинонимы);
	Если Не ПереводитьВМета Тогда
		ТекстМета = Текст;
	КонецЕсли; 
	ТаблицаТаблиц.Сортировать("ИмяМета");
	ЧастьПараметров = "";
	Если ЗначениеЗаполнено(ТипСУБД) Тогда
		ДиалектSQL = "MSSQL";
		ПозицияНачалаПараметров = Найти(ТекстМета, "p_0:");
		Если ПозицияНачалаПараметров > 0 Тогда
			ЧастьПараметров = Сред(ТекстМета, ПозицияНачалаПараметров);
			ТекстМета = Лев(ТекстМета, ПозицияНачалаПараметров - 1);
		КонецЕсли; 
	Иначе
		ДиалектSQL = "SDBL";
	КонецЕсли; 
	ШаблонИмениПараметра = "Parameter";
	Если ЗначениеЗаполнено(ЧастьПараметров) Тогда
		ТекстовыйДокумент = Новый ТекстовыйДокумент;
		ТекстовыйДокумент.УстановитьТекст(ЧастьПараметров);
		Для Счетчик = 1 По ТекстовыйДокумент.КоличествоСтрок() Цикл
			СтрокаПараметра = ТаблицаПараметров.Добавить();
			Фрагменты = ирОбщий.СтрРазделитьЛкс(ТекстовыйДокумент.ПолучитьСтроку(Счетчик), ":");
			СтрокаПараметра.Имя = СокрЛП(Фрагменты[0]);
			СтрокаПараметра.Значение = СокрЛП(Фрагменты[1]);
		КонецЦикла;
	КонецЕсли;
	
	// Встроим имена парамаетров вместо знаков "?"
	ЗаписьXML = Новый ЗаписьXML;
	ЗаписьXML.УстановитьСтроку("");
	ИндексПараметра = 0;
	СтрокаПоиска = "?";
	Пока ТекстМета <> "" Цикл
		ПозицияПараметра = Найти(ТекстМета, СтрокаПоиска);
		Если ПозицияПараметра > 0 Тогда
			Если ИндексПараметра < ТаблицаПараметров.Количество() Тогда
				СтрокаПараметра = ТаблицаПараметров[ИндексПараметра];
			Иначе
				СтрокаПараметра = ТаблицаПараметров.Добавить();
				СтрокаПараметра.Имя = ШаблонИмениПараметра + XMLСтрока(ТаблицаПараметров.Количество());
			КонецЕсли; 
			ЗаписьXML.ЗаписатьБезОбработки(Лев(ТекстМета, ПозицияПараметра - 1) + СтрокаПоиска + ОбозначениеПараметраБД(СтрокаПараметра));
			ТекстМета = Сред(ТекстМета, ПозицияПараметра + СтрДлина(СтрокаПоиска));
			ИндексПараметра = ИндексПараметра + 1;
		Иначе
			ЗаписьXML.ЗаписатьБезОбработки(ТекстМета);
			ТекстМета = "";
		КонецЕсли; 
	КонецЦикла;
	ТекстМета = ЗаписьXML.Закрыть();
	
	МаркерСтрокиВРодномMSSQL = "N'";
	МаркерБинариВРодномMSSQL = "0x";
	Для Каждого СтрокаПараметра Из ТаблицаПараметров Цикл
		Если ТипСУБД = "DBMSSQL" Тогда
			ЛитералЗначения = СтрокаПараметра.Значение;
			Если ЗначениеЗаполнено(СтрокаПараметра.ТипЗначения) Тогда
				// ТипСобытия = sp_executesql 
				Если ирОбщий.СтрНачинаетсяСЛкс(ЛитералЗначения, МаркерСтрокиВРодномMSSQL) Тогда
					ЛитералЗначения = ирОбщий.СтрокаБезКонцаЛкс(Сред(ЛитералЗначения, СтрДлина(МаркерСтрокиВРодномMSSQL) + 1), 1);
					СтрокаПараметра.Значение1С = ЛитералЗначения;
					СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Строка");
				ИначеЕсли ирОбщий.СтрНачинаетсяСЛкс(ЛитералЗначения, "'") Тогда
					ЛитералЗначения = ирОбщий.СтрокаБезКонцаЛкс(Сред(ЛитералЗначения, 2), 1);
					Попытка
						СтрокаПараметра.Значение1С = ирОбщий.СтрокаВДатуЛкс(ЛитералЗначения);
						СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Дата");
					Исключение
						Пустышка = 0;
					КонецПопытки;
				Иначе
					Попытка
						СтрокаПараметра.Значение1С = Число(ЛитералЗначения);
						СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Число");
					Исключение
					КонецПопытки; 
				КонецЕсли; 
			Иначе
				// ТипСобытия = DBMSSQL
				Если Прав(ЛитералЗначения, 1) = "N" Тогда
					ЛитералЗначения = ирОбщий.СтрокаБезКонцаЛкс(ЛитералЗначения, 1);
					Попытка
						СтрокаПараметра.Значение1С = Число(ЛитералЗначения);
						СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Число");
					Исключение
						Пустышка = 0;
					КонецПопытки;
				ИначеЕсли Лев(ЛитералЗначения, 1) = "'" Тогда
					СтрокаПараметра.Значение1С = Сред(ирОбщий.СтрокаБезКонцаЛкс(ЛитералЗначения, 1), 2);
					СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Строка");
				ИначеЕсли СтрДлина(ЛитералЗначения) = СтрДлина("20010101000000") Тогда
					ЛитералЗначения = "" + (Число(Лев(ЛитералЗначения, 1)) - 2) + Сред(ЛитералЗначения, 2);
					Попытка
						СтрокаПараметра.Значение1С = Дата(ЛитералЗначения);
						СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("Дата");
					Исключение
						Пустышка = 0;
					КонецПопытки;
				КонецЕсли; 
			КонецЕсли; 
			Если Не ЗначениеЗаполнено(СтрокаПараметра.ТипЗначения1С) Тогда
				шПрефиксПараметра = "\?";
				шОперацияСравнения = "\s*(?:=|<=?|>=?)\s*";
				Вхождения = ирОбщий.НайтиРегВыражениеЛкс(ТекстМета, 
					"(" + мПлатформа.шИмя + "\." + мПлатформа.шИмя + ")" + шОперацияСравнения + шПрефиксПараметра + СтрокаПараметра.Имя + "(?!\d)" 
					+ "|" + шПрефиксПараметра + СтрокаПараметра.Имя + шОперацияСравнения + "(" + мПлатформа.шИмя + "\." + мПлатформа.шИмя + ")"
					+ "|(" + мПлатформа.шИмя + "\." + мПлатформа.шИмя + ")\s+IN\s*\([^\)]*" + шПрефиксПараметра + СтрокаПараметра.Имя + "(?!\d)",, Ложь);
				Если ЗначениеЗаполнено(Вхождения) Тогда
					ПолноеИмяПоля = Вхождения[0].Группы[0];
					Если ПолноеИмяПоля = Неопределено Тогда
						ПолноеИмяПоля = Вхождения[0].Группы[1];
					КонецЕсли;
					Если ПолноеИмяПоля = Неопределено Тогда
						ПолноеИмяПоля = Вхождения[0].Группы[2];
					КонецЕсли;
					ФрагментыИмени = ирОбщий.СтрРазделитьЛкс(ПолноеИмяПоля);
					ПолноеИмяТаблицыБД = "";
					Если Не ВсеСинонимы.Свойство(ФрагментыИмени[0], ПолноеИмяТаблицыБД) Тогда
						Продолжить;
					КонецЕсли;
					СтрокаПоля = ирКэш.ПоляТаблицыБДЛкс(ПолноеИмяТаблицыБД).Найти(ФрагментыИмени[1], "Имя");
					СтрокаПараметра.ТипЗначения1С = СтрокаПоля.ТипЗначения;
					Типы = СтрокаПоля.ТипЗначения.Типы();
					Если Истина
						И Типы.Количество() = 1 
						И ирОбщий.ЛиТипСсылкиБДЛкс(Типы[0])
					Тогда
						УИД = мПлатформа.УникальныйИдентификаторИзСтроки(СтрокаПараметра.Значение);
						СтрокаПараметра.Значение1С = ирОбщий.ПолучитьМенеджерЛкс(Типы[0]).ПолучитьСсылку(УИД);
						Продолжить;
					КонецЕсли;
				КонецЕсли;
				СтрокаПараметра.Значение1С = ЛитералЗначения;
				СтрокаПараметра.ТипЗначения1С = Новый ОписаниеТипов("ДвоичныеДанные");
			КонецЕсли;
		КонецЕсли; 
	КонецЦикла;

	Если Истина
		И ПересобратьТекст 
		И ЗначениеЗаполнено(ТекстМета) 
	Тогда 
		Если Не ЗначениеЗаполнено(ТипСУБД) Тогда
			ПолеТекста = ирОбщий.НовыйАнализаторКодаЛкс(1);
			КонструкторЗапроса = ПолеТекста.ПолучитьФорму("КонструкторЗапроса");
			КонструкторЗапроса.Английский1С = Не ПереводитьВМета;
			ИменованныеПараметры = Истина;
			Если ПолеТекста.ЗагрузитьТекстВКонструктор(ТекстМета, КонструкторЗапроса,, ДиалектSQL, ИменованныеПараметры) Тогда 
				ТекстМета = КонструкторЗапроса.СобратьПолныйТекст(, Истина);
			КонецЕсли;        
		Иначе
			ЛиКончаетсяНаGO = ирОбщий.СтрКончаетсяНаЛкс(СокрП(ТекстМета), "GO");
			ПриложениеФорматер = мПлатформа.ФайлФорматераЗапросовБД();
			ВременныйФайлИсточник = ПолучитьИмяВременногоФайла("sql");
			ТекстовыйДокумент = Новый ТекстовыйДокумент;
			ТекстовыйДокумент.УстановитьТекст(ТекстМета);
			ТекстовыйДокумент.Записать(ВременныйФайлИсточник, КодировкаТекста.UTF8);
			ВременныйФайлПриемник = ПолучитьИмяВременногоФайла("sql");       
			// Онлайн тестер https://poorsql.com/
			// Параметры:
			//is  indentString (default: \t)
			//st  spacesPerTab (default: 4)
			//mw  maxLineWidth (default: 999)
			//sb  statementBreaks (default: 2) // Количество пустых строк между запросами пакета
			//cb  clauseBreaks (default: 1) // Количество пустых строк между секциями запроса
			//tc  trailingCommas (default: false) // располагать запятую в конце строки элемента списка
			//sac spaceAfterExpandedComma (default: false)
			//ebc expandBetweenConditions (default: true)
			//ebe expandBooleanExpressions (default: true)
			//ecs expandCaseStatements (default: true)
			//ecl expandCommaLists (default: true) // Разворачивать остальные списки по одному элементу на строку
			//eil expandInLists (default: true)    // Разворачивать списки правого операнда IN по одному элементу на строку
			//uk  uppercaseKeywords (default: true)
			//sk  standardizeKeywords (default: false)
			//ae  allowParsingErrors (default: false) // пропускать ошибки парсинга и добавлять при их наличии комментарий в начало текста
			//e   extensions (default: sql)
			//r   recursive (default: false)
			//b   backups (default: true)
			//b   outputFileOrFolder (default: none; if set, overrides the backup option)
			//l   languageCode (default: current if supported or EN; valid values include EN, FR and ES)
			Результат = ирОбщий.ВыполнитьКомандуОСЛкс("""" + ПриложениеФорматер.ПолноеИмя + """ """ + ВременныйФайлИсточник + """ /o:""" + ВременныйФайлПриемник + """ /tc /ae");
			ТекстовыйДокумент.Прочитать(ВременныйФайлПриемник, КодировкаТекста.UTF8);
			НомерСтроки = ТекстовыйДокумент.КоличествоСтрок();
			// Удалим добавленную инструкцию GO
			Пока НомерСтроки > 1 И Не ЛиКончаетсяНаGO Цикл
				СтрокаТекста = ТекстовыйДокумент.ПолучитьСтроку(НомерСтроки);
				Если ПустаяСтрока(СтрокаТекста) Или СтрокаТекста = "GO" Тогда
					ТекстовыйДокумент.УдалитьСтроку(НомерСтроки);
				Иначе
					Прервать;
				КонецЕсли;
				НомерСтроки = НомерСтроки - 1;
			КонецЦикла;
			ТекстМета = ТекстовыйДокумент.ПолучитьТекст();
			УдалитьФайлы(ВременныйФайлИсточник);
			УдалитьФайлы(ВременныйФайлПриемник);
		КонецЕсли;
	КонецЕсли; 
	Если ПереводСПотерейСинтаксиса Тогда
		ЗаписьXML = Новый ЗаписьXML;
		ЗаписьXML.УстановитьСтроку("");
		ИндексПараметра = 0;
		Пока ТекстМета <> "" Цикл
			ПозицияПараметра = 0;
			Если ИндексПараметра < ТаблицаПараметров.Количество() Тогда
				СтрокаПоиска = "?" + ОбозначениеПараметраБД(ТаблицаПараметров[ИндексПараметра]);
				ПозицияПараметра = Найти(ТекстМета, СтрокаПоиска);
			КонецЕсли; 
			Если Истина
				И ПозицияПараметра > 0 
			Тогда
				ЗаписьXML.ЗаписатьБезОбработки(Лев(ТекстМета, ПозицияПараметра - 1) + ТаблицаПараметров[ИндексПараметра].Значение);
				ИндексПараметра = ИндексПараметра + 1;
				ТекстМета = Сред(ТекстМета, ПозицияПараметра + СтрДлина(СтрокаПоиска));
			Иначе
				ЗаписьXML.ЗаписатьБезОбработки(ТекстМета);
				ТекстМета = "";
			КонецЕсли; 
		КонецЦикла;
		ТекстМета = ЗаписьXML.Закрыть();
	КонецЕсли; 
	Результат = Новый Структура();
	Результат.Вставить("Текст", ТекстМета);
	Результат.Вставить("Параметры", ТаблицаПараметров);
	Результат.Вставить("Таблицы", ТаблицаТаблиц);
	Возврат Результат;
	
КонецФункции

Функция ОбозначениеПараметраБД(Знач СтрокаПараметра)
	
	//Возврат "/*&" + СтрокаПараметра.Имя + "*/";
	Возврат СтрокаПараметра.Имя;

КонецФункции

Функция НайтиВызовыМетодовМодулей(ИмяМодуля, НомерНачальнойСтроки, НомерКонечнойСтроки, ТолькоОдну = Истина)

	Результат = Новый Массив();
	Для Каждого СтрокаТаблицы Из ТаблицаЖурнала Цикл
		ФрагментыНачальнойСтрокиМодуля = ирОбщий.СтрРазделитьЛкс(СтрокаТаблицы.СтрокаМодуля, ":", Истина);
		Если ФрагментыНачальнойСтрокиМодуля[0] <> ИмяМодуля Тогда
			Результат.Вставить(0, СтрокаТаблицы);
			Если ТолькоОдну Тогда
				Прервать;
			КонецЕсли; 
		КонецЕсли; 
		НомерСтрокиМодуля = Число(ФрагментыНачальнойСтрокиМодуля[1]);
		Если Ложь
			Или НомерСтрокиМодуля < НомерНачальнойСтроки 
			Или НомерСтрокиМодуля > НомерКонечнойСтроки 
		Тогда
			Результат.Вставить(0, СтрокаТаблицы);
			Если ТолькоОдну Тогда
				Прервать;
			КонецЕсли; 
		КонецЕсли; 
	КонецЦикла;
	Возврат Результат;

КонецФункции

// Получить словарь имен таблиц входящих в запрос и соответствующих
// шаблонам словаря метаданных
//
// Параметры:
//  ТекстЗапроса - Строка, текст запроса для которого строится словарь
//  СловарьШаблоновМетаданных - Соответствие, словарь шаблонов метаданных
//  ТипСУБД - Строка (Перечисление.ТипСУБД)
//
// Возвращаемое значение:
//  Соответствие - Словарь имен таблиц запроса
//
Функция ИменаМетаданныхЗапросаСУБД(Знач ТекстЗапроса, СловарьШаблоновМетаданных, ТипСУБД = "") Экспорт
	
	Результат = Новый ТаблицаЗначений;
	Результат.Колонки.Добавить("Имя");
	Результат.Колонки.Добавить("Длина");
	ТекстЗапроса = НРег(ТекстЗапроса);
	//Если НРег(ТипСУБД) = НРег("DB2") Тогда
	//	ТекстЗапроса = ВРег(ТекстЗапроса);
	//КонецЕсли;
	Для Каждого СтрокаСловаряМетаданных Из СловарьШаблоновМетаданных Цикл
		Если Найти(ТекстЗапроса, СтрокаСловаряМетаданных.БыстрыйМаркер) = 0 Тогда 
			Продолжить;
		КонецЕсли;
		ТаблицаВхождений = ирОбщий.НайтиРегВыражениеЛкс(ТекстЗапроса,,,,,,, Истина, СтрокаСловаряМетаданных.Вычислитель,, ТаблицаВхождений);
		Для Каждого СтрокаВхождения Из ТаблицаВхождений Цикл
			НоваяСтрока = Результат.Добавить();
			НоваяСтрока.Имя = Сред(СтрокаВхождения.ТекстВхождения, 2);
			НоваяСтрока.Длина = СтрДлина(НоваяСтрока.Имя); // Мультиметка241027_122402
		КонецЦикла;
	КонецЦикла;
	Результат.Сортировать("Длина Убыв");
	Результат.Свернуть("Имя");
	
	Возврат Результат;
	
КонецФункции // ПолучитьСловарьЗапроса()

// Получить число из строки в которой число находится в начале строки
//
// Праметры:
//  ИсходнаяСтрока - Строка, строка в которой находится число
//
// Возвращаемое значение:
//  Число - если оно есть, неопределено, если числа нет
//
Функция ПолучитьЧислоСтрокой(ИсходнаяСтрока, КоличествоСимволов)
	
	КоличествоСимволов = 0;
	ДлинаСтроки = СтрДлина(ИсходнаяСтрока);
	
	Для Сч = 1 По ДлинаСтроки Цикл
		
		ТекущийСимвол = КодСимвола(Сред(ИсходнаяСтрока, Сч, 1));
		
		Если 48 <= ТекущийСимвол И ТекущийСимвол <= 57 Тогда
			КоличествоСимволов = КоличествоСимволов + 1;
		Иначе
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если КоличествоСимволов > 0 Тогда
		Возврат Лев(ИсходнаяСтрока, КоличествоСимволов);
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции // ПолучитьЧислоСтрокой()

// Перевести часть запроса из терминов СУБД в термины метаданных
//
// Параметры:
//  Запрос - Строка, сапрос в терминах СУБД
//  СтруктураХраненияБазыДанных - ТаблицаЗначений
//  СловарьШаблоновМетаданных - Соответствие, подробности в функции
//                 ПолучитьСловарьШаблоновМетаданных()
//  ТипСУБД - Строка, (Перечисление.ТипСУБД)
//  выхТаблицы - ТаблицаЗначений, *Неопределено - описание использованных в тексте таблиц, заполняется только если таблица передана (без колонок)
//
// Возвращаемое значение:
//  Строка, запрос в терминах метаданных
//
Функция ПеревестиТекстБДВТерминыМетаданных(Знач Текст, СтруктураХраненияБазыДанных = Неопределено,
    СловарьШаблоновМетаданных = Неопределено, Знач ТипСУБД = "", выхТаблицы = Неопределено, ДопустимыйРазмер = 20000, ПереводитьИндексы = Истина, ПереводитьВМета = Истина,
	ЭтоГарантированоТекстСобственнойБазы = Ложь, выхВсеСинонимы = Неопределено) Экспорт
										  
	Если выхТаблицы <> Неопределено Тогда
		#Если Сервер И Не Сервер Тогда
		    выхТаблицы = Новый ТаблицаЗначений;
		#КонецЕсли
		//выхТаблицы.Колонки.Очистить();
		Если выхТаблицы.Колонки.Найти("ИмяБД") = Неопределено Тогда
			выхТаблицы.Колонки.Добавить("ИмяБД", Новый ОписаниеТипов("Строка"));
		КонецЕсли; 
		Если выхТаблицы.Колонки.Найти("ИмяМета") = Неопределено Тогда
			выхТаблицы.Колонки.Добавить("ИмяМета", Новый ОписаниеТипов("Строка"));
		КонецЕсли; 
	КонецЕсли; 
	Если Ложь
		Или Не ЗначениеЗаполнено(Текст) 
	Тогда
		Возврат "";
	КонецЕсли; 
	Если Истина
		И ДопустимыйРазмер > 0 
		И СтрДлина(Текст) > ДопустимыйРазмер
		И ПереводитьВМета
	Тогда
		Возврат РезультатПереводаСлишкомБольшогоТекста();
	КонецЕсли;
	выхВсеСинонимы = Новый Структура;
	Если ирОбщий.СтрокиРавныЛкс(ТипСУБД, "sdbl") Тогда
		ТипСУБД = "";
	КонецЕсли; 
	АдресЧужойСхемыБД = "";
	Если Не ЭтоГарантированоТекстСобственнойБазы Тогда
		АдресЧужойСхемыБД = мАдресЧужойСхемыБД;
	КонецЕсли; 
	Если СтруктураХраненияБазыДанных = Неопределено Тогда
		СтруктураХраненияБазыДанных = ирКэш.СтруктураХраненияБДЛкс(ЗначениеЗаполнено(ТипСУБД), АдресЧужойСхемыБД);
	КонецЕсли; 
	Если СловарьШаблоновМетаданных = Неопределено Тогда
		СловарьШаблоновМетаданных = ирКэш.СловарьШаблоновХраненияБДЛкс(ЗначениеЗаполнено(ТипСУБД), АдресЧужойСхемыБД);
	КонецЕсли; 
	Текст = Текст + Символы.ПС;
	ИменаМетаданныхЗапроса = ИменаМетаданныхЗапросаСУБД(Текст, СловарьШаблоновМетаданных, ТипСУБД);
	
	//Если НРег(ТипСУБД) = НРег("DB2") Тогда
	//	Текст = ВРег(Текст);
	//	ТекстНрег = Текст;
	//Иначе
		ТекстНрег = "";
	//КонецЕсли; 
	
	// Поиск имен таблиц в строке запроса
	Если ИменаМетаданныхЗапроса.Количество() > 10 Тогда
		Индикатор = ирОбщий.ПолучитьИндикаторПроцессаЛкс(ИменаМетаданныхЗапроса.Количество(), "Преобразование в имена метаданных");
	КонецЕсли;
	Для Каждого ИмяМетаданного Из ИменаМетаданныхЗапроса Цикл
		Если Индикатор <> Неопределено Тогда
			ирОбщий.ОбработатьИндикаторЛкс(Индикатор);
		КонецЕсли;
		ОписаниеТаблицы = Неопределено;
		Если выхТаблицы <> Неопределено Тогда
			Если Найти(ИмяМетаданного.Имя, ".") = 0 Тогда
				ОписаниеТаблицы = выхТаблицы.Добавить();
				ОписаниеТаблицы.ИмяБД = ИмяМетаданного.Имя;
			КонецЕсли; 
		КонецЕсли;
		// Получить имя таблицы
		СтрокаСтруктуры = СтруктураХраненияБазыДанных.Найти(ирОбщий.ПоследнийФрагментЛкс(ИмяМетаданного.Имя), "КраткоеИмяТаблицыХранения");
		Если СтрокаСтруктуры = Неопределено Тогда
			ирОбщий.СообщитьЛкс("В схеме БД не найдено таблицы " + ИмяМетаданного.Имя);
			Продолжить;
		КонецЕсли;
		ирОбщий.ПеревестиКолонкиСтруктурыХраненияБДПоляЛкс(СтрокаСтруктуры.Поля);
		ИмяТаблицы = СтрокаСтруктуры.КраткоеИмяТаблицыХранения;
		МетаПолноеИмяТаблицы = СтрокаСтруктуры.ИмяТаблицы;
		Если ОписаниеТаблицы <> Неопределено Тогда
			ОписаниеТаблицы.ИмяМета = МетаПолноеИмяТаблицы;
		КонецЕсли;
		Если Не ПереводитьВМета Тогда
			Продолжить;
		КонецЕсли;
		Если ТекстНрег = "" Тогда
			ТекстНрег = НРег(Текст);
		КонецЕсли;
		Синонимы = СинонимыТаблицыБД(Текст, ИмяТаблицы, МетаПолноеИмяТаблицы, ТипСУБД);
		//Синонимы.Вставить(ИмяТаблицы, МетаИмяТаблицы);
		ЭтоТабличнаяЧасть = СтрокаСтруктуры.Назначение = "ТабличнаяЧасть";
		Для Каждого СтрокаПоля Из СтрокаСтруктуры.Поля Цикл
			Если Найти(ТекстНрег, НРег(СтрокаПоля.ИмяПоляХранения)) = 0 Тогда
				Продолжить;
			КонецЕсли; 
			ПредставлениеПоля = ирОбщий.ПредставлениеПоляБДЛкс(СтрокаПоля, ТипСУБД <> "", ЭтоТабличнаяЧасть);
			Если Не ЗначениеЗаполнено(ПредставлениеПоля) Тогда
				Продолжить;
			КонецЕсли; 
			мРегВыражениеМета.Pattern = шГраничныйСимволИмени + СтрокаПоля.ИмяПоляХранения + "([^&" + шБуква + "\d]|Balance|Turnover|Receipt|Expense|^|$)";
			// Можно оптимизировать путем создания шаблона мультизамены
			Текст = мРегВыражениеМета.Заменить(Текст, "$1" + ПредставлениеПоля + "$2"); 
		КонецЦикла;
		Если ПереводитьИндексы Тогда
			Для Каждого СтрокаИндекса Из СтрокаСтруктуры.Индексы Цикл
				ирОбщий.ЗаполнитьИмяИндексаХраненияЛкс(СтрокаИндекса, ТипСУБД <> "", СтрокаСтруктуры);
				СтрокаДляЗамены = СтрокаИндекса.ИмяИндексаХранения;
				Если Найти(ТекстНрег, НРег(СтрокаДляЗамены)) = 0 Тогда
					Продолжить;
				КонецЕсли; 
				ПредставлениеИндекса = СтрокаИндекса.ИмяИндекса;
				// Имена индексов достаточно уникальны, чтобы не проверять границы слова
				мРегВыражениеМета.Pattern = СтрокаДляЗамены;
				Текст = мРегВыражениеМета.Заменить(Текст, ПредставлениеИндекса); 
			КонецЦикла;
		КонецЕсли; 
		Для Каждого Синоним Из Синонимы Цикл
			// Заменить псевдоним таблицы
			мРегВыражениеМета.Pattern = шГраничныйСимволИмени + Синоним.Ключ + шГраничныйСимволИмени;
			Текст = мРегВыражениеМета.Заменить(Текст, "$1" + Синоним.Значение + "$2");
			//мРегВыражениеМета.Pattern = ИмяТаблицы + " " + Синоним.Ключ + шГраничныйСимволИмени;
			//Запрос = мРегВыражениеМета.Заменить(Текст, ИмяТаблицы + " AS " + Синоним.Значение + "$1");
			выхВсеСинонимы.Вставить(Синоним.Значение, МетаПолноеИмяТаблицы);
		КонецЦикла;
		// Заменить имя таблицы
		мРегВыражениеМета.Pattern = шГраничныйСимволИмени + СтрокаСтруктуры.ИмяТаблицыХранения + шГраничныйСимволИмени;
		Текст = мРегВыражениеМета.Заменить(Текст, "$1" + МетаПолноеИмяТаблицы + "$2");
		Если НРег(СтрокаСтруктуры.ИмяТаблицыХранения) <> СтрокаСтруктуры.КраткоеИмяТаблицыХранения Тогда
			мРегВыражениеМета.Pattern = шГраничныйСимволИмени + СтрокаСтруктуры.КраткоеИмяТаблицыХранения + шГраничныйСимволИмени;
			Текст = мРегВыражениеМета.Заменить(Текст, "$1" + ирОбщий.ПоследнийФрагментЛкс(МетаПолноеИмяТаблицы) + "$2");
		КонецЕсли; 
	КонецЦикла;
	Если Индикатор <> Неопределено Тогда
		ирОбщий.ОсвободитьИндикаторПроцессаЛкс(Индикатор);
	КонецЕсли; 
	// Заменим приведение к ссылочному типу 
	Если Не ЗначениеЗаполнено(ТипСУБД) Тогда
		Текст = СтрЗаменить(Текст, "AS REF(", "AS (");
	КонецЕсли; 
	Текст = СокрП(Текст);
	Возврат Текст;
	
КонецФункции

Функция РезультатПереводаСлишкомБольшогоТекста() Экспорт 
	
	Возврат "<Текст слишком большой для быстрого перевода>"

КонецФункции

// Получить синонимы имени таблицы в запросе
//
// Параметры:
//  Запрос - Строка, текст запроса
//  ИмяТаблицы - Строка, имя таблицы для которой ищутся синонимы
//  МетаИмяТаблицы - Строка, имя таблицы в терминах метаданных
//
// Возвращаемое занчение:
//  Соответствие, имена синонимов и их мета имена
//
Функция СинонимыТаблицыБД(ТекстЗапросаБД, ИмяТаблицы, МетаИмяТаблицы, ТипСУБД = "") Экспорт
	
	Синонимы = Новый Соответствие;
	мРегВыражениеМета.Pattern = ИмяТаблицы + " " + "(?:([" + шБуква + "\d]+)|\(([" + шБуква + "\d]+)\))" + шГраничныйСимволИмени;
	Вхождения = мРегВыражениеМета.НайтиВхождения(ТекстЗапросаБД);
	ПсевдонимТаблицы = ирОбщий.ПоследнийФрагментЛкс(МетаИмяТаблицы, ".");
	Для Каждого Вхождение Из Вхождения Цикл
		Синоним = Вхождение.SubMatches(0);
		Если Синоним = Неопределено Тогда
			Синоним = Вхождение.SubMatches(1);
		КонецЕсли; 
		Если Ложь
			Или (Истина
				И ирОбщий.СтрокиРавныЛкс(ТипСУБД, "DBMSSQL")
				И ирОбщий.СтрокиРавныЛкс(Синоним, "WITH"))
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "WHERE")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "GROUP")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "HAVING")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "SET")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "ORDER")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "THEN")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "INNER")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "LEFT")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "RIGHT")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "OUTER")
			Или ирОбщий.СтрокиРавныЛкс(Синоним, "ON")
		Тогда
			Продолжить;
		КонецЕсли; 
		Синонимы.Вставить(Синоним, ПсевдонимТаблицы + "_" + Синоним);
	КонецЦикла;
	Возврат Синонимы;
	
КонецФункции

//ирПортативный лФайл = Новый Файл(ИспользуемоеИмяФайла);
//ирПортативный ПолноеИмяФайлаБазовогоМодуля = Лев(лФайл.Путь, СтрДлина(лФайл.Путь) - СтрДлина("Модули\")) + "ирПортативный.epf";
//ирПортативный #Если Клиент Тогда
//ирПортативный 	Контейнер = Новый Структура();
//ирПортативный 	Оповестить("ирПолучитьБазовуюФорму", Контейнер);
//ирПортативный 	Если Не Контейнер.Свойство("ирПортативный", ирПортативный) Тогда
//ирПортативный 		ирПортативный = ВнешниеОбработки.ПолучитьФорму(ПолноеИмяФайлаБазовогоМодуля);
//ирПортативный 		ирПортативный.Открыть();
//ирПортативный 	КонецЕсли; 
//ирПортативный #Иначе
//ирПортативный 	ирПортативный = ВнешниеОбработки.Создать(ПолноеИмяФайлаБазовогоМодуля, Ложь); // Это будет второй экземпляр объекта
//ирПортативный #КонецЕсли
//ирПортативный ирОбщий = ирПортативный.ОбщийМодульЛкс("ирОбщий");
//ирПортативный ирКэш = ирПортативный.ОбщийМодульЛкс("ирКэш");
//ирПортативный ирСервер = ирПортативный.ОбщийМодульЛкс("ирСервер");
//ирПортативный ирКлиент = ирПортативный.ОбщийМодульЛкс("ирКлиент");

мПлатформа = ирКэш.Получить();
ЭтотОбъект.КоличествоПотоков = 8;
мНепустыеКолонкиЖурнала = Новый Структура();
//ЭтотОбъект.НаложениеПриДозагрузкеСекунд = 30;
мРегВыражениеПараметры = ирОбщий.НовоеРегВыражениеЛкс();
Если мРегВыражениеПараметры <> Неопределено Тогда
	мРегВыражениеПараметры.IgnoreCase = Истина;
	мРегВыражениеПараметры.Global = Истина;
	мРегВыражениеПараметры.Pattern = "([^A-ZА-ЯЁ_0-9]|^)(?:[A-F0-9]+:[A-F0-9]+|0x[A-F0-9]+|[A-F0-9]+(?:-[A-F0-9]+)+-[A-F0-9]+|(#TT?)[_A-F0-9]+|[0-9]+)"; // для стирания констант и имен временных таблиц
	мРегВыражениеМета = ирОбщий.НовоеРегВыражениеЛкс();
	мРегВыражениеМета.IgnoreCase = Истина;
	мРегВыражениеМета.Global = Истина;
КонецЕсли;

мСписокКолонок = Новый СписокЗначений;
//мСписокКолонок.Добавить("Name",                 "Событие"); // В некоторых случаях нужно
мСписокКолонок.Добавить("SrcName",              "Источник");
мСписокКолонок.Добавить("OSThread",             "ПотокОС");
мСписокКолонок.Добавить("process",              "ТипПроцессаОС");
мСписокКолонок.Добавить("p:processName",        "Инфобаза");
мСписокКолонок.Добавить("t:clientID",           "Соединение_");
мСписокКолонок.Добавить("t:applicationName",    "Приложение");
мСписокКолонок.Добавить("t:computerName",       "Компьютер");
мСписокКолонок.Добавить("t:connectID",          "TCPСоединение");
мСписокКолонок.Добавить("SessionID",            "Сеанс");
мСписокКолонок.Добавить("Usr",                  "Пользователь");
мСписокКолонок.Добавить("OSException",          "ИсключениеОС");
мСписокКолонок.Добавить("Interface",            "Интерфейс");
мСписокКолонок.Добавить("Method",               "Метод");
мСписокКолонок.Добавить("Exception",            "ТипИсключения");
мСписокКолонок.Добавить("Descr",                "Описание");
мСписокКолонок.Добавить("ClientComputerName",   "КомпьютерКлиента");
мСписокКолонок.Добавить("ServerComputerName",   "КомпьютерСервера");
мСписокКолонок.Добавить("UserName",             "Пользователь");
мСписокКолонок.Добавить("ConnectString",        "СтрокаСоединения");
мСписокКолонок.Добавить("ProcessName",          "ТипПроцессаОС");
мСписокКолонок.Добавить("SrcProcessName",       "Инфобаза");
//мСписокКолонок.Добавить("Trans",                "Транзакция");
мСписокКолонок.Добавить("Func",                 "Действие");
мСписокКолонок.Добавить("Sdbl",                 "ТекстSDBL");
мСписокКолонок.Добавить("dbpid",                "ПроцессСУБД");
мСписокКолонок.Добавить("Sql",                  "ТекстСУБД");
мСписокКолонок.Добавить("NParams",              "КоличествоПараметров");
мСписокКолонок.Добавить("Rows",                 "ЧислоСтрок");
мСписокКолонок.Добавить("RowsAffected",         "ЧислоИзменныхСтрок");
мСписокКолонок.Добавить("planSQLText",          "ПланСУБД");
мСписокКолонок.Добавить("Context",              "Контекст");
мСписокКолонок.Добавить("CatName",              "КаталогСУБД");
мСписокКолонок.Добавить("WaitConnections",      "Блокираторы");
мСписокКолонок.Добавить("FileName",             "ФайлСУБД");
мСписокКолонок.Добавить("DeadlockConnectionIntersections", "Взаимоблокировка");
мСписокКолонок.Добавить("Finish",               "ПричинаЗавершения");
мСписокКолонок.Добавить("Err",                  "ТипСообщения");
мСписокКолонок.Добавить("Txt",                  "ТекстСообщения");
мСписокКолонок.Добавить("URI",                  "Ресурс");
мСписокКолонок.Добавить("Administrator",        "Администратор");
мСписокКолонок.Добавить("Body",                 "РазмерЗапроса");
мСписокКолонок.Добавить("Cluster",              "НомерПорта");
мСписокКолонок.Добавить("Val",                  "ЗначениеПараметра");

// Свойства найденные экспериментально. Они не документированы.
мСписокКолонок.Добавить("Query",                "ТекстЗапроса1С"); // События: QERR
мСписокКолонок.Добавить("From",                 "From_"); // События: DBV8DBENG
//мСписокКолонок.Добавить("AppID",                "AppID"); // События: QERR, DBMSSQL (дублируется t:applicationName), SDBL (дублирует t:applicationName)
мСписокКолонок.Добавить("Index",                "Index_");
//мСписокКолонок.Добавить("setUnhandledExceptionFilter", "setUnhandledExceptionFilter");
мСписокКолонок.Добавить("Module",               "МодульКонфигурации"); // События: EXCPCNTX
мСписокКолонок.Добавить("ClientID",             "Клиент"); // События: EXCPCNTX, SCALL, EXCP (дублируется t:clientID ?)

мТаблицаКолонок = Новый ТаблицаЗначений;
мТаблицаКолонок.Колонки.Добавить("ВнутреннееИмя", Новый ОписаниеТипов("Строка"));
мТаблицаКолонок.Колонки.Добавить("ИмяВТаблице", Новый ОписаниеТипов("Строка"));
мТаблицаКолонок.Индексы.Добавить("ВнутреннееИмя");
мТаблицаКолонок.Индексы.Добавить("ИмяВТаблице");
//мЧисловыеСвойства = Новый Структура;
мСоответствиеКолонок = Новый Соответствие;
МетаРеквизиты = Метаданные().ТабличныеЧасти.ТаблицаЖурнала.Реквизиты;
Для Каждого ЭлементСписка Из мСписокКолонок Цикл
	мСоответствиеКолонок.Вставить(НРег(ЭлементСписка.Значение), ЭлементСписка.Представление); 
	СтрокаСоответствия = мТаблицаКолонок.Добавить();
	СтрокаСоответствия.ВнутреннееИмя = ЭлементСписка.Значение;
	СтрокаСоответствия.ИмяВТаблице = ЭлементСписка.Представление;
	//Если МетаРеквизиты[ЭлементСписка.Представление].Тип.СодержитТип(Тип("Число")) Тогда 
	//	мЧисловыеСвойства.Вставить(ЭлементСписка.Представление);
	//КонецЕсли; 
КонецЦикла;
Для Каждого МетаРеквизит Из МетаРеквизиты Цикл
	Если мСоответствиеКолонок[МетаРеквизит.Имя] = Неопределено Тогда
		мСоответствиеКолонок.Вставить(НРег(МетаРеквизит.Имя), МетаРеквизит.Имя);
	КонецЕсли; 
КонецЦикла; 

мКартыФайлов = Новый ТаблицаЗначений;
мКартыФайлов.Колонки.Добавить("ИмяКаталогаПроцесса", Новый ОписаниеТипов("Строка"));
мКартыФайлов.Колонки.Добавить("ИмяТекущегоФайла", Новый ОписаниеТипов("Строка"));
мКартыФайлов.Колонки.Добавить("СигнатураТекущегоФайла", Новый ОписаниеТипов("Строка"));
мКартыФайлов.Колонки.Добавить("ОтборПоСеансу");
мКартыФайлов.Колонки.Добавить("ПозицияНачала", Новый ОписаниеТипов("Число"));
мКартыФайлов.Колонки.Добавить("ПозицияКонца", Новый ОписаниеТипов("Число"));
мКартыФайлов.Колонки.Добавить("НачалоПериода", Новый ОписаниеТипов("Дата"));
мКартыФайлов.Колонки.Добавить("КонецПериода", Новый ОписаниеТипов("Дата"));
мКартыФайлов.Колонки.Добавить("ДатаИзмененияФайла", Новый ОписаниеТипов("Дата"));
мКартыФайлов.Индексы.Добавить("ИмяКаталогаПроцесса, ИмяТекущегоФайла, СигнатураТекущегоФайла, ОтборПоСеансу");

мСвойстваСИменамиБД = Новый Структура();
мСвойстваСИменамиБД.Вставить("ТекстБД", Ложь);
мСвойстваСИменамиБД.Вставить("ТекстSDBL", Ложь);
мСвойстваСИменамиБД.Вставить("ТекстSDBLШаблон", Ложь);
мСвойстваСИменамиБД.Вставить("ТекстСУБД", Истина);
мСвойстваСИменамиБД.Вставить("ТекстСУБДШаблон", Ложь);
мСвойстваСИменамиБД.Вставить("ПланСУБД", Истина);
мСвойстваСИменамиБД.Вставить("Взаимоблокировка", Ложь);
мСвойстваСИменамиБД.Вставить("Regions", Ложь);
мСвойстваСИменамиБД.Вставить("Locks", Ложь);

мСерверныеТипыПроцессов = Новый Структура;
мСерверныеТипыПроцессов.Вставить("_adminprocess"); // ##AdminProcess##
мСерверныеТипыПроцессов.Вставить("_rphost");
мСерверныеТипыПроцессов.Вставить("_rmngr");
мСерверныеТипыПроцессов.Вставить("_ragent");

мЛиТрассаПоПользователю = Ложь;
МаксТысячСобытий = 100;
мАдресЧужойСхемыБД = "";
шБуква = мПлатформа.шБуква;
шГраничныйСимволИмени = "([^&" + шБуква + "\d]|^|$)";
мИменаВозвращаемыхСвойств = "мКартыФайлов, мНепустыеКолонкиЖурнала, мТипСУБД";
мТаблицаЖурнала = ТаблицаЖурнала;
