العمل مع MS SQL من Powershell على Linux

هذه المقالة عملية بحتة ومخصصة لقصتي الحزينة.


التحضير لـ Zero Touch PROD for RDS (MS SQL) ، والتي كانت جميع آذاننا صاخبة حولها ، قدمت عرضًا تقديميًا (POC - Proof Of Concept) عن الأتمتة: مجموعة من البرامج النصية بوويرشيل. بعد العرض التقديمي ، عندما تلاشى التصفيق العالي المطول ، وتحول إلى تصفيق مستمر ، أخبروني أن كل هذا أمر جيد ، ولكن فقط لأسباب أيديولوجية لدينا جميعًا عبيد جنكينز يعملون تحت لينكس!

هل هذا ممكن؟ لاتخاذ مثل هذا دافئ ، أنبوب ديسيبل من تحت ويندوز ووضعه في حرارة powerhell جدا تحت لينكس؟ أليس هذا قاسيا؟


كان علي أن أغوص في هذا المزيج الغريب من التكنولوجيا. بالطبع ، توقفت كل ما عندي من مخطوطات 30+ عن العمل لدهشتي ، في يوم عمل واحد تمكنت من إصلاح كل شيء. أنا أكتب في المطاردة الساخنة. لذلك ، ما هي المخاطر التي يمكن أن تصادفك عند نقل البرامج النصية powerhell من Windows إلى Linux؟

sqlcmd vs Invoke-SqlCmd


اسمحوا لي أن أذكرك بالفرق الرئيسي بينهما. تعمل الأداة sqlcmd القديمة الجيدة أيضًا على نظام Linux ، مع وظائف متطابقة تقريبًا. لتنفيذ الاستعلام ، نجتاز -Q ، وملف الإدخال كـ -i ، والإخراج -o. هنا فقط أسماء الملفات ، بالطبع ، حساسة لحالة الأحرف. إذا كنت تستخدم -i ، فاكتب في الملف في النهاية:

GO EXIT 

إذا لم يكن هناك EXIT في النهاية ، فسيذهب sqlcmd لانتظار المدخلات ، وإذا لم يكن هناك GO قبل الخروج ، فلن يعمل الأمر الأخير. كل الإخراج ، يختار ، الرسائل ، الطباعة ، وما إلى ذلك ، يحصل على ملف الإخراج.

إرجاع Invoke-SqlCmd النتيجة كـ DataSet أو DataTables أو DataRows. لذلك ، إذا تمكنت من معالجة نتيجة التحديد البسيط من خلال sqlcmd ، تحليل ناتجها ، فإن إخراج شيء معقد أمر شبه مستحيل: يوجد Invoke-SqlCmd لهذا الغرض. لكن هذا الفريق له نكاته الخاصة:

  • إذا قمت بتمرير الملف إليه عبر -InputFile ، فلن تكون هناك حاجة إلى EXIT ، علاوة على ذلك ، فإنه يعطي خطأ في بناء الجملة
  • -OutputFile لا ، الأمر يعود لك النتيجة ككائن
  • هناك نوعان من بناء الجملة لتحديد خادم: -ServerInstance -Username -Password -Database وعبر -ConnectionString . بشكل غريب ، في الحالة الأولى ، لا يمكنك تحديد منفذ آخر غير 1433.
  • إخراج النص ، من النوع PRINT ، الذي يتم "اكتشافه" بواسطة sqlcmd بطريقة أولية ، يمثل مشكلة بالنسبة Invoke-SqlCmd
  • والأهم من ذلك: على الأرجح هذا cmdlet ليس في لينكس الخاص بك!

وهذه هي المشكلة الرئيسية. فقط في شهر مارس ، أصبح هذا الأمر cmdlet متاحًا للأنظمة الأساسية بخلاف النوافذ ، وأخيراً يمكننا المضي قدمًا!

استبدال متغير


Sqlcmd لديه استبدال متغير مع -v ، مثل ذلك:

 # $conn    sqlcmd $cmd = $conn + " -i D:\apps\SlaveJobs\KillSpid.sql -o killspid.res -v spid =`"" + $spid + "`" -v age =`"" + $age + "`"" Invoke-Expression $cmd 

في البرنامج النصي SQL ، نستخدم البدائل:

 set @spid=$(spid) set @age=$(age) 

لذلك هنا. في * لا شىء ، لا تعمل البدائل المتغيرة . يتم تجاهل الخيار -v . Invoke-SqlCmd يتجاهل المتغيرات . على الرغم من أنه يتم تجاهل المعلمة التي تحدد المتغيرات نفسها ، فإن البدائل نفسها تعمل - يمكنك استخدام أي متغيرات من Shell. ومع ذلك ، فقد شعرت بالإهانة من المتغيرات وقررت عدم الاعتماد عليها على الإطلاق ، وتصرفت بشكل تقريبي وبدائي ، حيث أن النصوص البرمجية لـ sql قصيرة:

 # prepend the parameters "declare @age int, @spid int" | Add-Content "q.sql" "set @spid=" + $spid | Add-Content "q.sql" "set @age=" + $age | Add-Content "q.sql" foreach ($line in Get-Content "Sqlserver/Automation/KillSpid.sql") { $line | Add-Content "q.sql" } $cmd = "/opt/mssql-tools/bin/" + $conn + " -i q.sql -o res.log" 

هذا ، كما فهمت ، هو اختبار من إصدار يونكس.

تحميل الملفات


في إصدار Windows ، كانت أي عملية قمت بها مصحوبة بمراجعة: لقد أجرينا sqlcmd ، ونوعًا من الإساءة في ملف الإخراج ، قمنا بإرفاق هذا الملف بلوحة المراجعة. لحسن الحظ ، كان خادم SQL يعمل على نفس خادم جنكينز ، وقد تم القيام بشيء مثل هذا:

 CREATE procedure AuditUpload @id int, @filename varchar(256) as set nocount on declare @sql varchar(max) CREATE TABLE #multi (filer NVARCHAR(MAX)) set @sql='BULK INSERT #multi FROM '''+@filename +''' WITH (ROWTERMINATOR = ''\0'',CODEPAGE = ''ACP'')' exec (@sql) select @sql=filer from #multi update JenkinsAudit set multiliner=@sql where ID=@id return 

وبالتالي ، نبتلع ملف BCP بالكامل ، ونشحن جدول التدقيق في حقل nvarchar (max). بالطبع ، لقد انهار هذا النظام برمته ، لأنه بدلاً من خادم SQL ، حصلت على RDS ، ولا يعمل BULK INSERT على \\ UNC بسبب محاولة أخذ قفل خاص إلى ملف ، وبواسطة RDS فإن هذا محكوم عليه بشكل عام من البداية. لذلك قررت تغيير تصميم النظام ، وتخزين خط التدقيق بخط:

 CREATE TABLE AuditOut ( ID int NULL, TextLine nvarchar(max) NULL, n int IDENTITY(1,1) PRIMARY KEY ) 

والكتابة إلى هذا الجدول مثل هذا:

 function WriteAudit([string]$Filename, [string]$ConnStr, [string]$Tabname, [string]$Jobname) { # get $lastid of the last execution --    #create grid and populate it with data from file $audit = Get-Content $Filename $DT = new-object Data.DataTable $COL1 = new-object Data.DataColumn; $COL1.ColumnName = "ID"; $COL1.DataType = [System.Type]::GetType("System.Int32") $COL2 = new-object Data.DataColumn; $COL2.ColumnName = "TextLine"; $COL2.DataType = [System.Type]::GetType("System.String") $DT.Columns.Add($COL1) $DT.Columns.Add($COL2) foreach ($line in $audit) { $DR = $dt.NewRow() $DR.Item("ID") = $lastid $DR.Item("TextLine") = $line $DT.Rows.Add($DR) } # write it to table $conn=new-object System.Data.SqlClient.SQLConnection $conn.ConnectionString = $ConnStr $conn.Open() $bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $ConnStr $bulkCopy.DestinationTableName = $Tabname $bulkCopy.BatchSize = 50000 $bulkCopy.BulkCopyTimeout = 0 $bulkCopy.WriteToServer($DT) $conn.Close() } 

لتحديد المحتوى ، حدد حسب المعرف ، واختر n (هوية) بالترتيب.

في المقالة التالية سوف أتناول بمزيد من التفصيل كيف يتفاعل كل هذا مع جينكينز.

Source: https://habr.com/ru/post/ar447100/


All Articles