Синтаксический анализ SQL Server ScriptDom
Команда разработчиков, с которыми я работаю, использует проекты данных SQL для большой части работы, которую мы должны выполнить с существующей базой данных. Мы находимся в течение нескольких недель, и было несколько ошибок, но опыт в целом был хорошим.
Однако, когда мы начинаем развертывание в производство, команда dba отказалась принять DACPAC в качестве метода развертывания. Вместо этого они хотят видеть традиционный скрипт для каждого оператора DML или DDL.
В настоящее время предполагается создать разностный сценарий между готовым проектом SQL и производственной средой, а затем проанализировать его в отдельных сценариях. Не хорошо, я знаю.
Для разбора скрипта разницы, кажется, есть два варианта:
- Разбор скрипта на основе команды пакетного разделения, GO. Довольно простые решения, но многообещающие.
- Или используйте Microsoft.SqlServer.TransactSql.ScriptDom. Это выглядит более перспективным на будущее, но кажется гораздо более сложным.
Я сейчас пробую ScriptDom, но у меня проблемы с его пониманием. Мои текущие, но не только проблемы, заключаются в следующем.
Я пытаюсь проанализировать следующий SQL с помощью ScriptDOM в C#:
CREATE TABLE dbo.MyTable
(
MyColumn VARCHAR(255)
)
Но не вижу, как получить доступ к размеру VARCHAR, в данном случае 255.
Код, который я использую, выглядит следующим образом:
TSqlFragment sqlFragment = parser.Parse(textReader, out errors);
SQLVisitor myVisitor = new SQLVisitor();
sqlFragment.Accept(myVisitor);
public override void ExplicitVisit(CreateTableStatement node)
{
// node.SchemaObjectName.Identifiers to access the table name
// node.Definition.ColumnDefinitions to access the column attributes
}
Из каждого определения столбца я ожидал найти свойство длины или подобное. Однако у меня также есть подозрение, что вы можете использовать Шаблон посетителя, с которым я борюсь, для повторного анализа каждого определения столбца. Есть идеи?
3 ответа
Здорово, что вы используете ssdt!
Самый простой способ справиться с этим, когда у вас есть администраторы баз данных, которые не хотят работать с dacpacs, - это предварительно сгенерировать сценарий развертывания, используя sqlpackage.exe.
То, как я это делаю, это...
- Проверьте код t-sql в проект
- Сборка сервера сборок проекта ssdt
- Развертывание и запуск тестов на ci сервере
- используйте sqlpackage.exe /action:script, чтобы сравнить dacpac с QA, PROD и т. д. и сгенерировать сценарий развертывания.
Затем администраторы баз данных берут этот сценарий (или, когда мы будем готовы, мы сообщаем им номер сборки, чтобы получить) - они могут просмотреть и развернуть этот сценарий.
Что следует отметить:
- Вам понадобится доступ к prod db или к зеркальной копии, которую вы можете использовать, вам не нужны dbo или что-то еще, только разрешения ( https://the.agilesql.club/Blogs/Ed-Elliott/What-Permissions-Do-I-Need-To-Generate-A-Deploy-Script-With-SSDT)
- Сценарии действительны только до тех пор, пока схема в prod db не изменится - поэтому, если вы сгенерируете 4 сценария и запустите сценарий 1, остальные три будут недействительными, и вам потребуется перезапустить сборку для создания сценария.
Если у вас нет настройки CI, вы можете просто использовать sqlpackage.exe для генерации скрипта без автоматических битов:)
Надеюсь, поможет!
издание
Я не думаю, что вам нужен посетитель здесь вообще. Если я правильно понимаю вашу цель, вы хотели бы взять TSQL, сгенерированный SSDT, проанализировать его с помощью SQLDOM и затем распечатать пакеты по отдельности. Код для этого будет выглядеть примерно так:
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlServer.TransactSql.ScriptDom;
namespace ScriptDomDemo
{
class Program
{
static void Main(string[] args)
{
TSql120Parser parser = new TSql120Parser(false);
IList<ParseError> errors;
using (StringReader sr = new StringReader(@"create table t1 (c1 int primary key)
GO
create table t2 (c1 int primary key)"))
{
TSqlFragment fragment = parser.Parse(sr, out errors);
IEnumerable<string> batches = GetBatches(fragment);
foreach (var batch in batches)
{
Console.WriteLine(batch);
}
}
}
private static IEnumerable<string> GetBatches(TSqlFragment fragment)
{
Sql120ScriptGenerator sg = new Sql120ScriptGenerator();
TSqlScript script = fragment as TSqlScript;
if (script != null)
{
foreach (var batch in script.Batches)
{
yield return ScriptFragment(sg, batch);
}
}
else
{
// TSqlFragment is a TSqlBatch or a TSqlStatement
yield return ScriptFragment(sg, fragment);
}
}
private static string ScriptFragment(SqlScriptGenerator sg, TSqlFragment fragment)
{
string resultString;
sg.GenerateScript(fragment, out resultString);
return resultString;
}
}
}
Что касается того, как работать с этими AST, я считаю, что проще всего использовать отладчик Visual Studio для визуализации дерева, потому что вы можете видеть фактический тип каждого узла и все его свойства. Как видите, для анализа TSQL требуется всего лишь немного кода.
#reference Microsoft.SqlServer.BatchParser
#reference Microsoft.SqlServer.BatchParserClient
using System;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Management.Common;
namespace ScriptParser
{
class Program
{
static void Main(string[] args)
{
ExecuteBatch batcher = new ExecuteBatch();
string text = File.ReadAllText("ASqlFile.sql");
StringCollection statements = batcher.GetStatements(text);
foreach (string statement in statements)
{
Console.WriteLine(statement);
}
}
}
}