diff --git a/client/src/connection/itc/ItcLibraryAdapter.ts b/client/src/connection/itc/ItcLibraryAdapter.ts index f84a5b864..6c3f3171f 100644 --- a/client/src/connection/itc/ItcLibraryAdapter.ts +++ b/client/src/connection/itc/ItcLibraryAdapter.ts @@ -59,27 +59,19 @@ class ItcLibraryAdapter implements LibraryAdapter { count: -1, }; } - public async getLibraries(): Promise<{ items: LibraryItem[]; count: number; }> { - const sql = ` - %let OUTPUT; - proc sql; - select catx(',', libname, readonly) as libname_target into: OUTPUT separated by '~' - from sashelp.vlibnam order by libname asc; - quit; - %put &OUTPUT; %put ; + const code = ` + $runner.GetLibraries() `; - const libNames = processQueryRows( - await this.runCode(sql, "", ""), - ); - - const libraries = libNames.map((lineText): LibraryItem => { - const [libName, readOnlyValue] = lineText.split(","); + const output = await executeRawCode(code); + const rawLibraries = JSON.parse(output).libraries; + const libraries = rawLibraries.map((row: string[]) => { + const [libName, readOnlyValue] = row; return { type: "library", uid: libName, @@ -161,24 +153,14 @@ class ItcLibraryAdapter implements LibraryAdapter { items: LibraryItem[]; count: number; }> { - const sql = ` - %let OUTPUT; - proc sql; - select memname into: OUTPUT separated by '~' - from sashelp.vtable - where libname='${item.name!}' - order by memname asc; - quit; - %put &OUTPUT; %put ; - `; - - const tableNames = processQueryRows( - await this.runCode(sql, "", ""), - ); - const tables = tableNames.map((lineText): LibraryItem => { - const [table] = lineText.split(","); + const code = ` + $runner.GetTables("${item.name}") + `; + const output = await executeRawCode(code); + const rawTables = JSON.parse(output).tables; + const tables = rawTables.map((table: string): LibraryItem => { return { type: "table", uid: `${item.name!}.${table}`, diff --git a/client/src/connection/itc/script.ts b/client/src/connection/itc/script.ts index eca30fff2..103931470 100644 --- a/client/src/connection/itc/script.ts +++ b/client/src/connection/itc/script.ts @@ -243,14 +243,14 @@ class SASRunner{ Write-Host "${LineCodes.ResultsFetchedCode}" } - + [void]GetDatasetRecords([string]$tableName, [int]$start = 0, [int]$limit = 100) { $objRecordSet = New-Object -comobject ADODB.Recordset $objRecordSet.ActiveConnection = $this.dataConnection # This is needed to set the properties for sas formats. $objRecordSet.Properties.Item("SAS Formats").Value = "_ALL_" $objRecordSet.Open( - $tableName, + $tableName, [System.Reflection.Missing]::Value, # Use the active connection 2, # adOpenDynamic 1, # adLockReadOnly @@ -278,7 +278,7 @@ class SASRunner{ $objRecordSet.Close() $objRecordSet.Open( - "SELECT COUNT(1) FROM $tableName", + "SELECT COUNT(1) FROM $tableName", $this.dataConnection, 3, 1, 1 ) # adOpenStatic, adLockReadOnly, adCmdText $count = $objRecordSet.Fields.Item(0).Value @@ -295,8 +295,8 @@ class SASRunner{ $objRecordSet = New-Object -comobject ADODB.Recordset $objRecordSet.ActiveConnection = $this.dataConnection $query = @" - select name, type, format - from sashelp.vcolumn + select name, type, format + from sashelp.vcolumn where libname='$libname' and memname='$memname'; "@ $objRecordSet.Open( @@ -306,7 +306,7 @@ class SASRunner{ 1, # adLockReadOnly 1 # adCmdText ) - + $rows = $objRecordSet.GetRows() $objRecordSet.Close() @@ -533,5 +533,70 @@ class SASRunner{ Write-Host (@{success=$false; message=$Error[0].Exception.Message} | ConvertTo-Json) } } + + [void]GetLibraries() { + $objRecordSet = New-Object -comobject ADODB.Recordset + $objRecordSet.ActiveConnection = $this.dataConnection + $query = @" + select distinct libname, readonly + from sashelp.vlibnam + order by libname asc +"@ + $objRecordSet.Open( + $query, + [System.Reflection.Missing]::Value, # Use the active connection + 2, # adOpenDynamic + 1, # adLockReadOnly + 1 # adCmdText + ) + + $records = [System.Collections.Generic.List[object[]]]::new() + while (-not $objRecordSet.EOF) { + $row = @() + for ($i = 0; $i -lt $objRecordSet.Fields.Count; $i++) { + $row += $objRecordSet.Fields.Item($i).Value + } + $records.Add($row) + $objRecordSet.MoveNext() + } + $objRecordSet.Close() + + $result = New-Object psobject + $result | Add-Member -MemberType NoteProperty -Name "libraries" -Value $records + $result | Add-Member -MemberType NoteProperty -Name "count" -Value $records.Count + + Write-Host $(ConvertTo-Json -Depth 10 -InputObject $result -Compress) + } + + [void]GetTables([string]$libname) { + $objRecordSet = New-Object -comobject ADODB.Recordset + $objRecordSet.ActiveConnection = $this.dataConnection + $query = @" + select memname + from sashelp.vtable + where libname='$libname' + order by memname asc +"@ + $objRecordSet.Open( + $query, + [System.Reflection.Missing]::Value, # Use the active connection + 2, # adOpenDynamic + 1, # adLockReadOnly + 1 # adCmdText + ) + + $records = @() + while (-not $objRecordSet.EOF) { + $records += $objRecordSet.Fields.Item(0).Value + $objRecordSet.MoveNext() + } + $objRecordSet.Close() + + $result = New-Object psobject + $result | Add-Member -MemberType NoteProperty -Name "tables" -Value $records + $result | Add-Member -MemberType NoteProperty -Name "count" -Value $records.Count + + Write-Host $(ConvertTo-Json -Depth 10 -InputObject $result -Compress) + } } `; diff --git a/client/test/connection/itc/ItcLibraryAdapter.test.ts b/client/test/connection/itc/ItcLibraryAdapter.test.ts index a02ec6ec6..501dc9be3 100644 --- a/client/test/connection/itc/ItcLibraryAdapter.test.ts +++ b/client/test/connection/itc/ItcLibraryAdapter.test.ts @@ -11,16 +11,7 @@ import * as connection from "../../../src/connection"; import { MockSession } from "./Coderunner.test"; const mockOutput = () => ({ - LIBOUTPUT: ` - -test1,yes~test2,no - -`, "SELECT COUNT(1)": `1234`, - TABLEOUTPUT: ` - -test1~test2 -`, }); class DatasetMockSession extends MockSession { @@ -107,7 +98,6 @@ describe("ItcLibraryAdapter tests", () => { }); it("loads libraries", async () => { - const libraryAdapter = new ItcLibraryAdapter(); const expectedLibraries: LibraryItem[] = [ { uid: "test1", @@ -125,6 +115,17 @@ describe("ItcLibraryAdapter tests", () => { }, ]; + const mockOutput = JSON.stringify({ + libraries: [ + ["test1", "yes"], + ["test2", "no"], + ], + count: 2, + }); + + sessionStub.returns(new DatasetMockSession([mockOutput])); + + const libraryAdapter = new ItcLibraryAdapter(); const response = await libraryAdapter.getLibraries(); expect(response.items).to.eql(expectedLibraries); @@ -227,7 +228,14 @@ describe("ItcLibraryAdapter tests", () => { type: "library", readOnly: true, }; - const libraryAdapter = new ItcLibraryAdapter(); + + const mockOutput = JSON.stringify({ + tables: ["test1", "test2"], + count: 2, + }); + + sessionStub.returns(new DatasetMockSession([mockOutput])); + const expectedTables: LibraryItem[] = [ { library: "lib", @@ -247,6 +255,7 @@ describe("ItcLibraryAdapter tests", () => { }, ]; + const libraryAdapter = new ItcLibraryAdapter(); const response = await libraryAdapter.getTables(library); expect(response.items).to.eql(expectedTables);