Laborante kun MS SQL de Powershell sur Linukso

Ĉi tiu artikolo estas pure praktika kaj estas dediĉita al mia malĝoja rakonto

Preparante por Zero Touch PROD por RDS (MS SQL), pri kiu ĉiuj niaj oreloj zumis, mi faris prezenton (POC - Proof Of Concept) de aŭtomatigo: aro da powershell-skriptoj. Post la prezentado, kiam la ŝtorma, longedaŭra aplaŭdo estingiĝis, transformiĝante en senĉesan aplaŭdon, oni diris al mi - ĉio ĉi estas bona, sed nur pro ideologiaj kialoj, ĉiuj niaj Jenkins-sklavoj laboras per Linukso!

Ĉu tio eblas? Prenu tian varman, lampan DBA de sub Vindozo kaj algluu ĝin en la varmecon de Powershell sub Linukso? Ĉu ĉi tio ne estas kruela?

Laborante kun MS SQL de Powershell sur Linukso
Mi devis mergi min en ĉi tiu stranga kombinaĵo de teknologioj. Kompreneble, ĉiuj miaj 30+ skriptoj ĉesis funkcii. Je mia surprizo, mi sukcesis ripari ĉion en unu labortago. Mi skribas en varma postkuro. Do, kiajn malfacilaĵojn vi povas renkonti dum transdono de Powershell-skriptoj de Vindozo al Linukso?

sqlcmd kontraŭ Invoke-SqlCmd

Lasu min memorigi al vi la ĉefan diferencon inter ili. Bona malnova utileco sqlcmd Ĝi ankaŭ funkcias sub Linukso, kun preskaŭ identa funkcieco. Ni pasas la demandon por ekzekuti -Q, la enigdosieron kiel -i, kaj la eligon kiel -o. Sed la dosiernomoj, kompreneble, estas igitaj majusklaj. Se vi uzas -i, tiam en la dosiero skribu ĉe la fino:

GO
EXIT

Se ne estas ELIRO ĉe la fino, tiam sqlcmd daŭrigos atendi enigon, kaj se antaŭe ELIREJO ne GO, tiam la lasta komando ne funkcios. La eligodosiero enhavas ĉiujn eligojn, elektojn, mesaĝojn, presitajn ktp.

Invoke-SqlCmd produktas la rezulton kiel DataSet, DataTables aŭ DataRows. Tial, se vi procesas la rezulton de simpla elekto, vi povas uzi sqlcmd, analizinte ĝian eliron, estas preskaŭ neeble derivi ion kompleksan: por tio ekzistas Alvoki-SqlCmd. Sed ĉi tiu teamo ankaŭ havas siajn proprajn ŝercojn:

  • Se vi translokigas dosieron al ŝi per -Eniga dosiero, tiam ELIREJO ne bezonata, krome, ĝi produktas sintaksan eraron
  • -EligoDosiero ne, la komando resendas al vi la rezulton kiel objekton
  • Estas du sintaksoj por specifi servilon: -Servilo-Instanco -Uzantnomo -Pasvorto -Datumbazo kaj tra -ConnectionString. Sufiĉe strange, en la unua kazo ne eblas specifi alian havenon ol 1433.
  • teksta eligo, tajpu PRINT, kiu estas simple "kaptita" sqlcmdpor Alvoki-SqlCmd estas problemo
  • Kaj plej grave: Plej verŝajne via Linukso ne havas ĉi tiun cmdlet!

Kaj ĉi tio estas la ĉefa problemo. Nur en marto ĉi tiu cmdlet iĝis havebla por ne-Vindozaj platformoj, kaj finfine ni povas antaŭeniri!

Variebla Anstataŭigo

sqlcmd havas varian anstataŭigon uzante -v, ekzemple jene:

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

En la SQL-skripto ni uzas anstataŭaĵojn:

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

Do jen ĝi. En *nix variaj anstataŭigoj ne funkcias... Parametro -v ignorita. U Alvoki-SqlCmd ignorita -Varibeloj. Kvankam la parametro, kiu specifas la variablojn mem, estas ignorita, la anstataŭoj mem funkcias—vi povas uzi ajnajn variablojn de Ŝelo. Tamen mi ofendiĝis pro la variabloj kaj decidis tute ne dependi de ili, kaj agis malĝentile kaj primitive, ĉar la SQL-skriptoj estas mallongaj:

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

Ĉi tio, kiel vi komprenas, estas testo jam el la Unikso-versio.

Alŝutante dosierojn

En la Vindoza versio, ajna operacio estis akompanita de revizio: ni kuris sqlcmd, ricevis ian misuzon en la eligodosiero, alfiksis ĉi tiun dosieron al la revizia plato. Feliĉe, SQL-servilo funkciis sur la sama servilo kiel Jenkins, ĝi estis farita io kiel ĉi tio:

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

Tiel, ni glutas la BCP-dosieron tute kaj ŝovas ĝin en la kampon nvarchar(max) de la revizia tabelo. Kompreneble, ĉi tiu tuta sistemo disfalis, ĉar anstataŭ SQL-servilo mi ricevis RDS, kaj BULK INSERT tute ne funkcias per UNC pro provo preni ekskluzivan ŝlosilon sur dosiero, kaj kun RDS ĉi tio estas ĝenerale kondamnita de la komenco mem. Do mi decidis ŝanĝi la sistemdezajnon, konservante la revizion linio post linio:

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

Kaj skribu en ĉi tiu tabelo tiel:

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

Por elekti enhavon, vi devas elekti per ID, elektante en ordo n (identeco).

En la sekva artikolo mi eniros pli detale pri kiel ĉi tio ĉio interagas kun Jenkins.

fonto: www.habr.com

Aldoni komenton