Delo z MS SQL iz Powershell v Linuxu

Ta članek je povsem praktičen in je posvečen moji žalostni zgodbi

Priprava na Zero Touch PROD za RDS (MS SQL), o katerem so nam brenčala vsa ušesa, sem naredil predstavitev (POC - Proof Of Concept) avtomatizacije: nabor powershell skript. Po predstavitvi, ko je burno, dolgotrajno ploskanje potihnilo in se spremenilo v neprekinjen aplavz, so mi rekli - vse to je dobro, vendar samo iz ideoloških razlogov, vsi naši Jenkinsovi sužnji delajo na Linuxu!

Je to možno? Vzeti tako topel, svetilni DBA izpod Windows in ga vtakniti v samo vročino powershell pod Linuxom? Ali ni to kruto?

Delo z MS SQL iz Powershell v Linuxu
Moral sem se potopiti v to čudno kombinacijo tehnologij. Seveda je vseh mojih 30+ skriptov prenehalo delovati. Na moje presenečenje mi je uspelo vse popraviti v enem delovnem dnevu. Pišem v zasledovanju. Torej, na katere pasti lahko naletite pri prenosu skriptov PowerShell iz sistema Windows v Linux?

sqlcmd proti Invoke-SqlCmd

Naj vas spomnim na glavno razliko med njima. Dobri stari pripomoček sqlcmd Deluje tudi pod Linuxom, s skoraj enako funkcionalnostjo. Poizvedbo posredujemo za izvedbo -Q, vhodno datoteko kot -i in izhod kot -o. Toda imena datotek seveda razlikujejo med velikimi in malimi črkami. Če uporabljate -i, potem v datoteko na koncu napišite:

GO
EXIT

Če na koncu ni EXIT, bo sqlcmd nadaljeval s čakanjem na vnos in če prej EXIT ne bo GO, potem zadnji ukaz ne bo deloval. Izhodna datoteka vsebuje vse izhodne podatke, izbire, sporočila, tiskanje itd.

Invoke-SqlCmd ustvari rezultat kot DataSet, DataTables ali DataRows. Če torej obdelate rezultat preproste izbire, lahko uporabite sqlcmd, po razčlenjevanju njegovega rezultata je skoraj nemogoče izpeljati nekaj kompleksnega: za to obstaja Invoke-SqlCmd. Toda ta ekipa ima tudi svoje šale:

  • Če ji prenesete datoteko prek -Vhodna datoteka, Potem EXIT ni potreben, poleg tega povzroči sintaktično napako
  • -Izhodna datoteka ne, ukaz vam vrne rezultat kot objekt
  • Obstajata dve sintaksi za podajanje strežnika: -Primer strežnika -Uporabniško ime -Geslo -Zbirka podatkov in skozi -ConnectionString. Nenavadno je, da v prvem primeru ni mogoče določiti vrat, ki niso 1433.
  • izpis besedila, tip PRINT, ki se preprosto “uje” sqlcmdza Invoke-SqlCmd je problem
  • In kar je najpomembnejše: Najverjetneje vaš Linux nima tega ukaza cmdlet!

In to je glavni problem. Samo marca ta cmdlet postal na voljo za platforme, ki niso Windows, in končno gremo lahko naprej!

Zamenjava spremenljivke

sqlcmd ima zamenjavo spremenljivke z -v, na primer takole:

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

V skriptu SQL uporabljamo zamenjave:

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

Torej, tukaj je. V *nix zamenjave spremenljivk ne delujejo... Parameter -v prezrti. U Invoke-SqlCmd prezrti -Spremenljivke. Čeprav je parameter, ki določa same spremenljivke, prezrt, same zamenjave delujejo – uporabite lahko katero koli spremenljivko iz Shell-a. Vendar sem bil užaljen zaradi spremenljivk in sem se odločil, da sploh ne bom odvisen od njih, in sem ravnal nesramno in primitivno, saj so skripti SQL kratki:

# 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"

Kot razumete, je to test že iz različice Unix.

Nalaganje datotek

V različici sistema Windows je vsako operacijo spremljala revizija: zagnali smo sqlcmd, prejeli nekakšno zlorabo v izhodni datoteki in to datoteko priložili revizijski plošči. Na srečo je strežnik SQL deloval na istem strežniku kot Jenkins, narejeno je bilo nekako takole:

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

Tako v celoti pogoltnemo datoteko BCP in jo potisnemo v polje nvarchar(max) revizijske tabele. Seveda je ves ta sistem razpadel, saj sem namesto SQL strežnika dobil RDS, BULK INSERT pa preko UNC sploh ne deluje zaradi poskusa ekskluzivnega zaklepanja datoteke, pri RDS pa je to praviloma obsojeno na propad. sam začetek. Zato sem se odločil spremeniti zasnovo sistema in shraniti revizijo vrstico za vrstico:

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

In v to tabelo zapišite takole:

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() 
  }  

Če želite izbrati vsebino, morate izbrati po ID-ju, pri čemer izberite v vrstnem redu n (identiteta).

V naslednjem članku bom podrobneje razložil, kako vse to vpliva na Jenkinsa.

Vir: www.habr.com

Dodaj komentar