Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 CLR-функция и журнал транзакций  [new]
sheich
Member

Откуда:
Сообщений: 209
Привет. Наткнулся на странную вещь. Кажется, некоторые user-defined функции, основанные на моих собственных CLR-сборках пытаются производить какие-то записи в журнал транзакций, хотя они предназначены просто для чтения (причем даже не из БД).

Конкретная ситуация следующая: по определенным причинам происходит ошибка при insert в таблицу БД и xact_state() становится равным -1 (Нефиксируемая транзакция). Далее, через несколько строк в процедуре происходит ряд проверок и, если были ошибки, происходит откат транзакции. Все гладко, хорошо. Но - сразу после возникновения этой ошибки (при insert) идет вызов CLR-функции, которая просто выбирает из xml определенный элемент, и вот в этот момент вываливается ошибка:

Msg 3930, Level 16, State 1, Procedure rsp_***, Line 90
Текущая транзакция не может быть зафиксирована и не может поддерживать операции, производящие запись в файл журнала. Выполните откат транзакции.
Msg 3998, Level 16, State 1, Line 1
Нефиксируемая транзакция обнаружена в конце пакета. Был выполнен откат транзакции.

Библиотека была написана на C# и с помощью create assembly задействована в SQL Server (2005). Причем, если просто тупо заменить вызов этой функции на вызов другой CLR-функции, которая, например, обрабатывает даты - никакой ошибки не будет!

Я не могу понять: там же, в этом методе класса, просто происходит поиск элемента по xml-дереву. Причем тут журнал транзакций???
26 июн 09, 13:57    [7347984]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
DeColo®es
Member

Откуда: Москва
Сообщений: 5499
Блог
sheich,
Текст функции предстоит угадать местным форумчанам самостоятельно? ;)
26 июн 09, 15:24    [7348697]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
sheich
Member

Откуда:
Сообщений: 209
DeColo®es
sheich,
Текст функции предстоит угадать местным форумчанам самостоятельно? ;)

я ж говорю, она просто обрабатывает xml
ALTER FUNCTION [dbo].[node_value](@doc [xml], @node [nvarchar](max), @index [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS EXTERNAL NAME [Xml].[Oranta.Xml].[getValueSQL]
текст исходной, на C# тоже нужен?
26 июн 09, 15:29    [7348745]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
DeColo®es
Member

Откуда: Москва
Сообщений: 5499
Блог
sheich
текст исходной, на C# тоже нужен?
Так о нем и речь!
26 июн 09, 17:44    [7349794]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
sheich
Member

Откуда:
Сообщений: 209
DeColo®es
sheich
текст исходной, на C# тоже нужен?
Так о нем и речь!

ОК. Рискну выложить.
namespace Oranta
{
	using System;
	using System.Data.SqlTypes;
	using System.Xml;
	using System.IO;
	using System.Collections.Generic;

	using System.Xml.Xsl;
	using System.Xml.XPath;
	using System.Text;

	using System.Collections;
	using Microsoft.SqlServer.Server;

	using System.Configuration;
	using Microsoft.Win32;


	public class StringEncoder : StringWriter
	{
		Encoding encoding;


		public StringEncoder(StringBuilder builder, Encoding encoding)
			: base(builder)
		{
			this.encoding = encoding;
		}

		public override Encoding Encoding
		{
			get { return encoding; }
		}
	}


	public class Config
	{
		RegistryKey HKLM;


		public string getKey(string name)
		{
			HKLM = Registry.LocalMachine;

			try
			{
				return HKLM.OpenSubKey("Software\\Oranta\\B2B").GetValue(name).ToString();
			}
			catch
			{
				return null;
			}
		}
	}


	public class Xml
	{
		//	Имя нового корневого тэга должно быть таким страшным,
		//	что бы минимизировать вероятность совпадения с одним из уже имеющихся в xml.

		private string needRoot = "m4nf9g03bdf72nx7agf64gf8";


		private string makeDocument(string doc)
		{
			//	Обрамляем xml данные дополнительным тэгом,
			//	что бы избежать ошибки множественности корневых элементов.
			//	Это вызвано тем, что со стороны SQL сервера может быть передан набор xml элементов, не имеющий единого корня.

			return "<" + needRoot + ">" + doc + "</" + needRoot + ">";
		}


		private string takeDocument(XmlDocument d)
		{
			string s = null;

			try
			{
				s = d.SelectSingleNode("/" + needRoot + "[1]").InnerXml;
			}
			catch { }


			return s;
		}


		private static XmlDocument loadDocument(string doc)
		{
			XmlDocument d = new XmlDocument();

			try
			{
				d.LoadXml(new Xml().makeDocument(doc));
			}
			catch { }


			return d;
		}


		private static string getRoot()
		{
			return new Xml().needRoot;
		}


		private static string getString(SqlString var)
		{
			string s = null;

			try
			{
				s = var.Value;
			}
			catch { }


			return s;
		}


		private static string getString(SqlXml var)
		{
			string s = null;

			try
			{
				s = var.Value;
			}
			catch { }


			return s;
		}


		private static string toString(string s)
		{
			try
			{
				int i = 1 / s.Length;
			}
			catch
			{
				s = "";
			}


			return s;
		}


		private static XmlNode MakeNode(string nodeName, string nodeValue)
		{
			XmlDocument doc = new XmlDocument();
			XmlNodeType t;
			XmlNode node = null;

			try
			{
				//	Если имя элемента не начинается с @
				int i = 1 / nodeName.IndexOf('@');

				t = XmlNodeType.Element;
			}
			catch
			{
				t = XmlNodeType.Attribute;
				nodeName = nodeName.Replace("@", "");
			}

			try
			{
				node = doc.CreateNode(t, nodeName, null);
				node.InnerText = nodeValue;
			}
			catch { }


			return node;
		}


		private static void IncludeNode(XmlNode node, XmlDocument doc)
		{
			try
			{
				XmlNode n = doc.ImportNode(node, true);

				doc.DocumentElement.AppendChild(n);
			}
			catch { }
		}


		private static void IncludeNodeContext(XmlNode node, string nodeContext, XmlDocument doc)
		{
			try
			{
				//	Попробуем найти ветки, удовлетворяющие условиям
				XmlNodeList c = doc.SelectNodes("//" + nodeContext);

				foreach (XmlNode c_next in c)
				{
					XmlNode n = doc.ImportNode(node, true);

					switch (node.NodeType)
					{
						case XmlNodeType.Element: c_next.AppendChild(n); break;
						case XmlNodeType.Attribute: c_next.Attributes.Append(n as XmlAttribute); break;
					}
				}
			}
			catch { }

		}


		public static SqlString getValueSQL(SqlXml doc, SqlString node, SqlString index)
		{
			return getValue(getString(doc), getString(node), getString(index), 0);
		}


		public static SqlString getNodeSQL(SqlXml doc, SqlString node, SqlString index)
		{
			return getValue(getString(doc), getString(node), getString(index), 1);
		}


		public static SqlString getCountSQL(SqlXml doc, SqlString node, SqlString index)
		{
			return getValue(getString(doc), getString(node), getString(index), 2);
		}


		public static SqlString getNameSQL(SqlXml doc, SqlString node, SqlString index)
		{
			return getValue(getString(doc), getString(node), getString(index), 3);
		}


		[SqlFunction(TableDefinition = "name nvarchar(64), value nvarchar(max)", FillRowMethodName = "addRow")]
		public static IEnumerable getValuesSQL(SqlXml doc, SqlString node, SqlString index)
		{
			return getValues(getString(doc), getString(node), getString(index), 1);
		}


		private static void addRow(object o, out string name, out string value)
		{
			value = null;
			name = null;

			try
			{
				Dictionary<string, string> row = (Dictionary<string, string>)o;

				foreach (KeyValuePair<string, string> p in row)
				{
					value = p.Value;
					name = p.Key;
				}
			}
			catch { }
		}


		private static List<Dictionary<string, string>> getValues(string doc, string node, string index, int mode)
		{
			XmlDocument d = loadDocument(getString(doc));

			List<Dictionary<string, string>> values = new List<Dictionary<string, string>>();

			try
			{
				XmlNodeList nodes = getNodes(d, getPath(node, index, mode));

				foreach (XmlNode n in nodes)
				{
					Dictionary<string, string> row = new Dictionary<string, string>();
					row.Add(n.LocalName, n.InnerText);

					values.Add(row);
				}
			}
			catch { }


			return values;
		}


		private static string getValue(string doc, string node, string index, int mode)
		{
			XmlDocument d = loadDocument(doc);

			string s = null;

			try
			{
				XmlNodeList nodes = getNodes(d, getPath(node, index, mode));

				//	Что ищем?
				switch (mode)
				{
					//	Текстовое значение элемента
					case 0:
					//	Сам элемент
					case 1:
					{
						try
						{
							s = getNodeValues(nodes, mode);
						}
						catch { }
					}
					break;

					//	Количество элементов
					case 2:
					{
						try
						{
							s = nodes.Count.ToString();
						}
						catch { }
					}
					break;

					//	Имя элемента
					case 3:
					{
						try
						{
							s = d.SelectSingleNode(getPath(node, index, mode)).Name;
						}
						catch { }
					}
					break;

				}

			}
			catch { }


			//	Если найденное значение пустое, сделаем его = null
			try
			{
				int i = 1 / s.Trim().Length;
			}
			catch { s = null; }

			//	Отладка
			//	if (error.ToString().Length != 0) { s += error; }


			return s;
		}


		public static SqlString joinXmlSql(SqlXml doc, SqlXml vars, SqlXml not_change)
		{
			return joinXml(getString(doc), getString(vars), getString(not_change));
		}


		private static string joinXml(string doc, string vars, string not_change)
		{
			XmlDocument d = loadDocument(doc);
			XmlDocument v = loadDocument(vars);
			XmlDocument n = loadDocument(not_change);

			XmlDocument nodes = loadDocument(null);

			Dictionary<string, string> names = new Dictionary<string, string>();

			try
			{
				//	Сначала выберем все первые вхождения элементов из доп. списка
				foreach (XmlNode node in v.SelectNodes("/" + getRoot() + "//*[not(*)]"))
				{
					if (!names.ContainsKey(node.LocalName))
					{
						names.Add(node.LocalName, null);
						IncludeNode(node, nodes);
					}
				}

				//	Добавим все первые вхождения еще не встречавшихся элементов из основного документа
				foreach (XmlNode node in d.SelectNodes("/" + getRoot() + "//*[not(*)]"))
				{
					if (!names.ContainsKey(node.LocalName))
					{
						names.Add(node.LocalName, null);
						IncludeNode(node, nodes);
					}
				}

				//	Удалим все элементы, имена которых встречаются в списке not_change
				foreach (XmlNode node in n.SelectNodes("/" + getRoot() + "//*[not(*)]"))
				{
					try
					{
						XmlNode node_found = nodes.SelectSingleNode("//" + node.LocalName);
						nodes.DocumentElement.RemoveChild(node_found);
					}
					catch { }
				}

			}
			catch { }


			return new Xml().takeDocument(nodes);
		}


		public static SqlString getModifySql(SqlXml doc, SqlString node, SqlString index, SqlString value)
		{
			return getModify(getString(doc), getString(node), getString(index), getString(value));
		}


		private static string getModify(string doc, string node, string index, string value)
		{
			XmlDocument d = loadDocument(doc);

			string path = getPath(node, index, 1);

			try
			{
				//	Поищем нужный элемент(ы)
				XmlNodeList nodes = getNodes(d, path);

				switch (value == null)
				{

					//	Редактирование/Вставка
					case false:
					{
						try
						{
							int i = 0;

							//	Попробуем отредактировать элемент(ы)
							foreach (XmlNode n in nodes)
							{
								n.InnerText = value;
								i++;
							}

							//	Попробуем вызвать ошибку деления на ноль, что будет означать, что ни один элемент не был найден/обновлён
							i = 1 / i;
						}
						catch
						{
							//	Попробуем вставить новый элемент
							try
							{
								int s = node.LastIndexOf("/") + 1;
								int c = node.LastIndexOf("[") + 1;

								if (c > s) { node = node.Substring(0, c - 1); }

								try
								{
									//	Если путь сложный (присутствует знак /)
									int i = 1 / s;

									string nodeName = node.Substring(s, node.Length - s);
									string nodeContext = node.Substring(0, s - 1);

									IncludeNodeContext(MakeNode(nodeName, value), nodeContext, d);
								}

								//	Если путь простой
								catch
								{
									IncludeNode(MakeNode(node, value), d);
								}

							}
							catch { }
						}
					}
					break;

					//	Удаление	
					case true:
					{
						try
						{
							foreach (XmlNode n in nodes)
							{
								switch (n.NodeType)
								{
									case XmlNodeType.Element: n.ParentNode.RemoveChild(n); break;
									case XmlNodeType.Attribute:
									{
										XmlAttribute a = n as XmlAttribute;

										a.OwnerElement.RemoveAll();
									}
									break;
								}
							}
						}
						catch { }
					}
					break;

				}
			}
			catch { }

			//	Отладка
			//	IncludeNode(MakeNode("path", path), d); 


			return new Xml().takeDocument(d);
		}


		private static XmlNodeList getNodes(XmlDocument doc, string path)
		{
			XmlNodeList nodes = null;

			try
			{
				nodes = doc.SelectNodes(path);
			}
			catch { }


			return nodes;
		}


		private static string getNodeValues(XmlNodeList nodes, int mode)
		{
			string s = "";

			foreach (XmlNode n in nodes)
			{
				switch (mode)
				{
					case 0:
					{
						s += n.InnerText;
					}
					break;

					case 1:
					{
						s += n.OuterXml;
					}
					break;
				}
			}


			return s;
		}


		private static string getPath(string node, string index, int mode)
		{
			string path = null;

			//	Если имя искомого элемента на задано, ищем все элементы
			try
			{
				//	не null
				int i = node.ToString().Length;

				//	не пустое
				i = 1 / i;
			}
			catch { node = "*"; }

			//	Проверим index
			try
			{
				//	Число ли это?
				int i = Convert.ToInt32(index);

				try
				{
					//	Если число, попытаемся вызвать ошибку деления на ноль
					i = 1 / i;
				}
				//	Если index = 0, сделаем его пустым
				catch { index = ""; }
			}

			//	Если это не число, ничего страшного :)
			catch { }

			//	И, наконец, запустим финальную проверку
			finally
			{
				try
				{
					//	Если index не включает [], добавим их
					if (
						index.ToString().Substring(0, 1) != "[" &&
						index.ToString().Substring((index.ToString().Length - 1), 1) != "]"
						)
					{
						index = "[" + index.ToString() + "]";
					}
				}
				//	Значит, index = null. Делаем его просто пустым
				catch
				{
					index = "";
				}
			}

			//	Что ищем?
			switch (mode)
			{
				//	Текстовое значение элемента
				case 0:
				{
					//	Всегда берем только первый найденный элемент
					index += "[1]";
				}
				break;

			}

			//	Будем всегда искать от нашего страшного корня
			node = "/" + getRoot() + "//" + node.ToString();

			//	Сформируем xpath 
			try
			{
				int i = 1 / index.ToString().Length;

				//	Если index не пустой
				path = "(" + node.ToString() + ")" + index.ToString();
			}
			catch
			{
				//	Если index пустой
				path = node.ToString();
			}


			return path;
		}


		public static SqlString getTransformSQL(SqlXml doc, SqlString node, SqlString path)
		{
			return getTransform(getString(doc), getString(node), getString(path));
		}


		private static string getTransform(string doc, string node, string path)
		{
			XmlDocument d = loadDocument(doc);

			string s = "";

			try
			{
				path = new Config().getKey("Root") + "/templates/" + path + ".xslt";
			}
			catch { }

			try
			{
				string n = null;

				try
				{
					n = d.SelectSingleNode("//" + node).OuterXml;
				}
				catch
				{
					n = d.SelectSingleNode("/").OuterXml;
				}

				try
				{
					s = TransformFromString(new StringReader(n), path);
				}
				catch { }
			}
			catch { }


			return s;
		}


		private static string TransformFromString(StringReader s, string path)
		{
			XslCompiledTransform xslt = new XslCompiledTransform();
			XPathDocument xpath = new XPathDocument(s);
			XPathNavigator navigator = xpath.CreateNavigator();

			xslt.Load(path);

			XmlReader reader = navigator.ReadSubtree();

			//	StringEncoder writer = new StringEncoder(new StringBuilder(), Encoding.GetEncoding("Windows-1251"));

			StringWriter writer = new StringWriter();

			xslt.Transform(reader, null, writer);


			return writer.ToString();
		}






	}



}

26 июн 09, 17:50    [7349841]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
sheich
Member

Откуда:
Сообщений: 209
Ну так, что, никаких идей ни у кого нет? Что там такое может происходить в методах обработки Xml, что требует записи в журнал транзакций??? Они либо значение ветки возвращают, либо саму ветку (с варациями).

Причем у меня есть и другие функции, ссылающиеся на CLR-сборки, и их вызов не приводит к ошибке записи в журнал.
29 июн 09, 18:23    [7355882]     Ответить | Цитировать Сообщить модератору
 Re: CLR-функция и журнал транзакций  [new]
sp
Member

Откуда:
Сообщений: 3947
что-то активность в форуме резко упала - в отпуска спецы ушли (
6 июл 09, 01:14    [7378724]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить