MS SQL használata Powershellből Linuxon

Ez a cikk tisztán gyakorlati, és a szomorú történetemnek szenteltem.

Készülve a Zero Touch PROD RDS-re (MS SQL), amiről már ezerrel dicsértek, egy automatizálási prezentációt (POC - Proof Of Concept) készítettem PowerShell szkriptekből. A prezentáció után, amikor elültek a zúgó, hosszan tartó tapsok, amelyek ovációvá alakultak, azt mondták - mindez szép, de ideológiai okok miatt a Jenkins slave-eink mind Linux alatt futnak!

Lehet ez így? Egy ilyen lelkes, barátságos DBA-t Windows alól áttenni a PowerShell poklába Linuxra? Nem túl kemény ez?

MS SQL használata Powershellből Linuxon
Belenyúltam ebbe a furcsa technológiai kombinációba. Természetesen az összes 30+ szkriptem leállt. Meglepetésemre egy munkanap alatt sikerült mindent kijavítanom. Most írok a forró nyomokon. Tehát, milyen buktatókba ütközhet, ha PowerShell szkripteket Windowsról Linuxra visz?

sqlcmd vs Invoke-SqlCmd

Emlékeztessem a fő különbséget közöttük. A régi jó eszköz sqlcmd Linux alatt is működik, szinte azonos funkcionalitással. A végrehajtandó lekérdezést -Q-val, a bemeneti fájlt -i-val, a kimenetet pedig -o-val adjuk meg. Csak az fájlnevek - így van - kis- és nagybetű érzékenyek. Ha -i-t használ, a fájl végére írja be:

GO
EXIT

Ha a végén nem lesz EXIT, akkor a sqlcmd várni fog a bemenetre, míg ha az előtt van EXIT nem fogja GO, akkor az utolsó parancs nem fut végig. A kimeneti fájlba kerül minden output, select, üzenetek, print stb.

Az Invoke-SqlCmd DataSet, DataTables vagy DataRows formájában adja vissza az eredményt. Tehát, ha egy egyszerű select eredményét szeretné feldolgozni, akkor ezt megteheti a sqlcmd, a kimenetét elemezve, de valami bonyolultat kiszűrni szinte lehetetlen: ezt az Invoke-SqlCmd-ról van szó. De ennek a parancsnak is vannak saját trükkjei:Ha fájlt ad át neki a

  • -InputFile nem szükséges, sőt, szintaktikai hibát ad vissza., akkor EXIT -OutputFile
  • nem, a parancs egy objektum formájában adja vissza az eredményt. A szerver megadásához két szintaxis létezik:
  • -ServerInstance -Username -Password -Database és a -ConnectionString . Furcsa módon, az első esetben nem lehet más portot megadni, mint 1433.A szöveges output, mint például a PRINT, amely egyszerűen «elfogható»
  • , a sqlcmdproblémát jelent De ennek a parancsnak is vannak saját trükkjei: És a legfontosabb:
  • valószínűleg a Linuxban nincs ilyen cmdlet! És ez a fő probléma. Csak márciusban vált elérhetővé ez a cmdlet nem Windows platformok számára,

, és végre előre tudunk lépni! Változók behelyettesítéseA változók behelyettesítése.

A változók behelyettesítése.

Az sqlcmd-ben a -v paraméter segítségével érvényesíthetjük a változókat, például így:

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

SQL skriptben változókat használunk:

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

Nos, a *nix változó érvényesítések nem működnek. A paraméter -v figyelmen kívül marad. A De ennek a parancsnak is vannak saját trükkjei: figyelmen kívül marad -Változók. Bár a paraméter, amely az éppen aktuális változókat beállítja, figyelmen kívül marad, a változatok működnek — bármilyen Shell-ből származó változót használhat. Azonban megharagudtam a változókra, és úgy döntöttem, hogy egyáltalán nem függök tőlük, és durva, primitív módszert alkalmaztam, lévén a SQL scriptjeim rövidek:

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

Ahogy azt megértette, ez már a Unix verzióval készült teszt.

Fájlok betöltése

A Windows verzióban bármilyen műveletet auditáltak: végrehajtottuk az sqlcmd-t, kaptunk valamilyen hibát a kimeneti fájlban, és csatoltuk ezt a fájlt az audit táblázathoz. Szerencsére a SQL szerver ugyanazon a szerveren futott, mint a Jenkins, ezt körülbelül így csináltuk:

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

Így az egész BCP fájlt lenyeljük, és az audit tábla nvarchar(max) mezőjébe tesszük. Természetesen az egész rendszer szétesett, mert a SQL server helyett RDS-t kaptam, és a BULK INSERT egyáltalán nem működik UNC-n, mivel megpróbál kizárólagos zárat venni a fájlra, ami RDS-sel eleve kudarcra van ítélve. Így úgy döntöttem, hogy a rendszer designját megváltoztatom, és az auditot soronként tárolom:

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

És így írom be ezt a táblázatba:

function WriteAudit([string]$Filename, [string]$ConnStr, 
     [string]$Tabname, [string]$Jobname)
{
  # get $lastid of the last execution  -- kihagyva a cikk 위해
	
  #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() 
  }  

A tartalom lekérdezéséhez SELECT-et kell végezni ID alapján, az n (identity) sorrendben választva.

A következő cikkben részletesebben meg fogom vizsgálni, hogyan működik mindez a Jenkinsszel.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster