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

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

يجهز الى منتج زيرو تاتش بالنسبة لـ RDS (MS SQL)، الذي كانت كل آذاننا تطن حوله، قدمت عرضًا تقديميًا (POC - إثبات المفهوم) للأتمتة: مجموعة من نصوص powershell. بعد العرض التقديمي، عندما هدأ التصفيق العاصف المطول، وتحول إلى تصفيق متواصل، قالوا لي - كل هذا جيد، ولكن لأسباب أيديولوجية فقط، يعمل جميع عبيد جينكينز لدينا على نظام Linux!

هل هذا ممكن؟ هل تأخذ مثل هذا المصباح الدافئ DBA من نظام التشغيل Windows وتلصقه في حرارة Powershell التي تعمل بنظام Linux؟ أليس هذا قاسيا؟

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

sqlcmd مقابل Invoc-SqlCmd

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

GO
EXIT

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

يُنتج Invoc-SqlCmd النتيجة على هيئة DataSet أو DataTables أو DataRows. لذلك، إذا قمت بمعالجة نتيجة تحديد بسيط، يمكنك استخدامها المساعدة Sqlcmd، بعد تحليل مخرجاته، يكاد يكون من المستحيل استخلاص شيء معقد: لهذا يوجد استدعاء-SqlCmd. لكن هذا الفريق لديه أيضًا نكاته الخاصة:

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

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

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

يحتوي sqlcmd على استبدال متغير باستخدام -v، على سبيل المثال مثل هذا:

# $conn содержит начало команды sqlcmd
$cmd = $conn + " -i D:appsSlaveJobsKillSpid.sql -o killspid.res 
  -v spid =`"" + $spid + "`" -v age =`"" + $age + "`""
Invoke-Expression $cmd

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

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

حتى هنا هو عليه. في * لا شىء البدائل المتغيرة لا تعمل. معامل -v تم تجاهله. ش استدعاء-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 يعمل على نفس الخادم الذي يعمل به Jenkins، وقد تم الأمر على النحو التالي:

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 = '' '',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 (الهوية).

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

المصدر: www.habr.com

إضافة تعليق